diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/genwqe/card_base.h | 1 | ||||
-rw-r--r-- | drivers/misc/genwqe/card_utils.c | 12 | ||||
-rw-r--r-- | drivers/misc/lkdtm_bugs.c | 3 | ||||
-rw-r--r-- | drivers/misc/lkdtm_perms.c | 7 | ||||
-rw-r--r-- | drivers/misc/mei/amthif.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/bus-fixup.c | 100 | ||||
-rw-r--r-- | drivers/misc/mei/bus.c | 217 | ||||
-rw-r--r-- | drivers/misc/mei/client.c | 23 | ||||
-rw-r--r-- | drivers/misc/mei/client.h | 2 | ||||
-rw-r--r-- | drivers/misc/mei/hw-me-regs.h | 2 | ||||
-rw-r--r-- | drivers/misc/mei/hw-me.c | 90 | ||||
-rw-r--r-- | drivers/misc/mei/hw-me.h | 2 | ||||
-rw-r--r-- | drivers/misc/mei/hw-txe.c | 18 | ||||
-rw-r--r-- | drivers/misc/mei/init.c | 6 | ||||
-rw-r--r-- | drivers/misc/mei/interrupt.c | 7 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 45 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 38 | ||||
-rw-r--r-- | drivers/misc/mei/pci-me.c | 1 |
18 files changed, 423 insertions, 153 deletions
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 fc2794b513fa..147b83011b58 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -740,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; } @@ -756,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/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index f336206d4b1f..91edd0b55e5c 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -85,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) 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 75b9d4ac8b1e..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,7 +272,7 @@ 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); + 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; @@ -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 60415a2bfcbd..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; @@ -1167,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)}, |