From ee87937f760493ac0eed2e7dff7e16b24f0020a0 Mon Sep 17 00:00:00 2001 From: Yu Qiaowei Date: Tue, 25 Jan 2022 11:20:52 +0800 Subject: [PATCH] video: rockchip: rga3: Fix memory out-of-bounds on iova When the virtual address has an in-page offset, iova needs to be offset to the corresponding starting point. Update driver version to 1.2.1 Signed-off-by: Yu Qiaowei Change-Id: I01e5109bd684f573920773d0d1e08685d1214a40 --- drivers/video/rockchip/rga3/include/rga_drv.h | 11 +++++++- drivers/video/rockchip/rga3/rga_dma_buf.c | 28 ++++++++++++------- drivers/video/rockchip/rga3/rga_mm.c | 9 ++++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/video/rockchip/rga3/include/rga_drv.h b/drivers/video/rockchip/rga3/include/rga_drv.h index 49fcff8aaa1e..52beeb35cba7 100644 --- a/drivers/video/rockchip/rga3/include/rga_drv.h +++ b/drivers/video/rockchip/rga3/include/rga_drv.h @@ -75,7 +75,7 @@ #define DRIVER_MAJOR_VERISON 1 #define DRIVER_MINOR_VERSION 2 -#define DRIVER_REVISION_VERSION 0 +#define DRIVER_REVISION_VERSION 1 #define DRIVER_VERSION (STR(DRIVER_MAJOR_VERISON) "." STR(DRIVER_MINOR_VERSION) \ "." STR(DRIVER_REVISION_VERSION)) @@ -181,6 +181,12 @@ struct rga_dma_buffer { dma_addr_t iova; unsigned long size; + /* + * The offset of the first page of the sgt. + * Since alloc iova must be page aligned, the offset of the first page is + * identified separately. + */ + size_t offset; /* The core of the mapping */ int core; @@ -194,6 +200,9 @@ struct rga_virt_addr { int page_count; unsigned long size; + /* The offset of the first page of the virtual address */ + size_t offset; + int result; }; diff --git a/drivers/video/rockchip/rga3/rga_dma_buf.c b/drivers/video/rockchip/rga3/rga_dma_buf.c index ad4fbec74ecb..640854da834e 100644 --- a/drivers/video/rockchip/rga3/rga_dma_buf.c +++ b/drivers/video/rockchip/rga3/rga_dma_buf.c @@ -497,7 +497,8 @@ static int rga_viraddr_get_channel_info(struct rga_img_info_t *channel_info, unsigned long size; unsigned long start_addr; unsigned int count; - int order = 0; + int pages_order = 0; + int page_table_order = 0; uint32_t *page_table = NULL; struct page **pages = NULL; @@ -543,18 +544,20 @@ static int rga_viraddr_get_channel_info(struct rga_img_info_t *channel_info, count = rga_buf_size_cal(channel_info->yrgb_addr, channel_info->uv_addr, channel_info->v_addr, format, channel_info->vir_w, channel_info->vir_h, - &start_addr, &size); + &start_addr, NULL); + size = count * PAGE_SIZE; /* alloc pages and page_table */ - order = get_order(size / 4096 * sizeof(struct page *)); - pages = (struct page **)__get_free_pages(GFP_KERNEL, order); + pages_order = get_order(count * sizeof(struct page *)); + pages = (struct page **)__get_free_pages(GFP_KERNEL, pages_order); if (pages == NULL) { pr_err("Can not alloc pages for pages\n"); ret = -ENOMEM; goto out_free_buffer; } - page_table = (uint32_t *)__get_free_pages(GFP_KERNEL, order); + page_table_order = get_order(count * sizeof(uint32_t *)); + page_table = (uint32_t *)__get_free_pages(GFP_KERNEL, page_table_order); if (page_table == NULL) { pr_err("Can not alloc pages for page_table\n"); ret = -ENOMEM; @@ -604,7 +607,12 @@ static int rga_viraddr_get_channel_info(struct rga_img_info_t *channel_info, goto out_free_sg; } - channel_info->yrgb_addr = iova; + /* + * When the virtual address has an in-page offset, it needs to be offset to + * the corresponding starting point. + */ + channel_info->yrgb_addr = iova + (channel_info->yrgb_addr & (~PAGE_MASK)); + alloc_buffer->iova = iova; alloc_buffer->size = size; alloc_buffer->cookie = cookie; @@ -613,8 +621,8 @@ static int rga_viraddr_get_channel_info(struct rga_img_info_t *channel_info, sg_free_table(&sgt); - free_pages((unsigned long)pages, order); - free_pages((unsigned long)page_table, order); + free_pages((unsigned long)pages, pages_order); + free_pages((unsigned long)page_table, page_table_order); *rga_dma_buffer = alloc_buffer; @@ -625,10 +633,10 @@ out_free_sg: rga_iommu_dma_free_iova(cookie, iova, size); out_free_pages_table: - free_pages((unsigned long)page_table, order); + free_pages((unsigned long)page_table, page_table_order); out_free_pages: - free_pages((unsigned long)pages, order); + free_pages((unsigned long)pages, pages_order); out_free_buffer: kfree(alloc_buffer); diff --git a/drivers/video/rockchip/rga3/rga_mm.c b/drivers/video/rockchip/rga3/rga_mm.c index 3a97766e5c24..83a18006184c 100644 --- a/drivers/video/rockchip/rga3/rga_mm.c +++ b/drivers/video/rockchip/rga3/rga_mm.c @@ -216,6 +216,7 @@ static int rga_alloc_sgt(struct rga_virt_addr *virt_addr, virt_dma_buf->sgt = sgt; virt_dma_buf->size = virt_addr->size; + virt_dma_buf->offset = virt_addr->offset; return 0; @@ -267,10 +268,11 @@ static int rga_alloc_virt_addr(struct rga_virt_addr **virt_addr_p, /* Calculate page size. */ count = rga_buf_size_cal(viraddr, viraddr, viraddr, format, memory_parm->width, memory_parm->height, - &start_addr, &size); + &start_addr, NULL); + size = count * PAGE_SIZE; /* alloc pages and page_table */ - order = get_order(size / 4096 * sizeof(struct page *)); + order = get_order(count * sizeof(struct page *)); pages = (struct page **)__get_free_pages(GFP_KERNEL, order); if (pages == NULL) { pr_err("%s can not alloc pages for pages\n", __func__); @@ -301,6 +303,7 @@ static int rga_alloc_virt_addr(struct rga_virt_addr **virt_addr_p, virt_addr->pages_order = order; virt_addr->page_count = count; virt_addr->size = size; + virt_addr->offset = viraddr & (~PAGE_MASK); virt_addr->result = result; return 0; @@ -702,7 +705,7 @@ dma_addr_t rga_mm_lookup_iova(struct rga_internal_buffer *buffer, int core) for (i = 0; i < buffer->dma_buffer_size; i++) if (buffer->dma_buffer[i].core == core) - return buffer->dma_buffer[i].iova; + return buffer->dma_buffer[i].iova + buffer->dma_buffer[i].offset; return 0; }