18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/char/mem.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Added devfs support. 88c2ecf20Sopenharmony_ci * Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu> 98c2ecf20Sopenharmony_ci * Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 168c2ecf20Sopenharmony_ci#include <linux/mman.h> 178c2ecf20Sopenharmony_ci#include <linux/random.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/raw.h> 208c2ecf20Sopenharmony_ci#include <linux/tty.h> 218c2ecf20Sopenharmony_ci#include <linux/capability.h> 228c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 238c2ecf20Sopenharmony_ci#include <linux/device.h> 248c2ecf20Sopenharmony_ci#include <linux/highmem.h> 258c2ecf20Sopenharmony_ci#include <linux/backing-dev.h> 268c2ecf20Sopenharmony_ci#include <linux/shmem_fs.h> 278c2ecf20Sopenharmony_ci#include <linux/splice.h> 288c2ecf20Sopenharmony_ci#include <linux/pfn.h> 298c2ecf20Sopenharmony_ci#include <linux/export.h> 308c2ecf20Sopenharmony_ci#include <linux/io.h> 318c2ecf20Sopenharmony_ci#include <linux/uio.h> 328c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 338c2ecf20Sopenharmony_ci#include <linux/security.h> 348c2ecf20Sopenharmony_ci#include <linux/pseudo_fs.h> 358c2ecf20Sopenharmony_ci#include <uapi/linux/magic.h> 368c2ecf20Sopenharmony_ci#include <linux/mount.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#ifdef CONFIG_IA64 398c2ecf20Sopenharmony_ci# include <linux/efi.h> 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define DEVMEM_MINOR 1 438c2ecf20Sopenharmony_ci#define DEVPORT_MINOR 4 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic inline unsigned long size_inside_page(unsigned long start, 468c2ecf20Sopenharmony_ci unsigned long size) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci unsigned long sz; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci sz = PAGE_SIZE - (start & (PAGE_SIZE - 1)); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return min(sz, size); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE 568c2ecf20Sopenharmony_cistatic inline int valid_phys_addr_range(phys_addr_t addr, size_t count) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return addr + count <= __pa(high_memory); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return 1; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#ifdef CONFIG_STRICT_DEVMEM 688c2ecf20Sopenharmony_cistatic inline int page_is_allowed(unsigned long pfn) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return devmem_is_allowed(pfn); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_cistatic inline int range_is_allowed(unsigned long pfn, unsigned long size) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci u64 from = ((u64)pfn) << PAGE_SHIFT; 758c2ecf20Sopenharmony_ci u64 to = from + size; 768c2ecf20Sopenharmony_ci u64 cursor = from; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci while (cursor < to) { 798c2ecf20Sopenharmony_ci if (!devmem_is_allowed(pfn)) 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci cursor += PAGE_SIZE; 828c2ecf20Sopenharmony_ci pfn++; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci return 1; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci#else 878c2ecf20Sopenharmony_cistatic inline int page_is_allowed(unsigned long pfn) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci return 1; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_cistatic inline int range_is_allowed(unsigned long pfn, unsigned long size) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci return 1; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci#endif 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#ifndef unxlate_dev_mem_ptr 988c2ecf20Sopenharmony_ci#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr 998c2ecf20Sopenharmony_civoid __weak unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci#endif 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline bool should_stop_iteration(void) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci if (need_resched()) 1078c2ecf20Sopenharmony_ci cond_resched(); 1088c2ecf20Sopenharmony_ci return fatal_signal_pending(current); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * This funcion reads the *physical* memory. The f_pos points directly to the 1138c2ecf20Sopenharmony_ci * memory location. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic ssize_t read_mem(struct file *file, char __user *buf, 1168c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci phys_addr_t p = *ppos; 1198c2ecf20Sopenharmony_ci ssize_t read, sz; 1208c2ecf20Sopenharmony_ci void *ptr; 1218c2ecf20Sopenharmony_ci char *bounce; 1228c2ecf20Sopenharmony_ci int err; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (p != *ppos) 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!valid_phys_addr_range(p, count)) 1288c2ecf20Sopenharmony_ci return -EFAULT; 1298c2ecf20Sopenharmony_ci read = 0; 1308c2ecf20Sopenharmony_ci#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED 1318c2ecf20Sopenharmony_ci /* we don't have page 0 mapped on sparc and m68k.. */ 1328c2ecf20Sopenharmony_ci if (p < PAGE_SIZE) { 1338c2ecf20Sopenharmony_ci sz = size_inside_page(p, count); 1348c2ecf20Sopenharmony_ci if (sz > 0) { 1358c2ecf20Sopenharmony_ci if (clear_user(buf, sz)) 1368c2ecf20Sopenharmony_ci return -EFAULT; 1378c2ecf20Sopenharmony_ci buf += sz; 1388c2ecf20Sopenharmony_ci p += sz; 1398c2ecf20Sopenharmony_ci count -= sz; 1408c2ecf20Sopenharmony_ci read += sz; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci#endif 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci bounce = kmalloc(PAGE_SIZE, GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (!bounce) 1478c2ecf20Sopenharmony_ci return -ENOMEM; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci while (count > 0) { 1508c2ecf20Sopenharmony_ci unsigned long remaining; 1518c2ecf20Sopenharmony_ci int allowed, probe; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci sz = size_inside_page(p, count); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci err = -EPERM; 1568c2ecf20Sopenharmony_ci allowed = page_is_allowed(p >> PAGE_SHIFT); 1578c2ecf20Sopenharmony_ci if (!allowed) 1588c2ecf20Sopenharmony_ci goto failed; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci err = -EFAULT; 1618c2ecf20Sopenharmony_ci if (allowed == 2) { 1628c2ecf20Sopenharmony_ci /* Show zeros for restricted memory. */ 1638c2ecf20Sopenharmony_ci remaining = clear_user(buf, sz); 1648c2ecf20Sopenharmony_ci } else { 1658c2ecf20Sopenharmony_ci /* 1668c2ecf20Sopenharmony_ci * On ia64 if a page has been mapped somewhere as 1678c2ecf20Sopenharmony_ci * uncached, then it must also be accessed uncached 1688c2ecf20Sopenharmony_ci * by the kernel or data corruption may occur. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci ptr = xlate_dev_mem_ptr(p); 1718c2ecf20Sopenharmony_ci if (!ptr) 1728c2ecf20Sopenharmony_ci goto failed; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci probe = copy_from_kernel_nofault(bounce, ptr, sz); 1758c2ecf20Sopenharmony_ci unxlate_dev_mem_ptr(p, ptr); 1768c2ecf20Sopenharmony_ci if (probe) 1778c2ecf20Sopenharmony_ci goto failed; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci remaining = copy_to_user(buf, bounce, sz); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (remaining) 1838c2ecf20Sopenharmony_ci goto failed; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci buf += sz; 1868c2ecf20Sopenharmony_ci p += sz; 1878c2ecf20Sopenharmony_ci count -= sz; 1888c2ecf20Sopenharmony_ci read += sz; 1898c2ecf20Sopenharmony_ci if (should_stop_iteration()) 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci kfree(bounce); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci *ppos += read; 1958c2ecf20Sopenharmony_ci return read; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cifailed: 1988c2ecf20Sopenharmony_ci kfree(bounce); 1998c2ecf20Sopenharmony_ci return err; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic ssize_t write_mem(struct file *file, const char __user *buf, 2038c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci phys_addr_t p = *ppos; 2068c2ecf20Sopenharmony_ci ssize_t written, sz; 2078c2ecf20Sopenharmony_ci unsigned long copied; 2088c2ecf20Sopenharmony_ci void *ptr; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (p != *ppos) 2118c2ecf20Sopenharmony_ci return -EFBIG; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!valid_phys_addr_range(p, count)) 2148c2ecf20Sopenharmony_ci return -EFAULT; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci written = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED 2198c2ecf20Sopenharmony_ci /* we don't have page 0 mapped on sparc and m68k.. */ 2208c2ecf20Sopenharmony_ci if (p < PAGE_SIZE) { 2218c2ecf20Sopenharmony_ci sz = size_inside_page(p, count); 2228c2ecf20Sopenharmony_ci /* Hmm. Do something? */ 2238c2ecf20Sopenharmony_ci buf += sz; 2248c2ecf20Sopenharmony_ci p += sz; 2258c2ecf20Sopenharmony_ci count -= sz; 2268c2ecf20Sopenharmony_ci written += sz; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci#endif 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci while (count > 0) { 2318c2ecf20Sopenharmony_ci int allowed; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci sz = size_inside_page(p, count); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci allowed = page_is_allowed(p >> PAGE_SHIFT); 2368c2ecf20Sopenharmony_ci if (!allowed) 2378c2ecf20Sopenharmony_ci return -EPERM; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Skip actual writing when a page is marked as restricted. */ 2408c2ecf20Sopenharmony_ci if (allowed == 1) { 2418c2ecf20Sopenharmony_ci /* 2428c2ecf20Sopenharmony_ci * On ia64 if a page has been mapped somewhere as 2438c2ecf20Sopenharmony_ci * uncached, then it must also be accessed uncached 2448c2ecf20Sopenharmony_ci * by the kernel or data corruption may occur. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci ptr = xlate_dev_mem_ptr(p); 2478c2ecf20Sopenharmony_ci if (!ptr) { 2488c2ecf20Sopenharmony_ci if (written) 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci return -EFAULT; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci copied = copy_from_user(ptr, buf, sz); 2548c2ecf20Sopenharmony_ci unxlate_dev_mem_ptr(p, ptr); 2558c2ecf20Sopenharmony_ci if (copied) { 2568c2ecf20Sopenharmony_ci written += sz - copied; 2578c2ecf20Sopenharmony_ci if (written) 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci return -EFAULT; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci buf += sz; 2648c2ecf20Sopenharmony_ci p += sz; 2658c2ecf20Sopenharmony_ci count -= sz; 2668c2ecf20Sopenharmony_ci written += sz; 2678c2ecf20Sopenharmony_ci if (should_stop_iteration()) 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci *ppos += written; 2728c2ecf20Sopenharmony_ci return written; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ciint __weak phys_mem_access_prot_allowed(struct file *file, 2768c2ecf20Sopenharmony_ci unsigned long pfn, unsigned long size, pgprot_t *vma_prot) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci return 1; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci#ifndef __HAVE_PHYS_MEM_ACCESS_PROT 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * Architectures vary in how they handle caching for addresses 2858c2ecf20Sopenharmony_ci * outside of main memory. 2868c2ecf20Sopenharmony_ci * 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci#ifdef pgprot_noncached 2898c2ecf20Sopenharmony_cistatic int uncached_access(struct file *file, phys_addr_t addr) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci#if defined(CONFIG_IA64) 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * On ia64, we ignore O_DSYNC because we cannot tolerate memory 2948c2ecf20Sopenharmony_ci * attribute aliases. 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_ci return !(efi_mem_attributes(addr) & EFI_MEMORY_WB); 2978c2ecf20Sopenharmony_ci#elif defined(CONFIG_MIPS) 2988c2ecf20Sopenharmony_ci { 2998c2ecf20Sopenharmony_ci extern int __uncached_access(struct file *file, 3008c2ecf20Sopenharmony_ci unsigned long addr); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return __uncached_access(file, addr); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci#else 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * Accessing memory above the top the kernel knows about or through a 3078c2ecf20Sopenharmony_ci * file pointer 3088c2ecf20Sopenharmony_ci * that was marked O_DSYNC will be done non-cached. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci if (file->f_flags & O_DSYNC) 3118c2ecf20Sopenharmony_ci return 1; 3128c2ecf20Sopenharmony_ci return addr >= __pa(high_memory); 3138c2ecf20Sopenharmony_ci#endif 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci#endif 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, 3188c2ecf20Sopenharmony_ci unsigned long size, pgprot_t vma_prot) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci#ifdef pgprot_noncached 3218c2ecf20Sopenharmony_ci phys_addr_t offset = pfn << PAGE_SHIFT; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (uncached_access(file, offset)) 3248c2ecf20Sopenharmony_ci return pgprot_noncached(vma_prot); 3258c2ecf20Sopenharmony_ci#endif 3268c2ecf20Sopenharmony_ci return vma_prot; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci#endif 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 3318c2ecf20Sopenharmony_cistatic unsigned long get_unmapped_area_mem(struct file *file, 3328c2ecf20Sopenharmony_ci unsigned long addr, 3338c2ecf20Sopenharmony_ci unsigned long len, 3348c2ecf20Sopenharmony_ci unsigned long pgoff, 3358c2ecf20Sopenharmony_ci unsigned long flags) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci if (!valid_mmap_phys_addr_range(pgoff, len)) 3388c2ecf20Sopenharmony_ci return (unsigned long) -EINVAL; 3398c2ecf20Sopenharmony_ci return pgoff << PAGE_SHIFT; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/* permit direct mmap, for read, write or exec */ 3438c2ecf20Sopenharmony_cistatic unsigned memory_mmap_capabilities(struct file *file) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci return NOMMU_MAP_DIRECT | 3468c2ecf20Sopenharmony_ci NOMMU_MAP_READ | NOMMU_MAP_WRITE | NOMMU_MAP_EXEC; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic unsigned zero_mmap_capabilities(struct file *file) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci return NOMMU_MAP_COPY; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* can't do an in-place private mapping if there's no MMU */ 3558c2ecf20Sopenharmony_cistatic inline int private_mapping_ok(struct vm_area_struct *vma) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci return vma->vm_flags & VM_MAYSHARE; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci#else 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic inline int private_mapping_ok(struct vm_area_struct *vma) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci return 1; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci#endif 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic const struct vm_operations_struct mmap_mem_ops = { 3688c2ecf20Sopenharmony_ci#ifdef CONFIG_HAVE_IOREMAP_PROT 3698c2ecf20Sopenharmony_ci .access = generic_access_phys 3708c2ecf20Sopenharmony_ci#endif 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int mmap_mem(struct file *file, struct vm_area_struct *vma) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci size_t size = vma->vm_end - vma->vm_start; 3768c2ecf20Sopenharmony_ci phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Does it even fit in phys_addr_t? */ 3798c2ecf20Sopenharmony_ci if (offset >> PAGE_SHIFT != vma->vm_pgoff) 3808c2ecf20Sopenharmony_ci return -EINVAL; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* It's illegal to wrap around the end of the physical address space. */ 3838c2ecf20Sopenharmony_ci if (offset + (phys_addr_t)size - 1 < offset) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) 3878c2ecf20Sopenharmony_ci return -EINVAL; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (!private_mapping_ok(vma)) 3908c2ecf20Sopenharmony_ci return -ENOSYS; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (!range_is_allowed(vma->vm_pgoff, size)) 3938c2ecf20Sopenharmony_ci return -EPERM; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size, 3968c2ecf20Sopenharmony_ci &vma->vm_page_prot)) 3978c2ecf20Sopenharmony_ci return -EINVAL; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, 4008c2ecf20Sopenharmony_ci size, 4018c2ecf20Sopenharmony_ci vma->vm_page_prot); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci vma->vm_ops = &mmap_mem_ops; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* Remap-pfn-range will mark the range VM_IO */ 4068c2ecf20Sopenharmony_ci if (remap_pfn_range(vma, 4078c2ecf20Sopenharmony_ci vma->vm_start, 4088c2ecf20Sopenharmony_ci vma->vm_pgoff, 4098c2ecf20Sopenharmony_ci size, 4108c2ecf20Sopenharmony_ci vma->vm_page_prot)) { 4118c2ecf20Sopenharmony_ci return -EAGAIN; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int mmap_kmem(struct file *file, struct vm_area_struct *vma) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci unsigned long pfn; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Turn a kernel-virtual address into a physical page frame */ 4218c2ecf20Sopenharmony_ci pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* 4248c2ecf20Sopenharmony_ci * RED-PEN: on some architectures there is more mapped memory than 4258c2ecf20Sopenharmony_ci * available in mem_map which pfn_valid checks for. Perhaps should add a 4268c2ecf20Sopenharmony_ci * new macro here. 4278c2ecf20Sopenharmony_ci * 4288c2ecf20Sopenharmony_ci * RED-PEN: vmalloc is not supported right now. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci if (!pfn_valid(pfn)) 4318c2ecf20Sopenharmony_ci return -EIO; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci vma->vm_pgoff = pfn; 4348c2ecf20Sopenharmony_ci return mmap_mem(file, vma); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/* 4388c2ecf20Sopenharmony_ci * This function reads the *virtual* memory as seen by the kernel. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_cistatic ssize_t read_kmem(struct file *file, char __user *buf, 4418c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci unsigned long p = *ppos; 4448c2ecf20Sopenharmony_ci ssize_t low_count, read, sz; 4458c2ecf20Sopenharmony_ci char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ 4468c2ecf20Sopenharmony_ci int err = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci read = 0; 4498c2ecf20Sopenharmony_ci if (p < (unsigned long) high_memory) { 4508c2ecf20Sopenharmony_ci low_count = count; 4518c2ecf20Sopenharmony_ci if (count > (unsigned long)high_memory - p) 4528c2ecf20Sopenharmony_ci low_count = (unsigned long)high_memory - p; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED 4558c2ecf20Sopenharmony_ci /* we don't have page 0 mapped on sparc and m68k.. */ 4568c2ecf20Sopenharmony_ci if (p < PAGE_SIZE && low_count > 0) { 4578c2ecf20Sopenharmony_ci sz = size_inside_page(p, low_count); 4588c2ecf20Sopenharmony_ci if (clear_user(buf, sz)) 4598c2ecf20Sopenharmony_ci return -EFAULT; 4608c2ecf20Sopenharmony_ci buf += sz; 4618c2ecf20Sopenharmony_ci p += sz; 4628c2ecf20Sopenharmony_ci read += sz; 4638c2ecf20Sopenharmony_ci low_count -= sz; 4648c2ecf20Sopenharmony_ci count -= sz; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci#endif 4678c2ecf20Sopenharmony_ci while (low_count > 0) { 4688c2ecf20Sopenharmony_ci sz = size_inside_page(p, low_count); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * On ia64 if a page has been mapped somewhere as 4728c2ecf20Sopenharmony_ci * uncached, then it must also be accessed uncached 4738c2ecf20Sopenharmony_ci * by the kernel or data corruption may occur 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci kbuf = xlate_dev_kmem_ptr((void *)p); 4768c2ecf20Sopenharmony_ci if (!virt_addr_valid(kbuf)) 4778c2ecf20Sopenharmony_ci return -ENXIO; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (copy_to_user(buf, kbuf, sz)) 4808c2ecf20Sopenharmony_ci return -EFAULT; 4818c2ecf20Sopenharmony_ci buf += sz; 4828c2ecf20Sopenharmony_ci p += sz; 4838c2ecf20Sopenharmony_ci read += sz; 4848c2ecf20Sopenharmony_ci low_count -= sz; 4858c2ecf20Sopenharmony_ci count -= sz; 4868c2ecf20Sopenharmony_ci if (should_stop_iteration()) { 4878c2ecf20Sopenharmony_ci count = 0; 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (count > 0) { 4948c2ecf20Sopenharmony_ci kbuf = (char *)__get_free_page(GFP_KERNEL); 4958c2ecf20Sopenharmony_ci if (!kbuf) 4968c2ecf20Sopenharmony_ci return -ENOMEM; 4978c2ecf20Sopenharmony_ci while (count > 0) { 4988c2ecf20Sopenharmony_ci sz = size_inside_page(p, count); 4998c2ecf20Sopenharmony_ci if (!is_vmalloc_or_module_addr((void *)p)) { 5008c2ecf20Sopenharmony_ci err = -ENXIO; 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci sz = vread(kbuf, (char *)p, sz); 5048c2ecf20Sopenharmony_ci if (!sz) 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci if (copy_to_user(buf, kbuf, sz)) { 5078c2ecf20Sopenharmony_ci err = -EFAULT; 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci count -= sz; 5118c2ecf20Sopenharmony_ci buf += sz; 5128c2ecf20Sopenharmony_ci read += sz; 5138c2ecf20Sopenharmony_ci p += sz; 5148c2ecf20Sopenharmony_ci if (should_stop_iteration()) 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci free_page((unsigned long)kbuf); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci *ppos = p; 5208c2ecf20Sopenharmony_ci return read ? read : err; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic ssize_t do_write_kmem(unsigned long p, const char __user *buf, 5258c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci ssize_t written, sz; 5288c2ecf20Sopenharmony_ci unsigned long copied; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci written = 0; 5318c2ecf20Sopenharmony_ci#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED 5328c2ecf20Sopenharmony_ci /* we don't have page 0 mapped on sparc and m68k.. */ 5338c2ecf20Sopenharmony_ci if (p < PAGE_SIZE) { 5348c2ecf20Sopenharmony_ci sz = size_inside_page(p, count); 5358c2ecf20Sopenharmony_ci /* Hmm. Do something? */ 5368c2ecf20Sopenharmony_ci buf += sz; 5378c2ecf20Sopenharmony_ci p += sz; 5388c2ecf20Sopenharmony_ci count -= sz; 5398c2ecf20Sopenharmony_ci written += sz; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci#endif 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci while (count > 0) { 5448c2ecf20Sopenharmony_ci void *ptr; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci sz = size_inside_page(p, count); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * On ia64 if a page has been mapped somewhere as uncached, then 5508c2ecf20Sopenharmony_ci * it must also be accessed uncached by the kernel or data 5518c2ecf20Sopenharmony_ci * corruption may occur. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci ptr = xlate_dev_kmem_ptr((void *)p); 5548c2ecf20Sopenharmony_ci if (!virt_addr_valid(ptr)) 5558c2ecf20Sopenharmony_ci return -ENXIO; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci copied = copy_from_user(ptr, buf, sz); 5588c2ecf20Sopenharmony_ci if (copied) { 5598c2ecf20Sopenharmony_ci written += sz - copied; 5608c2ecf20Sopenharmony_ci if (written) 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci return -EFAULT; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci buf += sz; 5658c2ecf20Sopenharmony_ci p += sz; 5668c2ecf20Sopenharmony_ci count -= sz; 5678c2ecf20Sopenharmony_ci written += sz; 5688c2ecf20Sopenharmony_ci if (should_stop_iteration()) 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci *ppos += written; 5738c2ecf20Sopenharmony_ci return written; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/* 5778c2ecf20Sopenharmony_ci * This function writes to the *virtual* memory as seen by the kernel. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_cistatic ssize_t write_kmem(struct file *file, const char __user *buf, 5808c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci unsigned long p = *ppos; 5838c2ecf20Sopenharmony_ci ssize_t wrote = 0; 5848c2ecf20Sopenharmony_ci ssize_t virtr = 0; 5858c2ecf20Sopenharmony_ci char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ 5868c2ecf20Sopenharmony_ci int err = 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (p < (unsigned long) high_memory) { 5898c2ecf20Sopenharmony_ci unsigned long to_write = min_t(unsigned long, count, 5908c2ecf20Sopenharmony_ci (unsigned long)high_memory - p); 5918c2ecf20Sopenharmony_ci wrote = do_write_kmem(p, buf, to_write, ppos); 5928c2ecf20Sopenharmony_ci if (wrote != to_write) 5938c2ecf20Sopenharmony_ci return wrote; 5948c2ecf20Sopenharmony_ci p += wrote; 5958c2ecf20Sopenharmony_ci buf += wrote; 5968c2ecf20Sopenharmony_ci count -= wrote; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (count > 0) { 6008c2ecf20Sopenharmony_ci kbuf = (char *)__get_free_page(GFP_KERNEL); 6018c2ecf20Sopenharmony_ci if (!kbuf) 6028c2ecf20Sopenharmony_ci return wrote ? wrote : -ENOMEM; 6038c2ecf20Sopenharmony_ci while (count > 0) { 6048c2ecf20Sopenharmony_ci unsigned long sz = size_inside_page(p, count); 6058c2ecf20Sopenharmony_ci unsigned long n; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (!is_vmalloc_or_module_addr((void *)p)) { 6088c2ecf20Sopenharmony_ci err = -ENXIO; 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci n = copy_from_user(kbuf, buf, sz); 6128c2ecf20Sopenharmony_ci if (n) { 6138c2ecf20Sopenharmony_ci err = -EFAULT; 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci vwrite(kbuf, (char *)p, sz); 6178c2ecf20Sopenharmony_ci count -= sz; 6188c2ecf20Sopenharmony_ci buf += sz; 6198c2ecf20Sopenharmony_ci virtr += sz; 6208c2ecf20Sopenharmony_ci p += sz; 6218c2ecf20Sopenharmony_ci if (should_stop_iteration()) 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci free_page((unsigned long)kbuf); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci *ppos = p; 6288c2ecf20Sopenharmony_ci return virtr + wrote ? : err; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic ssize_t read_port(struct file *file, char __user *buf, 6328c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci unsigned long i = *ppos; 6358c2ecf20Sopenharmony_ci char __user *tmp = buf; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (!access_ok(buf, count)) 6388c2ecf20Sopenharmony_ci return -EFAULT; 6398c2ecf20Sopenharmony_ci while (count-- > 0 && i < 65536) { 6408c2ecf20Sopenharmony_ci if (__put_user(inb(i), tmp) < 0) 6418c2ecf20Sopenharmony_ci return -EFAULT; 6428c2ecf20Sopenharmony_ci i++; 6438c2ecf20Sopenharmony_ci tmp++; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci *ppos = i; 6468c2ecf20Sopenharmony_ci return tmp-buf; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic ssize_t write_port(struct file *file, const char __user *buf, 6508c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci unsigned long i = *ppos; 6538c2ecf20Sopenharmony_ci const char __user *tmp = buf; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!access_ok(buf, count)) 6568c2ecf20Sopenharmony_ci return -EFAULT; 6578c2ecf20Sopenharmony_ci while (count-- > 0 && i < 65536) { 6588c2ecf20Sopenharmony_ci char c; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (__get_user(c, tmp)) { 6618c2ecf20Sopenharmony_ci if (tmp > buf) 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci return -EFAULT; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci outb(c, i); 6668c2ecf20Sopenharmony_ci i++; 6678c2ecf20Sopenharmony_ci tmp++; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci *ppos = i; 6708c2ecf20Sopenharmony_ci return tmp-buf; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic ssize_t read_null(struct file *file, char __user *buf, 6748c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic ssize_t write_null(struct file *file, const char __user *buf, 6808c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci return count; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic ssize_t read_iter_null(struct kiocb *iocb, struct iov_iter *to) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic ssize_t write_iter_null(struct kiocb *iocb, struct iov_iter *from) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci size_t count = iov_iter_count(from); 6938c2ecf20Sopenharmony_ci iov_iter_advance(from, count); 6948c2ecf20Sopenharmony_ci return count; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf, 6988c2ecf20Sopenharmony_ci struct splice_desc *sd) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci return sd->len; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic ssize_t splice_write_null(struct pipe_inode_info *pipe, struct file *out, 7048c2ecf20Sopenharmony_ci loff_t *ppos, size_t len, unsigned int flags) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci size_t written = 0; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci while (iov_iter_count(iter)) { 7148c2ecf20Sopenharmony_ci size_t chunk = iov_iter_count(iter), n; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (chunk > PAGE_SIZE) 7178c2ecf20Sopenharmony_ci chunk = PAGE_SIZE; /* Just for latency reasons */ 7188c2ecf20Sopenharmony_ci n = iov_iter_zero(chunk, iter); 7198c2ecf20Sopenharmony_ci if (!n && iov_iter_count(iter)) 7208c2ecf20Sopenharmony_ci return written ? written : -EFAULT; 7218c2ecf20Sopenharmony_ci written += n; 7228c2ecf20Sopenharmony_ci if (signal_pending(current)) 7238c2ecf20Sopenharmony_ci return written ? written : -ERESTARTSYS; 7248c2ecf20Sopenharmony_ci cond_resched(); 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci return written; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic ssize_t read_zero(struct file *file, char __user *buf, 7308c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci size_t cleared = 0; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci while (count) { 7358c2ecf20Sopenharmony_ci size_t chunk = min_t(size_t, count, PAGE_SIZE); 7368c2ecf20Sopenharmony_ci size_t left; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci left = clear_user(buf + cleared, chunk); 7398c2ecf20Sopenharmony_ci if (unlikely(left)) { 7408c2ecf20Sopenharmony_ci cleared += (chunk - left); 7418c2ecf20Sopenharmony_ci if (!cleared) 7428c2ecf20Sopenharmony_ci return -EFAULT; 7438c2ecf20Sopenharmony_ci break; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci cleared += chunk; 7468c2ecf20Sopenharmony_ci count -= chunk; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci if (signal_pending(current)) 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci cond_resched(); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return cleared; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int mmap_zero(struct file *file, struct vm_area_struct *vma) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 7598c2ecf20Sopenharmony_ci return -ENOSYS; 7608c2ecf20Sopenharmony_ci#endif 7618c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_SHARED) 7628c2ecf20Sopenharmony_ci return shmem_zero_setup(vma); 7638c2ecf20Sopenharmony_ci vma_set_anonymous(vma); 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic unsigned long get_unmapped_area_zero(struct file *file, 7688c2ecf20Sopenharmony_ci unsigned long addr, unsigned long len, 7698c2ecf20Sopenharmony_ci unsigned long pgoff, unsigned long flags) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 7728c2ecf20Sopenharmony_ci if (flags & MAP_SHARED) { 7738c2ecf20Sopenharmony_ci /* 7748c2ecf20Sopenharmony_ci * mmap_zero() will call shmem_zero_setup() to create a file, 7758c2ecf20Sopenharmony_ci * so use shmem's get_unmapped_area in case it can be huge; 7768c2ecf20Sopenharmony_ci * and pass NULL for file as in mmap.c's get_unmapped_area(), 7778c2ecf20Sopenharmony_ci * so as not to confuse shmem with our handle on "/dev/zero". 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_ci return shmem_get_unmapped_area(NULL, addr, len, pgoff, flags); 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* Otherwise flags & MAP_PRIVATE: with no shmem object beneath it */ 7838c2ecf20Sopenharmony_ci return current->mm->get_unmapped_area(file, addr, len, pgoff, flags); 7848c2ecf20Sopenharmony_ci#else 7858c2ecf20Sopenharmony_ci return -ENOSYS; 7868c2ecf20Sopenharmony_ci#endif 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic ssize_t write_full(struct file *file, const char __user *buf, 7908c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci return -ENOSPC; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci/* 7968c2ecf20Sopenharmony_ci * Special lseek() function for /dev/null and /dev/zero. Most notably, you 7978c2ecf20Sopenharmony_ci * can fopen() both devices with "a" now. This was previously impossible. 7988c2ecf20Sopenharmony_ci * -- SRB. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_cistatic loff_t null_lseek(struct file *file, loff_t offset, int orig) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci return file->f_pos = 0; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci/* 8068c2ecf20Sopenharmony_ci * The memory devices use the full 32/64 bits of the offset, and so we cannot 8078c2ecf20Sopenharmony_ci * check against negative addresses: they are ok. The return value is weird, 8088c2ecf20Sopenharmony_ci * though, in that case (0). 8098c2ecf20Sopenharmony_ci * 8108c2ecf20Sopenharmony_ci * also note that seeking relative to the "end of file" isn't supported: 8118c2ecf20Sopenharmony_ci * it has no meaning, so it returns -EINVAL. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_cistatic loff_t memory_lseek(struct file *file, loff_t offset, int orig) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci loff_t ret; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci inode_lock(file_inode(file)); 8188c2ecf20Sopenharmony_ci switch (orig) { 8198c2ecf20Sopenharmony_ci case SEEK_CUR: 8208c2ecf20Sopenharmony_ci offset += file->f_pos; 8218c2ecf20Sopenharmony_ci fallthrough; 8228c2ecf20Sopenharmony_ci case SEEK_SET: 8238c2ecf20Sopenharmony_ci /* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */ 8248c2ecf20Sopenharmony_ci if ((unsigned long long)offset >= -MAX_ERRNO) { 8258c2ecf20Sopenharmony_ci ret = -EOVERFLOW; 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci file->f_pos = offset; 8298c2ecf20Sopenharmony_ci ret = file->f_pos; 8308c2ecf20Sopenharmony_ci force_successful_syscall_return(); 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci default: 8338c2ecf20Sopenharmony_ci ret = -EINVAL; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci inode_unlock(file_inode(file)); 8368c2ecf20Sopenharmony_ci return ret; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic struct inode *devmem_inode; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci#ifdef CONFIG_IO_STRICT_DEVMEM 8428c2ecf20Sopenharmony_civoid revoke_devmem(struct resource *res) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci /* pairs with smp_store_release() in devmem_init_inode() */ 8458c2ecf20Sopenharmony_ci struct inode *inode = smp_load_acquire(&devmem_inode); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* 8488c2ecf20Sopenharmony_ci * Check that the initialization has completed. Losing the race 8498c2ecf20Sopenharmony_ci * is ok because it means drivers are claiming resources before 8508c2ecf20Sopenharmony_ci * the fs_initcall level of init and prevent /dev/mem from 8518c2ecf20Sopenharmony_ci * establishing mappings. 8528c2ecf20Sopenharmony_ci */ 8538c2ecf20Sopenharmony_ci if (!inode) 8548c2ecf20Sopenharmony_ci return; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* 8578c2ecf20Sopenharmony_ci * The expectation is that the driver has successfully marked 8588c2ecf20Sopenharmony_ci * the resource busy by this point, so devmem_is_allowed() 8598c2ecf20Sopenharmony_ci * should start returning false, however for performance this 8608c2ecf20Sopenharmony_ci * does not iterate the entire resource range. 8618c2ecf20Sopenharmony_ci */ 8628c2ecf20Sopenharmony_ci if (devmem_is_allowed(PHYS_PFN(res->start)) && 8638c2ecf20Sopenharmony_ci devmem_is_allowed(PHYS_PFN(res->end))) { 8648c2ecf20Sopenharmony_ci /* 8658c2ecf20Sopenharmony_ci * *cringe* iomem=relaxed says "go ahead, what's the 8668c2ecf20Sopenharmony_ci * worst that can happen?" 8678c2ecf20Sopenharmony_ci */ 8688c2ecf20Sopenharmony_ci return; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci unmap_mapping_range(inode->i_mapping, res->start, resource_size(res), 1); 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci#endif 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic int open_port(struct inode *inode, struct file *filp) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci int rc; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 8808c2ecf20Sopenharmony_ci return -EPERM; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci rc = security_locked_down(LOCKDOWN_DEV_MEM); 8838c2ecf20Sopenharmony_ci if (rc) 8848c2ecf20Sopenharmony_ci return rc; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (iminor(inode) != DEVMEM_MINOR) 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* 8908c2ecf20Sopenharmony_ci * Use a unified address space to have a single point to manage 8918c2ecf20Sopenharmony_ci * revocations when drivers want to take over a /dev/mem mapped 8928c2ecf20Sopenharmony_ci * range. 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_ci inode->i_mapping = devmem_inode->i_mapping; 8958c2ecf20Sopenharmony_ci filp->f_mapping = inode->i_mapping; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci return 0; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci#define zero_lseek null_lseek 9018c2ecf20Sopenharmony_ci#define full_lseek null_lseek 9028c2ecf20Sopenharmony_ci#define write_zero write_null 9038c2ecf20Sopenharmony_ci#define write_iter_zero write_iter_null 9048c2ecf20Sopenharmony_ci#define open_mem open_port 9058c2ecf20Sopenharmony_ci#define open_kmem open_mem 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic const struct file_operations __maybe_unused mem_fops = { 9088c2ecf20Sopenharmony_ci .llseek = memory_lseek, 9098c2ecf20Sopenharmony_ci .read = read_mem, 9108c2ecf20Sopenharmony_ci .write = write_mem, 9118c2ecf20Sopenharmony_ci .mmap = mmap_mem, 9128c2ecf20Sopenharmony_ci .open = open_mem, 9138c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 9148c2ecf20Sopenharmony_ci .get_unmapped_area = get_unmapped_area_mem, 9158c2ecf20Sopenharmony_ci .mmap_capabilities = memory_mmap_capabilities, 9168c2ecf20Sopenharmony_ci#endif 9178c2ecf20Sopenharmony_ci}; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic const struct file_operations __maybe_unused kmem_fops = { 9208c2ecf20Sopenharmony_ci .llseek = memory_lseek, 9218c2ecf20Sopenharmony_ci .read = read_kmem, 9228c2ecf20Sopenharmony_ci .write = write_kmem, 9238c2ecf20Sopenharmony_ci .mmap = mmap_kmem, 9248c2ecf20Sopenharmony_ci .open = open_kmem, 9258c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 9268c2ecf20Sopenharmony_ci .get_unmapped_area = get_unmapped_area_mem, 9278c2ecf20Sopenharmony_ci .mmap_capabilities = memory_mmap_capabilities, 9288c2ecf20Sopenharmony_ci#endif 9298c2ecf20Sopenharmony_ci}; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic const struct file_operations null_fops = { 9328c2ecf20Sopenharmony_ci .llseek = null_lseek, 9338c2ecf20Sopenharmony_ci .read = read_null, 9348c2ecf20Sopenharmony_ci .write = write_null, 9358c2ecf20Sopenharmony_ci .read_iter = read_iter_null, 9368c2ecf20Sopenharmony_ci .write_iter = write_iter_null, 9378c2ecf20Sopenharmony_ci .splice_write = splice_write_null, 9388c2ecf20Sopenharmony_ci}; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic const struct file_operations __maybe_unused port_fops = { 9418c2ecf20Sopenharmony_ci .llseek = memory_lseek, 9428c2ecf20Sopenharmony_ci .read = read_port, 9438c2ecf20Sopenharmony_ci .write = write_port, 9448c2ecf20Sopenharmony_ci .open = open_port, 9458c2ecf20Sopenharmony_ci}; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic const struct file_operations zero_fops = { 9488c2ecf20Sopenharmony_ci .llseek = zero_lseek, 9498c2ecf20Sopenharmony_ci .write = write_zero, 9508c2ecf20Sopenharmony_ci .read_iter = read_iter_zero, 9518c2ecf20Sopenharmony_ci .read = read_zero, 9528c2ecf20Sopenharmony_ci .write_iter = write_iter_zero, 9538c2ecf20Sopenharmony_ci .mmap = mmap_zero, 9548c2ecf20Sopenharmony_ci .get_unmapped_area = get_unmapped_area_zero, 9558c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 9568c2ecf20Sopenharmony_ci .mmap_capabilities = zero_mmap_capabilities, 9578c2ecf20Sopenharmony_ci#endif 9588c2ecf20Sopenharmony_ci}; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic const struct file_operations full_fops = { 9618c2ecf20Sopenharmony_ci .llseek = full_lseek, 9628c2ecf20Sopenharmony_ci .read_iter = read_iter_zero, 9638c2ecf20Sopenharmony_ci .write = write_full, 9648c2ecf20Sopenharmony_ci}; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic const struct memdev { 9678c2ecf20Sopenharmony_ci const char *name; 9688c2ecf20Sopenharmony_ci umode_t mode; 9698c2ecf20Sopenharmony_ci const struct file_operations *fops; 9708c2ecf20Sopenharmony_ci fmode_t fmode; 9718c2ecf20Sopenharmony_ci} devlist[] = { 9728c2ecf20Sopenharmony_ci#ifdef CONFIG_DEVMEM 9738c2ecf20Sopenharmony_ci [DEVMEM_MINOR] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET }, 9748c2ecf20Sopenharmony_ci#endif 9758c2ecf20Sopenharmony_ci#ifdef CONFIG_DEVKMEM 9768c2ecf20Sopenharmony_ci [2] = { "kmem", 0, &kmem_fops, FMODE_UNSIGNED_OFFSET }, 9778c2ecf20Sopenharmony_ci#endif 9788c2ecf20Sopenharmony_ci [3] = { "null", 0666, &null_fops, 0 }, 9798c2ecf20Sopenharmony_ci#ifdef CONFIG_DEVPORT 9808c2ecf20Sopenharmony_ci [4] = { "port", 0, &port_fops, 0 }, 9818c2ecf20Sopenharmony_ci#endif 9828c2ecf20Sopenharmony_ci [5] = { "zero", 0666, &zero_fops, 0 }, 9838c2ecf20Sopenharmony_ci [7] = { "full", 0666, &full_fops, 0 }, 9848c2ecf20Sopenharmony_ci [8] = { "random", 0666, &random_fops, FMODE_NOWAIT }, 9858c2ecf20Sopenharmony_ci [9] = { "urandom", 0666, &urandom_fops, FMODE_NOWAIT }, 9868c2ecf20Sopenharmony_ci#ifdef CONFIG_PRINTK 9878c2ecf20Sopenharmony_ci [11] = { "kmsg", 0644, &kmsg_fops, 0 }, 9888c2ecf20Sopenharmony_ci#endif 9898c2ecf20Sopenharmony_ci}; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic int memory_open(struct inode *inode, struct file *filp) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci int minor; 9948c2ecf20Sopenharmony_ci const struct memdev *dev; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci minor = iminor(inode); 9978c2ecf20Sopenharmony_ci if (minor >= ARRAY_SIZE(devlist)) 9988c2ecf20Sopenharmony_ci return -ENXIO; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci dev = &devlist[minor]; 10018c2ecf20Sopenharmony_ci if (!dev->fops) 10028c2ecf20Sopenharmony_ci return -ENXIO; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci filp->f_op = dev->fops; 10058c2ecf20Sopenharmony_ci filp->f_mode |= dev->fmode; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (dev->fops->open) 10088c2ecf20Sopenharmony_ci return dev->fops->open(inode, filp); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci return 0; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic const struct file_operations memory_fops = { 10148c2ecf20Sopenharmony_ci .open = memory_open, 10158c2ecf20Sopenharmony_ci .llseek = noop_llseek, 10168c2ecf20Sopenharmony_ci}; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic char *mem_devnode(struct device *dev, umode_t *mode) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci if (mode && devlist[MINOR(dev->devt)].mode) 10218c2ecf20Sopenharmony_ci *mode = devlist[MINOR(dev->devt)].mode; 10228c2ecf20Sopenharmony_ci return NULL; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic struct class *mem_class; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic int devmem_fs_init_fs_context(struct fs_context *fc) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci return init_pseudo(fc, DEVMEM_MAGIC) ? 0 : -ENOMEM; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic struct file_system_type devmem_fs_type = { 10338c2ecf20Sopenharmony_ci .name = "devmem", 10348c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10358c2ecf20Sopenharmony_ci .init_fs_context = devmem_fs_init_fs_context, 10368c2ecf20Sopenharmony_ci .kill_sb = kill_anon_super, 10378c2ecf20Sopenharmony_ci}; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic int devmem_init_inode(void) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci static struct vfsmount *devmem_vfs_mount; 10428c2ecf20Sopenharmony_ci static int devmem_fs_cnt; 10438c2ecf20Sopenharmony_ci struct inode *inode; 10448c2ecf20Sopenharmony_ci int rc; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci rc = simple_pin_fs(&devmem_fs_type, &devmem_vfs_mount, &devmem_fs_cnt); 10478c2ecf20Sopenharmony_ci if (rc < 0) { 10488c2ecf20Sopenharmony_ci pr_err("Cannot mount /dev/mem pseudo filesystem: %d\n", rc); 10498c2ecf20Sopenharmony_ci return rc; 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci inode = alloc_anon_inode(devmem_vfs_mount->mnt_sb); 10538c2ecf20Sopenharmony_ci if (IS_ERR(inode)) { 10548c2ecf20Sopenharmony_ci rc = PTR_ERR(inode); 10558c2ecf20Sopenharmony_ci pr_err("Cannot allocate inode for /dev/mem: %d\n", rc); 10568c2ecf20Sopenharmony_ci simple_release_fs(&devmem_vfs_mount, &devmem_fs_cnt); 10578c2ecf20Sopenharmony_ci return rc; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* 10618c2ecf20Sopenharmony_ci * Publish /dev/mem initialized. 10628c2ecf20Sopenharmony_ci * Pairs with smp_load_acquire() in revoke_devmem(). 10638c2ecf20Sopenharmony_ci */ 10648c2ecf20Sopenharmony_ci smp_store_release(&devmem_inode, inode); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci return 0; 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic int __init chr_dev_init(void) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci int minor; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (register_chrdev(MEM_MAJOR, "mem", &memory_fops)) 10748c2ecf20Sopenharmony_ci printk("unable to get major %d for memory devs\n", MEM_MAJOR); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci mem_class = class_create(THIS_MODULE, "mem"); 10778c2ecf20Sopenharmony_ci if (IS_ERR(mem_class)) 10788c2ecf20Sopenharmony_ci return PTR_ERR(mem_class); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci mem_class->devnode = mem_devnode; 10818c2ecf20Sopenharmony_ci for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) { 10828c2ecf20Sopenharmony_ci if (!devlist[minor].name) 10838c2ecf20Sopenharmony_ci continue; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* 10868c2ecf20Sopenharmony_ci * Create /dev/port? 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_ci if ((minor == DEVPORT_MINOR) && !arch_has_dev_port()) 10898c2ecf20Sopenharmony_ci continue; 10908c2ecf20Sopenharmony_ci if ((minor == DEVMEM_MINOR) && devmem_init_inode() != 0) 10918c2ecf20Sopenharmony_ci continue; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor), 10948c2ecf20Sopenharmony_ci NULL, devlist[minor].name); 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return tty_init(); 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cifs_initcall(chr_dev_init); 1101