778 lines · 16.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  linux/drivers/char/mem.c * *  Copyright (C) 1991, 1992  Linus Torvalds * *  Added devfs support. *    Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu> *  Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com> */ #include <linux/mm.h>#include <linux/miscdevice.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/mman.h>#include <linux/random.h>#include <linux/init.h>#include <linux/tty.h>#include <linux/capability.h>#include <linux/ptrace.h>#include <linux/device.h>#include <linux/highmem.h>#include <linux/backing-dev.h>#include <linux/shmem_fs.h>#include <linux/splice.h>#include <linux/pfn.h>#include <linux/export.h>#include <linux/io.h>#include <linux/uio.h>#include <linux/uaccess.h>#include <linux/security.h> #define DEVMEM_MINOR	1#define DEVPORT_MINOR	4 static inline unsigned long size_inside_page(unsigned long start,					     unsigned long size){	unsigned long sz; 	sz = PAGE_SIZE - (start & (PAGE_SIZE - 1)); 	return min(sz, size);} #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGEstatic inline int valid_phys_addr_range(phys_addr_t addr, size_t count){	return addr + count <= __pa(high_memory);} static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size){	return 1;}#endif #ifdef CONFIG_STRICT_DEVMEMstatic inline int page_is_allowed(unsigned long pfn){	return devmem_is_allowed(pfn);}#elsestatic inline int page_is_allowed(unsigned long pfn){	return 1;}#endif static inline bool should_stop_iteration(void){	if (need_resched())		cond_resched();	return signal_pending(current);} /* * This funcion reads the *physical* memory. The f_pos points directly to the * memory location. */static ssize_t read_mem(struct file *file, char __user *buf,			size_t count, loff_t *ppos){	phys_addr_t p = *ppos;	ssize_t read, sz;	void *ptr;	char *bounce;	int err; 	if (p != *ppos)		return 0; 	if (!valid_phys_addr_range(p, count))		return -EFAULT;	read = 0;#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED	/* we don't have page 0 mapped on sparc and m68k.. */	if (p < PAGE_SIZE) {		sz = size_inside_page(p, count);		if (sz > 0) {			if (clear_user(buf, sz))				return -EFAULT;			buf += sz;			p += sz;			count -= sz;			read += sz;		}	}#endif 	bounce = kmalloc(PAGE_SIZE, GFP_KERNEL);	if (!bounce)		return -ENOMEM; 	while (count > 0) {		unsigned long remaining;		int allowed, probe; 		sz = size_inside_page(p, count); 		err = -EPERM;		allowed = page_is_allowed(p >> PAGE_SHIFT);		if (!allowed)			goto failed; 		err = -EFAULT;		if (allowed == 2) {			/* Show zeros for restricted memory. */			remaining = clear_user(buf, sz);		} else {			/*			 * On ia64 if a page has been mapped somewhere as			 * uncached, then it must also be accessed uncached			 * by the kernel or data corruption may occur.			 */			ptr = xlate_dev_mem_ptr(p);			if (!ptr)				goto failed; 			probe = copy_from_kernel_nofault(bounce, ptr, sz);			unxlate_dev_mem_ptr(p, ptr);			if (probe)				goto failed; 			remaining = copy_to_user(buf, bounce, sz);		} 		if (remaining)			goto failed; 		buf += sz;		p += sz;		count -= sz;		read += sz;		if (should_stop_iteration())			break;	}	kfree(bounce); 	*ppos += read;	return read; failed:	kfree(bounce);	return err;} static ssize_t write_mem(struct file *file, const char __user *buf,			 size_t count, loff_t *ppos){	phys_addr_t p = *ppos;	ssize_t written, sz;	unsigned long copied;	void *ptr; 	if (p != *ppos)		return -EFBIG; 	if (!valid_phys_addr_range(p, count))		return -EFAULT; 	written = 0; #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED	/* we don't have page 0 mapped on sparc and m68k.. */	if (p < PAGE_SIZE) {		sz = size_inside_page(p, count);		/* Hmm. Do something? */		buf += sz;		p += sz;		count -= sz;		written += sz;	}#endif 	while (count > 0) {		int allowed; 		sz = size_inside_page(p, count); 		allowed = page_is_allowed(p >> PAGE_SHIFT);		if (!allowed)			return -EPERM; 		/* Skip actual writing when a page is marked as restricted. */		if (allowed == 1) {			/*			 * On ia64 if a page has been mapped somewhere as			 * uncached, then it must also be accessed uncached			 * by the kernel or data corruption may occur.			 */			ptr = xlate_dev_mem_ptr(p);			if (!ptr) {				if (written)					break;				return -EFAULT;			} 			copied = copy_from_user(ptr, buf, sz);			unxlate_dev_mem_ptr(p, ptr);			if (copied) {				written += sz - copied;				if (written)					break;				return -EFAULT;			}		} 		buf += sz;		p += sz;		count -= sz;		written += sz;		if (should_stop_iteration())			break;	} 	*ppos += written;	return written;} int __weak phys_mem_access_prot_allowed(struct file *file,	unsigned long pfn, unsigned long size, pgprot_t *vma_prot){	return 1;} #ifndef __HAVE_PHYS_MEM_ACCESS_PROT /* * Architectures vary in how they handle caching for addresses * outside of main memory. * */#ifdef pgprot_noncachedstatic int uncached_access(struct file *file, phys_addr_t addr){	/*	 * Accessing memory above the top the kernel knows about or through a	 * file pointer	 * that was marked O_DSYNC will be done non-cached.	 */	if (file->f_flags & O_DSYNC)		return 1;	return addr >= __pa(high_memory);}#endif static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,				     unsigned long size, pgprot_t vma_prot){#ifdef pgprot_noncached	phys_addr_t offset = pfn << PAGE_SHIFT; 	if (uncached_access(file, offset))		return pgprot_noncached(vma_prot);#endif	return vma_prot;}#endif #ifndef CONFIG_MMUstatic unsigned long get_unmapped_area_mem(struct file *file,					   unsigned long addr,					   unsigned long len,					   unsigned long pgoff,					   unsigned long flags){	if (!valid_mmap_phys_addr_range(pgoff, len))		return (unsigned long) -EINVAL;	return pgoff << PAGE_SHIFT;} /* permit direct mmap, for read, write or exec */static unsigned memory_mmap_capabilities(struct file *file){	return NOMMU_MAP_DIRECT |		NOMMU_MAP_READ | NOMMU_MAP_WRITE | NOMMU_MAP_EXEC;} static unsigned zero_mmap_capabilities(struct file *file){	return NOMMU_MAP_COPY;} /* can't do an in-place private mapping if there's no MMU */static inline int private_mapping_ok(struct vm_area_desc *desc){	return is_nommu_shared_vma_flags(&desc->vma_flags);}#else static inline int private_mapping_ok(struct vm_area_desc *desc){	return 1;}#endif static const struct vm_operations_struct mmap_mem_ops = {#ifdef CONFIG_HAVE_IOREMAP_PROT	.access = generic_access_phys#endif}; static int mmap_mem_prepare(struct vm_area_desc *desc){	struct file *file = desc->file;	const size_t size = vma_desc_size(desc);	const phys_addr_t offset = (phys_addr_t)desc->pgoff << PAGE_SHIFT; 	/* Does it even fit in phys_addr_t? */	if (offset >> PAGE_SHIFT != desc->pgoff)		return -EINVAL; 	/* It's illegal to wrap around the end of the physical address space. */	if (offset + (phys_addr_t)size - 1 < offset)		return -EINVAL; 	if (!valid_mmap_phys_addr_range(desc->pgoff, size))		return -EINVAL; 	if (!private_mapping_ok(desc))		return -ENOSYS; 	if (!range_is_allowed(desc->pgoff, size))		return -EPERM; 	if (!phys_mem_access_prot_allowed(file, desc->pgoff, size,					  &desc->page_prot))		return -EINVAL; 	desc->page_prot = phys_mem_access_prot(file, desc->pgoff,					       size,					       desc->page_prot); 	desc->vm_ops = &mmap_mem_ops; 	/* Remap-pfn-range will mark the range with the I/O flag. */	mmap_action_remap_full(desc, desc->pgoff);	desc->action.error_override = -EAGAIN; 	return 0;} #ifdef CONFIG_DEVPORTstatic ssize_t read_port(struct file *file, char __user *buf,			 size_t count, loff_t *ppos){	unsigned long i = *ppos;	char __user *tmp = buf; 	if (!access_ok(buf, count))		return -EFAULT;	while (count-- > 0 && i < 65536) {		if (__put_user(inb(i), tmp) < 0)			return -EFAULT;		i++;		tmp++;	}	*ppos = i;	return tmp-buf;} static ssize_t write_port(struct file *file, const char __user *buf,			  size_t count, loff_t *ppos){	unsigned long i = *ppos;	const char __user *tmp = buf; 	if (!access_ok(buf, count))		return -EFAULT;	while (count-- > 0 && i < 65536) {		char c; 		if (__get_user(c, tmp)) {			if (tmp > buf)				break;			return -EFAULT;		}		outb(c, i);		i++;		tmp++;	}	*ppos = i;	return tmp-buf;}#endif static ssize_t read_null(struct file *file, char __user *buf,			 size_t count, loff_t *ppos){	return 0;} static ssize_t write_null(struct file *file, const char __user *buf,			  size_t count, loff_t *ppos){	return count;} static ssize_t read_iter_null(struct kiocb *iocb, struct iov_iter *to){	return 0;} static ssize_t write_iter_null(struct kiocb *iocb, struct iov_iter *from){	size_t count = iov_iter_count(from);	iov_iter_advance(from, count);	return count;} static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,			struct splice_desc *sd){	return sd->len;} static ssize_t splice_write_null(struct pipe_inode_info *pipe, struct file *out,				 loff_t *ppos, size_t len, unsigned int flags){	return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null);} static int uring_cmd_null(struct io_uring_cmd *ioucmd, unsigned int issue_flags){	return 0;} static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter){	size_t written = 0; 	while (iov_iter_count(iter)) {		size_t chunk = iov_iter_count(iter), n; 		if (chunk > PAGE_SIZE)			chunk = PAGE_SIZE;	/* Just for latency reasons */		n = iov_iter_zero(chunk, iter);		if (!n && iov_iter_count(iter))			return written ? written : -EFAULT;		written += n;		if (signal_pending(current))			return written ? written : -ERESTARTSYS;		if (!need_resched())			continue;		if (iocb->ki_flags & IOCB_NOWAIT)			return written ? written : -EAGAIN;		cond_resched();	}	return written;} static ssize_t read_zero(struct file *file, char __user *buf,			 size_t count, loff_t *ppos){	size_t cleared = 0; 	while (count) {		size_t chunk = min_t(size_t, count, PAGE_SIZE);		size_t left; 		left = clear_user(buf + cleared, chunk);		if (unlikely(left)) {			cleared += (chunk - left);			if (!cleared)				return -EFAULT;			break;		}		cleared += chunk;		count -= chunk; 		if (signal_pending(current))			break;		cond_resched();	} 	return cleared;} 

Lines 1–500 of 778.