18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * \file drm_scatter.c 38c2ecf20Sopenharmony_ci * IOCTLs to manage scatter/gather memory 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * \author Gareth Hughes <gareth@valinux.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 128c2ecf20Sopenharmony_ci * All Rights Reserved. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 158c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 168c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 178c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 188c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 198c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 228c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 238c2ecf20Sopenharmony_ci * Software. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 268c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 278c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 288c2ecf20Sopenharmony_ci * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 298c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 308c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 318c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/mm.h> 358c2ecf20Sopenharmony_ci#include <linux/slab.h> 368c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <drm/drm.h> 398c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 408c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "drm_legacy.h" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define DEBUG_SCATTER 0 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void drm_sg_cleanup(struct drm_sg_mem * entry) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct page *page; 498c2ecf20Sopenharmony_ci int i; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci for (i = 0; i < entry->pages; i++) { 528c2ecf20Sopenharmony_ci page = entry->pagelist[i]; 538c2ecf20Sopenharmony_ci if (page) 548c2ecf20Sopenharmony_ci ClearPageReserved(page); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci vfree(entry->virtual); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci kfree(entry->busaddr); 608c2ecf20Sopenharmony_ci kfree(entry->pagelist); 618c2ecf20Sopenharmony_ci kfree(entry); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_civoid drm_legacy_sg_cleanup(struct drm_device *dev) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg && 678c2ecf20Sopenharmony_ci drm_core_check_feature(dev, DRIVER_LEGACY)) { 688c2ecf20Sopenharmony_ci drm_sg_cleanup(dev->sg); 698c2ecf20Sopenharmony_ci dev->sg = NULL; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci#ifdef _LP64 738c2ecf20Sopenharmony_ci# define ScatterHandle(x) (unsigned int)((x >> 32) + (x & ((1L << 32) - 1))) 748c2ecf20Sopenharmony_ci#else 758c2ecf20Sopenharmony_ci# define ScatterHandle(x) (unsigned int)(x) 768c2ecf20Sopenharmony_ci#endif 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ciint drm_legacy_sg_alloc(struct drm_device *dev, void *data, 798c2ecf20Sopenharmony_ci struct drm_file *file_priv) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct drm_scatter_gather *request = data; 828c2ecf20Sopenharmony_ci struct drm_sg_mem *entry; 838c2ecf20Sopenharmony_ci unsigned long pages, i, j; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci DRM_DEBUG("\n"); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_LEGACY)) 888c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SG)) 918c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (request->size > SIZE_MAX - PAGE_SIZE) 948c2ecf20Sopenharmony_ci return -EINVAL; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (dev->sg) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (!entry) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE; 1048c2ecf20Sopenharmony_ci DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci entry->pages = pages; 1078c2ecf20Sopenharmony_ci entry->pagelist = kcalloc(pages, sizeof(*entry->pagelist), GFP_KERNEL); 1088c2ecf20Sopenharmony_ci if (!entry->pagelist) { 1098c2ecf20Sopenharmony_ci kfree(entry); 1108c2ecf20Sopenharmony_ci return -ENOMEM; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL); 1148c2ecf20Sopenharmony_ci if (!entry->busaddr) { 1158c2ecf20Sopenharmony_ci kfree(entry->pagelist); 1168c2ecf20Sopenharmony_ci kfree(entry); 1178c2ecf20Sopenharmony_ci return -ENOMEM; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci entry->virtual = vmalloc_32(pages << PAGE_SHIFT); 1218c2ecf20Sopenharmony_ci if (!entry->virtual) { 1228c2ecf20Sopenharmony_ci kfree(entry->busaddr); 1238c2ecf20Sopenharmony_ci kfree(entry->pagelist); 1248c2ecf20Sopenharmony_ci kfree(entry); 1258c2ecf20Sopenharmony_ci return -ENOMEM; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* This also forces the mapping of COW pages, so our page list 1298c2ecf20Sopenharmony_ci * will be valid. Please don't remove it... 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci memset(entry->virtual, 0, pages << PAGE_SHIFT); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci entry->handle = ScatterHandle((unsigned long)entry->virtual); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci DRM_DEBUG("handle = %08lx\n", entry->handle); 1368c2ecf20Sopenharmony_ci DRM_DEBUG("virtual = %p\n", entry->virtual); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci for (i = (unsigned long)entry->virtual, j = 0; j < pages; 1398c2ecf20Sopenharmony_ci i += PAGE_SIZE, j++) { 1408c2ecf20Sopenharmony_ci entry->pagelist[j] = vmalloc_to_page((void *)i); 1418c2ecf20Sopenharmony_ci if (!entry->pagelist[j]) 1428c2ecf20Sopenharmony_ci goto failed; 1438c2ecf20Sopenharmony_ci SetPageReserved(entry->pagelist[j]); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci request->handle = entry->handle; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dev->sg = entry; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#if DEBUG_SCATTER 1518c2ecf20Sopenharmony_ci /* Verify that each page points to its virtual address, and vice 1528c2ecf20Sopenharmony_ci * versa. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci { 1558c2ecf20Sopenharmony_ci int error = 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < pages; i++) { 1588c2ecf20Sopenharmony_ci unsigned long *tmp; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci tmp = page_address(entry->pagelist[i]); 1618c2ecf20Sopenharmony_ci for (j = 0; 1628c2ecf20Sopenharmony_ci j < PAGE_SIZE / sizeof(unsigned long); 1638c2ecf20Sopenharmony_ci j++, tmp++) { 1648c2ecf20Sopenharmony_ci *tmp = 0xcafebabe; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci tmp = (unsigned long *)((u8 *) entry->virtual + 1678c2ecf20Sopenharmony_ci (PAGE_SIZE * i)); 1688c2ecf20Sopenharmony_ci for (j = 0; 1698c2ecf20Sopenharmony_ci j < PAGE_SIZE / sizeof(unsigned long); 1708c2ecf20Sopenharmony_ci j++, tmp++) { 1718c2ecf20Sopenharmony_ci if (*tmp != 0xcafebabe && error == 0) { 1728c2ecf20Sopenharmony_ci error = 1; 1738c2ecf20Sopenharmony_ci DRM_ERROR("Scatter allocation error, " 1748c2ecf20Sopenharmony_ci "pagelist does not match " 1758c2ecf20Sopenharmony_ci "virtual mapping\n"); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci tmp = page_address(entry->pagelist[i]); 1798c2ecf20Sopenharmony_ci for (j = 0; 1808c2ecf20Sopenharmony_ci j < PAGE_SIZE / sizeof(unsigned long); 1818c2ecf20Sopenharmony_ci j++, tmp++) { 1828c2ecf20Sopenharmony_ci *tmp = 0; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci if (error == 0) 1868c2ecf20Sopenharmony_ci DRM_ERROR("Scatter allocation matches pagelist\n"); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci#endif 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci failed: 1938c2ecf20Sopenharmony_ci drm_sg_cleanup(entry); 1948c2ecf20Sopenharmony_ci return -ENOMEM; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciint drm_legacy_sg_free(struct drm_device *dev, void *data, 1988c2ecf20Sopenharmony_ci struct drm_file *file_priv) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct drm_scatter_gather *request = data; 2018c2ecf20Sopenharmony_ci struct drm_sg_mem *entry; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_LEGACY)) 2048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_SG)) 2078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci entry = dev->sg; 2108c2ecf20Sopenharmony_ci dev->sg = NULL; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!entry || entry->handle != request->handle) 2138c2ecf20Sopenharmony_ci return -EINVAL; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci DRM_DEBUG("virtual = %p\n", entry->virtual); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci drm_sg_cleanup(entry); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 221