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