diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2017-11-29 14:56:42 +0100 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2017-11-29 14:56:42 +0100 |
commit | 2eecc3c4c63955c3d5860b44422dccd229ec7043 (patch) | |
tree | a3256780705eb959d17dd4c4a622a89f1001d90d /drivers/video | |
parent | 9a315efc7e2646e75e8cc9d1d79e508589ad2202 (diff) | |
parent | b271e8fa67a6d9c4600274a25636cfe00fdd1b68 (diff) |
Merge tag 'tegra-l4t-r21.6' into toradex_tk1_l4t_r21.6
Merge NVIDIA's latest Linux for Tegra aka L4T R21.6 Linux kernel changes
from git://nv-tegra.nvidia.com/linux-3.10.git commit:
b271e8fa67a6d9c4600274a25636cfe00fdd1b68
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Acked-by: Dominik Sliwa <dominik.sliwa@toradex.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/tegra/dc/dsi_debug.c | 47 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 6 | ||||
-rw-r--r-- | drivers/video/tegra/host/bus_client.c | 101 | ||||
-rw-r--r-- | drivers/video/tegra/host/host1x/host1x.c | 4 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_job.c | 19 | ||||
-rw-r--r-- | drivers/video/tegra/nvmap/nvmap.c | 29 | ||||
-rw-r--r-- | drivers/video/tegra/nvmap/nvmap_dmabuf.c | 19 | ||||
-rw-r--r-- | drivers/video/tegra/nvmap/nvmap_ioctl.c | 90 | ||||
-rw-r--r-- | drivers/video/tegra/nvmap/nvmap_mm.c | 15 | ||||
-rw-r--r-- | drivers/video/tegra/nvmap/nvmap_priv.h | 11 |
10 files changed, 219 insertions, 122 deletions
diff --git a/drivers/video/tegra/dc/dsi_debug.c b/drivers/video/tegra/dc/dsi_debug.c index 9e3288be4cce..a29acca7491b 100644 --- a/drivers/video/tegra/dc/dsi_debug.c +++ b/drivers/video/tegra/dc/dsi_debug.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/dsi_debug.c * - * Copyright (c) 2013-2014 NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2013-2017 NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -31,6 +31,8 @@ #ifdef CONFIG_DEBUG_FS +#define MAX_PANEL_REG_READ_SIZE 300 + static int dbg_dsi_show(struct seq_file *s, void *unused) { struct tegra_dc_dsi_data *dsi = s->private; @@ -152,36 +154,43 @@ static int read_panel_get(struct seq_file *s, void *unused) struct tegra_dc_dsi_data *dsi = s->private; struct tegra_dc *dc = dsi->dc; int err = 0; - u8 buf[300] = {0}; + u8 buf[MAX_PANEL_REG_READ_SIZE] = {0}; int j = 0 , b = 0 , k; u32 payload_size = 0; if (!dsi->enabled) { dev_info(&dc->ndev->dev, " controller suspended\n"); - return -EINVAL; -} + return -EINVAL; + } seq_printf(s, "max ret payload size:0x%x\npanel reg addr:0x%x\n", max_ret_payload_size, panel_reg_addr); - if (max_ret_payload_size == 0) { - seq_puts(s, "echo was not successful\n"); - return err; -} + + if ((max_ret_payload_size > MAX_PANEL_REG_READ_SIZE) || + (max_ret_payload_size == 0)) { + seq_printf(s, "payload size should be positive value < 0x%x\n", + MAX_PANEL_REG_READ_SIZE); + return err; + } + + if (panel_reg_addr >= MAX_PANEL_REG_READ_SIZE) { + seq_puts(s, "panel reg addr is outside the range\n"); + return err; + } + + if (max_ret_payload_size > + (MAX_PANEL_REG_READ_SIZE - panel_reg_addr)) + max_ret_payload_size = + MAX_PANEL_REG_READ_SIZE - panel_reg_addr; + err = tegra_dsi_read_data(dsi->dc, dsi, max_ret_payload_size, panel_reg_addr, buf); - seq_printf(s, " Read data[%d] ", b); - - for (b = 1; b < (max_ret_payload_size+1); b++) { - j = (b*4)-1; - for (k = j; k > (j-4); k--) - if ((k%4) == 0 && b != max_ret_payload_size) { - seq_printf(s, " %x ", buf[k]); - seq_printf(s, "\n Read data[%d] ", b); - } - else - seq_printf(s, " %x ", buf[k]); + for (b = 0; b < max_ret_payload_size; b += 4) { + seq_printf(s, "\n Read data[%d] ", j++); + for (k = b+4; k > b; k--) + seq_printf(s, " %x ", buf[k-1]); } seq_puts(s, "\n"); diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index cb1a6c838839..eb8cf5d83852 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (c) 2010-2015, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2010-2016, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -2219,6 +2219,10 @@ static unsigned long tegra12x_hdmi_determine_parent( f = m % 1000; /* fractional parts */ f = (0 == f) ? f : (1000 - f); /* round-up */ if (0 == f) { /* exact match */ + if ((ref / 2 * b) < 100000000) { + /* parent clock runs at a minumum of 100MHz */ + continue; + } b = n; fr = f; break; diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index 2bf11ed426c0..eabb09f1a683 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -173,6 +173,9 @@ struct nvhost_channel_userctx { u32 priority; int clientid; bool timeout_debug_dump; + + /* lock to protect this structure from concurrent ioctl usage */ + struct mutex ioctl_lock; }; static int nvhost_channelrelease(struct inode *inode, struct file *filp) @@ -276,6 +279,7 @@ static int __nvhost_channelopen(struct inode *inode, pdata = dev_get_drvdata(ch->dev->dev.parent); priv->timeout = pdata->nvhost_timeout_default; priv->timeout_debug_dump = true; + mutex_init(&priv->ioctl_lock); if (!tegra_platform_is_silicon()) priv->timeout = 0; mutex_unlock(&channel_lock); @@ -394,12 +398,17 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, u32 __user *waitbases = (u32 *)(uintptr_t)args->waitbases; u32 __user *fences = (u32 *)(uintptr_t)args->fences; u32 __user *class_ids = (u32 *)(uintptr_t)args->class_ids; + struct nvhost_device_data *pdata = platform_get_drvdata(ctx->ch->dev); struct nvhost_master *host = nvhost_get_host(ctx->ch->dev); u32 *local_waitbases = NULL, *local_class_ids = NULL; int err, i, hwctx_syncpt_idx = -1; - if (num_syncpt_incrs > host->info.nb_pts) + if ((num_syncpt_incrs < 1) || (num_syncpt_incrs > + host->info.nb_pts)) + return -EINVAL; + + if (num_cmdbufs < 0 || num_syncpt_incrs < 0) return -EINVAL; job = nvhost_job_alloc(ctx->ch, @@ -449,6 +458,13 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, if (err) cmdbuf_ext.pre_fence = -1; + if (class_id && + class_id != pdata->class && + class_id != NV_HOST1X_CLASS_ID) { + err = -EINVAL; + goto fail; + } + nvhost_job_add_gather(job, cmdbuf.mem, cmdbuf.words, cmdbuf.offset, class_id, cmdbuf_ext.pre_fence); @@ -504,6 +520,8 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, for (i = 0; i < num_syncpt_incrs; ++i) { u32 waitbase; struct nvhost_syncpt_incr sp; + bool found = false; + int j; /* Copy */ err = copy_from_user(&sp, syncpt_incrs + i, sizeof(sp)); @@ -511,7 +529,19 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, goto fail; /* Validate */ - if (sp.syncpt_id >= host->info.nb_pts) { + if (sp.syncpt_id == 0) { + err = -EINVAL; + goto fail; + } + + for (j = 0; j < NVHOST_MODULE_MAX_SYNCPTS; ++j) { + if (pdata->syncpts[j] == sp.syncpt_id) { + found = true; + break; + } + } + + if (!found) { err = -EINVAL; goto fail; } @@ -580,7 +610,15 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, * syncpoint is used. */ if (args->flags & BIT(NVHOST_SUBMIT_FLAG_SYNC_FENCE_FD)) { - struct nvhost_ctrl_sync_fence_info pts[num_syncpt_incrs]; + struct nvhost_ctrl_sync_fence_info *pts; + + pts = kzalloc(num_syncpt_incrs * + sizeof(struct nvhost_ctrl_sync_fence_info), + GFP_KERNEL); + if (!pts) { + err = -ENOMEM; + goto fail; + } for (i = 0; i < num_syncpt_incrs; i++) { pts[i].id = job->sp[i].id; @@ -589,6 +627,7 @@ static int nvhost_ioctl_channel_submit(struct nvhost_channel_userctx *ctx, err = nvhost_sync_create_fence_fd(ctx->ch->dev, pts, num_syncpt_incrs, "fence", &args->fence); + kfree(pts); if (err) goto fail; } else if (num_syncpt_incrs == 1) @@ -739,8 +778,12 @@ static long nvhost_channelctl(struct file *filp, return -EFAULT; } + /* serialize calls from this fd */ + mutex_lock(&priv->ioctl_lock); + if (!priv->ch->dev) { pr_warn("Channel already unmapped\n"); + mutex_unlock(&priv->ioctl_lock); return -EFAULT; } @@ -772,7 +815,6 @@ static long nvhost_channelctl(struct file *filp, put_unused_fd(fd); break; } - fd_install(fd, file); err = __nvhost_channelopen(NULL, priv->ch, file); if (err) { @@ -782,6 +824,7 @@ static long nvhost_channelctl(struct file *filp, } ((struct nvhost_channel_open_args *)buf)->channel_fd = fd; + fd_install(fd, file); break; } case NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS: @@ -798,8 +841,12 @@ static long nvhost_channelctl(struct file *filp, platform_get_drvdata(priv->ch->dev); struct nvhost_get_param_arg *arg = (struct nvhost_get_param_arg *)buf; - if (arg->param >= NVHOST_MODULE_MAX_SYNCPTS) - return -EINVAL; + + if (arg->param >= NVHOST_MODULE_MAX_SYNCPTS) { + err = -EINVAL; + break; + } + /* if we already have required syncpt then return it ... */ if (pdata->syncpts[arg->param]) { arg->value = pdata->syncpts[arg->param]; @@ -808,8 +855,10 @@ static long nvhost_channelctl(struct file *filp, /* ... otherwise get a new syncpt dynamically */ arg->value = nvhost_get_syncpt_host_managed(pdata->pdev, arg->param); - if (!arg->value) - return -EAGAIN; + if (!arg->value) { + err = -EAGAIN; + break; + } /* ... and store it for further references */ pdata->syncpts[arg->param] = arg->value; break; @@ -827,8 +876,10 @@ static long nvhost_channelctl(struct file *filp, if (args_name) { if (strncpy_from_user(name, args_name, - sizeof(name)) < 0) - return -EFAULT; + sizeof(name)) < 0) { + err = -EFAULT; + break; + } name[sizeof(name) - 1] = '\0'; } else { name[0] = '\0'; @@ -842,8 +893,10 @@ static long nvhost_channelctl(struct file *filp, snprintf(set_name, sizeof(set_name), "%s_%s", dev_name(&pdata->pdev->dev), name); args->value = nvhost_get_syncpt_client_managed(set_name); - if (!args->value) - return -EAGAIN; + if (!args->value) { + err = -EAGAIN; + break; + } /* ... and store it for further references */ pdata->client_managed_syncpt = args->value; break; @@ -856,8 +909,10 @@ static long nvhost_channelctl(struct file *filp, platform_get_drvdata(priv->ch->dev); if (!args->value) break; - if (args->value != pdata->client_managed_syncpt) - return -EINVAL; + if (args->value != pdata->client_managed_syncpt) { + err = -EINVAL; + break; + } nvhost_free_syncpt(args->value); pdata->client_managed_syncpt = 0; break; @@ -878,8 +933,10 @@ static long nvhost_channelctl(struct file *filp, struct nvhost_get_param_arg *arg = (struct nvhost_get_param_arg *)buf; if (arg->param >= NVHOST_MODULE_MAX_WAITBASES - || !pdata->waitbases[arg->param]) - return -EINVAL; + || !pdata->waitbases[arg->param]) { + err = -EINVAL; + break; + } arg->value = pdata->waitbases[arg->param]; break; } @@ -898,9 +955,13 @@ static long nvhost_channelctl(struct file *filp, platform_get_drvdata(priv->ch->dev); struct nvhost_get_param_arg *arg = (struct nvhost_get_param_arg *)buf; - if (arg->param >= NVHOST_MODULE_MAX_MODMUTEXES - || !pdata->modulemutexes[arg->param]) - return -EINVAL; + + if (arg->param >= NVHOST_MODULE_MAX_MODMUTEXES || + !pdata->modulemutexes[arg->param]) { + err = -EINVAL; + break; + } + arg->value = pdata->modulemutexes[arg->param]; break; } @@ -1019,6 +1080,8 @@ static long nvhost_channelctl(struct file *filp, break; } + mutex_unlock(&priv->ioctl_lock); + if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ)) err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd)); diff --git a/drivers/video/tegra/host/host1x/host1x.c b/drivers/video/tegra/host/host1x/host1x.c index 5631189c354f..522219484286 100644 --- a/drivers/video/tegra/host/host1x/host1x.c +++ b/drivers/video/tegra/host/host1x/host1x.c @@ -206,11 +206,11 @@ static int nvhost_ioctl_ctrl_sync_fence_create(struct nvhost_ctrl_userctx *ctx, name[0] = '\0'; } - pts = kmalloc(sizeof(*pts) * args->num_pts, GFP_KERNEL); + pts = kmalloc_array(args->num_pts, sizeof(*pts), GFP_KERNEL); if (!pts) return -ENOMEM; - + /* Multiplication overflow would have errored in kmalloc_array */ if (copy_from_user(pts, args_pts, sizeof(*pts) * args->num_pts)) { err = -EFAULT; goto out; diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index 2100749d60dc..cd83b965a112 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -3,7 +3,7 @@ * * Tegra Graphics Host Job * - * Copyright (c) 2010-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2010-2016, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -38,21 +38,22 @@ static size_t job_size(u32 num_cmdbufs, u32 num_relocs, u32 num_waitchks, u32 num_syncpts) { - s64 num_unpins = num_cmdbufs + num_relocs; - s64 total; + u64 num_unpins = (u64)num_cmdbufs + (u64)num_relocs; + u64 total; total = sizeof(struct nvhost_job) - + num_relocs * sizeof(struct nvhost_reloc) - + num_relocs * sizeof(struct nvhost_reloc_shift) + + (u64)num_relocs * sizeof(struct nvhost_reloc) + + (u64)num_relocs * sizeof(struct nvhost_reloc_shift) + num_unpins * sizeof(struct nvhost_job_unpin) - + num_waitchks * sizeof(struct nvhost_waitchk) - + num_cmdbufs * sizeof(struct nvhost_job_gather) + + (u64)num_waitchks * sizeof(struct nvhost_waitchk) + + (u64)num_cmdbufs * sizeof(struct nvhost_job_gather) + num_unpins * sizeof(dma_addr_t) + num_unpins * sizeof(struct nvhost_pinid) - + num_syncpts * sizeof(struct nvhost_job_syncpt); + + (u64)num_syncpts * sizeof(struct nvhost_job_syncpt); - if(total > ULONG_MAX) + if (total > UINT_MAX) return 0; + return (size_t)total; } diff --git a/drivers/video/tegra/nvmap/nvmap.c b/drivers/video/tegra/nvmap/nvmap.c index 16eeeb2638d5..662646a2d6c6 100644 --- a/drivers/video/tegra/nvmap/nvmap.c +++ b/drivers/video/tegra/nvmap/nvmap.c @@ -3,7 +3,7 @@ * * Memory manager for Tegra GPU * - * Copyright (c) 2009-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2009-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -196,6 +196,9 @@ void *__nvmap_kmap(struct nvmap_handle *h, unsigned int pagenum) if (!h) return NULL; + if (!h->alloc) + goto out; + if (pagenum >= h->size >> PAGE_SHIFT) goto out; prot = nvmap_pgprot(h, PG_PROT_KERNEL); @@ -222,7 +225,7 @@ void __nvmap_kunmap(struct nvmap_handle *h, unsigned int pagenum, phys_addr_t paddr; struct vm_struct *area = NULL; - if (!h || + if (!h || !h->alloc || WARN_ON(!virt_addr_valid(h)) || WARN_ON(!addr)) return; @@ -265,6 +268,9 @@ void *__nvmap_mmap(struct nvmap_handle *h) if (!h) return NULL; + if (!h->alloc) + goto put_handle; + prot = nvmap_pgprot(h, PG_PROT_KERNEL); if (h->heap_pgalloc) { @@ -273,7 +279,7 @@ void *__nvmap_mmap(struct nvmap_handle *h) pages = nvmap_pages(h->pgalloc.pages, h->size >> PAGE_SHIFT); if (!pages) - return NULL; + goto put_handle; vaddr = vm_map_ram(pages, h->size >> PAGE_SHIFT, -1, prot); nvmap_altfree(pages, @@ -293,10 +299,8 @@ void *__nvmap_mmap(struct nvmap_handle *h) adj_size = PAGE_ALIGN(adj_size); v = alloc_vm_area(adj_size, 0); - if (!v) { - nvmap_handle_put(h); - return NULL; - } + if (!v) + goto put_handle; p = v->addr + (h->carveout->base & ~PAGE_MASK); ioremap_page_range((ulong)v->addr, (ulong)v->addr + adj_size, @@ -306,11 +310,14 @@ void *__nvmap_mmap(struct nvmap_handle *h) * the handle will not be freed while the kernel mapping exists. * nvmap_handle_put will be called by unmapping this address */ return p; +put_handle: + nvmap_handle_put(h); + return NULL; } void __nvmap_munmap(struct nvmap_handle *h, void *addr) { - if (!h || + if (!h || !h->alloc || WARN_ON(!virt_addr_valid(h)) || WARN_ON(!addr)) return; @@ -444,6 +451,11 @@ struct sg_table *__nvmap_sg_table(struct nvmap_client *client, if (!h) return ERR_PTR(-EINVAL); + if (!h->alloc) { + err = -EINVAL; + goto put_handle; + } + npages = PAGE_ALIGN(h->size) >> PAGE_SHIFT; sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { @@ -473,6 +485,7 @@ struct sg_table *__nvmap_sg_table(struct nvmap_client *client, err: kfree(sgt); +put_handle: nvmap_handle_put(h); return ERR_PTR(err); } diff --git a/drivers/video/tegra/nvmap/nvmap_dmabuf.c b/drivers/video/tegra/nvmap/nvmap_dmabuf.c index e0ade759a340..d5b0c8805766 100644 --- a/drivers/video/tegra/nvmap/nvmap_dmabuf.c +++ b/drivers/video/tegra/nvmap/nvmap_dmabuf.c @@ -599,7 +599,6 @@ err_nomem: int __nvmap_dmabuf_fd(struct nvmap_client *client, struct dma_buf *dmabuf, int flags) { - int fd; int start_fd = CONFIG_NVMAP_FD_START; #ifdef CONFIG_NVMAP_DEFER_FD_RECYCLE @@ -615,14 +614,8 @@ int __nvmap_dmabuf_fd(struct nvmap_client *client, * __FD_SETSIZE limitation issue for select(), * pselect() syscalls. */ - fd = __alloc_fd(current->files, start_fd, - sysctl_nr_open, flags); - if (fd < 0) - return fd; - - fd_install(fd, dmabuf->file); - - return fd; + return __alloc_fd(current->files, start_fd, + sysctl_nr_open, flags); } int nvmap_get_dmabuf_fd(struct nvmap_client *client, struct nvmap_handle *h) @@ -634,12 +627,8 @@ int nvmap_get_dmabuf_fd(struct nvmap_client *client, struct nvmap_handle *h) if (IS_ERR(dmabuf)) return PTR_ERR(dmabuf); fd = __nvmap_dmabuf_fd(client, dmabuf, O_CLOEXEC); - if (fd < 0) - goto err_out; - return fd; - -err_out: - dma_buf_put(dmabuf); + if (IS_ERR_VALUE(fd)) + dma_buf_put(dmabuf); return fd; } diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.c b/drivers/video/tegra/nvmap/nvmap_ioctl.c index 850afd1ff437..3f7573a9ffcc 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.c +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.c @@ -3,7 +3,7 @@ * * User-space interface to nvmap * - * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -88,6 +88,7 @@ int nvmap_ioctl_pinop(struct file *filp, bool is_pin, void __user *arg, return -EFAULT; op.handles = (__u32 *)(uintptr_t)op32.handles; op.count = op32.count; + op.addr = (unsigned long *)(uintptr_t)op32.addr; } else #endif if (copy_from_user(&op, arg, sizeof(op))) @@ -226,6 +227,33 @@ const struct file_operations nvmap_fd_fops = { .mmap = nvmap_share_mmap, }; +static int nvmap_install_fd(struct nvmap_client *client, + struct nvmap_handle *handle, int fd, void __user *arg, + void *op, size_t op_size, bool free) +{ + int err = 0; + + if (IS_ERR_VALUE(fd)) { + err = fd; + goto fd_fail; + } + + if (copy_to_user(arg, op, op_size)) { + err = -EFAULT; + goto copy_fail; + } + + fd_install(fd, handle->dmabuf->file); + return err; + +copy_fail: + put_unused_fd(fd); +fd_fail: + if (free) + nvmap_free_handle(client, handle); + return err; +} + int nvmap_ioctl_getfd(struct file *filp, void __user *arg) { struct nvmap_handle *handle; @@ -241,14 +269,9 @@ int nvmap_ioctl_getfd(struct file *filp, void __user *arg) op.fd = nvmap_get_dmabuf_fd(client, handle); nvmap_handle_put(handle); - if (op.fd < 0) - return op.fd; - if (copy_to_user(arg, &op, sizeof(op))) { - sys_close(op.fd); - return -EFAULT; - } - return 0; + return nvmap_install_fd(client, handle, + op.fd, arg, &op, sizeof(op), 0); } int nvmap_ioctl_alloc(struct file *filp, void __user *arg) @@ -309,30 +332,11 @@ int nvmap_ioctl_alloc_kind(struct file *filp, void __user *arg) return err; } -int nvmap_create_fd(struct nvmap_client *client, struct nvmap_handle *h) -{ - int fd; - - fd = __nvmap_dmabuf_fd(client, h->dmabuf, O_CLOEXEC); - BUG_ON(fd == 0); - if (fd < 0) { - pr_err("Out of file descriptors"); - return fd; - } - /* __nvmap_dmabuf_fd() associates fd with dma_buf->file *. - * fd close drops one ref count on dmabuf->file *. - * to balance ref count, ref count dma_buf. - */ - get_dma_buf(h->dmabuf); - return fd; -} - int nvmap_ioctl_create(struct file *filp, unsigned int cmd, void __user *arg) { struct nvmap_create_handle op; struct nvmap_handle_ref *ref = NULL; struct nvmap_client *client = filp->private_data; - int err = 0; int fd = 0; if (copy_from_user(&op, arg, sizeof(op))) @@ -354,20 +358,10 @@ int nvmap_ioctl_create(struct file *filp, unsigned int cmd, void __user *arg) if (IS_ERR(ref)) return PTR_ERR(ref); - fd = nvmap_create_fd(client, ref->handle); - if (fd < 0) - err = fd; - + fd = nvmap_get_dmabuf_fd(client, ref->handle); op.handle = fd; - - if (copy_to_user(arg, &op, sizeof(op))) { - err = -EFAULT; - nvmap_free_handle(client, __nvmap_ref_to_id(ref)); - } - - if (err && fd > 0) - sys_close(fd); - return err; + return nvmap_install_fd(client, ref->handle, fd, + arg, &op, sizeof(op), 1); } int nvmap_map_into_caller_ptr(struct file *filp, void __user *arg, bool is32) @@ -472,9 +466,9 @@ int nvmap_ioctl_get_param(struct file *filp, void __user *arg, bool is32) struct nvmap_handle_param __user *uarg = arg; struct nvmap_handle_param op; struct nvmap_client *client = filp->private_data; - struct nvmap_handle_ref *ref; - struct nvmap_handle *h; - u64 result; + struct nvmap_handle_ref *ref = NULL; + struct nvmap_handle *h = NULL; + u64 result = 0; int err = 0; #ifdef CONFIG_COMPAT @@ -500,6 +494,9 @@ int nvmap_ioctl_get_param(struct file *filp, void __user *arg, bool is32) } err = nvmap_get_handle_param(client, ref, op.param, &result); + if (err) { + goto ref_fail; + } #ifdef CONFIG_COMPAT if (is32) @@ -936,6 +933,11 @@ int __nvmap_do_cache_maint(struct nvmap_client *client, if (!h) return -EFAULT; + if ((start >= h->size) || (end > h->size)) { + nvmap_handle_put(h); + return -EFAULT; + } + if (op == NVMAP_CACHE_OP_INV) op = NVMAP_CACHE_OP_WB_INV; @@ -1088,7 +1090,7 @@ int nvmap_ioctl_cache_maint_list(struct file *filp, void __user *arg, if (copy_from_user(&op, arg, sizeof(op))) return -EFAULT; - if (!op.nr) + if (!op.nr || op.nr > UINT_MAX / sizeof(u32)) return -EINVAL; if (!access_ok(VERIFY_READ, op.handles, op.nr * sizeof(u32))) diff --git a/drivers/video/tegra/nvmap/nvmap_mm.c b/drivers/video/tegra/nvmap/nvmap_mm.c index 133f00b1aaa9..fbf510081be7 100644 --- a/drivers/video/tegra/nvmap/nvmap_mm.c +++ b/drivers/video/tegra/nvmap/nvmap_mm.c @@ -3,7 +3,7 @@ * * Some MM related functionality specific to nvmap. * - * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2013-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -193,6 +193,10 @@ void nvmap_zap_handle(struct nvmap_handle *handle, u32 offset, u32 size) size = PAGE_ALIGN((offset & ~PAGE_MASK) + size); + if ((offset >= handle->size) || (offset > handle->size - size) || + (size > handle->size)) + return; + mutex_lock(&handle->lock); vmas = &handle->vmas; list_for_each_entry(vma_list, vmas, list) { @@ -231,6 +235,15 @@ int nvmap_reserve_pages(struct nvmap_handle **handles, u32 *offsets, u32 *sizes, { int i; + /* validates all page params first */ + for (i = 0; i < nr; i++) { + u32 size = sizes[i] ? sizes[i] : handles[i]->size; + u32 offset = sizes[i] ? offsets[i] : 0; + + if ((offset != 0) || (size != handles[i]->size)) + return -EINVAL; + } + for (i = 0; i < nr; i++) { u32 size = sizes[i] ? sizes[i] : handles[i]->size; u32 offset = sizes[i] ? offsets[i] : 0; diff --git a/drivers/video/tegra/nvmap/nvmap_priv.h b/drivers/video/tegra/nvmap/nvmap_priv.h index a90a3269cd62..705745702abd 100644 --- a/drivers/video/tegra/nvmap/nvmap_priv.h +++ b/drivers/video/tegra/nvmap/nvmap_priv.h @@ -3,7 +3,7 @@ * * GPU memory management driver for Tegra * - * Copyright (c) 2009-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2009-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -467,10 +467,13 @@ static inline void nvmap_handle_mk(struct nvmap_handle *h, void (*fn)(struct page **)) { int i; - int start_page = PAGE_ALIGN(offset) >> PAGE_SHIFT; - int end_page = (offset + size) >> PAGE_SHIFT; + u32 start_page = offset >> PAGE_SHIFT; + u32 end_page = PAGE_ALIGN(offset + size) >> PAGE_SHIFT; - if (h->heap_pgalloc) { + if (h->heap_pgalloc && + (offset < h->size) && + (size <= h->size) && + (offset <= (h->size - size))) { for (i = start_page; i < end_page; i++) fn(&h->pgalloc.pages[i]); } |