diff options
Diffstat (limited to 'drivers/virt/fsl_hypervisor.c')
| -rw-r--r-- | drivers/virt/fsl_hypervisor.c | 88 |
1 files changed, 42 insertions, 46 deletions
diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c index d294f67d6f84..e92e2ceb12a4 100644 --- a/drivers/virt/fsl_hypervisor.c +++ b/drivers/virt/fsl_hypervisor.c @@ -34,6 +34,7 @@ #include <linux/slab.h> #include <linux/poll.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/reboot.h> #include <linux/uaccess.h> #include <linux/notifier.h> @@ -156,7 +157,7 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p) unsigned int i; long ret = 0; - int num_pinned; /* return value from get_user_pages() */ + int num_pinned = 0; /* return value from get_user_pages_fast() */ phys_addr_t remote_paddr; /* The next address in the remote buffer */ uint32_t count; /* The number of bytes left to copy */ @@ -173,7 +174,7 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p) return -EINVAL; /* - * The array of pages returned by get_user_pages() covers only + * The array of pages returned by get_user_pages_fast() covers only * page-aligned memory. Since the user buffer is probably not * page-aligned, we need to handle the discrepancy. * @@ -214,15 +215,18 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p) * hypervisor. */ lb_offset = param.local_vaddr & (PAGE_SIZE - 1); + if (param.count == 0 || + param.count > U64_MAX - lb_offset - PAGE_SIZE + 1) + return -EINVAL; num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT; /* Allocate the buffers we need */ /* * 'pages' is an array of struct page pointers that's initialized by - * get_user_pages(). + * get_user_pages_fast(). */ - pages = kzalloc(num_pages * sizeof(struct page *), GFP_KERNEL); + pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); if (!pages) { pr_debug("fsl-hv: could not allocate page list\n"); return -ENOMEM; @@ -237,20 +241,15 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p) if (!sg_list_unaligned) { pr_debug("fsl-hv: could not allocate S/G list\n"); ret = -ENOMEM; - goto exit; + goto free_pages; } sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list)); /* Get the physical addresses of the source buffer */ - down_read(¤t->mm->mmap_sem); - num_pinned = get_user_pages(current, current->mm, - param.local_vaddr - lb_offset, num_pages, - (param.source == -1) ? READ : WRITE, - 0, pages, NULL); - up_read(¤t->mm->mmap_sem); + num_pinned = get_user_pages_fast(param.local_vaddr - lb_offset, + num_pages, param.source != -1 ? FOLL_WRITE : 0, pages); if (num_pinned != num_pages) { - /* get_user_pages() failed */ pr_debug("fsl-hv: could not lock source buffer\n"); ret = (num_pinned < 0) ? num_pinned : -EFAULT; goto exit; @@ -292,13 +291,13 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p) virt_to_phys(sg_list), num_pages); exit: - if (pages) { - for (i = 0; i < num_pages; i++) - if (pages[i]) - put_page(pages[i]); + if (pages && (num_pinned > 0)) { + for (i = 0; i < num_pinned; i++) + put_page(pages[i]); } kfree(sg_list_unaligned); +free_pages: kfree(pages); if (!ret) @@ -334,8 +333,8 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) struct fsl_hv_ioctl_prop param; char __user *upath, *upropname; void __user *upropval; - char *path = NULL, *propname = NULL; - void *propval = NULL; + char *path, *propname; + void *propval; int ret = 0; /* Get the parameters from the user. */ @@ -347,32 +346,30 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) upropval = (void __user *)(uintptr_t)param.propval; path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN); - if (IS_ERR(path)) { - ret = PTR_ERR(path); - goto out; - } + if (IS_ERR(path)) + return PTR_ERR(path); propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN); if (IS_ERR(propname)) { ret = PTR_ERR(propname); - goto out; + goto err_free_path; } if (param.proplen > FH_DTPROP_MAX_PROPLEN) { ret = -EINVAL; - goto out; + goto err_free_propname; } propval = kmalloc(param.proplen, GFP_KERNEL); if (!propval) { ret = -ENOMEM; - goto out; + goto err_free_propname; } if (set) { if (copy_from_user(propval, upropval, param.proplen)) { ret = -EFAULT; - goto out; + goto err_free_propval; } param.ret = fh_partition_set_dtprop(param.handle, @@ -391,7 +388,7 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) if (copy_to_user(upropval, propval, param.proplen) || put_user(param.proplen, &p->proplen)) { ret = -EFAULT; - goto out; + goto err_free_propval; } } } @@ -399,10 +396,12 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) if (put_user(param.ret, &p->ret)) ret = -EFAULT; -out: - kfree(path); +err_free_propval: kfree(propval); +err_free_propname: kfree(propname); +err_free_path: + kfree(path); return ret; } @@ -568,16 +567,16 @@ static irqreturn_t fsl_hv_state_change_isr(int irq, void *data) /* * Returns a bitmask indicating whether a read will block */ -static unsigned int fsl_hv_poll(struct file *filp, struct poll_table_struct *p) +static __poll_t fsl_hv_poll(struct file *filp, struct poll_table_struct *p) { struct doorbell_queue *dbq = filp->private_data; unsigned long flags; - unsigned int mask; + __poll_t mask; spin_lock_irqsave(&dbq->lock, flags); poll_wait(filp, &dbq->wait, p); - mask = (dbq->head == dbq->tail) ? 0 : (POLLIN | POLLRDNORM); + mask = (dbq->head == dbq->tail) ? 0 : (EPOLLIN | EPOLLRDNORM); spin_unlock_irqrestore(&dbq->lock, flags); @@ -660,7 +659,6 @@ static int fsl_hv_open(struct inode *inode, struct file *filp) { struct doorbell_queue *dbq; unsigned long flags; - int ret = 0; dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL); if (!dbq) { @@ -677,7 +675,7 @@ static int fsl_hv_open(struct inode *inode, struct file *filp) filp->private_data = dbq; - return ret; + return 0; } /* @@ -688,15 +686,13 @@ static int fsl_hv_close(struct inode *inode, struct file *filp) struct doorbell_queue *dbq = filp->private_data; unsigned long flags; - int ret = 0; - spin_lock_irqsave(&db_list_lock, flags); list_del(&dbq->list); spin_unlock_irqrestore(&db_list_lock, flags); kfree(dbq); - return ret; + return 0; } static const struct file_operations fsl_hv_fops = { @@ -706,7 +702,7 @@ static const struct file_operations fsl_hv_fops = { .poll = fsl_hv_poll, .read = fsl_hv_read, .unlocked_ioctl = fsl_hv_ioctl, - .compat_ioctl = fsl_hv_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice fsl_hv_misc_dev = { @@ -800,7 +796,7 @@ static int has_fsl_hypervisor(void) if (!node) return 0; - ret = of_find_property(node, "fsl,hv-version", NULL) != NULL; + ret = of_property_present(node, "fsl,hv-version"); of_node_put(node); @@ -843,9 +839,9 @@ static int __init fsl_hypervisor_init(void) handle = of_get_property(np, "interrupts", NULL); irq = irq_of_parse_and_map(np, 0); - if (!handle || (irq == NO_IRQ)) { - pr_err("fsl-hv: no 'interrupts' property in %s node\n", - np->full_name); + if (!handle || !irq) { + pr_err("fsl-hv: no 'interrupts' property in %pOF node\n", + np); continue; } @@ -872,8 +868,8 @@ static int __init fsl_hypervisor_init(void) */ dbisr->partition = ret = get_parent_handle(np); if (ret < 0) { - pr_err("fsl-hv: node %s has missing or " - "malformed parent\n", np->full_name); + pr_err("fsl-hv: node %pOF has missing or " + "malformed parent\n", np); kfree(dbisr); continue; } @@ -884,8 +880,8 @@ static int __init fsl_hypervisor_init(void) ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr); if (ret < 0) { - pr_err("fsl-hv: could not request irq %u for node %s\n", - irq, np->full_name); + pr_err("fsl-hv: could not request irq %u for node %pOF\n", + irq, np); kfree(dbisr); continue; } |
