xref: /openbmc/linux/drivers/char/mem.c (revision ae531c26c5c2a28ca1b35a75b39b3b256850f2c8)
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>
81da177e4SLinus Torvalds  *  Shared /dev/zero mmaping 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>
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #include <asm/uaccess.h>
311da177e4SLinus Torvalds #include <asm/io.h>
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds #ifdef CONFIG_IA64
341da177e4SLinus Torvalds # include <linux/efi.h>
351da177e4SLinus Torvalds #endif
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds /*
381da177e4SLinus Torvalds  * Architectures vary in how they handle caching for addresses
391da177e4SLinus Torvalds  * outside of main memory.
401da177e4SLinus Torvalds  *
411da177e4SLinus Torvalds  */
421da177e4SLinus Torvalds static inline int uncached_access(struct file *file, unsigned long addr)
431da177e4SLinus Torvalds {
44ca5cd877SAl Viro #if defined(__i386__) && !defined(__arch_um__)
451da177e4SLinus Torvalds 	/*
461da177e4SLinus Torvalds 	 * On the PPro and successors, the MTRRs are used to set
471da177e4SLinus Torvalds 	 * memory types for physical addresses outside main memory,
481da177e4SLinus Torvalds 	 * so blindly setting PCD or PWT on those pages is wrong.
491da177e4SLinus Torvalds 	 * For Pentiums and earlier, the surround logic should disable
501da177e4SLinus Torvalds 	 * caching for the high addresses through the KEN pin, but
511da177e4SLinus Torvalds 	 * we maintain the tradition of paranoia in this code.
521da177e4SLinus Torvalds 	 */
531da177e4SLinus Torvalds 	if (file->f_flags & O_SYNC)
541da177e4SLinus Torvalds 		return 1;
551da177e4SLinus Torvalds  	return !( test_bit(X86_FEATURE_MTRR, boot_cpu_data.x86_capability) ||
561da177e4SLinus Torvalds 		  test_bit(X86_FEATURE_K6_MTRR, boot_cpu_data.x86_capability) ||
571da177e4SLinus Torvalds 		  test_bit(X86_FEATURE_CYRIX_ARR, boot_cpu_data.x86_capability) ||
581da177e4SLinus Torvalds 		  test_bit(X86_FEATURE_CENTAUR_MCR, boot_cpu_data.x86_capability) )
591da177e4SLinus Torvalds 	  && addr >= __pa(high_memory);
60ca5cd877SAl Viro #elif defined(__x86_64__) && !defined(__arch_um__)
611da177e4SLinus Torvalds 	/*
621da177e4SLinus Torvalds 	 * This is broken because it can generate memory type aliases,
631da177e4SLinus Torvalds 	 * which can cause cache corruptions
641da177e4SLinus Torvalds 	 * But it is only available for root and we have to be bug-to-bug
651da177e4SLinus Torvalds 	 * compatible with i386.
661da177e4SLinus Torvalds 	 */
671da177e4SLinus Torvalds 	if (file->f_flags & O_SYNC)
681da177e4SLinus Torvalds 		return 1;
691da177e4SLinus Torvalds 	/* same behaviour as i386. PAT always set to cached and MTRRs control the
701da177e4SLinus Torvalds 	   caching behaviour.
711da177e4SLinus Torvalds 	   Hopefully a full PAT implementation will fix that soon. */
721da177e4SLinus Torvalds 	return 0;
731da177e4SLinus Torvalds #elif defined(CONFIG_IA64)
741da177e4SLinus Torvalds 	/*
751da177e4SLinus Torvalds 	 * On ia64, we ignore O_SYNC because we cannot tolerate memory attribute aliases.
761da177e4SLinus Torvalds 	 */
771da177e4SLinus Torvalds 	return !(efi_mem_attributes(addr) & EFI_MEMORY_WB);
7824e9d0b9SRalf Baechle #elif defined(CONFIG_MIPS)
7924e9d0b9SRalf Baechle 	{
8024e9d0b9SRalf Baechle 		extern int __uncached_access(struct file *file,
8124e9d0b9SRalf Baechle 					     unsigned long addr);
8224e9d0b9SRalf Baechle 
8324e9d0b9SRalf Baechle 		return __uncached_access(file, addr);
8424e9d0b9SRalf Baechle 	}
851da177e4SLinus Torvalds #else
861da177e4SLinus Torvalds 	/*
871da177e4SLinus Torvalds 	 * Accessing memory above the top the kernel knows about or through a file pointer
881da177e4SLinus Torvalds 	 * that was marked O_SYNC will be done non-cached.
891da177e4SLinus Torvalds 	 */
901da177e4SLinus Torvalds 	if (file->f_flags & O_SYNC)
911da177e4SLinus Torvalds 		return 1;
921da177e4SLinus Torvalds 	return addr >= __pa(high_memory);
931da177e4SLinus Torvalds #endif
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
97136939a2SBjorn Helgaas static inline int valid_phys_addr_range(unsigned long addr, size_t count)
981da177e4SLinus Torvalds {
99136939a2SBjorn Helgaas 	if (addr + count > __pa(high_memory))
1001da177e4SLinus Torvalds 		return 0;
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 	return 1;
1031da177e4SLinus Torvalds }
10480851ef2SBjorn Helgaas 
10506c67befSLennert Buytenhek static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
10680851ef2SBjorn Helgaas {
10780851ef2SBjorn Helgaas 	return 1;
10880851ef2SBjorn Helgaas }
1091da177e4SLinus Torvalds #endif
1101da177e4SLinus Torvalds 
111*ae531c26SArjan van de Ven #ifdef CONFIG_NONPROMISC_DEVMEM
112*ae531c26SArjan van de Ven static inline int range_is_allowed(unsigned long from, unsigned long to)
113*ae531c26SArjan van de Ven {
114*ae531c26SArjan van de Ven 	unsigned long cursor;
115*ae531c26SArjan van de Ven 
116*ae531c26SArjan van de Ven 	cursor = from >> PAGE_SHIFT;
117*ae531c26SArjan van de Ven 	while ((cursor << PAGE_SHIFT) < to) {
118*ae531c26SArjan van de Ven 		if (!devmem_is_allowed(cursor)) {
119*ae531c26SArjan van de Ven 			printk(KERN_INFO "Program %s tried to read /dev/mem "
120*ae531c26SArjan van de Ven 				"between %lx->%lx.\n",
121*ae531c26SArjan van de Ven 				current->comm, from, to);
122*ae531c26SArjan van de Ven 			return 0;
123*ae531c26SArjan van de Ven 		}
124*ae531c26SArjan van de Ven 		cursor++;
125*ae531c26SArjan van de Ven 	}
126*ae531c26SArjan van de Ven 	return 1;
127*ae531c26SArjan van de Ven }
128*ae531c26SArjan van de Ven #else
129*ae531c26SArjan van de Ven static inline int range_is_allowed(unsigned long from, unsigned long to)
130*ae531c26SArjan van de Ven {
131*ae531c26SArjan van de Ven 	return 1;
132*ae531c26SArjan van de Ven }
133*ae531c26SArjan van de Ven #endif
134*ae531c26SArjan van de Ven 
1351da177e4SLinus Torvalds /*
1361da177e4SLinus Torvalds  * This funcion reads the *physical* memory. The f_pos points directly to the
1371da177e4SLinus Torvalds  * memory location.
1381da177e4SLinus Torvalds  */
1391da177e4SLinus Torvalds static ssize_t read_mem(struct file * file, char __user * buf,
1401da177e4SLinus Torvalds 			size_t count, loff_t *ppos)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds 	unsigned long p = *ppos;
1431da177e4SLinus Torvalds 	ssize_t read, sz;
1441da177e4SLinus Torvalds 	char *ptr;
1451da177e4SLinus Torvalds 
146136939a2SBjorn Helgaas 	if (!valid_phys_addr_range(p, count))
1471da177e4SLinus Torvalds 		return -EFAULT;
1481da177e4SLinus Torvalds 	read = 0;
1491da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
1501da177e4SLinus Torvalds 	/* we don't have page 0 mapped on sparc and m68k.. */
1511da177e4SLinus Torvalds 	if (p < PAGE_SIZE) {
1521da177e4SLinus Torvalds 		sz = PAGE_SIZE - p;
1531da177e4SLinus Torvalds 		if (sz > count)
1541da177e4SLinus Torvalds 			sz = count;
1551da177e4SLinus Torvalds 		if (sz > 0) {
1561da177e4SLinus Torvalds 			if (clear_user(buf, sz))
1571da177e4SLinus Torvalds 				return -EFAULT;
1581da177e4SLinus Torvalds 			buf += sz;
1591da177e4SLinus Torvalds 			p += sz;
1601da177e4SLinus Torvalds 			count -= sz;
1611da177e4SLinus Torvalds 			read += sz;
1621da177e4SLinus Torvalds 		}
1631da177e4SLinus Torvalds 	}
1641da177e4SLinus Torvalds #endif
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	while (count > 0) {
1671da177e4SLinus Torvalds 		/*
1681da177e4SLinus Torvalds 		 * Handle first page in case it's not aligned
1691da177e4SLinus Torvalds 		 */
1701da177e4SLinus Torvalds 		if (-p & (PAGE_SIZE - 1))
1711da177e4SLinus Torvalds 			sz = -p & (PAGE_SIZE - 1);
1721da177e4SLinus Torvalds 		else
1731da177e4SLinus Torvalds 			sz = PAGE_SIZE;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 		sz = min_t(unsigned long, sz, count);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 		/*
1781da177e4SLinus Torvalds 		 * On ia64 if a page has been mapped somewhere as
1791da177e4SLinus Torvalds 		 * uncached, then it must also be accessed uncached
1801da177e4SLinus Torvalds 		 * by the kernel or data corruption may occur
1811da177e4SLinus Torvalds 		 */
1821da177e4SLinus Torvalds 		ptr = xlate_dev_mem_ptr(p);
1831da177e4SLinus Torvalds 
184*ae531c26SArjan van de Ven 		if (!range_is_allowed(p, p+count))
185*ae531c26SArjan van de Ven 			return -EPERM;
1861da177e4SLinus Torvalds 		if (copy_to_user(buf, ptr, sz))
1871da177e4SLinus Torvalds 			return -EFAULT;
1881da177e4SLinus Torvalds 		buf += sz;
1891da177e4SLinus Torvalds 		p += sz;
1901da177e4SLinus Torvalds 		count -= sz;
1911da177e4SLinus Torvalds 		read += sz;
1921da177e4SLinus Torvalds 	}
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 	*ppos += read;
1951da177e4SLinus Torvalds 	return read;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds static ssize_t write_mem(struct file * file, const char __user * buf,
1991da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	unsigned long p = *ppos;
2021da177e4SLinus Torvalds 	ssize_t written, sz;
2031da177e4SLinus Torvalds 	unsigned long copied;
2041da177e4SLinus Torvalds 	void *ptr;
2051da177e4SLinus Torvalds 
206136939a2SBjorn Helgaas 	if (!valid_phys_addr_range(p, count))
2071da177e4SLinus Torvalds 		return -EFAULT;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	written = 0;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
2121da177e4SLinus Torvalds 	/* we don't have page 0 mapped on sparc and m68k.. */
2131da177e4SLinus Torvalds 	if (p < PAGE_SIZE) {
2141da177e4SLinus Torvalds 		unsigned long sz = PAGE_SIZE - p;
2151da177e4SLinus Torvalds 		if (sz > count)
2161da177e4SLinus Torvalds 			sz = count;
2171da177e4SLinus Torvalds 		/* Hmm. Do something? */
2181da177e4SLinus Torvalds 		buf += sz;
2191da177e4SLinus Torvalds 		p += sz;
2201da177e4SLinus Torvalds 		count -= sz;
2211da177e4SLinus Torvalds 		written += sz;
2221da177e4SLinus Torvalds 	}
2231da177e4SLinus Torvalds #endif
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	while (count > 0) {
2261da177e4SLinus Torvalds 		/*
2271da177e4SLinus Torvalds 		 * Handle first page in case it's not aligned
2281da177e4SLinus Torvalds 		 */
2291da177e4SLinus Torvalds 		if (-p & (PAGE_SIZE - 1))
2301da177e4SLinus Torvalds 			sz = -p & (PAGE_SIZE - 1);
2311da177e4SLinus Torvalds 		else
2321da177e4SLinus Torvalds 			sz = PAGE_SIZE;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 		sz = min_t(unsigned long, sz, count);
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 		/*
2371da177e4SLinus Torvalds 		 * On ia64 if a page has been mapped somewhere as
2381da177e4SLinus Torvalds 		 * uncached, then it must also be accessed uncached
2391da177e4SLinus Torvalds 		 * by the kernel or data corruption may occur
2401da177e4SLinus Torvalds 		 */
2411da177e4SLinus Torvalds 		ptr = xlate_dev_mem_ptr(p);
2421da177e4SLinus Torvalds 
243*ae531c26SArjan van de Ven 		if (!range_is_allowed(p, p+sz))
244*ae531c26SArjan van de Ven 			return -EPERM;
2451da177e4SLinus Torvalds 		copied = copy_from_user(ptr, buf, sz);
2461da177e4SLinus Torvalds 		if (copied) {
247c654d60eSJan Beulich 			written += sz - copied;
248c654d60eSJan Beulich 			if (written)
249c654d60eSJan Beulich 				break;
2501da177e4SLinus Torvalds 			return -EFAULT;
2511da177e4SLinus Torvalds 		}
2521da177e4SLinus Torvalds 		buf += sz;
2531da177e4SLinus Torvalds 		p += sz;
2541da177e4SLinus Torvalds 		count -= sz;
2551da177e4SLinus Torvalds 		written += sz;
2561da177e4SLinus Torvalds 	}
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	*ppos += written;
2591da177e4SLinus Torvalds 	return written;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
26244ac8413SBjorn Helgaas #ifndef __HAVE_PHYS_MEM_ACCESS_PROT
26344ac8413SBjorn Helgaas static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
26444ac8413SBjorn Helgaas 				     unsigned long size, pgprot_t vma_prot)
26544ac8413SBjorn Helgaas {
26644ac8413SBjorn Helgaas #ifdef pgprot_noncached
26744ac8413SBjorn Helgaas 	unsigned long offset = pfn << PAGE_SHIFT;
26844ac8413SBjorn Helgaas 
26944ac8413SBjorn Helgaas 	if (uncached_access(file, offset))
27044ac8413SBjorn Helgaas 		return pgprot_noncached(vma_prot);
27144ac8413SBjorn Helgaas #endif
27244ac8413SBjorn Helgaas 	return vma_prot;
27344ac8413SBjorn Helgaas }
27444ac8413SBjorn Helgaas #endif
27544ac8413SBjorn Helgaas 
2765da6185bSDavid Howells #ifndef CONFIG_MMU
2775da6185bSDavid Howells static unsigned long get_unmapped_area_mem(struct file *file,
2785da6185bSDavid Howells 					   unsigned long addr,
2795da6185bSDavid Howells 					   unsigned long len,
2805da6185bSDavid Howells 					   unsigned long pgoff,
2815da6185bSDavid Howells 					   unsigned long flags)
2825da6185bSDavid Howells {
2835da6185bSDavid Howells 	if (!valid_mmap_phys_addr_range(pgoff, len))
2845da6185bSDavid Howells 		return (unsigned long) -EINVAL;
2858a93258cSBenjamin Herrenschmidt 	return pgoff << PAGE_SHIFT;
2865da6185bSDavid Howells }
2875da6185bSDavid Howells 
2885da6185bSDavid Howells /* can't do an in-place private mapping if there's no MMU */
2895da6185bSDavid Howells static inline int private_mapping_ok(struct vm_area_struct *vma)
2905da6185bSDavid Howells {
2915da6185bSDavid Howells 	return vma->vm_flags & VM_MAYSHARE;
2925da6185bSDavid Howells }
2935da6185bSDavid Howells #else
2945da6185bSDavid Howells #define get_unmapped_area_mem	NULL
2955da6185bSDavid Howells 
2965da6185bSDavid Howells static inline int private_mapping_ok(struct vm_area_struct *vma)
2975da6185bSDavid Howells {
2985da6185bSDavid Howells 	return 1;
2995da6185bSDavid Howells }
3005da6185bSDavid Howells #endif
3015da6185bSDavid Howells 
3021da177e4SLinus Torvalds static int mmap_mem(struct file * file, struct vm_area_struct * vma)
3031da177e4SLinus Torvalds {
30480851ef2SBjorn Helgaas 	size_t size = vma->vm_end - vma->vm_start;
30580851ef2SBjorn Helgaas 
30606c67befSLennert Buytenhek 	if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
30780851ef2SBjorn Helgaas 		return -EINVAL;
30880851ef2SBjorn Helgaas 
3095da6185bSDavid Howells 	if (!private_mapping_ok(vma))
3105da6185bSDavid Howells 		return -ENOSYS;
3115da6185bSDavid Howells 
3128b150478SRoland Dreier 	vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
31380851ef2SBjorn Helgaas 						 size,
3141da177e4SLinus Torvalds 						 vma->vm_page_prot);
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	/* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
3171da177e4SLinus Torvalds 	if (remap_pfn_range(vma,
3181da177e4SLinus Torvalds 			    vma->vm_start,
3191da177e4SLinus Torvalds 			    vma->vm_pgoff,
32080851ef2SBjorn Helgaas 			    size,
3211da177e4SLinus Torvalds 			    vma->vm_page_prot))
3221da177e4SLinus Torvalds 		return -EAGAIN;
3231da177e4SLinus Torvalds 	return 0;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds static int mmap_kmem(struct file * file, struct vm_area_struct * vma)
3271da177e4SLinus Torvalds {
3284bb82551SLinus Torvalds 	unsigned long pfn;
3294bb82551SLinus Torvalds 
3306d3154ccSLinus Torvalds 	/* Turn a kernel-virtual address into a physical page frame */
3316d3154ccSLinus Torvalds 	pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT;
3324bb82551SLinus Torvalds 
3331da177e4SLinus Torvalds 	/*
3341da177e4SLinus Torvalds 	 * RED-PEN: on some architectures there is more mapped memory
3351da177e4SLinus Torvalds 	 * than available in mem_map which pfn_valid checks
3361da177e4SLinus Torvalds 	 * for. Perhaps should add a new macro here.
3371da177e4SLinus Torvalds 	 *
3381da177e4SLinus Torvalds 	 * RED-PEN: vmalloc is not supported right now.
3391da177e4SLinus Torvalds 	 */
3404bb82551SLinus Torvalds 	if (!pfn_valid(pfn))
3411da177e4SLinus Torvalds 		return -EIO;
3424bb82551SLinus Torvalds 
3434bb82551SLinus Torvalds 	vma->vm_pgoff = pfn;
3441da177e4SLinus Torvalds 	return mmap_mem(file, vma);
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
34750b1fdbdSVivek Goyal #ifdef CONFIG_CRASH_DUMP
34850b1fdbdSVivek Goyal /*
34950b1fdbdSVivek Goyal  * Read memory corresponding to the old kernel.
35050b1fdbdSVivek Goyal  */
351315c215cSVivek Goyal static ssize_t read_oldmem(struct file *file, char __user *buf,
35250b1fdbdSVivek Goyal 				size_t count, loff_t *ppos)
35350b1fdbdSVivek Goyal {
354315c215cSVivek Goyal 	unsigned long pfn, offset;
35550b1fdbdSVivek Goyal 	size_t read = 0, csize;
356315c215cSVivek Goyal 	int rc = 0;
35750b1fdbdSVivek Goyal 
35850b1fdbdSVivek Goyal 	while (count) {
35950b1fdbdSVivek Goyal 		pfn = *ppos / PAGE_SIZE;
360315c215cSVivek Goyal 		if (pfn > saved_max_pfn)
361315c215cSVivek Goyal 			return read;
36250b1fdbdSVivek Goyal 
363315c215cSVivek Goyal 		offset = (unsigned long)(*ppos % PAGE_SIZE);
364315c215cSVivek Goyal 		if (count > PAGE_SIZE - offset)
365315c215cSVivek Goyal 			csize = PAGE_SIZE - offset;
366315c215cSVivek Goyal 		else
367315c215cSVivek Goyal 			csize = count;
36850b1fdbdSVivek Goyal 
369315c215cSVivek Goyal 		rc = copy_oldmem_page(pfn, buf, csize, offset, 1);
370315c215cSVivek Goyal 		if (rc < 0)
371315c215cSVivek Goyal 			return rc;
37250b1fdbdSVivek Goyal 		buf += csize;
37350b1fdbdSVivek Goyal 		*ppos += csize;
37450b1fdbdSVivek Goyal 		read += csize;
37550b1fdbdSVivek Goyal 		count -= csize;
37650b1fdbdSVivek Goyal 	}
37750b1fdbdSVivek Goyal 	return read;
37850b1fdbdSVivek Goyal }
37950b1fdbdSVivek Goyal #endif
38050b1fdbdSVivek Goyal 
3811da177e4SLinus Torvalds extern long vread(char *buf, char *addr, unsigned long count);
3821da177e4SLinus Torvalds extern long vwrite(char *buf, char *addr, unsigned long count);
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds /*
3851da177e4SLinus Torvalds  * This function reads the *virtual* memory as seen by the kernel.
3861da177e4SLinus Torvalds  */
3871da177e4SLinus Torvalds static ssize_t read_kmem(struct file *file, char __user *buf,
3881da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
3891da177e4SLinus Torvalds {
3901da177e4SLinus Torvalds 	unsigned long p = *ppos;
3911da177e4SLinus Torvalds 	ssize_t low_count, read, sz;
3921da177e4SLinus Torvalds 	char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 	read = 0;
3951da177e4SLinus Torvalds 	if (p < (unsigned long) high_memory) {
3961da177e4SLinus Torvalds 		low_count = count;
3971da177e4SLinus Torvalds 		if (count > (unsigned long) high_memory - p)
3981da177e4SLinus Torvalds 			low_count = (unsigned long) high_memory - p;
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
4011da177e4SLinus Torvalds 		/* we don't have page 0 mapped on sparc and m68k.. */
4021da177e4SLinus Torvalds 		if (p < PAGE_SIZE && low_count > 0) {
4031da177e4SLinus Torvalds 			size_t tmp = PAGE_SIZE - p;
4041da177e4SLinus Torvalds 			if (tmp > low_count) tmp = low_count;
4051da177e4SLinus Torvalds 			if (clear_user(buf, tmp))
4061da177e4SLinus Torvalds 				return -EFAULT;
4071da177e4SLinus Torvalds 			buf += tmp;
4081da177e4SLinus Torvalds 			p += tmp;
4091da177e4SLinus Torvalds 			read += tmp;
4101da177e4SLinus Torvalds 			low_count -= tmp;
4111da177e4SLinus Torvalds 			count -= tmp;
4121da177e4SLinus Torvalds 		}
4131da177e4SLinus Torvalds #endif
4141da177e4SLinus Torvalds 		while (low_count > 0) {
4151da177e4SLinus Torvalds 			/*
4161da177e4SLinus Torvalds 			 * Handle first page in case it's not aligned
4171da177e4SLinus Torvalds 			 */
4181da177e4SLinus Torvalds 			if (-p & (PAGE_SIZE - 1))
4191da177e4SLinus Torvalds 				sz = -p & (PAGE_SIZE - 1);
4201da177e4SLinus Torvalds 			else
4211da177e4SLinus Torvalds 				sz = PAGE_SIZE;
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 			sz = min_t(unsigned long, sz, low_count);
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds 			/*
4261da177e4SLinus Torvalds 			 * On ia64 if a page has been mapped somewhere as
4271da177e4SLinus Torvalds 			 * uncached, then it must also be accessed uncached
4281da177e4SLinus Torvalds 			 * by the kernel or data corruption may occur
4291da177e4SLinus Torvalds 			 */
4301da177e4SLinus Torvalds 			kbuf = xlate_dev_kmem_ptr((char *)p);
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 			if (copy_to_user(buf, kbuf, sz))
4331da177e4SLinus Torvalds 				return -EFAULT;
4341da177e4SLinus Torvalds 			buf += sz;
4351da177e4SLinus Torvalds 			p += sz;
4361da177e4SLinus Torvalds 			read += sz;
4371da177e4SLinus Torvalds 			low_count -= sz;
4381da177e4SLinus Torvalds 			count -= sz;
4391da177e4SLinus Torvalds 		}
4401da177e4SLinus Torvalds 	}
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	if (count > 0) {
4431da177e4SLinus Torvalds 		kbuf = (char *)__get_free_page(GFP_KERNEL);
4441da177e4SLinus Torvalds 		if (!kbuf)
4451da177e4SLinus Torvalds 			return -ENOMEM;
4461da177e4SLinus Torvalds 		while (count > 0) {
4471da177e4SLinus Torvalds 			int len = count;
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 			if (len > PAGE_SIZE)
4501da177e4SLinus Torvalds 				len = PAGE_SIZE;
4511da177e4SLinus Torvalds 			len = vread(kbuf, (char *)p, len);
4521da177e4SLinus Torvalds 			if (!len)
4531da177e4SLinus Torvalds 				break;
4541da177e4SLinus Torvalds 			if (copy_to_user(buf, kbuf, len)) {
4551da177e4SLinus Torvalds 				free_page((unsigned long)kbuf);
4561da177e4SLinus Torvalds 				return -EFAULT;
4571da177e4SLinus Torvalds 			}
4581da177e4SLinus Torvalds 			count -= len;
4591da177e4SLinus Torvalds 			buf += len;
4601da177e4SLinus Torvalds 			read += len;
4611da177e4SLinus Torvalds 			p += len;
4621da177e4SLinus Torvalds 		}
4631da177e4SLinus Torvalds 		free_page((unsigned long)kbuf);
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds  	*ppos = p;
4661da177e4SLinus Torvalds  	return read;
4671da177e4SLinus Torvalds }
4681da177e4SLinus Torvalds 
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds static inline ssize_t
4711da177e4SLinus Torvalds do_write_kmem(void *p, unsigned long realp, const char __user * buf,
4721da177e4SLinus Torvalds 	      size_t count, loff_t *ppos)
4731da177e4SLinus Torvalds {
4741da177e4SLinus Torvalds 	ssize_t written, sz;
4751da177e4SLinus Torvalds 	unsigned long copied;
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds 	written = 0;
4781da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
4791da177e4SLinus Torvalds 	/* we don't have page 0 mapped on sparc and m68k.. */
4801da177e4SLinus Torvalds 	if (realp < PAGE_SIZE) {
4811da177e4SLinus Torvalds 		unsigned long sz = PAGE_SIZE - realp;
4821da177e4SLinus Torvalds 		if (sz > count)
4831da177e4SLinus Torvalds 			sz = count;
4841da177e4SLinus Torvalds 		/* Hmm. Do something? */
4851da177e4SLinus Torvalds 		buf += sz;
4861da177e4SLinus Torvalds 		p += sz;
4871da177e4SLinus Torvalds 		realp += sz;
4881da177e4SLinus Torvalds 		count -= sz;
4891da177e4SLinus Torvalds 		written += sz;
4901da177e4SLinus Torvalds 	}
4911da177e4SLinus Torvalds #endif
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	while (count > 0) {
4941da177e4SLinus Torvalds 		char *ptr;
4951da177e4SLinus Torvalds 		/*
4961da177e4SLinus Torvalds 		 * Handle first page in case it's not aligned
4971da177e4SLinus Torvalds 		 */
4981da177e4SLinus Torvalds 		if (-realp & (PAGE_SIZE - 1))
4991da177e4SLinus Torvalds 			sz = -realp & (PAGE_SIZE - 1);
5001da177e4SLinus Torvalds 		else
5011da177e4SLinus Torvalds 			sz = PAGE_SIZE;
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 		sz = min_t(unsigned long, sz, count);
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 		/*
5061da177e4SLinus Torvalds 		 * On ia64 if a page has been mapped somewhere as
5071da177e4SLinus Torvalds 		 * uncached, then it must also be accessed uncached
5081da177e4SLinus Torvalds 		 * by the kernel or data corruption may occur
5091da177e4SLinus Torvalds 		 */
5101da177e4SLinus Torvalds 		ptr = xlate_dev_kmem_ptr(p);
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 		copied = copy_from_user(ptr, buf, sz);
5131da177e4SLinus Torvalds 		if (copied) {
514c654d60eSJan Beulich 			written += sz - copied;
515c654d60eSJan Beulich 			if (written)
516c654d60eSJan Beulich 				break;
5171da177e4SLinus Torvalds 			return -EFAULT;
5181da177e4SLinus Torvalds 		}
5191da177e4SLinus Torvalds 		buf += sz;
5201da177e4SLinus Torvalds 		p += sz;
5211da177e4SLinus Torvalds 		realp += sz;
5221da177e4SLinus Torvalds 		count -= sz;
5231da177e4SLinus Torvalds 		written += sz;
5241da177e4SLinus Torvalds 	}
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds 	*ppos += written;
5271da177e4SLinus Torvalds 	return written;
5281da177e4SLinus Torvalds }
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds /*
5321da177e4SLinus Torvalds  * This function writes to the *virtual* memory as seen by the kernel.
5331da177e4SLinus Torvalds  */
5341da177e4SLinus Torvalds static ssize_t write_kmem(struct file * file, const char __user * buf,
5351da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
5361da177e4SLinus Torvalds {
5371da177e4SLinus Torvalds 	unsigned long p = *ppos;
5381da177e4SLinus Torvalds 	ssize_t wrote = 0;
5391da177e4SLinus Torvalds 	ssize_t virtr = 0;
5401da177e4SLinus Torvalds 	ssize_t written;
5411da177e4SLinus Torvalds 	char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 	if (p < (unsigned long) high_memory) {
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 		wrote = count;
5461da177e4SLinus Torvalds 		if (count > (unsigned long) high_memory - p)
5471da177e4SLinus Torvalds 			wrote = (unsigned long) high_memory - p;
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds 		written = do_write_kmem((void*)p, p, buf, wrote, ppos);
5501da177e4SLinus Torvalds 		if (written != wrote)
5511da177e4SLinus Torvalds 			return written;
5521da177e4SLinus Torvalds 		wrote = written;
5531da177e4SLinus Torvalds 		p += wrote;
5541da177e4SLinus Torvalds 		buf += wrote;
5551da177e4SLinus Torvalds 		count -= wrote;
5561da177e4SLinus Torvalds 	}
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	if (count > 0) {
5591da177e4SLinus Torvalds 		kbuf = (char *)__get_free_page(GFP_KERNEL);
5601da177e4SLinus Torvalds 		if (!kbuf)
5611da177e4SLinus Torvalds 			return wrote ? wrote : -ENOMEM;
5621da177e4SLinus Torvalds 		while (count > 0) {
5631da177e4SLinus Torvalds 			int len = count;
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 			if (len > PAGE_SIZE)
5661da177e4SLinus Torvalds 				len = PAGE_SIZE;
5671da177e4SLinus Torvalds 			if (len) {
5681da177e4SLinus Torvalds 				written = copy_from_user(kbuf, buf, len);
5691da177e4SLinus Torvalds 				if (written) {
570c654d60eSJan Beulich 					if (wrote + virtr)
571c654d60eSJan Beulich 						break;
5721da177e4SLinus Torvalds 					free_page((unsigned long)kbuf);
573c654d60eSJan Beulich 					return -EFAULT;
5741da177e4SLinus Torvalds 				}
5751da177e4SLinus Torvalds 			}
5761da177e4SLinus Torvalds 			len = vwrite(kbuf, (char *)p, len);
5771da177e4SLinus Torvalds 			count -= len;
5781da177e4SLinus Torvalds 			buf += len;
5791da177e4SLinus Torvalds 			virtr += len;
5801da177e4SLinus Torvalds 			p += len;
5811da177e4SLinus Torvalds 		}
5821da177e4SLinus Torvalds 		free_page((unsigned long)kbuf);
5831da177e4SLinus Torvalds 	}
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds  	*ppos = p;
5861da177e4SLinus Torvalds  	return virtr + wrote;
5871da177e4SLinus Torvalds }
5881da177e4SLinus Torvalds 
5894f911d64SRussell King #ifdef CONFIG_DEVPORT
5901da177e4SLinus Torvalds static ssize_t read_port(struct file * file, char __user * buf,
5911da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
5921da177e4SLinus Torvalds {
5931da177e4SLinus Torvalds 	unsigned long i = *ppos;
5941da177e4SLinus Torvalds 	char __user *tmp = buf;
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 	if (!access_ok(VERIFY_WRITE, buf, count))
5971da177e4SLinus Torvalds 		return -EFAULT;
5981da177e4SLinus Torvalds 	while (count-- > 0 && i < 65536) {
5991da177e4SLinus Torvalds 		if (__put_user(inb(i),tmp) < 0)
6001da177e4SLinus Torvalds 			return -EFAULT;
6011da177e4SLinus Torvalds 		i++;
6021da177e4SLinus Torvalds 		tmp++;
6031da177e4SLinus Torvalds 	}
6041da177e4SLinus Torvalds 	*ppos = i;
6051da177e4SLinus Torvalds 	return tmp-buf;
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds static ssize_t write_port(struct file * file, const char __user * buf,
6091da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
6101da177e4SLinus Torvalds {
6111da177e4SLinus Torvalds 	unsigned long i = *ppos;
6121da177e4SLinus Torvalds 	const char __user * tmp = buf;
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	if (!access_ok(VERIFY_READ,buf,count))
6151da177e4SLinus Torvalds 		return -EFAULT;
6161da177e4SLinus Torvalds 	while (count-- > 0 && i < 65536) {
6171da177e4SLinus Torvalds 		char c;
618c654d60eSJan Beulich 		if (__get_user(c, tmp)) {
619c654d60eSJan Beulich 			if (tmp > buf)
620c654d60eSJan Beulich 				break;
6211da177e4SLinus Torvalds 			return -EFAULT;
622c654d60eSJan Beulich 		}
6231da177e4SLinus Torvalds 		outb(c,i);
6241da177e4SLinus Torvalds 		i++;
6251da177e4SLinus Torvalds 		tmp++;
6261da177e4SLinus Torvalds 	}
6271da177e4SLinus Torvalds 	*ppos = i;
6281da177e4SLinus Torvalds 	return tmp-buf;
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds #endif
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds static ssize_t read_null(struct file * file, char __user * buf,
6331da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
6341da177e4SLinus Torvalds {
6351da177e4SLinus Torvalds 	return 0;
6361da177e4SLinus Torvalds }
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds static ssize_t write_null(struct file * file, const char __user * buf,
6391da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
6401da177e4SLinus Torvalds {
6411da177e4SLinus Torvalds 	return count;
6421da177e4SLinus Torvalds }
6431da177e4SLinus Torvalds 
6441ebd32fcSJens Axboe static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,
6451ebd32fcSJens Axboe 			struct splice_desc *sd)
6461ebd32fcSJens Axboe {
6471ebd32fcSJens Axboe 	return sd->len;
6481ebd32fcSJens Axboe }
6491ebd32fcSJens Axboe 
6501ebd32fcSJens Axboe static ssize_t splice_write_null(struct pipe_inode_info *pipe,struct file *out,
6511ebd32fcSJens Axboe 				 loff_t *ppos, size_t len, unsigned int flags)
6521ebd32fcSJens Axboe {
6531ebd32fcSJens Axboe 	return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null);
6541ebd32fcSJens Axboe }
6551ebd32fcSJens Axboe 
6561da177e4SLinus Torvalds static ssize_t read_zero(struct file * file, char __user * buf,
6571da177e4SLinus Torvalds 			 size_t count, loff_t *ppos)
6581da177e4SLinus Torvalds {
659557ed1faSNick Piggin 	size_t written;
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 	if (!count)
6621da177e4SLinus Torvalds 		return 0;
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	if (!access_ok(VERIFY_WRITE, buf, count))
6651da177e4SLinus Torvalds 		return -EFAULT;
6661da177e4SLinus Torvalds 
667557ed1faSNick Piggin 	written = 0;
668557ed1faSNick Piggin 	while (count) {
669557ed1faSNick Piggin 		unsigned long unwritten;
670557ed1faSNick Piggin 		size_t chunk = count;
6711da177e4SLinus Torvalds 
672557ed1faSNick Piggin 		if (chunk > PAGE_SIZE)
673557ed1faSNick Piggin 			chunk = PAGE_SIZE;	/* Just for latency reasons */
674557ed1faSNick Piggin 		unwritten = clear_user(buf, chunk);
675557ed1faSNick Piggin 		written += chunk - unwritten;
6761da177e4SLinus Torvalds 		if (unwritten)
677557ed1faSNick Piggin 			break;
678557ed1faSNick Piggin 		buf += chunk;
679557ed1faSNick Piggin 		count -= chunk;
680557ed1faSNick Piggin 		cond_resched();
6811da177e4SLinus Torvalds 	}
6821da177e4SLinus Torvalds 	return written ? written : -EFAULT;
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds static int mmap_zero(struct file * file, struct vm_area_struct * vma)
6861da177e4SLinus Torvalds {
687557ed1faSNick Piggin #ifndef CONFIG_MMU
688557ed1faSNick Piggin 	return -ENOSYS;
689557ed1faSNick Piggin #endif
6901da177e4SLinus Torvalds 	if (vma->vm_flags & VM_SHARED)
6911da177e4SLinus Torvalds 		return shmem_zero_setup(vma);
692557ed1faSNick Piggin 	return 0;
6931da177e4SLinus Torvalds }
6941da177e4SLinus Torvalds 
6951da177e4SLinus Torvalds static ssize_t write_full(struct file * file, const char __user * buf,
6961da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
6971da177e4SLinus Torvalds {
6981da177e4SLinus Torvalds 	return -ENOSPC;
6991da177e4SLinus Torvalds }
7001da177e4SLinus Torvalds 
7011da177e4SLinus Torvalds /*
7021da177e4SLinus Torvalds  * Special lseek() function for /dev/null and /dev/zero.  Most notably, you
7031da177e4SLinus Torvalds  * can fopen() both devices with "a" now.  This was previously impossible.
7041da177e4SLinus Torvalds  * -- SRB.
7051da177e4SLinus Torvalds  */
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds static loff_t null_lseek(struct file * file, loff_t offset, int orig)
7081da177e4SLinus Torvalds {
7091da177e4SLinus Torvalds 	return file->f_pos = 0;
7101da177e4SLinus Torvalds }
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds /*
7131da177e4SLinus Torvalds  * The memory devices use the full 32/64 bits of the offset, and so we cannot
7141da177e4SLinus Torvalds  * check against negative addresses: they are ok. The return value is weird,
7151da177e4SLinus Torvalds  * though, in that case (0).
7161da177e4SLinus Torvalds  *
7171da177e4SLinus Torvalds  * also note that seeking relative to the "end of file" isn't supported:
7181da177e4SLinus Torvalds  * it has no meaning, so it returns -EINVAL.
7191da177e4SLinus Torvalds  */
7201da177e4SLinus Torvalds static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
7211da177e4SLinus Torvalds {
7221da177e4SLinus Torvalds 	loff_t ret;
7231da177e4SLinus Torvalds 
724a7113a96SJosef Sipek 	mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
7251da177e4SLinus Torvalds 	switch (orig) {
7261da177e4SLinus Torvalds 		case 0:
7271da177e4SLinus Torvalds 			file->f_pos = offset;
7281da177e4SLinus Torvalds 			ret = file->f_pos;
7291da177e4SLinus Torvalds 			force_successful_syscall_return();
7301da177e4SLinus Torvalds 			break;
7311da177e4SLinus Torvalds 		case 1:
7321da177e4SLinus Torvalds 			file->f_pos += offset;
7331da177e4SLinus Torvalds 			ret = file->f_pos;
7341da177e4SLinus Torvalds 			force_successful_syscall_return();
7351da177e4SLinus Torvalds 			break;
7361da177e4SLinus Torvalds 		default:
7371da177e4SLinus Torvalds 			ret = -EINVAL;
7381da177e4SLinus Torvalds 	}
739a7113a96SJosef Sipek 	mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
7401da177e4SLinus Torvalds 	return ret;
7411da177e4SLinus Torvalds }
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds static int open_port(struct inode * inode, struct file * filp)
7441da177e4SLinus Torvalds {
7451da177e4SLinus Torvalds 	return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
7461da177e4SLinus Torvalds }
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds #define zero_lseek	null_lseek
7491da177e4SLinus Torvalds #define full_lseek      null_lseek
7501da177e4SLinus Torvalds #define write_zero	write_null
7511da177e4SLinus Torvalds #define read_full       read_zero
7521da177e4SLinus Torvalds #define open_mem	open_port
7531da177e4SLinus Torvalds #define open_kmem	open_mem
75450b1fdbdSVivek Goyal #define open_oldmem	open_mem
7551da177e4SLinus Torvalds 
75662322d25SArjan van de Ven static const struct file_operations mem_fops = {
7571da177e4SLinus Torvalds 	.llseek		= memory_lseek,
7581da177e4SLinus Torvalds 	.read		= read_mem,
7591da177e4SLinus Torvalds 	.write		= write_mem,
7601da177e4SLinus Torvalds 	.mmap		= mmap_mem,
7611da177e4SLinus Torvalds 	.open		= open_mem,
7625da6185bSDavid Howells 	.get_unmapped_area = get_unmapped_area_mem,
7631da177e4SLinus Torvalds };
7641da177e4SLinus Torvalds 
76562322d25SArjan van de Ven static const struct file_operations kmem_fops = {
7661da177e4SLinus Torvalds 	.llseek		= memory_lseek,
7671da177e4SLinus Torvalds 	.read		= read_kmem,
7681da177e4SLinus Torvalds 	.write		= write_kmem,
7691da177e4SLinus Torvalds 	.mmap		= mmap_kmem,
7701da177e4SLinus Torvalds 	.open		= open_kmem,
7715da6185bSDavid Howells 	.get_unmapped_area = get_unmapped_area_mem,
7721da177e4SLinus Torvalds };
7731da177e4SLinus Torvalds 
77462322d25SArjan van de Ven static const struct file_operations null_fops = {
7751da177e4SLinus Torvalds 	.llseek		= null_lseek,
7761da177e4SLinus Torvalds 	.read		= read_null,
7771da177e4SLinus Torvalds 	.write		= write_null,
7781ebd32fcSJens Axboe 	.splice_write	= splice_write_null,
7791da177e4SLinus Torvalds };
7801da177e4SLinus Torvalds 
7814f911d64SRussell King #ifdef CONFIG_DEVPORT
78262322d25SArjan van de Ven static const struct file_operations port_fops = {
7831da177e4SLinus Torvalds 	.llseek		= memory_lseek,
7841da177e4SLinus Torvalds 	.read		= read_port,
7851da177e4SLinus Torvalds 	.write		= write_port,
7861da177e4SLinus Torvalds 	.open		= open_port,
7871da177e4SLinus Torvalds };
7881da177e4SLinus Torvalds #endif
7891da177e4SLinus Torvalds 
79062322d25SArjan van de Ven static const struct file_operations zero_fops = {
7911da177e4SLinus Torvalds 	.llseek		= zero_lseek,
7921da177e4SLinus Torvalds 	.read		= read_zero,
7931da177e4SLinus Torvalds 	.write		= write_zero,
7941da177e4SLinus Torvalds 	.mmap		= mmap_zero,
7951da177e4SLinus Torvalds };
7961da177e4SLinus Torvalds 
7975da6185bSDavid Howells /*
7985da6185bSDavid Howells  * capabilities for /dev/zero
7995da6185bSDavid Howells  * - permits private mappings, "copies" are taken of the source of zeros
8005da6185bSDavid Howells  */
8011da177e4SLinus Torvalds static struct backing_dev_info zero_bdi = {
8021da177e4SLinus Torvalds 	.capabilities	= BDI_CAP_MAP_COPY,
8031da177e4SLinus Torvalds };
8041da177e4SLinus Torvalds 
80562322d25SArjan van de Ven static const struct file_operations full_fops = {
8061da177e4SLinus Torvalds 	.llseek		= full_lseek,
8071da177e4SLinus Torvalds 	.read		= read_full,
8081da177e4SLinus Torvalds 	.write		= write_full,
8091da177e4SLinus Torvalds };
8101da177e4SLinus Torvalds 
81150b1fdbdSVivek Goyal #ifdef CONFIG_CRASH_DUMP
81262322d25SArjan van de Ven static const struct file_operations oldmem_fops = {
81350b1fdbdSVivek Goyal 	.read	= read_oldmem,
81450b1fdbdSVivek Goyal 	.open	= open_oldmem,
81550b1fdbdSVivek Goyal };
81650b1fdbdSVivek Goyal #endif
81750b1fdbdSVivek Goyal 
8181da177e4SLinus Torvalds static ssize_t kmsg_write(struct file * file, const char __user * buf,
8191da177e4SLinus Torvalds 			  size_t count, loff_t *ppos)
8201da177e4SLinus Torvalds {
8211da177e4SLinus Torvalds 	char *tmp;
822cd140a5cSGuillaume Chazarain 	ssize_t ret;
8231da177e4SLinus Torvalds 
8241da177e4SLinus Torvalds 	tmp = kmalloc(count + 1, GFP_KERNEL);
8251da177e4SLinus Torvalds 	if (tmp == NULL)
8261da177e4SLinus Torvalds 		return -ENOMEM;
8271da177e4SLinus Torvalds 	ret = -EFAULT;
8281da177e4SLinus Torvalds 	if (!copy_from_user(tmp, buf, count)) {
8291da177e4SLinus Torvalds 		tmp[count] = 0;
8301da177e4SLinus Torvalds 		ret = printk("%s", tmp);
831cd140a5cSGuillaume Chazarain 		if (ret > count)
832cd140a5cSGuillaume Chazarain 			/* printk can add a prefix */
833cd140a5cSGuillaume Chazarain 			ret = count;
8341da177e4SLinus Torvalds 	}
8351da177e4SLinus Torvalds 	kfree(tmp);
8361da177e4SLinus Torvalds 	return ret;
8371da177e4SLinus Torvalds }
8381da177e4SLinus Torvalds 
83962322d25SArjan van de Ven static const struct file_operations kmsg_fops = {
8401da177e4SLinus Torvalds 	.write =	kmsg_write,
8411da177e4SLinus Torvalds };
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds static int memory_open(struct inode * inode, struct file * filp)
8441da177e4SLinus Torvalds {
8451da177e4SLinus Torvalds 	switch (iminor(inode)) {
8461da177e4SLinus Torvalds 		case 1:
8471da177e4SLinus Torvalds 			filp->f_op = &mem_fops;
8485da6185bSDavid Howells 			filp->f_mapping->backing_dev_info =
8495da6185bSDavid Howells 				&directly_mappable_cdev_bdi;
8501da177e4SLinus Torvalds 			break;
8511da177e4SLinus Torvalds 		case 2:
8521da177e4SLinus Torvalds 			filp->f_op = &kmem_fops;
8535da6185bSDavid Howells 			filp->f_mapping->backing_dev_info =
8545da6185bSDavid Howells 				&directly_mappable_cdev_bdi;
8551da177e4SLinus Torvalds 			break;
8561da177e4SLinus Torvalds 		case 3:
8571da177e4SLinus Torvalds 			filp->f_op = &null_fops;
8581da177e4SLinus Torvalds 			break;
8594f911d64SRussell King #ifdef CONFIG_DEVPORT
8601da177e4SLinus Torvalds 		case 4:
8611da177e4SLinus Torvalds 			filp->f_op = &port_fops;
8621da177e4SLinus Torvalds 			break;
8631da177e4SLinus Torvalds #endif
8641da177e4SLinus Torvalds 		case 5:
8651da177e4SLinus Torvalds 			filp->f_mapping->backing_dev_info = &zero_bdi;
8661da177e4SLinus Torvalds 			filp->f_op = &zero_fops;
8671da177e4SLinus Torvalds 			break;
8681da177e4SLinus Torvalds 		case 7:
8691da177e4SLinus Torvalds 			filp->f_op = &full_fops;
8701da177e4SLinus Torvalds 			break;
8711da177e4SLinus Torvalds 		case 8:
8721da177e4SLinus Torvalds 			filp->f_op = &random_fops;
8731da177e4SLinus Torvalds 			break;
8741da177e4SLinus Torvalds 		case 9:
8751da177e4SLinus Torvalds 			filp->f_op = &urandom_fops;
8761da177e4SLinus Torvalds 			break;
8771da177e4SLinus Torvalds 		case 11:
8781da177e4SLinus Torvalds 			filp->f_op = &kmsg_fops;
8791da177e4SLinus Torvalds 			break;
88050b1fdbdSVivek Goyal #ifdef CONFIG_CRASH_DUMP
88150b1fdbdSVivek Goyal 		case 12:
88250b1fdbdSVivek Goyal 			filp->f_op = &oldmem_fops;
88350b1fdbdSVivek Goyal 			break;
88450b1fdbdSVivek Goyal #endif
8851da177e4SLinus Torvalds 		default:
8861da177e4SLinus Torvalds 			return -ENXIO;
8871da177e4SLinus Torvalds 	}
8881da177e4SLinus Torvalds 	if (filp->f_op && filp->f_op->open)
8891da177e4SLinus Torvalds 		return filp->f_op->open(inode,filp);
8901da177e4SLinus Torvalds 	return 0;
8911da177e4SLinus Torvalds }
8921da177e4SLinus Torvalds 
89362322d25SArjan van de Ven static const struct file_operations memory_fops = {
8941da177e4SLinus Torvalds 	.open		= memory_open,	/* just a selector for the real open */
8951da177e4SLinus Torvalds };
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds static const struct {
8981da177e4SLinus Torvalds 	unsigned int		minor;
8991da177e4SLinus Torvalds 	char			*name;
9001da177e4SLinus Torvalds 	umode_t			mode;
90199ac48f5SArjan van de Ven 	const struct file_operations	*fops;
9021da177e4SLinus Torvalds } devlist[] = { /* list of minor devices */
9031da177e4SLinus Torvalds 	{1, "mem",     S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},
9041da177e4SLinus Torvalds 	{2, "kmem",    S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},
9051da177e4SLinus Torvalds 	{3, "null",    S_IRUGO | S_IWUGO,           &null_fops},
9064f911d64SRussell King #ifdef CONFIG_DEVPORT
9071da177e4SLinus Torvalds 	{4, "port",    S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},
9081da177e4SLinus Torvalds #endif
9091da177e4SLinus Torvalds 	{5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops},
9101da177e4SLinus Torvalds 	{7, "full",    S_IRUGO | S_IWUGO,           &full_fops},
9111da177e4SLinus Torvalds 	{8, "random",  S_IRUGO | S_IWUSR,           &random_fops},
9121da177e4SLinus Torvalds 	{9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops},
9131da177e4SLinus Torvalds 	{11,"kmsg",    S_IRUGO | S_IWUSR,           &kmsg_fops},
91450b1fdbdSVivek Goyal #ifdef CONFIG_CRASH_DUMP
91550b1fdbdSVivek Goyal 	{12,"oldmem",    S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops},
91650b1fdbdSVivek Goyal #endif
9171da177e4SLinus Torvalds };
9181da177e4SLinus Torvalds 
919ca8eca68Sgregkh@suse.de static struct class *mem_class;
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds static int __init chr_dev_init(void)
9221da177e4SLinus Torvalds {
9231da177e4SLinus Torvalds 	int i;
924e0bf68ddSPeter Zijlstra 	int err;
925e0bf68ddSPeter Zijlstra 
926e0bf68ddSPeter Zijlstra 	err = bdi_init(&zero_bdi);
927e0bf68ddSPeter Zijlstra 	if (err)
928e0bf68ddSPeter Zijlstra 		return err;
9291da177e4SLinus Torvalds 
9301da177e4SLinus Torvalds 	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
9311da177e4SLinus Torvalds 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
9321da177e4SLinus Torvalds 
933ca8eca68Sgregkh@suse.de 	mem_class = class_create(THIS_MODULE, "mem");
9347c69ef79SGreg Kroah-Hartman 	for (i = 0; i < ARRAY_SIZE(devlist); i++)
935ebf644c4SGreg Kroah-Hartman 		device_create(mem_class, NULL,
93653f46542SGreg Kroah-Hartman 			      MKDEV(MEM_MAJOR, devlist[i].minor),
937ebf644c4SGreg Kroah-Hartman 			      devlist[i].name);
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 	return 0;
9401da177e4SLinus Torvalds }
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds fs_initcall(chr_dev_init);
943