diff options
Diffstat (limited to 'drivers/misc/mei/main.c')
| -rw-r--r-- | drivers/misc/mei/main.c | 300 |
1 files changed, 125 insertions, 175 deletions
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 903f809b21f..66f0a1a0645 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -13,9 +13,6 @@ * more details. * */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -40,7 +37,6 @@ #include <linux/mei.h> #include "mei_dev.h" -#include "hw-me.h" #include "client.h" /** @@ -60,48 +56,45 @@ static int mei_open(struct inode *inode, struct file *file) int err; - err = -ENODEV; if (!misc->parent) - goto out; + return -ENODEV; pdev = container_of(misc->parent, struct pci_dev, dev); dev = pci_get_drvdata(pdev); if (!dev) - goto out; + return -ENODEV; mutex_lock(&dev->device_lock); - err = -ENOMEM; - cl = mei_cl_allocate(dev); - if (!cl) - goto out_unlock; + + cl = NULL; err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); - goto out_unlock; - } - err = -EMFILE; - if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { - dev_err(&dev->pdev->dev, "open_handle_count exceded %d", - MEI_MAX_OPEN_HANDLE_COUNT); - goto out_unlock; + goto err_unlock; } + err = -ENOMEM; + cl = mei_cl_allocate(dev); + if (!cl) + goto err_unlock; + + /* open_handle_count check is handled in the mei_cl_link */ err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); if (err) - goto out_unlock; + goto err_unlock; file->private_data = cl; + mutex_unlock(&dev->device_lock); return nonseekable_open(inode, file); -out_unlock: +err_unlock: mutex_unlock(&dev->device_lock); kfree(cl); -out: return err; } @@ -132,22 +125,12 @@ static int mei_release(struct inode *inode, struct file *file) } if (cl->state == MEI_FILE_CONNECTED) { cl->state = MEI_FILE_DISCONNECTING; - dev_dbg(&dev->pdev->dev, - "disconnecting client host client = %d, " - "ME client = %d\n", - cl->host_client_id, - cl->me_client_id); + cl_dbg(dev, cl, "disconnecting\n"); rets = mei_cl_disconnect(cl); } mei_cl_flush_queues(cl); - dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n", - cl->host_client_id, - cl->me_client_id); + cl_dbg(dev, cl, "removing\n"); - if (dev->open_handle_count > 0) { - clear_bit(cl->host_client_id, dev->host_clients_map); - dev->open_handle_count--; - } mei_cl_unlink(cl); @@ -165,10 +148,7 @@ static int mei_release(struct inode *inode, struct file *file) file->private_data = NULL; - if (cb) { - mei_io_cb_free(cb); - cb = NULL; - } + mei_io_cb_free(cb); kfree(cl); out: @@ -194,7 +174,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, struct mei_cl_cb *cb_pos = NULL; struct mei_cl_cb *cb = NULL; struct mei_device *dev; - int i; int rets; int err; @@ -204,24 +183,16 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, dev = cl->dev; + mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { rets = -ENODEV; goto out; } - if ((cl->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) { - /* Do not allow to read watchdog client */ - i = mei_me_cl_by_uuid(dev, &mei_wd_guid); - if (i >= 0) { - struct mei_me_client *me_client = &dev->me_clients[i]; - if (cl->me_client_id == me_client->client_id) { - rets = -EBADF; - goto out; - } - } - } else { - cl->sm_state &= ~MEI_WD_STATE_INDEPENDENCE_MSG_SENT; + if (length == 0) { + rets = 0; + goto out; } if (cl == &dev->iamthif_cl) { @@ -229,22 +200,24 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (cl->read_cb && cl->read_cb->buf_idx > *offset) { - cb = cl->read_cb; - goto copy_buffer; - } else if (cl->read_cb && cl->read_cb->buf_idx > 0 && - cl->read_cb->buf_idx <= *offset) { + if (cl->read_cb) { cb = cl->read_cb; - rets = 0; - goto free; - } else if ((!cl->read_cb || !cl->read_cb->buf_idx) && *offset > 0) { - /*Offset needs to be cleaned for contiguous reads*/ + /* read what left */ + if (cb->buf_idx > *offset) + goto copy_buffer; + /* offset is beyond buf_idx we have no more data return 0 */ + if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { + rets = 0; + goto free; + } + /* Offset needs to be cleaned for contiguous reads*/ + if (cb->buf_idx == 0 && *offset > 0) + *offset = 0; + } else if (*offset > 0) { *offset = 0; - rets = 0; - goto out; } - err = mei_cl_read_start(cl); + err = mei_cl_read_start(cl, length); if (err && err != -EBUSY) { dev_dbg(&dev->pdev->dev, "mei start read failure with status = %d\n", err); @@ -262,19 +235,16 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - (MEI_READ_COMPLETE == cl->reading_state || - MEI_FILE_INITIALIZING == cl->state || - MEI_FILE_DISCONNECTED == cl->state || - MEI_FILE_DISCONNECTING == cl->state))) { + MEI_READ_COMPLETE == cl->reading_state || + mei_cl_is_transitioning(cl))) { + if (signal_pending(current)) return -EINTR; return -ERESTARTSYS; } mutex_lock(&dev->device_lock); - if (MEI_FILE_INITIALIZING == cl->state || - MEI_FILE_DISCONNECTED == cl->state || - MEI_FILE_DISCONNECTING == cl->state) { + if (mei_cl_is_transitioning(cl)) { rets = -EBUSY; goto out; } @@ -292,9 +262,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } /* now copy the data to user space */ copy_buffer: - dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n", - cb->response_buffer.size); - dev_dbg(&dev->pdev->dev, "cb->buf_idx - %lu\n", cb->buf_idx); + dev_dbg(&dev->pdev->dev, "buf.size = %d buf.idx= %ld\n", + cb->response_buffer.size, cb->buf_idx); if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { rets = -EMSGSIZE; goto free; @@ -305,6 +274,7 @@ copy_buffer: length = min_t(size_t, length, cb->buf_idx - *offset); if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { + dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); rets = -EFAULT; goto free; } @@ -342,11 +312,10 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, { struct mei_cl *cl = file->private_data; struct mei_cl_cb *write_cb = NULL; - struct mei_msg_hdr mei_hdr; struct mei_device *dev; unsigned long timeout = 0; int rets; - int i; + int id; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -357,24 +326,30 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (dev->dev_state != MEI_DEV_ENABLED) { rets = -ENODEV; - goto err; + goto out; } - i = mei_me_cl_by_id(dev, cl->me_client_id); - if (i < 0) { - rets = -ENODEV; - goto err; + id = mei_me_cl_by_id(dev, cl->me_client_id); + if (id < 0) { + rets = -ENOTTY; + goto out; } - if (length > dev->me_clients[i].props.max_msg_length || length <= 0) { - rets = -EMSGSIZE; - goto err; + + if (length == 0) { + rets = 0; + goto out; + } + + if (length > dev->me_clients[id].props.max_msg_length) { + rets = -EFBIG; + goto out; } if (cl->state != MEI_FILE_CONNECTED) { - rets = -ENODEV; dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", cl->host_client_id, cl->me_client_id); - goto err; + rets = -ENODEV; + goto out; } if (cl == &dev->iamthif_cl) { write_cb = mei_amthif_find_read_list_entry(dev, file); @@ -412,27 +387,18 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (!write_cb) { dev_err(&dev->pdev->dev, "write cb allocation failed\n"); rets = -ENOMEM; - goto err; + goto out; } rets = mei_io_cb_alloc_req_buf(write_cb, length); if (rets) - goto err; - - dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length); + goto out; rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); - if (rets) - goto err; - - cl->sm_state = 0; - if (length == 4 && - ((memcmp(mei_wd_state_independence_msg[0], - write_cb->request_buffer.data, 4) == 0) || - (memcmp(mei_wd_state_independence_msg[1], - write_cb->request_buffer.data, 4) == 0) || - (memcmp(mei_wd_state_independence_msg[2], - write_cb->request_buffer.data, 4) == 0))) - cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT; + if (rets) { + dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); + rets = -EFAULT; + goto out; + } if (cl == &dev->iamthif_cl) { rets = mei_amthif_write(dev, write_cb); @@ -440,65 +406,17 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (rets) { dev_err(&dev->pdev->dev, "amthif write failed with status = %d\n", rets); - goto err; + goto out; } mutex_unlock(&dev->device_lock); return length; } - write_cb->fop_type = MEI_FOP_WRITE; - - dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n", - cl->host_client_id, cl->me_client_id); - rets = mei_cl_flow_ctrl_creds(cl); - if (rets < 0) - goto err; - - if (rets == 0 || !dev->hbuf_is_ready) { - write_cb->buf_idx = 0; - mei_hdr.msg_complete = 0; - cl->writing_state = MEI_WRITING; - goto out; - } - - dev->hbuf_is_ready = false; - if (length > mei_hbuf_max_len(dev)) { - mei_hdr.length = mei_hbuf_max_len(dev); - mei_hdr.msg_complete = 0; - } else { - mei_hdr.length = length; - mei_hdr.msg_complete = 1; - } - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - - dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", - MEI_HDR_PRM(&mei_hdr)); - if (mei_write_message(dev, &mei_hdr, write_cb->request_buffer.data)) { - rets = -ENODEV; - goto err; - } - cl->writing_state = MEI_WRITING; - write_cb->buf_idx = mei_hdr.length; - + rets = mei_cl_write(cl, write_cb, false); out: - if (mei_hdr.msg_complete) { - if (mei_cl_flow_ctrl_reduce(cl)) { - rets = -ENODEV; - goto err; - } - list_add_tail(&write_cb->list, &dev->write_waiting_list.list); - } else { - list_add_tail(&write_cb->list, &dev->write_list.list); - } - - mutex_unlock(&dev->device_lock); - return length; - -err: mutex_unlock(&dev->device_lock); - mei_io_cb_free(write_cb); + if (rets < 0) + mei_io_cb_free(write_cb); return rets; } @@ -541,11 +459,15 @@ static int mei_ioctl_connect_client(struct file *file, /* find ME client we're trying to connect to */ i = mei_me_cl_by_uuid(dev, &data->in_client_uuid); - if (i >= 0 && !dev->me_clients[i].props.fixed_address) { - cl->me_client_id = dev->me_clients[i].client_id; - cl->state = MEI_FILE_CONNECTING; + if (i < 0 || dev->me_clients[i].props.fixed_address) { + dev_dbg(&dev->pdev->dev, "Cannot connect to FW Client UUID = %pUl\n", + &data->in_client_uuid); + rets = -ENOTTY; + goto end; } + cl->me_client_id = dev->me_clients[i].client_id; + dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", cl->me_client_id); dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n", @@ -562,11 +484,11 @@ static int mei_ioctl_connect_client(struct file *file, rets = -ENODEV; goto end; } - clear_bit(cl->host_client_id, dev->host_clients_map); mei_cl_unlink(cl); kfree(cl); cl = NULL; + dev->iamthif_open_count++; file->private_data = &dev->iamthif_cl; client = &data->out_client_properties; @@ -579,11 +501,6 @@ static int mei_ioctl_connect_client(struct file *file, goto end; } - if (cl->state != MEI_FILE_CONNECTING) { - rets = -ENODEV; - goto end; - } - /* prepare the output buffer */ client = &data->out_client_properties; @@ -595,7 +512,6 @@ static int mei_ioctl_connect_client(struct file *file, rets = mei_cl_connect(cl, file); end: - dev_dbg(&dev->pdev->dev, "free connect cb memory."); return rets; } @@ -701,26 +617,33 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) unsigned int mask = 0; if (WARN_ON(!cl || !cl->dev)) - return mask; + return POLLERR; dev = cl->dev; mutex_lock(&dev->device_lock); - if (dev->dev_state != MEI_DEV_ENABLED) - goto out; - - - if (cl == &dev->iamthif_cl) { - mask = mei_amthif_poll(dev, file, wait); + if (!mei_cl_is_connected(cl)) { + mask = POLLERR; goto out; } mutex_unlock(&dev->device_lock); + + + if (cl == &dev->iamthif_cl) + return mei_amthif_poll(dev, file, wait); + poll_wait(file, &cl->tx_wait, wait); + mutex_lock(&dev->device_lock); - if (MEI_WRITE_COMPLETE == cl->writing_state) - mask |= (POLLIN | POLLRDNORM); + + if (!mei_cl_is_connected(cl)) { + mask = POLLERR; + goto out; + } + + mask |= (POLLIN | POLLRDNORM); out: mutex_unlock(&dev->device_lock); @@ -753,17 +676,44 @@ static struct miscdevice mei_misc_device = { .minor = MISC_DYNAMIC_MINOR, }; -int mei_register(struct device *dev) + +int mei_register(struct mei_device *dev) { - mei_misc_device.parent = dev; - return misc_register(&mei_misc_device); + int ret; + mei_misc_device.parent = &dev->pdev->dev; + ret = misc_register(&mei_misc_device); + if (ret) + return ret; + + if (mei_dbgfs_register(dev, mei_misc_device.name)) + dev_err(&dev->pdev->dev, "cannot register debugfs\n"); + + return 0; } +EXPORT_SYMBOL_GPL(mei_register); -void mei_deregister(void) +void mei_deregister(struct mei_device *dev) { + mei_dbgfs_deregister(dev); misc_deregister(&mei_misc_device); mei_misc_device.parent = NULL; } +EXPORT_SYMBOL_GPL(mei_deregister); + +static int __init mei_init(void) +{ + return mei_cl_bus_init(); +} + +static void __exit mei_exit(void) +{ + mei_cl_bus_exit(); +} + +module_init(mei_init); +module_exit(mei_exit); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); MODULE_LICENSE("GPL v2"); |
