summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorMarcel Ziswiler <marcel.ziswiler@toradex.com>2017-11-29 14:56:42 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2017-11-29 14:56:42 +0100
commit2eecc3c4c63955c3d5860b44422dccd229ec7043 (patch)
treea3256780705eb959d17dd4c4a622a89f1001d90d /drivers/video
parent9a315efc7e2646e75e8cc9d1d79e508589ad2202 (diff)
parentb271e8fa67a6d9c4600274a25636cfe00fdd1b68 (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.c47
-rw-r--r--drivers/video/tegra/dc/hdmi.c6
-rw-r--r--drivers/video/tegra/host/bus_client.c101
-rw-r--r--drivers/video/tegra/host/host1x/host1x.c4
-rw-r--r--drivers/video/tegra/host/nvhost_job.c19
-rw-r--r--drivers/video/tegra/nvmap/nvmap.c29
-rw-r--r--drivers/video/tegra/nvmap/nvmap_dmabuf.c19
-rw-r--r--drivers/video/tegra/nvmap/nvmap_ioctl.c90
-rw-r--r--drivers/video/tegra/nvmap/nvmap_mm.c15
-rw-r--r--drivers/video/tegra/nvmap/nvmap_priv.h11
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]);
}