xref: /openbmc/linux/drivers/char/mem.c (revision af901ca181d92aac3a7dc265144a9081a86d8f39)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/drivers/char/mem.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (C) 1991, 1992  Linus Torvalds
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *  Added devfs support.
71da177e4SLinus Torvalds  *    Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
8*af901ca1SAndré Goddard Rosa  *  Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/mm.h>
121da177e4SLinus Torvalds #include <linux/miscdevice.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/vmalloc.h>
151da177e4SLinus Torvalds #include <linux/mman.h>
161da177e4SLinus Torvalds #include <linux/random.h>
171da177e4SLinus Torvalds #include <linux/init.h>
181da177e4SLinus Torvalds #include <linux/raw.h>
191da177e4SLinus Torvalds #include <linux/tty.h>
201da177e4SLinus Torvalds #include <linux/capability.h>
211da177e4SLinus Torvalds #include <linux/ptrace.h>
221da177e4SLinus Torvalds #include <linux/device.h>
2350b1fdbdSVivek Goyal #include <linux/highmem.h>
2450b1fdbdSVivek Goyal #include <linux/crash_dump.h>
251da177e4SLinus Torvalds #include <linux/backing-dev.h>
26315c215cSVivek Goyal #include <linux/bootmem.h>
27d6b29d7cSJens Axboe #include <linux/splice.h>
28b8a3ad5bSLinus Torvalds #include <linux/pfn.h>
291f439647SJonathan Corbet #include <linux/smp_lock.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #include <asm/uaccess.h>
321da177e4SLinus Torvalds #include <asm/io.h>
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #ifdef CONFIG_IA64
351da177e4SLinus Torvalds # include <linux/efi.h>
361da177e4SLinus Torvalds #endif
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds  * Architectures vary in how they handle caching for addresses
401da177e4SLinus Torvalds  * outside of main memory.
411da177e4SLinus Torvalds  *
421da177e4SLinus Torvalds  */
431da177e4SLinus Torvalds static inline int uncached_access(struct file *file, unsigned long addr)
441da177e4SLinus Torvalds {
45f0970c13Svenkatesh.pallipadi@intel.com #if defined(CONFIG_IA64)
461da177e4SLinus Torvalds 	/*
471da177e4SLinus Torvalds 	 * On ia64, we ignore O_SYNC because we cannot tolerate memory attribute aliases.
481da177e4SLinus Torvalds 	 */
491da177e4SLinus Torvalds 	return !(efi_mem_attributes(addr) & EFI_MEMORY_WB);
5024e9d0b9SRalf Baechle #elif defined(CONFIG_MIPS)
5124e9d0b9SRalf Baechle 	{
5224e9d0b9SRalf Baechle 		extern int __uncached_access(struct file *file,
5324e9d0b9SRalf Baechle 					     unsigned long addr);
5424e9d0b9SRalf Baechle 
5524e9d0b9SRalf Baechle 		return __uncached_access(file, addr);
5624e9d0b9SRalf Baechle 	}
571da177e4SLinus Torvalds #else
581da177e4SLinus Torvalds 	/*
591da177e4SLinus Torvalds 	 * Accessing memory above the top the kernel knows about or through a file pointer
601da177e4SLinus Torvalds 	 * that was marked O_SYNC will be done non-cached.
611da177e4SLinus Torvalds 	 */
621da177e4SLinus Torvalds 	if (file->f_flags & O_SYNC)
631da177e4SLinus Torvalds 		return 1;
641da177e4SLinus Torvalds 	return addr >= __pa(high_memory);
651da177e4SLinus Torvalds #endif
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
69136939a2SBjorn Helgaas static inline int valid_phys_addr_range(unsigned long addr, size_t count)
701da177e4SLinus Torvalds {
71136939a2SBjorn Helgaas 	if (addr + count > __pa(high_memory))
721da177e4SLinus Torvalds 		return 0;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	return 1;
751da177e4SLinus Torvalds }
7680851ef2SBjorn Helgaas 
7706c67befSLennert Buytenhek static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
7880851ef2SBjorn Helgaas {
7980851ef2SBjorn Helgaas 	return 1;
8080851ef2SBjorn Helgaas }
811da177e4SLinus Torvalds #endif
821da177e4SLinus Torvalds 
83d092633bSIngo Molnar #ifdef CONFIG_STRICT_DEVMEM
84e2beb3eaSVenki Pallipadi static inline int range_is_allowed(unsigned long pfn, unsigned long size)
85ae531c26SArjan van de Ven {
86e2beb3eaSVenki Pallipadi 	u64 from = ((u64)pfn) << PAGE_SHIFT;
87e2beb3eaSVenki Pallipadi 	u64 to = from + size;
88e2beb3eaSVenki Pallipadi 	u64 cursor = from;
89ae531c26SArjan van de Ven 
90e2beb3eaSVenki Pallipadi 	while (cursor < to) {
91e2beb3eaSVenki Pallipadi 		if (!devmem_is_allowed(pfn)) {
92e2beb3eaSVenki Pallipadi 			printk(KERN_INFO
93e2beb3eaSVenki Pallipadi 		"Program %s tried to access /dev/mem between %Lx->%Lx.\n",
94ae531c26SArjan van de Ven 				current->comm, from, to);
95ae531c26SArjan van de Ven 			return 0;
96ae531c26SArjan van de Ven 		}
97e2beb3eaSVenki Pallipadi 		cursor += PAGE_SIZE;
98e2beb3eaSVenki Pallipadi 		pfn++;
99ae531c26SArjan van de Ven 	}
100ae531c26SArjan van de Ven 	return 1;
101ae531c26SArjan van de Ven }
102ae531c26SArjan van de Ven #else
103e2beb3eaSVenki Pallipadi static inline int range_is_allowed(unsigned long pfn, unsigned long size)
104ae531c26SArjan van de Ven {
105ae531c26SArjan van de Ven 	return 1;
106ae531c26SArjan van de Ven }
107ae531c26SArjan van de Ven #endif
108ae531c26SArjan van de Ven 
109e045fb2aSvenkatesh.pallipadi@intel.com void __attribute__((weak)) unxlate_dev_mem_ptr(unsigned long phys, void *addr)
110e045fb2aSvenkatesh.pallipadi@intel.com {
111e045fb2aSvenkatesh.pallipadi@intel.com }
112e045fb2aSvenkatesh.pallipadi@intel.com 
1131da177e4SLinus Torvalds /*
1141da177e4SLinus Torvalds  * This funcion reads the *physical* memory. The f_pos points directly to the
1151da177e4SLinus Torvalds  * memory location.
1161da177e4SLinus Torvalds  */
1171da177e4SLinus Torvalds static ssize_t read_mem(struct file * file, char __user * buf,
1181da177e4SLinus Torvalds 			size_t count, loff_t *ppos)
1191da177e4SLinus Torvalds {
1201da177e4SLinus Torvalds 	unsigned long p = *ppos;
1211da177e4SLinus Torvalds 	ssize_t read, sz;
1221da177e4SLinus Torvalds 	char *ptr;
1231da177e4SLinus Torvalds 
124136939a2SBjorn Helgaas 	if (!valid_phys_addr_range(p, count))
1251da177e4SLinus Torvalds 		return -EFAULT;
1261da177e4SLinus Torvalds 	read = 0;
1271da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
1281da177e4SLinus Torvalds 	/* we don't have page 0 mapped on sparc and m68k.. */
1291da177e4SLinus Torvalds 	if (p < PAGE_SIZE) {
1301da177e4SLinus Torvalds 		sz = PAGE_SIZE - p;
1311da177e4SLinus Torvalds 		if (sz > count)
1321da177e4SLinus Torvalds 			sz = count;
1331da177e4SLinus Torvalds 		if (sz > 0) {
1341da177e4SLinus Torvalds 			if (clear_user(buf, sz))
1351da177e4SLinus Torvalds 				return -EFAULT;
1361da177e4SLinus Torvalds 			buf += sz;
1371da177e4SLinus Torvalds 			p += sz;
1381da177e4SLinus Torvalds 			count -= sz;
1391da177e4SLinus Torvalds 			read += sz;
1401da177e4SLinus Torvalds 		}
1411da177e4SLinus Torvalds 	}
1421da177e4SLinus Torvalds #endif
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	while (count > 0) {
1451da177e4SLinus Torvalds 		/*
1461da177e4SLinus Torvalds 		 * Handle first page in case it's not aligned
1471da177e4SLinus Torvalds 		 */
1481da177e4SLinus Torvalds 		if (-p & (PAGE_SIZE - 1))
1491da177e4SLinus Torvalds 			sz = -p & (PAGE_SIZE - 1);
1501da177e4SLinus Torvalds 		else
1511da177e4SLinus Torvalds 			sz = PAGE_SIZE;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 		sz = min_t(unsigned long, sz, count);
1541da177e4SLinus Torvalds 
155e045fb2aSvenkatesh.pallipadi@intel.com 		if (!range_is_allowed(p >> PAGE_SHIFT, count))
156e045fb2aSvenkatesh.pallipadi@intel.com 			return -EPERM;
157e045fb2aSvenkatesh.pallipadi@intel.com 
1581da177e4SLinus Torvalds 		/*
1591da177e4SLinus Torvalds 		 * On ia64 if a page has been mapped somewhere as
1601da177e4SLinus Torvalds 		 * uncached, then it must also be accessed uncached
1611da177e4SLinus Torvalds 		 * by the kernel or data corruption may occur
1621da177e4SLinus Torvalds 		 */
1631da177e4SLinus Torvalds 		ptr = xlate_dev_mem_ptr(p);
164e045fb2aSvenkatesh.pallipadi@intel.com 		if (!ptr)
1651da177e4SLinus Torvalds 			return -EFAULT;
166e045fb2aSvenkatesh.pallipadi@intel.com 
167e045fb2aSvenkatesh.pallipadi@intel.com 		if (copy_to_user(buf, ptr, sz)) {
168e045fb2aSvenkatesh.pallipadi@intel.com 			unxlate_dev_mem_ptr(p, ptr);
169e045fb2aSvenkatesh.pallipadi@intel.com 			return -EFAULT;
170e045fb2aSvenkatesh.pallipadi@intel.com 		}
171e045fb2aSvenkatesh.pallipadi@intel.com 
172e045fb2aSvenkatesh.pallipadi@intel.com 		unxlate_dev_mem_ptr(p, ptr);
173e045fb2aSvenkatesh.pallipadi@intel.com 
1741da177e4SLinus Torvalds 		buf += sz;
1751da177e4SLinus Torvalds 		p += sz;
1761da177e4SLinus Torvalds 		count -= sz;
1771da177e4SLinus Torvalds 		read += sz;
1781da177e4SLinus Torvalds 	}
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	*ppos += read;
1811da177e4SLinus Torvalds 	return read;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds static ssize_t write_mem(struct file * file, const char __user * buf,
1851da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
1861da177e4SLinus Torvalds {
1871da177e4SLinus Torvalds 	unsigned long p = *ppos;
1881da177e4SLinus Torvalds 	ssize_t written, sz;
1891da177e4SLinus Torvalds 	unsigned long copied;
1901da177e4SLinus Torvalds 	void *ptr;
1911da177e4SLinus Torvalds 
192136939a2SBjorn Helgaas 	if (!valid_phys_addr_range(p, count))
1931da177e4SLinus Torvalds 		return -EFAULT;
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	written = 0;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
1981da177e4SLinus Torvalds 	/* we don't have page 0 mapped on sparc and m68k.. */
1991da177e4SLinus Torvalds 	if (p < PAGE_SIZE) {
2001da177e4SLinus Torvalds 		unsigned long sz = PAGE_SIZE - p;
2011da177e4SLinus Torvalds 		if (sz > count)
2021da177e4SLinus Torvalds 			sz = count;
2031da177e4SLinus Torvalds 		/* Hmm. Do something? */
2041da177e4SLinus Torvalds 		buf += sz;
2051da177e4SLinus Torvalds 		p += sz;
2061da177e4SLinus Torvalds 		count -= sz;
2071da177e4SLinus Torvalds 		written += sz;
2081da177e4SLinus Torvalds 	}
2091da177e4SLinus Torvalds #endif
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	while (count > 0) {
2121da177e4SLinus Torvalds 		/*
2131da177e4SLinus Torvalds 		 * Handle first page in case it's not aligned
2141da177e4SLinus Torvalds 		 */
2151da177e4SLinus Torvalds 		if (-p & (PAGE_SIZE - 1))
2161da177e4SLinus Torvalds 			sz = -p & (PAGE_SIZE - 1);
2171da177e4SLinus Torvalds 		else
2181da177e4SLinus Torvalds 			sz = PAGE_SIZE;
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 		sz = min_t(unsigned long, sz, count);
2211da177e4SLinus Torvalds 
222e045fb2aSvenkatesh.pallipadi@intel.com 		if (!range_is_allowed(p >> PAGE_SHIFT, sz))
223e045fb2aSvenkatesh.pallipadi@intel.com 			return -EPERM;
224e045fb2aSvenkatesh.pallipadi@intel.com 
2251da177e4SLinus Torvalds 		/*
2261da177e4SLinus Torvalds 		 * On ia64 if a page has been mapped somewhere as
2271da177e4SLinus Torvalds 		 * uncached, then it must also be accessed uncached
2281da177e4SLinus Torvalds 		 * by the kernel or data corruption may occur
2291da177e4SLinus Torvalds 		 */
2301da177e4SLinus Torvalds 		ptr = xlate_dev_mem_ptr(p);
231e045fb2aSvenkatesh.pallipadi@intel.com 		if (!ptr) {
232c654d60eSJan Beulich 			if (written)
233c654d60eSJan Beulich 				break;
2341da177e4SLinus Torvalds 			return -EFAULT;
2351da177e4SLinus Torvalds 		}
236e045fb2aSvenkatesh.pallipadi@intel.com 
237e045fb2aSvenkatesh.pallipadi@intel.com 		copied = copy_from_user(ptr, buf, sz);
238e045fb2aSvenkatesh.pallipadi@intel.com 		if (copied) {
239e045fb2aSvenkatesh.pallipadi@intel.com 			written += sz - copied;
240e045fb2aSvenkatesh.pallipadi@intel.com 			unxlate_dev_mem_ptr(p, ptr);
241e045fb2aSvenkatesh.pallipadi@intel.com 			if (written)
242e045fb2aSvenkatesh.pallipadi@intel.com 				break;
243e045fb2aSvenkatesh.pallipadi@intel.com 			return -EFAULT;
244e045fb2aSvenkatesh.pallipadi@intel.com 		}
245e045fb2aSvenkatesh.pallipadi@intel.com 
246e045fb2aSvenkatesh.pallipadi@intel.com 		unxlate_dev_mem_ptr(p, ptr);
247e045fb2aSvenkatesh.pallipadi@intel.com 
2481da177e4SLinus Torvalds 		buf += sz;
2491da177e4SLinus Torvalds 		p += sz;
2501da177e4SLinus Torvalds 		count -= sz;
2511da177e4SLinus Torvalds 		written += sz;
2521da177e4SLinus Torvalds 	}
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	*ppos += written;
2551da177e4SLinus Torvalds 	return written;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
258f0970c13Svenkatesh.pallipadi@intel.com int __attribute__((weak)) phys_mem_access_prot_allowed(struct file *file,
259f0970c13Svenkatesh.pallipadi@intel.com 	unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
260f0970c13Svenkatesh.pallipadi@intel.com {
261f0970c13Svenkatesh.pallipadi@intel.com 	return 1;
262f0970c13Svenkatesh.pallipadi@intel.com }
263f0970c13Svenkatesh.pallipadi@intel.com 
26444ac8413SBjorn Helgaas #ifndef __HAVE_PHYS_MEM_ACCESS_PROT
26544ac8413SBjorn Helgaas static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
26644ac8413SBjorn Helgaas 				     unsigned long size, pgprot_t vma_prot)
26744ac8413SBjorn Helgaas {
26844ac8413SBjorn Helgaas #ifdef pgprot_noncached
26944ac8413SBjorn Helgaas 	unsigned long offset = pfn << PAGE_SHIFT;
27044ac8413SBjorn Helgaas 
27144ac8413SBjorn Helgaas 	if (uncached_access(file, offset))
27244ac8413SBjorn Helgaas 		return pgprot_noncached(vma_prot);
27344ac8413SBjorn Helgaas #endif
27444ac8413SBjorn Helgaas 	return vma_prot;
27544ac8413SBjorn Helgaas }
27644ac8413SBjorn Helgaas #endif
27744ac8413SBjorn Helgaas 
2785da6185bSDavid Howells #ifndef CONFIG_MMU
2795da6185bSDavid Howells static unsigned long get_unmapped_area_mem(struct file *file,
2805da6185bSDavid Howells 					   unsigned long addr,
2815da6185bSDavid Howells 					   unsigned long len,
2825da6185bSDavid Howells 					   unsigned long pgoff,
2835da6185bSDavid Howells 					   unsigned long flags)
2845da6185bSDavid Howells {
2855da6185bSDavid Howells 	if (!valid_mmap_phys_addr_range(pgoff, len))
2865da6185bSDavid Howells 		return (unsigned long) -EINVAL;
2878a93258cSBenjamin Herrenschmidt 	return pgoff << PAGE_SHIFT;
2885da6185bSDavid Howells }
2895da6185bSDavid Howells 
2905da6185bSDavid Howells /* can't do an in-place private mapping if there's no MMU */
2915da6185bSDavid Howells static inline int private_mapping_ok(struct vm_area_struct *vma)
2925da6185bSDavid Howells {
2935da6185bSDavid Howells 	return vma->vm_flags & VM_MAYSHARE;
2945da6185bSDavid Howells }
2955da6185bSDavid Howells #else
2965da6185bSDavid Howells #define get_unmapped_area_mem	NULL
2975da6185bSDavid Howells 
2985da6185bSDavid Howells static inline int private_mapping_ok(struct vm_area_struct *vma)
2995da6185bSDavid Howells {
3005da6185bSDavid Howells 	return 1;
3015da6185bSDavid Howells }
3025da6185bSDavid Howells #endif
3035da6185bSDavid Howells 
304f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct mmap_mem_ops = {
3057ae8ed50SRik van Riel #ifdef CONFIG_HAVE_IOREMAP_PROT
3067ae8ed50SRik van Riel 	.access = generic_access_phys
3077ae8ed50SRik van Riel #endif
308e7f260a2Svenkatesh.pallipadi@intel.com };
309e7f260a2Svenkatesh.pallipadi@intel.com 
3101da177e4SLinus Torvalds static int mmap_mem(struct file * file, struct vm_area_struct * vma)
3111da177e4SLinus Torvalds {
31280851ef2SBjorn Helgaas 	size_t size = vma->vm_end - vma->vm_start;
31380851ef2SBjorn Helgaas 
31406c67befSLennert Buytenhek 	if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
31580851ef2SBjorn Helgaas 		return -EINVAL;
31680851ef2SBjorn Helgaas 
3175da6185bSDavid Howells 	if (!private_mapping_ok(vma))
3185da6185bSDavid Howells 		return -ENOSYS;
3195da6185bSDavid Howells 
320e2beb3eaSVenki Pallipadi 	if (!range_is_allowed(vma->vm_pgoff, size))
321e2beb3eaSVenki Pallipadi 		return -EPERM;
322e2beb3eaSVenki Pallipadi 
323f0970c13Svenkatesh.pallipadi@intel.com 	if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
324f0970c13Svenkatesh.pallipadi@intel.com 						&vma->vm_page_prot))
325f0970c13Svenkatesh.pallipadi@intel.com 		return -EINVAL;
326f0970c13Svenkatesh.pallipadi@intel.com 
3278b150478SRoland Dreier 	vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
32880851ef2SBjorn Helgaas 						 size,
3291da177e4SLinus Torvalds 						 vma->vm_page_prot);
3301da177e4SLinus Torvalds 
331e7f260a2Svenkatesh.pallipadi@intel.com 	vma->vm_ops = &mmap_mem_ops;
332e7f260a2Svenkatesh.pallipadi@intel.com 
3331da177e4SLinus Torvalds 	/* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
3341da177e4SLinus Torvalds 	if (remap_pfn_range(vma,
3351da177e4SLinus Torvalds 			    vma->vm_start,
3361da177e4SLinus Torvalds 			    vma->vm_pgoff,
33780851ef2SBjorn Helgaas 			    size,
338e7f260a2Svenkatesh.pallipadi@intel.com 			    vma->vm_page_prot)) {
3391da177e4SLinus Torvalds 		return -EAGAIN;
340e7f260a2Svenkatesh.pallipadi@intel.com 	}
3411da177e4SLinus Torvalds 	return 0;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds 
344b781ecb6SArjan van de Ven #ifdef CONFIG_DEVKMEM
3451da177e4SLinus Torvalds static int mmap_kmem(struct file * file, struct vm_area_struct * vma)
3461da177e4SLinus Torvalds {
3474bb82551SLinus Torvalds 	unsigned long pfn;
3484bb82551SLinus Torvalds 
3496d3154ccSLinus Torvalds 	/* Turn a kernel-virtual address into a physical page frame */
3506d3154ccSLinus Torvalds 	pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT;
3514bb82551SLinus Torvalds 
3521da177e4SLinus Torvalds 	/*
3531da177e4SLinus Torvalds 	 * RED-PEN: on some architectures there is more mapped memory
3541da177e4SLinus Torvalds 	 * than available in mem_map which pfn_valid checks
3551da177e4SLinus Torvalds 	 * for. Perhaps should add a new macro here.
3561da177e4SLinus Torvalds 	 *
3571da177e4SLinus Torvalds 	 * RED-PEN: vmalloc is not supported right now.
3581da177e4SLinus Torvalds 	 */
3594bb82551SLinus Torvalds 	if (!pfn_valid(pfn))
3601da177e4SLinus Torvalds 		return -EIO;
3614bb82551SLinus Torvalds 
3624bb82551SLinus Torvalds 	vma->vm_pgoff = pfn;
3631da177e4SLinus Torvalds 	return mmap_mem(file, vma);
3641da177e4SLinus Torvalds }
365b781ecb6SArjan van de Ven #endif
3661da177e4SLinus Torvalds 
36750b1fdbdSVivek Goyal #ifdef CONFIG_CRASH_DUMP
36850b1fdbdSVivek Goyal /*
36950b1fdbdSVivek Goyal  * Read memory corresponding to the old kernel.
37050b1fdbdSVivek Goyal  */
371315c215cSVivek Goyal static ssize_t read_oldmem(struct file *file, char __user *buf,
37250b1fdbdSVivek Goyal 				size_t count, loff_t *ppos)
37350b1fdbdSVivek Goyal {
374315c215cSVivek Goyal 	unsigned long pfn, offset;
37550b1fdbdSVivek Goyal 	size_t read = 0, csize;
376315c215cSVivek Goyal 	int rc = 0;
37750b1fdbdSVivek Goyal 
37850b1fdbdSVivek Goyal 	while (count) {
37950b1fdbdSVivek Goyal 		pfn = *ppos / PAGE_SIZE;
380315c215cSVivek Goyal 		if (pfn > saved_max_pfn)
381315c215cSVivek Goyal 			return read;
38250b1fdbdSVivek Goyal 
383315c215cSVivek Goyal 		offset = (unsigned long)(*ppos % PAGE_SIZE);
384315c215cSVivek Goyal 		if (count > PAGE_SIZE - offset)
385315c215cSVivek Goyal 			csize = PAGE_SIZE - offset;
386315c215cSVivek Goyal 		else
387315c215cSVivek Goyal 			csize = count;
38850b1fdbdSVivek Goyal 
389315c215cSVivek Goyal 		rc = copy_oldmem_page(pfn, buf, csize, offset, 1);
390315c215cSVivek Goyal 		if (rc < 0)
391315c215cSVivek Goyal 			return rc;
39250b1fdbdSVivek Goyal 		buf += csize;
39350b1fdbdSVivek Goyal 		*ppos += csize;
39450b1fdbdSVivek Goyal 		read += csize;
39550b1fdbdSVivek Goyal 		count -= csize;
39650b1fdbdSVivek Goyal 	}
39750b1fdbdSVivek Goyal 	return read;
39850b1fdbdSVivek Goyal }
39950b1fdbdSVivek Goyal #endif
40050b1fdbdSVivek Goyal 
401b781ecb6SArjan van de Ven #ifdef CONFIG_DEVKMEM
4021da177e4SLinus Torvalds /*
4031da177e4SLinus Torvalds  * This function reads the *virtual* memory as seen by the kernel.
4041da177e4SLinus Torvalds  */
4051da177e4SLinus Torvalds static ssize_t read_kmem(struct file *file, char __user *buf,
4061da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
4071da177e4SLinus Torvalds {
4081da177e4SLinus Torvalds 	unsigned long p = *ppos;
4091da177e4SLinus Torvalds 	ssize_t low_count, read, sz;
4101da177e4SLinus Torvalds 	char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	read = 0;
4131da177e4SLinus Torvalds 	if (p < (unsigned long) high_memory) {
4141da177e4SLinus Torvalds 		low_count = count;
4151da177e4SLinus Torvalds 		if (count > (unsigned long) high_memory - p)
4161da177e4SLinus Torvalds 			low_count = (unsigned long) high_memory - p;
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
4191da177e4SLinus Torvalds 		/* we don't have page 0 mapped on sparc and m68k.. */
4201da177e4SLinus Torvalds 		if (p < PAGE_SIZE && low_count > 0) {
4211da177e4SLinus Torvalds 			size_t tmp = PAGE_SIZE - p;
4221da177e4SLinus Torvalds 			if (tmp > low_count) tmp = low_count;
4231da177e4SLinus Torvalds 			if (clear_user(buf, tmp))
4241da177e4SLinus Torvalds 				return -EFAULT;
4251da177e4SLinus Torvalds 			buf += tmp;
4261da177e4SLinus Torvalds 			p += tmp;
4271da177e4SLinus Torvalds 			read += tmp;
4281da177e4SLinus Torvalds 			low_count -= tmp;
4291da177e4SLinus Torvalds 			count -= tmp;
4301da177e4SLinus Torvalds 		}
4311da177e4SLinus Torvalds #endif
4321da177e4SLinus Torvalds 		while (low_count > 0) {
4331da177e4SLinus Torvalds 			/*
4341da177e4SLinus Torvalds 			 * Handle first page in case it's not aligned
4351da177e4SLinus Torvalds 			 */
4361da177e4SLinus Torvalds 			if (-p & (PAGE_SIZE - 1))
4371da177e4SLinus Torvalds 				sz = -p & (PAGE_SIZE - 1);
4381da177e4SLinus Torvalds 			else
4391da177e4SLinus Torvalds 				sz = PAGE_SIZE;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 			sz = min_t(unsigned long, sz, low_count);
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 			/*
4441da177e4SLinus Torvalds 			 * On ia64 if a page has been mapped somewhere as
4451da177e4SLinus Torvalds 			 * uncached, then it must also be accessed uncached
4461da177e4SLinus Torvalds 			 * by the kernel or data corruption may occur
4471da177e4SLinus Torvalds 			 */
4481da177e4SLinus Torvalds 			kbuf = xlate_dev_kmem_ptr((char *)p);
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 			if (copy_to_user(buf, kbuf, sz))
4511da177e4SLinus Torvalds 				return -EFAULT;
4521da177e4SLinus Torvalds 			buf += sz;
4531da177e4SLinus Torvalds 			p += sz;
4541da177e4SLinus Torvalds 			read += sz;
4551da177e4SLinus Torvalds 			low_count -= sz;
4561da177e4SLinus Torvalds 			count -= sz;
4571da177e4SLinus Torvalds 		}
4581da177e4SLinus Torvalds 	}
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	if (count > 0) {
4611da177e4SLinus Torvalds 		kbuf = (char *)__get_free_page(GFP_KERNEL);
4621da177e4SLinus Torvalds 		if (!kbuf)
4631da177e4SLinus Torvalds 			return -ENOMEM;
4641da177e4SLinus Torvalds 		while (count > 0) {
4651da177e4SLinus Torvalds 			int len = count;
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 			if (len > PAGE_SIZE)
4681da177e4SLinus Torvalds 				len = PAGE_SIZE;
4691da177e4SLinus Torvalds 			len = vread(kbuf, (char *)p, len);
4701da177e4SLinus Torvalds 			if (!len)
4711da177e4SLinus Torvalds 				break;
4721da177e4SLinus Torvalds 			if (copy_to_user(buf, kbuf, len)) {
4731da177e4SLinus Torvalds 				free_page((unsigned long)kbuf);
4741da177e4SLinus Torvalds 				return -EFAULT;
4751da177e4SLinus Torvalds 			}
4761da177e4SLinus Torvalds 			count -= len;
4771da177e4SLinus Torvalds 			buf += len;
4781da177e4SLinus Torvalds 			read += len;
4791da177e4SLinus Torvalds 			p += len;
4801da177e4SLinus Torvalds 		}
4811da177e4SLinus Torvalds 		free_page((unsigned long)kbuf);
4821da177e4SLinus Torvalds 	}
4831da177e4SLinus Torvalds  	*ppos = p;
4841da177e4SLinus Torvalds  	return read;
4851da177e4SLinus Torvalds }
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds static inline ssize_t
4891da177e4SLinus Torvalds do_write_kmem(void *p, unsigned long realp, const char __user * buf,
4901da177e4SLinus Torvalds 	      size_t count, loff_t *ppos)
4911da177e4SLinus Torvalds {
4921da177e4SLinus Torvalds 	ssize_t written, sz;
4931da177e4SLinus Torvalds 	unsigned long copied;
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	written = 0;
4961da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
4971da177e4SLinus Torvalds 	/* we don't have page 0 mapped on sparc and m68k.. */
4981da177e4SLinus Torvalds 	if (realp < PAGE_SIZE) {
4991da177e4SLinus Torvalds 		unsigned long sz = PAGE_SIZE - realp;
5001da177e4SLinus Torvalds 		if (sz > count)
5011da177e4SLinus Torvalds 			sz = count;
5021da177e4SLinus Torvalds 		/* Hmm. Do something? */
5031da177e4SLinus Torvalds 		buf += sz;
5041da177e4SLinus Torvalds 		p += sz;
5051da177e4SLinus Torvalds 		realp += sz;
5061da177e4SLinus Torvalds 		count -= sz;
5071da177e4SLinus Torvalds 		written += sz;
5081da177e4SLinus Torvalds 	}
5091da177e4SLinus Torvalds #endif
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds 	while (count > 0) {
5121da177e4SLinus Torvalds 		char *ptr;
5131da177e4SLinus Torvalds 		/*
5141da177e4SLinus Torvalds 		 * Handle first page in case it's not aligned
5151da177e4SLinus Torvalds 		 */
5161da177e4SLinus Torvalds 		if (-realp & (PAGE_SIZE - 1))
5171da177e4SLinus Torvalds 			sz = -realp & (PAGE_SIZE - 1);
5181da177e4SLinus Torvalds 		else
5191da177e4SLinus Torvalds 			sz = PAGE_SIZE;
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 		sz = min_t(unsigned long, sz, count);
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds 		/*
5241da177e4SLinus Torvalds 		 * On ia64 if a page has been mapped somewhere as
5251da177e4SLinus Torvalds 		 * uncached, then it must also be accessed uncached
5261da177e4SLinus Torvalds 		 * by the kernel or data corruption may occur
5271da177e4SLinus Torvalds 		 */
5281da177e4SLinus Torvalds 		ptr = xlate_dev_kmem_ptr(p);
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 		copied = copy_from_user(ptr, buf, sz);
5311da177e4SLinus Torvalds 		if (copied) {
532c654d60eSJan Beulich 			written += sz - copied;
533c654d60eSJan Beulich 			if (written)
534c654d60eSJan Beulich 				break;
5351da177e4SLinus Torvalds 			return -EFAULT;
5361da177e4SLinus Torvalds 		}
5371da177e4SLinus Torvalds 		buf += sz;
5381da177e4SLinus Torvalds 		p += sz;
5391da177e4SLinus Torvalds 		realp += sz;
5401da177e4SLinus Torvalds 		count -= sz;
5411da177e4SLinus Torvalds 		written += sz;
5421da177e4SLinus Torvalds 	}
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	*ppos += written;
5451da177e4SLinus Torvalds 	return written;
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds /*
5501da177e4SLinus Torvalds  * This function writes to the *virtual* memory as seen by the kernel.
5511da177e4SLinus Torvalds  */
5521da177e4SLinus Torvalds static ssize_t write_kmem(struct file * file, const char __user * buf,
5531da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
5541da177e4SLinus Torvalds {
5551da177e4SLinus Torvalds 	unsigned long p = *ppos;
5561da177e4SLinus Torvalds 	ssize_t wrote = 0;
5571da177e4SLinus Torvalds 	ssize_t virtr = 0;
5581da177e4SLinus Torvalds 	ssize_t written;
5591da177e4SLinus Torvalds 	char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	if (p < (unsigned long) high_memory) {
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 		wrote = count;
5641da177e4SLinus Torvalds 		if (count > (unsigned long) high_memory - p)
5651da177e4SLinus Torvalds 			wrote = (unsigned long) high_memory - p;
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 		written = do_write_kmem((void*)p, p, buf, wrote, ppos);
5681da177e4SLinus Torvalds 		if (written != wrote)
5691da177e4SLinus Torvalds 			return written;
5701da177e4SLinus Torvalds 		wrote = written;
5711da177e4SLinus Torvalds 		p += wrote;
5721da177e4SLinus Torvalds 		buf += wrote;
5731da177e4SLinus Torvalds 		count -= wrote;
5741da177e4SLinus Torvalds 	}
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	if (count > 0) {
5771da177e4SLinus Torvalds 		kbuf = (char *)__get_free_page(GFP_KERNEL);
5781da177e4SLinus Torvalds 		if (!kbuf)
5791da177e4SLinus Torvalds 			return wrote ? wrote : -ENOMEM;
5801da177e4SLinus Torvalds 		while (count > 0) {
5811da177e4SLinus Torvalds 			int len = count;
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 			if (len > PAGE_SIZE)
5841da177e4SLinus Torvalds 				len = PAGE_SIZE;
5851da177e4SLinus Torvalds 			if (len) {
5861da177e4SLinus Torvalds 				written = copy_from_user(kbuf, buf, len);
5871da177e4SLinus Torvalds 				if (written) {
588c654d60eSJan Beulich 					if (wrote + virtr)
589c654d60eSJan Beulich 						break;
5901da177e4SLinus Torvalds 					free_page((unsigned long)kbuf);
591c654d60eSJan Beulich 					return -EFAULT;
5921da177e4SLinus Torvalds 				}
5931da177e4SLinus Torvalds 			}
5941da177e4SLinus Torvalds 			len = vwrite(kbuf, (char *)p, len);
5951da177e4SLinus Torvalds 			count -= len;
5961da177e4SLinus Torvalds 			buf += len;
5971da177e4SLinus Torvalds 			virtr += len;
5981da177e4SLinus Torvalds 			p += len;
5991da177e4SLinus Torvalds 		}
6001da177e4SLinus Torvalds 		free_page((unsigned long)kbuf);
6011da177e4SLinus Torvalds 	}
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds  	*ppos = p;
6041da177e4SLinus Torvalds  	return virtr + wrote;
6051da177e4SLinus Torvalds }
606b781ecb6SArjan van de Ven #endif
6071da177e4SLinus Torvalds 
6084f911d64SRussell King #ifdef CONFIG_DEVPORT
6091da177e4SLinus Torvalds static ssize_t read_port(struct file * file, char __user * buf,
6101da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
6111da177e4SLinus Torvalds {
6121da177e4SLinus Torvalds 	unsigned long i = *ppos;
6131da177e4SLinus Torvalds 	char __user *tmp = buf;
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	if (!access_ok(VERIFY_WRITE, buf, count))
6161da177e4SLinus Torvalds 		return -EFAULT;
6171da177e4SLinus Torvalds 	while (count-- > 0 && i < 65536) {
6181da177e4SLinus Torvalds 		if (__put_user(inb(i),tmp) < 0)
6191da177e4SLinus Torvalds 			return -EFAULT;
6201da177e4SLinus Torvalds 		i++;
6211da177e4SLinus Torvalds 		tmp++;
6221da177e4SLinus Torvalds 	}
6231da177e4SLinus Torvalds 	*ppos = i;
6241da177e4SLinus Torvalds 	return tmp-buf;
6251da177e4SLinus Torvalds }
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds static ssize_t write_port(struct file * file, const char __user * buf,
6281da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
6291da177e4SLinus Torvalds {
6301da177e4SLinus Torvalds 	unsigned long i = *ppos;
6311da177e4SLinus Torvalds 	const char __user * tmp = buf;
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds 	if (!access_ok(VERIFY_READ,buf,count))
6341da177e4SLinus Torvalds 		return -EFAULT;
6351da177e4SLinus Torvalds 	while (count-- > 0 && i < 65536) {
6361da177e4SLinus Torvalds 		char c;
637c654d60eSJan Beulich 		if (__get_user(c, tmp)) {
638c654d60eSJan Beulich 			if (tmp > buf)
639c654d60eSJan Beulich 				break;
6401da177e4SLinus Torvalds 			return -EFAULT;
641c654d60eSJan Beulich 		}
6421da177e4SLinus Torvalds 		outb(c,i);
6431da177e4SLinus Torvalds 		i++;
6441da177e4SLinus Torvalds 		tmp++;
6451da177e4SLinus Torvalds 	}
6461da177e4SLinus Torvalds 	*ppos = i;
6471da177e4SLinus Torvalds 	return tmp-buf;
6481da177e4SLinus Torvalds }
6491da177e4SLinus Torvalds #endif
6501da177e4SLinus Torvalds 
6511da177e4SLinus Torvalds static ssize_t read_null(struct file * file, char __user * buf,
6521da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
6531da177e4SLinus Torvalds {
6541da177e4SLinus Torvalds 	return 0;
6551da177e4SLinus Torvalds }
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds static ssize_t write_null(struct file * file, const char __user * buf,
6581da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
6591da177e4SLinus Torvalds {
6601da177e4SLinus Torvalds 	return count;
6611da177e4SLinus Torvalds }
6621da177e4SLinus Torvalds 
6631ebd32fcSJens Axboe static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,
6641ebd32fcSJens Axboe 			struct splice_desc *sd)
6651ebd32fcSJens Axboe {
6661ebd32fcSJens Axboe 	return sd->len;
6671ebd32fcSJens Axboe }
6681ebd32fcSJens Axboe 
6691ebd32fcSJens Axboe static ssize_t splice_write_null(struct pipe_inode_info *pipe,struct file *out,
6701ebd32fcSJens Axboe 				 loff_t *ppos, size_t len, unsigned int flags)
6711ebd32fcSJens Axboe {
6721ebd32fcSJens Axboe 	return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null);
6731ebd32fcSJens Axboe }
6741ebd32fcSJens Axboe 
6751da177e4SLinus Torvalds static ssize_t read_zero(struct file * file, char __user * buf,
6761da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
6771da177e4SLinus Torvalds {
678557ed1faSNick Piggin 	size_t written;
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 	if (!count)
6811da177e4SLinus Torvalds 		return 0;
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds 	if (!access_ok(VERIFY_WRITE, buf, count))
6841da177e4SLinus Torvalds 		return -EFAULT;
6851da177e4SLinus Torvalds 
686557ed1faSNick Piggin 	written = 0;
687557ed1faSNick Piggin 	while (count) {
688557ed1faSNick Piggin 		unsigned long unwritten;
689557ed1faSNick Piggin 		size_t chunk = count;
6901da177e4SLinus Torvalds 
691557ed1faSNick Piggin 		if (chunk > PAGE_SIZE)
692557ed1faSNick Piggin 			chunk = PAGE_SIZE;	/* Just for latency reasons */
693bb521c5dSNikanth Karthikesan 		unwritten = __clear_user(buf, chunk);
694557ed1faSNick Piggin 		written += chunk - unwritten;
6951da177e4SLinus Torvalds 		if (unwritten)
696557ed1faSNick Piggin 			break;
6972b838687SLinus Torvalds 		if (signal_pending(current))
6982b838687SLinus Torvalds 			return written ? written : -ERESTARTSYS;
699557ed1faSNick Piggin 		buf += chunk;
700557ed1faSNick Piggin 		count -= chunk;
701557ed1faSNick Piggin 		cond_resched();
7021da177e4SLinus Torvalds 	}
7031da177e4SLinus Torvalds 	return written ? written : -EFAULT;
7041da177e4SLinus Torvalds }
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds static int mmap_zero(struct file * file, struct vm_area_struct * vma)
7071da177e4SLinus Torvalds {
708557ed1faSNick Piggin #ifndef CONFIG_MMU
709557ed1faSNick Piggin 	return -ENOSYS;
710557ed1faSNick Piggin #endif
7111da177e4SLinus Torvalds 	if (vma->vm_flags & VM_SHARED)
7121da177e4SLinus Torvalds 		return shmem_zero_setup(vma);
713557ed1faSNick Piggin 	return 0;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds static ssize_t write_full(struct file * file, const char __user * buf,
7171da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
7181da177e4SLinus Torvalds {
7191da177e4SLinus Torvalds 	return -ENOSPC;
7201da177e4SLinus Torvalds }
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds /*
7231da177e4SLinus Torvalds  * Special lseek() function for /dev/null and /dev/zero.  Most notably, you
7241da177e4SLinus Torvalds  * can fopen() both devices with "a" now.  This was previously impossible.
7251da177e4SLinus Torvalds  * -- SRB.
7261da177e4SLinus Torvalds  */
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds static loff_t null_lseek(struct file * file, loff_t offset, int orig)
7291da177e4SLinus Torvalds {
7301da177e4SLinus Torvalds 	return file->f_pos = 0;
7311da177e4SLinus Torvalds }
7321da177e4SLinus Torvalds 
7331da177e4SLinus Torvalds /*
7341da177e4SLinus Torvalds  * The memory devices use the full 32/64 bits of the offset, and so we cannot
7351da177e4SLinus Torvalds  * check against negative addresses: they are ok. The return value is weird,
7361da177e4SLinus Torvalds  * though, in that case (0).
7371da177e4SLinus Torvalds  *
7381da177e4SLinus Torvalds  * also note that seeking relative to the "end of file" isn't supported:
7391da177e4SLinus Torvalds  * it has no meaning, so it returns -EINVAL.
7401da177e4SLinus Torvalds  */
7411da177e4SLinus Torvalds static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
7421da177e4SLinus Torvalds {
7431da177e4SLinus Torvalds 	loff_t ret;
7441da177e4SLinus Torvalds 
745a7113a96SJosef Sipek 	mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
7461da177e4SLinus Torvalds 	switch (orig) {
7471da177e4SLinus Torvalds 		case 0:
7481da177e4SLinus Torvalds 			file->f_pos = offset;
7491da177e4SLinus Torvalds 			ret = file->f_pos;
7501da177e4SLinus Torvalds 			force_successful_syscall_return();
7511da177e4SLinus Torvalds 			break;
7521da177e4SLinus Torvalds 		case 1:
7531da177e4SLinus Torvalds 			file->f_pos += offset;
7541da177e4SLinus Torvalds 			ret = file->f_pos;
7551da177e4SLinus Torvalds 			force_successful_syscall_return();
7561da177e4SLinus Torvalds 			break;
7571da177e4SLinus Torvalds 		default:
7581da177e4SLinus Torvalds 			ret = -EINVAL;
7591da177e4SLinus Torvalds 	}
760a7113a96SJosef Sipek 	mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
7611da177e4SLinus Torvalds 	return ret;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds static int open_port(struct inode * inode, struct file * filp)
7651da177e4SLinus Torvalds {
7661da177e4SLinus Torvalds 	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
7671da177e4SLinus Torvalds }
7681da177e4SLinus Torvalds 
7691da177e4SLinus Torvalds #define zero_lseek	null_lseek
7701da177e4SLinus Torvalds #define full_lseek      null_lseek
7711da177e4SLinus Torvalds #define write_zero	write_null
7721da177e4SLinus Torvalds #define read_full       read_zero
7731da177e4SLinus Torvalds #define open_mem	open_port
7741da177e4SLinus Torvalds #define open_kmem	open_mem
77550b1fdbdSVivek Goyal #define open_oldmem	open_mem
7761da177e4SLinus Torvalds 
77762322d25SArjan van de Ven static const struct file_operations mem_fops = {
7781da177e4SLinus Torvalds 	.llseek		= memory_lseek,
7791da177e4SLinus Torvalds 	.read		= read_mem,
7801da177e4SLinus Torvalds 	.write		= write_mem,
7811da177e4SLinus Torvalds 	.mmap		= mmap_mem,
7821da177e4SLinus Torvalds 	.open		= open_mem,
7835da6185bSDavid Howells 	.get_unmapped_area = get_unmapped_area_mem,
7841da177e4SLinus Torvalds };
7851da177e4SLinus Torvalds 
786b781ecb6SArjan van de Ven #ifdef CONFIG_DEVKMEM
78762322d25SArjan van de Ven static const struct file_operations kmem_fops = {
7881da177e4SLinus Torvalds 	.llseek		= memory_lseek,
7891da177e4SLinus Torvalds 	.read		= read_kmem,
7901da177e4SLinus Torvalds 	.write		= write_kmem,
7911da177e4SLinus Torvalds 	.mmap		= mmap_kmem,
7921da177e4SLinus Torvalds 	.open		= open_kmem,
7935da6185bSDavid Howells 	.get_unmapped_area = get_unmapped_area_mem,
7941da177e4SLinus Torvalds };
795b781ecb6SArjan van de Ven #endif
7961da177e4SLinus Torvalds 
79762322d25SArjan van de Ven static const struct file_operations null_fops = {
7981da177e4SLinus Torvalds 	.llseek		= null_lseek,
7991da177e4SLinus Torvalds 	.read		= read_null,
8001da177e4SLinus Torvalds 	.write		= write_null,
8011ebd32fcSJens Axboe 	.splice_write	= splice_write_null,
8021da177e4SLinus Torvalds };
8031da177e4SLinus Torvalds 
8044f911d64SRussell King #ifdef CONFIG_DEVPORT
80562322d25SArjan van de Ven static const struct file_operations port_fops = {
8061da177e4SLinus Torvalds 	.llseek		= memory_lseek,
8071da177e4SLinus Torvalds 	.read		= read_port,
8081da177e4SLinus Torvalds 	.write		= write_port,
8091da177e4SLinus Torvalds 	.open		= open_port,
8101da177e4SLinus Torvalds };
8111da177e4SLinus Torvalds #endif
8121da177e4SLinus Torvalds 
81362322d25SArjan van de Ven static const struct file_operations zero_fops = {
8141da177e4SLinus Torvalds 	.llseek		= zero_lseek,
8151da177e4SLinus Torvalds 	.read		= read_zero,
8161da177e4SLinus Torvalds 	.write		= write_zero,
8171da177e4SLinus Torvalds 	.mmap		= mmap_zero,
8181da177e4SLinus Torvalds };
8191da177e4SLinus Torvalds 
8205da6185bSDavid Howells /*
8215da6185bSDavid Howells  * capabilities for /dev/zero
8225da6185bSDavid Howells  * - permits private mappings, "copies" are taken of the source of zeros
8235da6185bSDavid Howells  */
8241da177e4SLinus Torvalds static struct backing_dev_info zero_bdi = {
825d993831fSJens Axboe 	.name		= "char/mem",
8261da177e4SLinus Torvalds 	.capabilities	= BDI_CAP_MAP_COPY,
8271da177e4SLinus Torvalds };
8281da177e4SLinus Torvalds 
82962322d25SArjan van de Ven static const struct file_operations full_fops = {
8301da177e4SLinus Torvalds 	.llseek		= full_lseek,
8311da177e4SLinus Torvalds 	.read		= read_full,
8321da177e4SLinus Torvalds 	.write		= write_full,
8331da177e4SLinus Torvalds };
8341da177e4SLinus Torvalds 
83550b1fdbdSVivek Goyal #ifdef CONFIG_CRASH_DUMP
83662322d25SArjan van de Ven static const struct file_operations oldmem_fops = {
83750b1fdbdSVivek Goyal 	.read	= read_oldmem,
83850b1fdbdSVivek Goyal 	.open	= open_oldmem,
83950b1fdbdSVivek Goyal };
84050b1fdbdSVivek Goyal #endif
84150b1fdbdSVivek Goyal 
8421da177e4SLinus Torvalds static ssize_t kmsg_write(struct file * file, const char __user * buf,
8431da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
8441da177e4SLinus Torvalds {
8451da177e4SLinus Torvalds 	char *tmp;
846cd140a5cSGuillaume Chazarain 	ssize_t ret;
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 	tmp = kmalloc(count + 1, GFP_KERNEL);
8491da177e4SLinus Torvalds 	if (tmp == NULL)
8501da177e4SLinus Torvalds 		return -ENOMEM;
8511da177e4SLinus Torvalds 	ret = -EFAULT;
8521da177e4SLinus Torvalds 	if (!copy_from_user(tmp, buf, count)) {
8531da177e4SLinus Torvalds 		tmp[count] = 0;
8541da177e4SLinus Torvalds 		ret = printk("%s", tmp);
855cd140a5cSGuillaume Chazarain 		if (ret > count)
856cd140a5cSGuillaume Chazarain 			/* printk can add a prefix */
857cd140a5cSGuillaume Chazarain 			ret = count;
8581da177e4SLinus Torvalds 	}
8591da177e4SLinus Torvalds 	kfree(tmp);
8601da177e4SLinus Torvalds 	return ret;
8611da177e4SLinus Torvalds }
8621da177e4SLinus Torvalds 
86362322d25SArjan van de Ven static const struct file_operations kmsg_fops = {
8641da177e4SLinus Torvalds 	.write =	kmsg_write,
8651da177e4SLinus Torvalds };
8661da177e4SLinus Torvalds 
867389e0cb9SKay Sievers static const struct memdev {
868389e0cb9SKay Sievers 	const char *name;
869e454cea2SKay Sievers 	mode_t mode;
870d6f47befSAdriano dos Santos Fernandes 	const struct file_operations *fops;
871d6f47befSAdriano dos Santos Fernandes 	struct backing_dev_info *dev_info;
872389e0cb9SKay Sievers } devlist[] = {
873e454cea2SKay Sievers 	 [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
874d6f47befSAdriano dos Santos Fernandes #ifdef CONFIG_DEVKMEM
875e454cea2SKay Sievers 	 [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
876d6f47befSAdriano dos Santos Fernandes #endif
877e454cea2SKay Sievers 	 [3] = { "null", 0666, &null_fops, NULL },
878d6f47befSAdriano dos Santos Fernandes #ifdef CONFIG_DEVPORT
879e454cea2SKay Sievers 	 [4] = { "port", 0, &port_fops, NULL },
880d6f47befSAdriano dos Santos Fernandes #endif
881e454cea2SKay Sievers 	 [5] = { "zero", 0666, &zero_fops, &zero_bdi },
882e454cea2SKay Sievers 	 [7] = { "full", 0666, &full_fops, NULL },
883e454cea2SKay Sievers 	 [8] = { "random", 0666, &random_fops, NULL },
884e454cea2SKay Sievers 	 [9] = { "urandom", 0666, &urandom_fops, NULL },
885e454cea2SKay Sievers 	[11] = { "kmsg", 0, &kmsg_fops, NULL },
886d6f47befSAdriano dos Santos Fernandes #ifdef CONFIG_CRASH_DUMP
887e454cea2SKay Sievers 	[12] = { "oldmem", 0, &oldmem_fops, NULL },
888d6f47befSAdriano dos Santos Fernandes #endif
889d6f47befSAdriano dos Santos Fernandes };
890d6f47befSAdriano dos Santos Fernandes 
8911da177e4SLinus Torvalds static int memory_open(struct inode *inode, struct file *filp)
8921da177e4SLinus Torvalds {
893389e0cb9SKay Sievers 	int minor;
894389e0cb9SKay Sievers 	const struct memdev *dev;
895389e0cb9SKay Sievers 	int ret = -ENXIO;
8961f439647SJonathan Corbet 
8971f439647SJonathan Corbet 	lock_kernel();
898d6f47befSAdriano dos Santos Fernandes 
899389e0cb9SKay Sievers 	minor = iminor(inode);
900389e0cb9SKay Sievers 	if (minor >= ARRAY_SIZE(devlist))
901389e0cb9SKay Sievers 		goto out;
902d6f47befSAdriano dos Santos Fernandes 
903389e0cb9SKay Sievers 	dev = &devlist[minor];
904389e0cb9SKay Sievers 	if (!dev->fops)
905389e0cb9SKay Sievers 		goto out;
906d6f47befSAdriano dos Santos Fernandes 
907389e0cb9SKay Sievers 	filp->f_op = dev->fops;
908389e0cb9SKay Sievers 	if (dev->dev_info)
909389e0cb9SKay Sievers 		filp->f_mapping->backing_dev_info = dev->dev_info;
910389e0cb9SKay Sievers 
911389e0cb9SKay Sievers 	if (dev->fops->open)
912389e0cb9SKay Sievers 		ret = dev->fops->open(inode, filp);
913d6f47befSAdriano dos Santos Fernandes 	else
914389e0cb9SKay Sievers 		ret = 0;
915389e0cb9SKay Sievers out:
9161f439647SJonathan Corbet 	unlock_kernel();
9171f439647SJonathan Corbet 	return ret;
9181da177e4SLinus Torvalds }
9191da177e4SLinus Torvalds 
92062322d25SArjan van de Ven static const struct file_operations memory_fops = {
921389e0cb9SKay Sievers 	.open		= memory_open,
9221da177e4SLinus Torvalds };
9231da177e4SLinus Torvalds 
924e454cea2SKay Sievers static char *mem_devnode(struct device *dev, mode_t *mode)
925e454cea2SKay Sievers {
926e454cea2SKay Sievers 	if (mode && devlist[MINOR(dev->devt)].mode)
927e454cea2SKay Sievers 		*mode = devlist[MINOR(dev->devt)].mode;
928e454cea2SKay Sievers 	return NULL;
929e454cea2SKay Sievers }
930e454cea2SKay Sievers 
931ca8eca68Sgregkh@suse.de static struct class *mem_class;
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds static int __init chr_dev_init(void)
9341da177e4SLinus Torvalds {
935389e0cb9SKay Sievers 	int minor;
936e0bf68ddSPeter Zijlstra 	int err;
937e0bf68ddSPeter Zijlstra 
938e0bf68ddSPeter Zijlstra 	err = bdi_init(&zero_bdi);
939e0bf68ddSPeter Zijlstra 	if (err)
940e0bf68ddSPeter Zijlstra 		return err;
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
9431da177e4SLinus Torvalds 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
9441da177e4SLinus Torvalds 
945ca8eca68Sgregkh@suse.de 	mem_class = class_create(THIS_MODULE, "mem");
946e454cea2SKay Sievers 	mem_class->devnode = mem_devnode;
947389e0cb9SKay Sievers 	for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
948389e0cb9SKay Sievers 		if (!devlist[minor].name)
949389e0cb9SKay Sievers 			continue;
950389e0cb9SKay Sievers 		device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
951389e0cb9SKay Sievers 			      NULL, devlist[minor].name);
952389e0cb9SKay Sievers 	}
9531da177e4SLinus Torvalds 
9541da177e4SLinus Torvalds 	return 0;
9551da177e4SLinus Torvalds }
9561da177e4SLinus Torvalds 
9571da177e4SLinus Torvalds fs_initcall(chr_dev_init);
958