diff options
Diffstat (limited to 'drivers/misc')
28 files changed, 543 insertions, 191 deletions
diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index af23d7dfe752..2e5233b60971 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c @@ -247,7 +247,9 @@ int cxl_start_context(struct cxl_context *ctx, u64 wed, cxl_ctx_get(); if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) { + put_pid(ctx->glpid); put_pid(ctx->pid); + ctx->glpid = ctx->pid = NULL; cxl_adapter_context_put(ctx->afu->adapter); cxl_ctx_put(); goto out; diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 5e506c19108a..5d36dcc7f47e 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -117,13 +117,12 @@ int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master, static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct cxl_context *ctx = vma->vm_file->private_data; - unsigned long address = (unsigned long)vmf->virtual_address; u64 area, offset; offset = vmf->pgoff << PAGE_SHIFT; pr_devel("%s: pe: %i address: 0x%lx offset: 0x%llx\n", - __func__, ctx->pe, address, offset); + __func__, ctx->pe, vmf->address, offset); if (ctx->afu->current_mode == CXL_MODE_DEDICATED) { area = ctx->afu->psn_phys; @@ -155,7 +154,7 @@ static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; } - vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); + vm_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT); mutex_unlock(&ctx->status_mutex); diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index d0b421f49b39..77080cc5fa0a 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -194,6 +194,16 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, ctx->mmio_err_ff = !!(work.flags & CXL_START_WORK_ERR_FF); /* + * Increment the mapped context count for adapter. This also checks + * if adapter_context_lock is taken. + */ + rc = cxl_adapter_context_get(ctx->afu->adapter); + if (rc) { + afu_release_irqs(ctx, ctx); + goto out; + } + + /* * We grab the PID here and not in the file open to allow for the case * where a process (master, some daemon, etc) has opened the chardev on * behalf of another process, so the AFU's mm gets bound to the process @@ -205,15 +215,6 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, ctx->pid = get_task_pid(current, PIDTYPE_PID); ctx->glpid = get_task_pid(current->group_leader, PIDTYPE_PID); - /* - * Increment the mapped context count for adapter. This also checks - * if adapter_context_lock is taken. - */ - rc = cxl_adapter_context_get(ctx->afu->adapter); - if (rc) { - afu_release_irqs(ctx, ctx); - goto out; - } trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr); @@ -221,6 +222,9 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, amr))) { afu_release_irqs(ctx, ctx); cxl_adapter_context_put(ctx->afu->adapter); + put_pid(ctx->glpid); + put_pid(ctx->pid); + ctx->glpid = ctx->pid = NULL; goto out; } diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h index cb851c14ca4b..5813b5f25006 100644 --- a/drivers/misc/genwqe/card_base.h +++ b/drivers/misc/genwqe/card_base.h @@ -41,7 +41,6 @@ #include "genwqe_driver.h" #define GENWQE_MSI_IRQS 4 /* Just one supported, no MSIx */ -#define GENWQE_FLAG_MSI_ENABLED (1 << 0) #define GENWQE_MAX_VFS 15 /* maximum 15 VFs are possible */ #define GENWQE_MAX_FUNCS 16 /* 1 PF and 15 VFs */ diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 8a679ecc8fd1..147b83011b58 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -352,17 +352,27 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, if (copy_from_user(sgl->lpage, user_addr + user_size - sgl->lpage_size, sgl->lpage_size)) { rc = -EFAULT; - goto err_out1; + goto err_out2; } } return 0; + err_out2: + __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage, + sgl->lpage_dma_addr); + sgl->lpage = NULL; + sgl->lpage_dma_addr = 0; err_out1: __genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage, sgl->fpage_dma_addr); + sgl->fpage = NULL; + sgl->fpage_dma_addr = 0; err_out: __genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl, sgl->sgl_dma_addr); + sgl->sgl = NULL; + sgl->sgl_dma_addr = 0; + sgl->sgl_size = 0; return -ENOMEM; } @@ -730,13 +740,10 @@ int genwqe_read_softreset(struct genwqe_dev *cd) int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) { int rc; - struct pci_dev *pci_dev = cd->pci_dev; - rc = pci_enable_msi_range(pci_dev, 1, count); + rc = pci_alloc_irq_vectors(cd->pci_dev, 1, count, PCI_IRQ_MSI); if (rc < 0) return rc; - - cd->flags |= GENWQE_FLAG_MSI_ENABLED; return 0; } @@ -746,12 +753,7 @@ int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) */ void genwqe_reset_interrupt_capability(struct genwqe_dev *cd) { - struct pci_dev *pci_dev = cd->pci_dev; - - if (cd->flags & GENWQE_FLAG_MSI_ENABLED) { - pci_disable_msi(pci_dev); - cd->flags &= ~GENWQE_FLAG_MSI_ENABLED; - } + pci_free_irq_vectors(cd->pci_dev); } /** diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 6b3bf9ab051d..c5a456b0a564 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -170,7 +170,7 @@ static void ibmasm_remove_one(struct pci_dev *pdev) ibmasm_unregister_uart(sp); dbg("Sending OS down message\n"); if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN)) - err("failed to get repsonse to 'Send OS State' command\n"); + err("failed to get response to 'Send OS State' command\n"); dbg("Disabling heartbeats\n"); ibmasm_heartbeat_exit(sp); dbg("Disabling interrupts\n"); diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index fdf954c2107f..cfa1039c62e7 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h @@ -21,6 +21,8 @@ void lkdtm_SPINLOCKUP(void); void lkdtm_HUNG_TASK(void); void lkdtm_ATOMIC_UNDERFLOW(void); void lkdtm_ATOMIC_OVERFLOW(void); +void lkdtm_CORRUPT_LIST_ADD(void); +void lkdtm_CORRUPT_LIST_DEL(void); /* lkdtm_heap.c */ void lkdtm_OVERWRITE_ALLOCATION(void); diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index 182ae1894b32..91edd0b55e5c 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -5,8 +5,13 @@ * test source files. */ #include "lkdtm.h" +#include <linux/list.h> #include <linux/sched.h> +struct lkdtm_list { + struct list_head node; +}; + /* * Make sure our attempts to over run the kernel stack doesn't trigger * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we @@ -80,7 +85,8 @@ noinline void lkdtm_CORRUPT_STACK(void) /* Use default char array length that triggers stack protection. */ char data[8]; - memset((void *)data, 0, 64); + memset((void *)data, 'a', 64); + pr_info("Corrupted stack with '%16s'...\n", data); } void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) @@ -146,3 +152,66 @@ void lkdtm_ATOMIC_OVERFLOW(void) pr_info("attempting bad atomic overflow\n"); atomic_inc(&over); } + +void lkdtm_CORRUPT_LIST_ADD(void) +{ + /* + * Initially, an empty list via LIST_HEAD: + * test_head.next = &test_head + * test_head.prev = &test_head + */ + LIST_HEAD(test_head); + struct lkdtm_list good, bad; + void *target[2] = { }; + void *redirection = ⌖ + + pr_info("attempting good list addition\n"); + + /* + * Adding to the list performs these actions: + * test_head.next->prev = &good.node + * good.node.next = test_head.next + * good.node.prev = test_head + * test_head.next = good.node + */ + list_add(&good.node, &test_head); + + pr_info("attempting corrupted list addition\n"); + /* + * In simulating this "write what where" primitive, the "what" is + * the address of &bad.node, and the "where" is the address held + * by "redirection". + */ + test_head.next = redirection; + list_add(&bad.node, &test_head); + + if (target[0] == NULL && target[1] == NULL) + pr_err("Overwrite did not happen, but no BUG?!\n"); + else + pr_err("list_add() corruption not detected!\n"); +} + +void lkdtm_CORRUPT_LIST_DEL(void) +{ + LIST_HEAD(test_head); + struct lkdtm_list item; + void *target[2] = { }; + void *redirection = ⌖ + + list_add(&item.node, &test_head); + + pr_info("attempting good list removal\n"); + list_del(&item.node); + + pr_info("attempting corrupted list removal\n"); + list_add(&item.node, &test_head); + + /* As with the list_add() test above, this corrupts "next". */ + item.node.next = redirection; + list_del(&item.node); + + if (target[0] == NULL && target[1] == NULL) + pr_err("Overwrite did not happen, but no BUG?!\n"); + else + pr_err("list_del() corruption not detected!\n"); +} diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index f9154b8d67f6..7eeb71a75549 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -197,6 +197,8 @@ struct crashtype crashtypes[] = { CRASHTYPE(EXCEPTION), CRASHTYPE(LOOP), CRASHTYPE(OVERFLOW), + CRASHTYPE(CORRUPT_LIST_ADD), + CRASHTYPE(CORRUPT_LIST_DEL), CRASHTYPE(CORRUPT_STACK), CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(OVERWRITE_ALLOCATION), diff --git a/drivers/misc/lkdtm_perms.c b/drivers/misc/lkdtm_perms.c index 45f1c0f96612..c7635a79341f 100644 --- a/drivers/misc/lkdtm_perms.c +++ b/drivers/misc/lkdtm_perms.c @@ -60,15 +60,18 @@ static noinline void execute_location(void *dst, bool write) static void execute_user_location(void *dst) { + int copied; + /* Intentionally crossing kernel/user memory boundary. */ void (*func)(void) = dst; pr_info("attempting ok execution at %p\n", do_nothing); do_nothing(); - if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE)) + copied = access_process_vm(current, (unsigned long)dst, do_nothing, + EXEC_SIZE, FOLL_WRITE); + if (copied < EXEC_SIZE) return; - flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE); pr_info("attempting bad execution at %p\n", func); func(); } diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 7ae89b4a21d5..466afb2611c6 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -144,7 +144,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_state = MEI_IAMTHIF_WRITING; cl->fp = cb->fp; - ret = mei_cl_write(cl, cb, false); + ret = mei_cl_write(cl, cb); if (ret < 0) return ret; diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index e9e6ea3ab73c..18e05ca7584f 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -38,6 +38,9 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; #define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) +#define MEI_UUID_MKHIF_FIX UUID_LE(0x55213584, 0x9a29, 0x4916, \ + 0xba, 0xdf, 0xf, 0xb7, 0xed, 0x68, 0x2a, 0xeb) + #define MEI_UUID_ANY NULL_UUID_LE /** @@ -69,6 +72,97 @@ static void blacklist(struct mei_cl_device *cldev) cldev->do_match = 0; } +#define OSTYPE_LINUX 2 +struct mei_os_ver { + __le16 build; + __le16 reserved1; + u8 os_type; + u8 major; + u8 minor; + u8 reserved2; +} __packed; + +#define MKHI_FEATURE_PTT 0x10 + +struct mkhi_rule_id { + __le16 rule_type; + u8 feature_id; + u8 reserved; +} __packed; + +struct mkhi_fwcaps { + struct mkhi_rule_id id; + u8 len; + u8 data[0]; +} __packed; + +#define MKHI_FWCAPS_GROUP_ID 0x3 +#define MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD 6 +struct mkhi_msg_hdr { + u8 group_id; + u8 command; + u8 reserved; + u8 result; +} __packed; + +struct mkhi_msg { + struct mkhi_msg_hdr hdr; + u8 data[0]; +} __packed; + +static int mei_osver(struct mei_cl_device *cldev) +{ + int ret; + const size_t size = sizeof(struct mkhi_msg_hdr) + + sizeof(struct mkhi_fwcaps) + + sizeof(struct mei_os_ver); + size_t length = 8; + char buf[size]; + struct mkhi_msg *req; + struct mkhi_fwcaps *fwcaps; + struct mei_os_ver *os_ver; + unsigned int mode = MEI_CL_IO_TX_BLOCKING | MEI_CL_IO_TX_INTERNAL; + + memset(buf, 0, size); + + req = (struct mkhi_msg *)buf; + req->hdr.group_id = MKHI_FWCAPS_GROUP_ID; + req->hdr.command = MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD; + + fwcaps = (struct mkhi_fwcaps *)req->data; + + fwcaps->id.rule_type = 0x0; + fwcaps->id.feature_id = MKHI_FEATURE_PTT; + fwcaps->len = sizeof(*os_ver); + os_ver = (struct mei_os_ver *)fwcaps->data; + os_ver->os_type = OSTYPE_LINUX; + + ret = __mei_cl_send(cldev->cl, buf, size, mode); + if (ret < 0) + return ret; + + ret = __mei_cl_recv(cldev->cl, buf, length, 0); + if (ret < 0) + return ret; + + return 0; +} + +static void mei_mkhi_fix(struct mei_cl_device *cldev) +{ + int ret; + + ret = mei_cldev_enable(cldev); + if (ret) + return; + + ret = mei_osver(cldev); + if (ret) + dev_err(&cldev->dev, "OS version command failed %d\n", ret); + + mei_cldev_disable(cldev); +} + /** * mei_wd - wd client on the bus, change protocol version * as the API has changed. @@ -162,7 +256,8 @@ static int mei_nfc_if_version(struct mei_cl *cl, WARN_ON(mutex_is_locked(&bus->device_lock)); - ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), 1); + ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), + MEI_CL_IO_TX_BLOCKING); if (ret < 0) { dev_err(bus->dev, "Could not send IF version cmd\n"); return ret; @@ -177,8 +272,8 @@ static int mei_nfc_if_version(struct mei_cl *cl, return -ENOMEM; ret = 0; - bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); - if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { + bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length, 0); + if (bytes_recv < if_version_length) { dev_err(bus->dev, "Could not read IF version\n"); ret = -EIO; goto err; @@ -309,6 +404,7 @@ static struct mei_fixup { MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), MEI_FIXUP(MEI_UUID_WD, mei_wd), + MEI_FIXUP(MEI_UUID_MKHIF_FIX, mei_mkhi_fix), }; /** diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 8cac7ef9ad0d..0037153c80a6 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -36,12 +36,12 @@ * @cl: host client * @buf: buffer to send * @length: buffer length - * @blocking: wait for write completion + * @mode: sending mode * * Return: written size bytes or < 0 on error */ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, - bool blocking) + unsigned int mode) { struct mei_device *bus; struct mei_cl_cb *cb; @@ -80,9 +80,11 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, goto out; } + cb->internal = !!(mode & MEI_CL_IO_TX_INTERNAL); + cb->blocking = !!(mode & MEI_CL_IO_TX_BLOCKING); memcpy(cb->buf.data, buf, length); - rets = mei_cl_write(cl, cb, blocking); + rets = mei_cl_write(cl, cb); out: mutex_unlock(&bus->device_lock); @@ -96,15 +98,18 @@ out: * @cl: host client * @buf: buffer to receive * @length: buffer length + * @mode: io mode * * Return: read size in bytes of < 0 on error */ -ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) +ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, + unsigned int mode) { struct mei_device *bus; struct mei_cl_cb *cb; size_t r_length; ssize_t rets; + bool nonblock = !!(mode & MEI_CL_IO_RX_NONBLOCK); if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -125,6 +130,11 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (rets && rets != -EBUSY) goto out; + if (nonblock) { + rets = -EAGAIN; + goto out; + } + /* wait on event only if there is no other waiter */ /* synchronized under device mutex */ if (!waitqueue_active(&cl->rx_wait)) { @@ -185,14 +195,30 @@ ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; - if (cl == NULL) - return -ENODEV; - - return __mei_cl_send(cl, buf, length, 1); + return __mei_cl_send(cl, buf, length, MEI_CL_IO_TX_BLOCKING); } EXPORT_SYMBOL_GPL(mei_cldev_send); /** + * mei_cldev_recv_nonblock - non block client receive (read) + * + * @cldev: me client device + * @buf: buffer to receive + * @length: buffer length + * + * Return: read size in bytes of < 0 on error + * -EAGAIN if function will block. + */ +ssize_t mei_cldev_recv_nonblock(struct mei_cl_device *cldev, u8 *buf, + size_t length) +{ + struct mei_cl *cl = cldev->cl; + + return __mei_cl_recv(cl, buf, length, MEI_CL_IO_RX_NONBLOCK); +} +EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock); + +/** * mei_cldev_recv - client receive (read) * * @cldev: me client device @@ -205,39 +231,45 @@ ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; - if (cl == NULL) - return -ENODEV; - - return __mei_cl_recv(cl, buf, length); + return __mei_cl_recv(cl, buf, length, 0); } EXPORT_SYMBOL_GPL(mei_cldev_recv); /** - * mei_cl_bus_event_work - dispatch rx event for a bus device - * and schedule new work + * mei_cl_bus_rx_work - dispatch rx event for a bus device * * @work: work */ -static void mei_cl_bus_event_work(struct work_struct *work) +static void mei_cl_bus_rx_work(struct work_struct *work) { struct mei_cl_device *cldev; struct mei_device *bus; - cldev = container_of(work, struct mei_cl_device, event_work); + cldev = container_of(work, struct mei_cl_device, rx_work); bus = cldev->bus; - if (cldev->event_cb) - cldev->event_cb(cldev, cldev->events, cldev->event_context); + if (cldev->rx_cb) + cldev->rx_cb(cldev); + + mutex_lock(&bus->device_lock); + mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); + mutex_unlock(&bus->device_lock); +} + +/** + * mei_cl_bus_notif_work - dispatch FW notif event for a bus device + * + * @work: work + */ +static void mei_cl_bus_notif_work(struct work_struct *work) +{ + struct mei_cl_device *cldev; - cldev->events = 0; + cldev = container_of(work, struct mei_cl_device, notif_work); - /* Prepare for the next read */ - if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { - mutex_lock(&bus->device_lock); - mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); - mutex_unlock(&bus->device_lock); - } + if (cldev->notif_cb) + cldev->notif_cb(cldev); } /** @@ -252,18 +284,13 @@ bool mei_cl_bus_notify_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; - if (!cldev || !cldev->event_cb) - return false; - - if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF))) + if (!cldev || !cldev->notif_cb) return false; if (!cl->notify_ev) return false; - set_bit(MEI_CL_EVENT_NOTIF, &cldev->events); - - schedule_work(&cldev->event_work); + schedule_work(&cldev->notif_work); cl->notify_ev = false; @@ -271,7 +298,7 @@ bool mei_cl_bus_notify_event(struct mei_cl *cl) } /** - * mei_cl_bus_rx_event - schedule rx event + * mei_cl_bus_rx_event - schedule rx event * * @cl: host client * @@ -282,66 +309,81 @@ bool mei_cl_bus_rx_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; - if (!cldev || !cldev->event_cb) - return false; - - if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX))) + if (!cldev || !cldev->rx_cb) return false; - set_bit(MEI_CL_EVENT_RX, &cldev->events); - - schedule_work(&cldev->event_work); + schedule_work(&cldev->rx_work); return true; } /** - * mei_cldev_register_event_cb - register event callback + * mei_cldev_register_rx_cb - register Rx event callback * * @cldev: me client devices - * @event_cb: callback function - * @events_mask: requested events bitmask - * @context: driver context data + * @rx_cb: callback function * * Return: 0 on success * -EALREADY if an callback is already registered * <0 on other errors */ -int mei_cldev_register_event_cb(struct mei_cl_device *cldev, - unsigned long events_mask, - mei_cldev_event_cb_t event_cb, void *context) +int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb) { struct mei_device *bus = cldev->bus; int ret; - if (cldev->event_cb) + if (!rx_cb) + return -EINVAL; + if (cldev->rx_cb) return -EALREADY; - cldev->events = 0; - cldev->events_mask = events_mask; - cldev->event_cb = event_cb; - cldev->event_context = context; - INIT_WORK(&cldev->event_work, mei_cl_bus_event_work); + cldev->rx_cb = rx_cb; + INIT_WORK(&cldev->rx_work, mei_cl_bus_rx_work); - if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { - mutex_lock(&bus->device_lock); - ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); - mutex_unlock(&bus->device_lock); - if (ret && ret != -EBUSY) - return ret; - } + mutex_lock(&bus->device_lock); + ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); + mutex_unlock(&bus->device_lock); + if (ret && ret != -EBUSY) + return ret; - if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) { - mutex_lock(&bus->device_lock); - ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0); - mutex_unlock(&bus->device_lock); - if (ret) - return ret; - } + return 0; +} +EXPORT_SYMBOL_GPL(mei_cldev_register_rx_cb); + +/** + * mei_cldev_register_notif_cb - register FW notification event callback + * + * @cldev: me client devices + * @notif_cb: callback function + * + * Return: 0 on success + * -EALREADY if an callback is already registered + * <0 on other errors + */ +int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, + mei_cldev_cb_t notif_cb) +{ + struct mei_device *bus = cldev->bus; + int ret; + + if (!notif_cb) + return -EINVAL; + + if (cldev->notif_cb) + return -EALREADY; + + cldev->notif_cb = notif_cb; + INIT_WORK(&cldev->notif_work, mei_cl_bus_notif_work); + + mutex_lock(&bus->device_lock); + ret = mei_cl_notify_request(cldev->cl, NULL, 1); + mutex_unlock(&bus->device_lock); + if (ret) + return ret; return 0; } -EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb); +EXPORT_SYMBOL_GPL(mei_cldev_register_notif_cb); /** * mei_cldev_get_drvdata - driver data getter @@ -403,7 +445,7 @@ EXPORT_SYMBOL_GPL(mei_cldev_ver); */ bool mei_cldev_enabled(struct mei_cl_device *cldev) { - return cldev->cl && mei_cl_is_connected(cldev->cl); + return mei_cl_is_connected(cldev->cl); } EXPORT_SYMBOL_GPL(mei_cldev_enabled); @@ -423,14 +465,13 @@ int mei_cldev_enable(struct mei_cl_device *cldev) cl = cldev->cl; - if (!cl) { + if (cl->state == MEI_FILE_UNINITIALIZED) { mutex_lock(&bus->device_lock); - cl = mei_cl_alloc_linked(bus); + ret = mei_cl_link(cl); mutex_unlock(&bus->device_lock); - if (IS_ERR(cl)) - return PTR_ERR(cl); + if (ret) + return ret; /* update pointers */ - cldev->cl = cl; cl->cldev = cldev; } @@ -471,19 +512,17 @@ int mei_cldev_disable(struct mei_cl_device *cldev) struct mei_cl *cl; int err; - if (!cldev || !cldev->cl) + if (!cldev) return -ENODEV; cl = cldev->cl; bus = cldev->bus; - cldev->event_cb = NULL; - mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { - dev_err(bus->dev, "Already disconnected"); + dev_dbg(bus->dev, "Already disconnected"); err = 0; goto out; } @@ -497,9 +536,6 @@ out: mei_cl_flush_queues(cl, NULL); mei_cl_unlink(cl); - kfree(cl); - cldev->cl = NULL; - mutex_unlock(&bus->device_lock); return err; } @@ -629,9 +665,13 @@ static int mei_cl_device_remove(struct device *dev) if (!cldev || !dev->driver) return 0; - if (cldev->event_cb) { - cldev->event_cb = NULL; - cancel_work_sync(&cldev->event_work); + if (cldev->rx_cb) { + cancel_work_sync(&cldev->rx_work); + cldev->rx_cb = NULL; + } + if (cldev->notif_cb) { + cancel_work_sync(&cldev->notif_work); + cldev->notif_cb = NULL; } cldrv = to_mei_cl_driver(dev->driver); @@ -754,6 +794,7 @@ static void mei_cl_bus_dev_release(struct device *dev) mei_me_cl_put(cldev->me_cl); mei_dev_bus_put(cldev->bus); + kfree(cldev->cl); kfree(cldev); } @@ -786,17 +827,25 @@ static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, struct mei_me_client *me_cl) { struct mei_cl_device *cldev; + struct mei_cl *cl; cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); if (!cldev) return NULL; + cl = mei_cl_allocate(bus); + if (!cl) { + kfree(cldev); + return NULL; + } + device_initialize(&cldev->dev); cldev->dev.parent = bus->dev; cldev->dev.bus = &mei_cl_bus_type; cldev->dev.type = &mei_cl_device_type; cldev->bus = mei_dev_bus_get(bus); cldev->me_cl = mei_me_cl_get(me_cl); + cldev->cl = cl; mei_cl_bus_set_name(cldev); cldev->is_added = 0; INIT_LIST_HEAD(&cldev->bus_list); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 6fe02350578d..391936c1aa04 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -425,7 +425,7 @@ static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) * * @cl: host client * @length: size of the buffer - * @type: operation type + * @fop_type: operation type * @fp: associated file pointer (might be NULL) * * Return: cb on success and NULL on failure @@ -459,7 +459,7 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, * * @cl: host client * @length: size of the buffer - * @type: operation type + * @fop_type: operation type * @fp: associated file pointer (might be NULL) * * Return: cb on success and NULL on failure @@ -571,7 +571,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) INIT_LIST_HEAD(&cl->rd_pending); INIT_LIST_HEAD(&cl->link); cl->writing_state = MEI_IDLE; - cl->state = MEI_FILE_INITIALIZING; + cl->state = MEI_FILE_UNINITIALIZED; cl->dev = dev; } @@ -672,7 +672,12 @@ int mei_cl_unlink(struct mei_cl *cl) list_del_init(&cl->link); - cl->state = MEI_FILE_INITIALIZING; + cl->state = MEI_FILE_UNINITIALIZED; + cl->writing_state = MEI_IDLE; + + WARN_ON(!list_empty(&cl->rd_completed) || + !list_empty(&cl->rd_pending) || + !list_empty(&cl->link)); return 0; } @@ -686,7 +691,7 @@ void mei_host_client_init(struct mei_device *dev) pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); - pm_runtime_autosuspend(dev->dev); + pm_request_autosuspend(dev->dev); } /** @@ -756,7 +761,7 @@ void mei_cl_set_disconnected(struct mei_cl *cl) struct mei_device *dev = cl->dev; if (cl->state == MEI_FILE_DISCONNECTED || - cl->state == MEI_FILE_INITIALIZING) + cl->state <= MEI_FILE_INITIALIZING) return; cl->state = MEI_FILE_DISCONNECTED; @@ -1598,18 +1603,17 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, * * @cl: host client * @cb: write callback with filled data - * @blocking: block until completed * * Return: number of bytes sent on success, <0 on failure. */ -int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) { struct mei_device *dev; struct mei_msg_data *buf; struct mei_msg_hdr mei_hdr; int size; int rets; - + bool blocking; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -1621,6 +1625,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) buf = &cb->buf; size = buf->size; + blocking = cb->blocking; cl_dbg(dev, cl, "size=%d\n", size); diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index d2bfabecd882..f2545af9be7b 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -219,7 +219,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp); int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, struct mei_cl_cb *cmpl_list); -int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 7ad15d678878..c8307e8b4c16 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -122,6 +122,8 @@ #define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ #define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ +#define MEI_DEV_ID_LBG 0xA1BA /* Lewisburg (SPT) */ + #define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */ #define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */ diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 56c2101e80ad..a05375a3338a 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -246,6 +246,36 @@ static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev) return hw->pg_state; } +static inline u32 me_intr_src(u32 hcsr) +{ + return hcsr & H_CSR_IS_MASK; +} + +/** + * me_intr_disable - disables mei device interrupts + * using supplied hcsr register value. + * + * @dev: the device structure + * @hcsr: supplied hcsr register value + */ +static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) +{ + hcsr &= ~H_CSR_IE_MASK; + mei_hcsr_set(dev, hcsr); +} + +/** + * mei_me_intr_clear - clear and stop interrupts + * + * @dev: the device structure + * @hcsr: supplied hcsr register value + */ +static inline void me_intr_clear(struct mei_device *dev, u32 hcsr) +{ + if (me_intr_src(hcsr)) + mei_hcsr_write(dev, hcsr); +} + /** * mei_me_intr_clear - clear and stop interrupts * @@ -255,8 +285,7 @@ static void mei_me_intr_clear(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - if (hcsr & H_CSR_IS_MASK) - mei_hcsr_write(dev, hcsr); + me_intr_clear(dev, hcsr); } /** * mei_me_intr_enable - enables mei device interrupts @@ -280,8 +309,19 @@ static void mei_me_intr_disable(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - hcsr &= ~H_CSR_IE_MASK; - mei_hcsr_set(dev, hcsr); + me_intr_disable(dev, hcsr); +} + +/** + * mei_me_synchronize_irq - wait for pending IRQ handlers + * + * @dev: the device structure + */ +static void mei_me_synchronize_irq(struct mei_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + + synchronize_irq(pdev->irq); } /** @@ -450,7 +490,7 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) /** - * mei_me_write_message - writes a message to mei device. + * mei_me_hbuf_write - writes a message to host hw buffer. * * @dev: the device structure * @header: mei HECI header of message @@ -458,9 +498,9 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) * * Return: -EIO if write has failed */ -static int mei_me_write_message(struct mei_device *dev, - struct mei_msg_hdr *header, - unsigned char *buf) +static int mei_me_hbuf_write(struct mei_device *dev, + struct mei_msg_hdr *header, + const unsigned char *buf) { unsigned long rem; unsigned long length = header->length; @@ -956,13 +996,14 @@ static void mei_me_pg_legacy_intr(struct mei_device *dev) * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler * * @dev: the device structure + * @intr_source: interrupt source */ -static void mei_me_d0i3_intr(struct mei_device *dev) +static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source) { struct mei_me_hw *hw = to_me_hw(dev); if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && - (hw->intr_source & H_D0I3C_IS)) { + (intr_source & H_D0I3C_IS)) { dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; if (hw->pg_state == MEI_PG_ON) { hw->pg_state = MEI_PG_OFF; @@ -981,7 +1022,7 @@ static void mei_me_d0i3_intr(struct mei_device *dev) wake_up(&dev->wait_pg); } - if (hw->pg_state == MEI_PG_ON && (hw->intr_source & H_IS)) { + if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) { /* * HW sent some data and we are in D0i3, so * we got here because of HW initiated exit from D0i3. @@ -996,13 +1037,14 @@ static void mei_me_d0i3_intr(struct mei_device *dev) * mei_me_pg_intr - perform pg processing in interrupt thread handler * * @dev: the device structure + * @intr_source: interrupt source */ -static void mei_me_pg_intr(struct mei_device *dev) +static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source) { struct mei_me_hw *hw = to_me_hw(dev); if (hw->d0i3_supported) - mei_me_d0i3_intr(dev); + mei_me_d0i3_intr(dev, intr_source); else mei_me_pg_legacy_intr(dev); } @@ -1121,19 +1163,16 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *)dev_id; - struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr; hcsr = mei_hcsr_read(dev); - if (!(hcsr & H_CSR_IS_MASK)) + if (!me_intr_src(hcsr)) return IRQ_NONE; - hw->intr_source = hcsr & H_CSR_IS_MASK; - dev_dbg(dev->dev, "interrupt source 0x%08X.\n", hw->intr_source); - - /* clear H_IS and H_D0I3C_IS bits in H_CSR to clear the interrupts */ - mei_hcsr_write(dev, hcsr); + dev_dbg(dev->dev, "interrupt source 0x%08X\n", me_intr_src(hcsr)); + /* disable interrupts on device */ + me_intr_disable(dev, hcsr); return IRQ_WAKE_THREAD; } @@ -1152,11 +1191,16 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) struct mei_device *dev = (struct mei_device *) dev_id; struct mei_cl_cb complete_list; s32 slots; + u32 hcsr; int rets = 0; dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n"); /* initialize our complete list */ mutex_lock(&dev->device_lock); + + hcsr = mei_hcsr_read(dev); + me_intr_clear(dev, hcsr); + mei_io_list_init(&complete_list); /* check if ME wants a reset */ @@ -1166,7 +1210,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) goto end; } - mei_me_pg_intr(dev); + mei_me_pg_intr(dev, me_intr_src(hcsr)); /* check if we need to start the dev */ if (!mei_host_is_ready(dev)) { @@ -1216,6 +1260,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); + mei_me_intr_enable(dev); mutex_unlock(&dev->device_lock); return IRQ_HANDLED; } @@ -1238,12 +1283,13 @@ static const struct mei_hw_ops mei_me_hw_ops = { .intr_clear = mei_me_intr_clear, .intr_enable = mei_me_intr_enable, .intr_disable = mei_me_intr_disable, + .synchronize_irq = mei_me_synchronize_irq, .hbuf_free_slots = mei_me_hbuf_empty_slots, .hbuf_is_ready = mei_me_hbuf_is_empty, .hbuf_max_len = mei_me_hbuf_max_len, - .write = mei_me_write_message, + .write = mei_me_hbuf_write, .rdbuf_full_slots = mei_me_count_full_read_slots, .read_hdr = mei_me_mecbrw_read, diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 2ee14dc1b2ea..cf64847a35b9 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -51,14 +51,12 @@ struct mei_cfg { * * @cfg: per device generation config and ops * @mem_addr: io memory address - * @intr_source: interrupt source * @pg_state: power gating state * @d0i3_supported: di03 support */ struct mei_me_hw { const struct mei_cfg *cfg; void __iomem *mem_addr; - u32 intr_source; enum mei_pg_state pg_state; bool d0i3_supported; }; diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index e6e5e55a12ed..e9f8c0aeec13 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -19,7 +19,7 @@ #include <linux/ktime.h> #include <linux/delay.h> #include <linux/kthread.h> -#include <linux/irqreturn.h> +#include <linux/interrupt.h> #include <linux/pm_runtime.h> #include <linux/mei.h> @@ -441,6 +441,18 @@ static void mei_txe_intr_enable(struct mei_device *dev) } /** + * mei_txe_synchronize_irq - wait for pending IRQ handlers + * + * @dev: the device structure + */ +static void mei_txe_synchronize_irq(struct mei_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + + synchronize_irq(pdev->irq); +} + +/** * mei_txe_pending_interrupts - check if there are pending interrupts * only Aliveness, Input ready, and output doorbell are of relevance * @@ -691,7 +703,8 @@ static void mei_txe_hw_config(struct mei_device *dev) */ static int mei_txe_write(struct mei_device *dev, - struct mei_msg_hdr *header, unsigned char *buf) + struct mei_msg_hdr *header, + const unsigned char *buf) { struct mei_txe_hw *hw = to_txe_hw(dev); unsigned long rem; @@ -981,11 +994,13 @@ static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack) hisr = mei_txe_br_reg_read(hw, HISR_REG); aliveness = mei_txe_aliveness_get(dev); - if (hhisr & IPC_HHIER_SEC && aliveness) + if (hhisr & IPC_HHIER_SEC && aliveness) { ipc_isr = mei_txe_sec_reg_read_silent(hw, SEC_IPC_HOST_INT_STATUS_REG); - else + } else { ipc_isr = 0; + hhisr &= ~IPC_HHIER_SEC; + } generated = generated || (hisr & HISR_INT_STS_MSK) || @@ -1165,6 +1180,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = { .intr_clear = mei_txe_intr_clear, .intr_enable = mei_txe_intr_enable, .intr_disable = mei_txe_intr_disable, + .synchronize_irq = mei_txe_synchronize_irq, .hbuf_free_slots = mei_txe_hbuf_empty_slots, .hbuf_is_ready = mei_txe_is_input_ready, diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 9a9c2484d107..41e5760a6886 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -122,6 +122,10 @@ int mei_reset(struct mei_device *dev) mei_dev_state_str(state), fw_sts_str); } + mei_clear_interrupts(dev); + + mei_synchronize_irq(dev); + /* we're already in reset, cancel the init timer * if the reset was called due the hbm protocol error * we need to call it before hw start @@ -273,8 +277,6 @@ int mei_restart(struct mei_device *dev) mutex_lock(&dev->device_lock); - mei_clear_interrupts(dev); - dev->dev_state = MEI_DEV_POWER_UP; dev->reset_count = 0; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 5a4893ce9c24..b584749bcc4a 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -118,7 +118,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (!mei_cl_is_connected(cl)) { cl_dbg(dev, cl, "not connected\n"); - list_move_tail(&cb->list, &complete_list->list); cb->status = -ENODEV; goto discard; } @@ -128,8 +127,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (buf_sz < cb->buf_idx) { cl_err(dev, cl, "message is too big len %d idx %zu\n", mei_hdr->length, cb->buf_idx); - - list_move_tail(&cb->list, &complete_list->list); cb->status = -EMSGSIZE; goto discard; } @@ -137,8 +134,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (cb->buf.size < buf_sz) { cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", cb->buf.size, mei_hdr->length, cb->buf_idx); - - list_move_tail(&cb->list, &complete_list->list); cb->status = -EMSGSIZE; goto discard; } @@ -158,6 +153,8 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, return 0; discard: + if (cb) + list_move_tail(&cb->list, &complete_list->list); mei_irq_discard_msg(dev, mei_hdr); return 0; } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index fa50635512e8..e1bf54481fd6 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -322,7 +322,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - rets = mei_cl_write(cl, cb, false); + rets = mei_cl_write(cl, cb); out: mutex_unlock(&dev->device_lock); return rets; @@ -653,7 +653,7 @@ static int mei_fasync(int fd, struct file *file, int band) } /** - * fw_status_show - mei device attribute show method + * fw_status_show - mei device fw_status attribute show method * * @device: device pointer * @attr: attribute pointer @@ -684,8 +684,49 @@ static ssize_t fw_status_show(struct device *device, } static DEVICE_ATTR_RO(fw_status); +/** + * hbm_ver_show - display HBM protocol version negotiated with FW + * + * @device: device pointer + * @attr: attribute pointer + * @buf: char out buffer + * + * Return: number of the bytes printed into buf or error + */ +static ssize_t hbm_ver_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct mei_device *dev = dev_get_drvdata(device); + struct hbm_version ver; + + mutex_lock(&dev->device_lock); + ver = dev->version; + mutex_unlock(&dev->device_lock); + + return sprintf(buf, "%u.%u\n", ver.major_version, ver.minor_version); +} +static DEVICE_ATTR_RO(hbm_ver); + +/** + * hbm_ver_drv_show - display HBM protocol version advertised by driver + * + * @device: device pointer + * @attr: attribute pointer + * @buf: char out buffer + * + * Return: number of the bytes printed into buf or error + */ +static ssize_t hbm_ver_drv_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u.%u\n", HBM_MAJOR_VERSION, HBM_MINOR_VERSION); +} +static DEVICE_ATTR_RO(hbm_ver_drv); + static struct attribute *mei_attrs[] = { &dev_attr_fw_status.attr, + &dev_attr_hbm_ver.attr, + &dev_attr_hbm_ver_drv.attr, NULL }; ATTRIBUTE_GROUPS(mei); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 1169fd9e7d02..699693cd8c59 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -55,7 +55,8 @@ extern const uuid_le mei_amthif_guid; /* File state */ enum file_state { - MEI_FILE_INITIALIZING = 0, + MEI_FILE_UNINITIALIZED = 0, + MEI_FILE_INITIALIZING, MEI_FILE_CONNECTING, MEI_FILE_CONNECTED, MEI_FILE_DISCONNECTING, @@ -109,6 +110,21 @@ enum mei_cb_file_ops { MEI_FOP_NOTIFY_STOP, }; +/** + * enum mei_cl_io_mode - io mode between driver and fw + * + * @MEI_CL_IO_TX_BLOCKING: send is blocking + * @MEI_CL_IO_TX_INTERNAL: internal communication between driver and FW + * + * @MEI_CL_IO_RX_NONBLOCK: recv is non-blocking + */ +enum mei_cl_io_mode { + MEI_CL_IO_TX_BLOCKING = BIT(0), + MEI_CL_IO_TX_INTERNAL = BIT(1), + + MEI_CL_IO_RX_NONBLOCK = BIT(2), +}; + /* * Intel MEI message data struct */ @@ -169,6 +185,7 @@ struct mei_cl; * @fp: pointer to file structure * @status: io status of the cb * @internal: communication between driver and FW flag + * @blocking: transmission blocking mode * @completed: the transfer or reception has completed */ struct mei_cl_cb { @@ -180,6 +197,7 @@ struct mei_cl_cb { const struct file *fp; int status; u32 internal:1; + u32 blocking:1; u32 completed:1; }; @@ -253,6 +271,7 @@ struct mei_cl { * @intr_clear : clear pending interrupts * @intr_enable : enable interrupts * @intr_disable : disable interrupts + * @synchronize_irq : synchronize irqs * * @hbuf_free_slots : query for write buffer empty slots * @hbuf_is_ready : query if write buffer is empty @@ -274,7 +293,6 @@ struct mei_hw_ops { int (*hw_start)(struct mei_device *dev); void (*hw_config)(struct mei_device *dev); - int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts); enum mei_pg_state (*pg_state)(struct mei_device *dev); bool (*pg_in_transition)(struct mei_device *dev); @@ -283,14 +301,14 @@ struct mei_hw_ops { void (*intr_clear)(struct mei_device *dev); void (*intr_enable)(struct mei_device *dev); void (*intr_disable)(struct mei_device *dev); + void (*synchronize_irq)(struct mei_device *dev); int (*hbuf_free_slots)(struct mei_device *dev); bool (*hbuf_is_ready)(struct mei_device *dev); size_t (*hbuf_max_len)(const struct mei_device *dev); - int (*write)(struct mei_device *dev, struct mei_msg_hdr *hdr, - unsigned char *buf); + const unsigned char *buf); int (*rdbuf_full_slots)(struct mei_device *dev); @@ -304,8 +322,9 @@ void mei_cl_bus_rescan(struct mei_device *bus); void mei_cl_bus_rescan_work(struct work_struct *work); void mei_cl_bus_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, - bool blocking); -ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); + unsigned int mode); +ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, + unsigned int mode); bool mei_cl_bus_rx_event(struct mei_cl *cl); bool mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); @@ -627,6 +646,11 @@ static inline void mei_disable_interrupts(struct mei_device *dev) dev->ops->intr_disable(dev); } +static inline void mei_synchronize_irq(struct mei_device *dev) +{ + dev->ops->synchronize_irq(dev); +} + static inline bool mei_host_is_ready(struct mei_device *dev) { return dev->ops->host_is_ready(dev); @@ -652,7 +676,7 @@ static inline size_t mei_hbuf_max_len(const struct mei_device *dev) } static inline int mei_write_message(struct mei_device *dev, - struct mei_msg_hdr *hdr, void *buf) + struct mei_msg_hdr *hdr, const void *buf) { return dev->ops->write(dev, hdr, buf); } diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index f3ffd883b232..f9c6ec4b98ab 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -87,6 +87,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)}, diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c index 1525870f460a..af2e077da4b8 100644 --- a/drivers/misc/sgi-gru/grumain.c +++ b/drivers/misc/sgi-gru/grumain.c @@ -283,7 +283,7 @@ static void gru_unload_mm_tracker(struct gru_state *gru, spin_lock(&gru->gs_asid_lock); BUG_ON((asids->mt_ctxbitmap & ctxbitmap) != ctxbitmap); asids->mt_ctxbitmap ^= ctxbitmap; - gru_dbg(grudev, "gid %d, gts %p, gms %p, ctxnum 0x%d, asidmap 0x%lx\n", + gru_dbg(grudev, "gid %d, gts %p, gms %p, ctxnum %d, asidmap 0x%lx\n", gru->gs_gid, gts, gms, gts->ts_ctxnum, gms->ms_asidmap[0]); spin_unlock(&gru->gs_asid_lock); spin_unlock(&gms->ms_asid_lock); @@ -932,7 +932,7 @@ int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf) unsigned long paddr, vaddr; unsigned long expires; - vaddr = (unsigned long)vmf->virtual_address; + vaddr = vmf->address; gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n", vma, vaddr, GSEG_BASE(vaddr)); STAT(nopfn); diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 557f9782c53c..0c26eaf5f62b 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -118,6 +118,8 @@ static DEFINE_SPINLOCK(xpnet_broadcast_lock); * now, the default is 64KB. */ #define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES) +/* 68 comes from min TCP+IP+MAC header */ +#define XPNET_MIN_MTU 68 /* 32KB has been determined to be the ideal */ #define XPNET_DEF_MTU (0x8000UL) @@ -330,22 +332,6 @@ xpnet_dev_stop(struct net_device *dev) return 0; } -static int -xpnet_dev_change_mtu(struct net_device *dev, int new_mtu) -{ - /* 68 comes from min TCP+IP+MAC header */ - if ((new_mtu < 68) || (new_mtu > XPNET_MAX_MTU)) { - dev_err(xpnet, "ifconfig %s mtu %d failed; value must be " - "between 68 and %ld\n", dev->name, new_mtu, - XPNET_MAX_MTU); - return -EINVAL; - } - - dev->mtu = new_mtu; - dev_dbg(xpnet, "ifconfig %s mtu set to %d\n", dev->name, new_mtu); - return 0; -} - /* * Notification that the other end has received the message and * DMA'd the skb information. At this point, they are done with @@ -519,7 +505,6 @@ static const struct net_device_ops xpnet_netdev_ops = { .ndo_open = xpnet_dev_open, .ndo_stop = xpnet_dev_stop, .ndo_start_xmit = xpnet_dev_hard_start_xmit, - .ndo_change_mtu = xpnet_dev_change_mtu, .ndo_tx_timeout = xpnet_dev_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, @@ -555,6 +540,8 @@ xpnet_init(void) xpnet_device->netdev_ops = &xpnet_netdev_ops; xpnet_device->mtu = XPNET_DEF_MTU; + xpnet_device->min_mtu = XPNET_MIN_MTU; + xpnet_device->max_mtu = XPNET_MAX_MTU; /* * Multicast assumes the LSB of the first octet is set for multicast diff --git a/drivers/misc/vmw_vmci/vmci_doorbell.c b/drivers/misc/vmw_vmci/vmci_doorbell.c index a8cee33ae8d2..b3fa738ae005 100644 --- a/drivers/misc/vmw_vmci/vmci_doorbell.c +++ b/drivers/misc/vmw_vmci/vmci_doorbell.c @@ -431,6 +431,12 @@ int vmci_doorbell_create(struct vmci_handle *handle, if (vmci_handle_is_invalid(*handle)) { u32 context_id = vmci_get_context_id(); + if (context_id == VMCI_INVALID_ID) { + pr_warn("Failed to get context ID\n"); + result = VMCI_ERROR_NO_RESOURCES; + goto free_mem; + } + /* Let resource code allocate a free ID for us */ new_handle = vmci_make_handle(context_id, VMCI_INVALID_ID); } else { @@ -525,7 +531,7 @@ int vmci_doorbell_destroy(struct vmci_handle handle) entry = container_of(resource, struct dbell_entry, resource); - if (vmci_guest_code_active()) { + if (!hlist_unhashed(&entry->node)) { int result; dbell_index_table_remove(entry); diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c index 896be150e28f..d7eaf1eb11e7 100644 --- a/drivers/misc/vmw_vmci/vmci_driver.c +++ b/drivers/misc/vmw_vmci/vmci_driver.c @@ -113,5 +113,5 @@ module_exit(vmci_drv_exit); MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface."); -MODULE_VERSION("1.1.4.0-k"); +MODULE_VERSION("1.1.5.0-k"); MODULE_LICENSE("GPL v2"); |