1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/drivers/char/mem.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1991, 1992 Linus Torvalds
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Added devfs support.
81da177e4SLinus Torvalds * Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
9af901ca1SAndré Goddard Rosa * Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/mm.h>
131da177e4SLinus Torvalds #include <linux/miscdevice.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/vmalloc.h>
161da177e4SLinus Torvalds #include <linux/mman.h>
171da177e4SLinus Torvalds #include <linux/random.h>
181da177e4SLinus Torvalds #include <linux/init.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>
241da177e4SLinus Torvalds #include <linux/backing-dev.h>
25c01d5b30SHugh Dickins #include <linux/shmem_fs.h>
26d6b29d7cSJens Axboe #include <linux/splice.h>
27b8a3ad5bSLinus Torvalds #include <linux/pfn.h>
2866300e66SPaul Gortmaker #include <linux/export.h>
29e1612de9SHaren Myneni #include <linux/io.h>
30e2e40f2cSChristoph Hellwig #include <linux/uio.h>
3135b6c7e4SRob Ward #include <linux/uaccess.h>
329b9d8ddaSMatthew Garrett #include <linux/security.h>
331da177e4SLinus Torvalds
343234ac66SDan Williams #define DEVMEM_MINOR 1
35e1612de9SHaren Myneni #define DEVPORT_MINOR 4
36e1612de9SHaren Myneni
size_inside_page(unsigned long start,unsigned long size)37f222318eSWu Fengguang static inline unsigned long size_inside_page(unsigned long start,
38f222318eSWu Fengguang unsigned long size)
39f222318eSWu Fengguang {
40f222318eSWu Fengguang unsigned long sz;
41f222318eSWu Fengguang
427fabadddSWu Fengguang sz = PAGE_SIZE - (start & (PAGE_SIZE - 1));
43f222318eSWu Fengguang
447fabadddSWu Fengguang return min(sz, size);
45f222318eSWu Fengguang }
46f222318eSWu Fengguang
471da177e4SLinus Torvalds #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
valid_phys_addr_range(phys_addr_t addr,size_t count)487e6735c3SCyril Chemparathy static inline int valid_phys_addr_range(phys_addr_t addr, size_t count)
491da177e4SLinus Torvalds {
50cfaf346cSChangli Gao return addr + count <= __pa(high_memory);
511da177e4SLinus Torvalds }
5280851ef2SBjorn Helgaas
valid_mmap_phys_addr_range(unsigned long pfn,size_t size)5306c67befSLennert Buytenhek static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
5480851ef2SBjorn Helgaas {
5580851ef2SBjorn Helgaas return 1;
5680851ef2SBjorn Helgaas }
571da177e4SLinus Torvalds #endif
581da177e4SLinus Torvalds
59d092633bSIngo Molnar #ifdef CONFIG_STRICT_DEVMEM
page_is_allowed(unsigned long pfn)60a4866aa8SKees Cook static inline int page_is_allowed(unsigned long pfn)
61a4866aa8SKees Cook {
62a4866aa8SKees Cook return devmem_is_allowed(pfn);
63a4866aa8SKees Cook }
range_is_allowed(unsigned long pfn,unsigned long size)64e2beb3eaSVenki Pallipadi static inline int range_is_allowed(unsigned long pfn, unsigned long size)
65ae531c26SArjan van de Ven {
66e2beb3eaSVenki Pallipadi u64 from = ((u64)pfn) << PAGE_SHIFT;
67e2beb3eaSVenki Pallipadi u64 to = from + size;
68e2beb3eaSVenki Pallipadi u64 cursor = from;
69ae531c26SArjan van de Ven
70e2beb3eaSVenki Pallipadi while (cursor < to) {
7139380b80SJiri Kosina if (!devmem_is_allowed(pfn))
72ae531c26SArjan van de Ven return 0;
73e2beb3eaSVenki Pallipadi cursor += PAGE_SIZE;
74e2beb3eaSVenki Pallipadi pfn++;
75ae531c26SArjan van de Ven }
76ae531c26SArjan van de Ven return 1;
77ae531c26SArjan van de Ven }
78ae531c26SArjan van de Ven #else
page_is_allowed(unsigned long pfn)79a4866aa8SKees Cook static inline int page_is_allowed(unsigned long pfn)
80a4866aa8SKees Cook {
81a4866aa8SKees Cook return 1;
82a4866aa8SKees Cook }
range_is_allowed(unsigned long pfn,unsigned long size)83e2beb3eaSVenki Pallipadi static inline int range_is_allowed(unsigned long pfn, unsigned long size)
84ae531c26SArjan van de Ven {
85ae531c26SArjan van de Ven return 1;
86ae531c26SArjan van de Ven }
87ae531c26SArjan van de Ven #endif
88ae531c26SArjan van de Ven
should_stop_iteration(void)898619e5bdSTetsuo Handa static inline bool should_stop_iteration(void)
908619e5bdSTetsuo Handa {
918619e5bdSTetsuo Handa if (need_resched())
928619e5bdSTetsuo Handa cond_resched();
93830a4e5cSJason A. Donenfeld return signal_pending(current);
948619e5bdSTetsuo Handa }
958619e5bdSTetsuo Handa
961da177e4SLinus Torvalds /*
971da177e4SLinus Torvalds * This funcion reads the *physical* memory. The f_pos points directly to the
981da177e4SLinus Torvalds * memory location.
991da177e4SLinus Torvalds */
read_mem(struct file * file,char __user * buf,size_t count,loff_t * ppos)1001da177e4SLinus Torvalds static ssize_t read_mem(struct file *file, char __user *buf,
1011da177e4SLinus Torvalds size_t count, loff_t *ppos)
1021da177e4SLinus Torvalds {
1037e6735c3SCyril Chemparathy phys_addr_t p = *ppos;
1041da177e4SLinus Torvalds ssize_t read, sz;
1054707a341SThierry Reding void *ptr;
10622ec1a2aSKees Cook char *bounce;
10722ec1a2aSKees Cook int err;
1081da177e4SLinus Torvalds
10908d2d00bSPetr Tesarik if (p != *ppos)
11008d2d00bSPetr Tesarik return 0;
11108d2d00bSPetr Tesarik
112136939a2SBjorn Helgaas if (!valid_phys_addr_range(p, count))
1131da177e4SLinus Torvalds return -EFAULT;
1141da177e4SLinus Torvalds read = 0;
1151da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
1161da177e4SLinus Torvalds /* we don't have page 0 mapped on sparc and m68k.. */
1171da177e4SLinus Torvalds if (p < PAGE_SIZE) {
1187fabadddSWu Fengguang sz = size_inside_page(p, count);
1191da177e4SLinus Torvalds if (sz > 0) {
1201da177e4SLinus Torvalds if (clear_user(buf, sz))
1211da177e4SLinus Torvalds return -EFAULT;
1221da177e4SLinus Torvalds buf += sz;
1231da177e4SLinus Torvalds p += sz;
1241da177e4SLinus Torvalds count -= sz;
1251da177e4SLinus Torvalds read += sz;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds #endif
1291da177e4SLinus Torvalds
13022ec1a2aSKees Cook bounce = kmalloc(PAGE_SIZE, GFP_KERNEL);
13122ec1a2aSKees Cook if (!bounce)
13222ec1a2aSKees Cook return -ENOMEM;
13322ec1a2aSKees Cook
1341da177e4SLinus Torvalds while (count > 0) {
135fa29e97bSWu Fengguang unsigned long remaining;
136b5b38200SKees Cook int allowed, probe;
137fa29e97bSWu Fengguang
138f222318eSWu Fengguang sz = size_inside_page(p, count);
1391da177e4SLinus Torvalds
14022ec1a2aSKees Cook err = -EPERM;
141a4866aa8SKees Cook allowed = page_is_allowed(p >> PAGE_SHIFT);
142a4866aa8SKees Cook if (!allowed)
14322ec1a2aSKees Cook goto failed;
14422ec1a2aSKees Cook
14522ec1a2aSKees Cook err = -EFAULT;
146a4866aa8SKees Cook if (allowed == 2) {
147a4866aa8SKees Cook /* Show zeros for restricted memory. */
148a4866aa8SKees Cook remaining = clear_user(buf, sz);
149a4866aa8SKees Cook } else {
1501da177e4SLinus Torvalds /*
151a4866aa8SKees Cook * On ia64 if a page has been mapped somewhere as
152a4866aa8SKees Cook * uncached, then it must also be accessed uncached
153a4866aa8SKees Cook * by the kernel or data corruption may occur.
1541da177e4SLinus Torvalds */
1551da177e4SLinus Torvalds ptr = xlate_dev_mem_ptr(p);
156e045fb2aSvenkatesh.pallipadi@intel.com if (!ptr)
15722ec1a2aSKees Cook goto failed;
158e045fb2aSvenkatesh.pallipadi@intel.com
159fe557319SChristoph Hellwig probe = copy_from_kernel_nofault(bounce, ptr, sz);
160e045fb2aSvenkatesh.pallipadi@intel.com unxlate_dev_mem_ptr(p, ptr);
161b5b38200SKees Cook if (probe)
16222ec1a2aSKees Cook goto failed;
16322ec1a2aSKees Cook
16422ec1a2aSKees Cook remaining = copy_to_user(buf, bounce, sz);
165a4866aa8SKees Cook }
166a4866aa8SKees Cook
167fa29e97bSWu Fengguang if (remaining)
16822ec1a2aSKees Cook goto failed;
169e045fb2aSvenkatesh.pallipadi@intel.com
1701da177e4SLinus Torvalds buf += sz;
1711da177e4SLinus Torvalds p += sz;
1721da177e4SLinus Torvalds count -= sz;
1731da177e4SLinus Torvalds read += sz;
1748619e5bdSTetsuo Handa if (should_stop_iteration())
1758619e5bdSTetsuo Handa break;
1761da177e4SLinus Torvalds }
17722ec1a2aSKees Cook kfree(bounce);
1781da177e4SLinus Torvalds
1791da177e4SLinus Torvalds *ppos += read;
1801da177e4SLinus Torvalds return read;
18122ec1a2aSKees Cook
18222ec1a2aSKees Cook failed:
18322ec1a2aSKees Cook kfree(bounce);
18422ec1a2aSKees Cook return err;
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds
write_mem(struct file * file,const char __user * buf,size_t count,loff_t * ppos)1871da177e4SLinus Torvalds static ssize_t write_mem(struct file *file, const char __user *buf,
1881da177e4SLinus Torvalds size_t count, loff_t *ppos)
1891da177e4SLinus Torvalds {
1907e6735c3SCyril Chemparathy phys_addr_t p = *ppos;
1911da177e4SLinus Torvalds ssize_t written, sz;
1921da177e4SLinus Torvalds unsigned long copied;
1931da177e4SLinus Torvalds void *ptr;
1941da177e4SLinus Torvalds
19508d2d00bSPetr Tesarik if (p != *ppos)
19608d2d00bSPetr Tesarik return -EFBIG;
19708d2d00bSPetr Tesarik
198136939a2SBjorn Helgaas if (!valid_phys_addr_range(p, count))
1991da177e4SLinus Torvalds return -EFAULT;
2001da177e4SLinus Torvalds
2011da177e4SLinus Torvalds written = 0;
2021da177e4SLinus Torvalds
2031da177e4SLinus Torvalds #ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
2041da177e4SLinus Torvalds /* we don't have page 0 mapped on sparc and m68k.. */
2051da177e4SLinus Torvalds if (p < PAGE_SIZE) {
2067fabadddSWu Fengguang sz = size_inside_page(p, count);
2071da177e4SLinus Torvalds /* Hmm. Do something? */
2081da177e4SLinus Torvalds buf += sz;
2091da177e4SLinus Torvalds p += sz;
2101da177e4SLinus Torvalds count -= sz;
2111da177e4SLinus Torvalds written += sz;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds #endif
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds while (count > 0) {
216a4866aa8SKees Cook int allowed;
217a4866aa8SKees Cook
218f222318eSWu Fengguang sz = size_inside_page(p, count);
2191da177e4SLinus Torvalds
220a4866aa8SKees Cook allowed = page_is_allowed(p >> PAGE_SHIFT);
221a4866aa8SKees Cook if (!allowed)
222e045fb2aSvenkatesh.pallipadi@intel.com return -EPERM;
223e045fb2aSvenkatesh.pallipadi@intel.com
224a4866aa8SKees Cook /* Skip actual writing when a page is marked as restricted. */
225a4866aa8SKees Cook if (allowed == 1) {
2261da177e4SLinus Torvalds /*
227a4866aa8SKees Cook * On ia64 if a page has been mapped somewhere as
228a4866aa8SKees Cook * uncached, then it must also be accessed uncached
229a4866aa8SKees Cook * by the kernel or data corruption may occur.
2301da177e4SLinus Torvalds */
2311da177e4SLinus Torvalds ptr = xlate_dev_mem_ptr(p);
232e045fb2aSvenkatesh.pallipadi@intel.com if (!ptr) {
233c654d60eSJan Beulich if (written)
234c654d60eSJan Beulich break;
2351da177e4SLinus Torvalds return -EFAULT;
2361da177e4SLinus Torvalds }
237e045fb2aSvenkatesh.pallipadi@intel.com
238e045fb2aSvenkatesh.pallipadi@intel.com copied = copy_from_user(ptr, buf, sz);
239fa29e97bSWu Fengguang unxlate_dev_mem_ptr(p, ptr);
240e045fb2aSvenkatesh.pallipadi@intel.com if (copied) {
241e045fb2aSvenkatesh.pallipadi@intel.com written += sz - copied;
242e045fb2aSvenkatesh.pallipadi@intel.com if (written)
243e045fb2aSvenkatesh.pallipadi@intel.com break;
244e045fb2aSvenkatesh.pallipadi@intel.com return -EFAULT;
245e045fb2aSvenkatesh.pallipadi@intel.com }
246a4866aa8SKees Cook }
247e045fb2aSvenkatesh.pallipadi@intel.com
2481da177e4SLinus Torvalds buf += sz;
2491da177e4SLinus Torvalds p += sz;
2501da177e4SLinus Torvalds count -= sz;
2511da177e4SLinus Torvalds written += sz;
2528619e5bdSTetsuo Handa if (should_stop_iteration())
2538619e5bdSTetsuo Handa break;
2541da177e4SLinus Torvalds }
2551da177e4SLinus Torvalds
2561da177e4SLinus Torvalds *ppos += written;
2571da177e4SLinus Torvalds return written;
2581da177e4SLinus Torvalds }
2591da177e4SLinus Torvalds
phys_mem_access_prot_allowed(struct file * file,unsigned long pfn,unsigned long size,pgprot_t * vma_prot)260d7d4d849SAndrew Morton int __weak phys_mem_access_prot_allowed(struct file *file,
261f0970c13Svenkatesh.pallipadi@intel.com unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
262f0970c13Svenkatesh.pallipadi@intel.com {
263f0970c13Svenkatesh.pallipadi@intel.com return 1;
264f0970c13Svenkatesh.pallipadi@intel.com }
265f0970c13Svenkatesh.pallipadi@intel.com
26644ac8413SBjorn Helgaas #ifndef __HAVE_PHYS_MEM_ACCESS_PROT
267d7d4d849SAndrew Morton
268d7d4d849SAndrew Morton /*
269d7d4d849SAndrew Morton * Architectures vary in how they handle caching for addresses
270d7d4d849SAndrew Morton * outside of main memory.
271d7d4d849SAndrew Morton *
272d7d4d849SAndrew Morton */
273ea56f411SDavid Howells #ifdef pgprot_noncached
uncached_access(struct file * file,phys_addr_t addr)2747e6735c3SCyril Chemparathy static int uncached_access(struct file *file, phys_addr_t addr)
275d7d4d849SAndrew Morton {
276d7d4d849SAndrew Morton /*
277d7d4d849SAndrew Morton * Accessing memory above the top the kernel knows about or through a
278d7d4d849SAndrew Morton * file pointer
279d7d4d849SAndrew Morton * that was marked O_DSYNC will be done non-cached.
280d7d4d849SAndrew Morton */
281d7d4d849SAndrew Morton if (file->f_flags & O_DSYNC)
282d7d4d849SAndrew Morton return 1;
283d7d4d849SAndrew Morton return addr >= __pa(high_memory);
284d7d4d849SAndrew Morton }
285ea56f411SDavid Howells #endif
286d7d4d849SAndrew Morton
phys_mem_access_prot(struct file * file,unsigned long pfn,unsigned long size,pgprot_t vma_prot)28744ac8413SBjorn Helgaas static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
28844ac8413SBjorn Helgaas unsigned long size, pgprot_t vma_prot)
28944ac8413SBjorn Helgaas {
29044ac8413SBjorn Helgaas #ifdef pgprot_noncached
2917e6735c3SCyril Chemparathy phys_addr_t offset = pfn << PAGE_SHIFT;
29244ac8413SBjorn Helgaas
29344ac8413SBjorn Helgaas if (uncached_access(file, offset))
29444ac8413SBjorn Helgaas return pgprot_noncached(vma_prot);
29544ac8413SBjorn Helgaas #endif
29644ac8413SBjorn Helgaas return vma_prot;
29744ac8413SBjorn Helgaas }
29844ac8413SBjorn Helgaas #endif
29944ac8413SBjorn Helgaas
3005da6185bSDavid Howells #ifndef CONFIG_MMU
get_unmapped_area_mem(struct file * file,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)3015da6185bSDavid Howells static unsigned long get_unmapped_area_mem(struct file *file,
3025da6185bSDavid Howells unsigned long addr,
3035da6185bSDavid Howells unsigned long len,
3045da6185bSDavid Howells unsigned long pgoff,
3055da6185bSDavid Howells unsigned long flags)
3065da6185bSDavid Howells {
3075da6185bSDavid Howells if (!valid_mmap_phys_addr_range(pgoff, len))
3085da6185bSDavid Howells return (unsigned long) -EINVAL;
3098a93258cSBenjamin Herrenschmidt return pgoff << PAGE_SHIFT;
3105da6185bSDavid Howells }
3115da6185bSDavid Howells
312b4caecd4SChristoph Hellwig /* permit direct mmap, for read, write or exec */
memory_mmap_capabilities(struct file * file)313b4caecd4SChristoph Hellwig static unsigned memory_mmap_capabilities(struct file *file)
314b4caecd4SChristoph Hellwig {
315b4caecd4SChristoph Hellwig return NOMMU_MAP_DIRECT |
316b4caecd4SChristoph Hellwig NOMMU_MAP_READ | NOMMU_MAP_WRITE | NOMMU_MAP_EXEC;
317b4caecd4SChristoph Hellwig }
318b4caecd4SChristoph Hellwig
zero_mmap_capabilities(struct file * file)319b4caecd4SChristoph Hellwig static unsigned zero_mmap_capabilities(struct file *file)
320b4caecd4SChristoph Hellwig {
321b4caecd4SChristoph Hellwig return NOMMU_MAP_COPY;
322b4caecd4SChristoph Hellwig }
323b4caecd4SChristoph Hellwig
3245da6185bSDavid Howells /* can't do an in-place private mapping if there's no MMU */
private_mapping_ok(struct vm_area_struct * vma)3255da6185bSDavid Howells static inline int private_mapping_ok(struct vm_area_struct *vma)
3265da6185bSDavid Howells {
327fc4f4be9SDavid Hildenbrand return is_nommu_shared_mapping(vma->vm_flags);
3285da6185bSDavid Howells }
3295da6185bSDavid Howells #else
3305da6185bSDavid Howells
private_mapping_ok(struct vm_area_struct * vma)3315da6185bSDavid Howells static inline int private_mapping_ok(struct vm_area_struct *vma)
3325da6185bSDavid Howells {
3335da6185bSDavid Howells return 1;
3345da6185bSDavid Howells }
3355da6185bSDavid Howells #endif
3365da6185bSDavid Howells
337f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct mmap_mem_ops = {
3387ae8ed50SRik van Riel #ifdef CONFIG_HAVE_IOREMAP_PROT
3397ae8ed50SRik van Riel .access = generic_access_phys
3407ae8ed50SRik van Riel #endif
341e7f260a2Svenkatesh.pallipadi@intel.com };
342e7f260a2Svenkatesh.pallipadi@intel.com
mmap_mem(struct file * file,struct vm_area_struct * vma)3431da177e4SLinus Torvalds static int mmap_mem(struct file *file, struct vm_area_struct *vma)
3441da177e4SLinus Torvalds {
34580851ef2SBjorn Helgaas size_t size = vma->vm_end - vma->vm_start;
346b299cde2SJulius Werner phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
347b299cde2SJulius Werner
348be62a320SCraig Bergstrom /* Does it even fit in phys_addr_t? */
349be62a320SCraig Bergstrom if (offset >> PAGE_SHIFT != vma->vm_pgoff)
350be62a320SCraig Bergstrom return -EINVAL;
351be62a320SCraig Bergstrom
352b299cde2SJulius Werner /* It's illegal to wrap around the end of the physical address space. */
35332829da5SJulius Werner if (offset + (phys_addr_t)size - 1 < offset)
354b299cde2SJulius Werner return -EINVAL;
35580851ef2SBjorn Helgaas
35606c67befSLennert Buytenhek if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
35780851ef2SBjorn Helgaas return -EINVAL;
35880851ef2SBjorn Helgaas
3595da6185bSDavid Howells if (!private_mapping_ok(vma))
3605da6185bSDavid Howells return -ENOSYS;
3615da6185bSDavid Howells
362e2beb3eaSVenki Pallipadi if (!range_is_allowed(vma->vm_pgoff, size))
363e2beb3eaSVenki Pallipadi return -EPERM;
364e2beb3eaSVenki Pallipadi
365f0970c13Svenkatesh.pallipadi@intel.com if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
366f0970c13Svenkatesh.pallipadi@intel.com &vma->vm_page_prot))
367f0970c13Svenkatesh.pallipadi@intel.com return -EINVAL;
368f0970c13Svenkatesh.pallipadi@intel.com
3698b150478SRoland Dreier vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
37080851ef2SBjorn Helgaas size,
3711da177e4SLinus Torvalds vma->vm_page_prot);
3721da177e4SLinus Torvalds
373e7f260a2Svenkatesh.pallipadi@intel.com vma->vm_ops = &mmap_mem_ops;
374e7f260a2Svenkatesh.pallipadi@intel.com
375314e51b9SKonstantin Khlebnikov /* Remap-pfn-range will mark the range VM_IO */
3761da177e4SLinus Torvalds if (remap_pfn_range(vma,
3771da177e4SLinus Torvalds vma->vm_start,
3781da177e4SLinus Torvalds vma->vm_pgoff,
37980851ef2SBjorn Helgaas size,
380e7f260a2Svenkatesh.pallipadi@intel.com vma->vm_page_prot)) {
3811da177e4SLinus Torvalds return -EAGAIN;
382e7f260a2Svenkatesh.pallipadi@intel.com }
3831da177e4SLinus Torvalds return 0;
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds
386f0a816fbSNiklas Schnelle #ifdef CONFIG_DEVPORT
read_port(struct file * file,char __user * buf,size_t count,loff_t * ppos)3871da177e4SLinus Torvalds static ssize_t read_port(struct file *file, char __user *buf,
3881da177e4SLinus Torvalds size_t count, loff_t *ppos)
3891da177e4SLinus Torvalds {
3901da177e4SLinus Torvalds unsigned long i = *ppos;
3911da177e4SLinus Torvalds char __user *tmp = buf;
3921da177e4SLinus Torvalds
39396d4f267SLinus Torvalds if (!access_ok(buf, count))
3941da177e4SLinus Torvalds return -EFAULT;
3951da177e4SLinus Torvalds while (count-- > 0 && i < 65536) {
3961da177e4SLinus Torvalds if (__put_user(inb(i), tmp) < 0)
3971da177e4SLinus Torvalds return -EFAULT;
3981da177e4SLinus Torvalds i++;
3991da177e4SLinus Torvalds tmp++;
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds *ppos = i;
4021da177e4SLinus Torvalds return tmp-buf;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds
write_port(struct file * file,const char __user * buf,size_t count,loff_t * ppos)4051da177e4SLinus Torvalds static ssize_t write_port(struct file *file, const char __user *buf,
4061da177e4SLinus Torvalds size_t count, loff_t *ppos)
4071da177e4SLinus Torvalds {
4081da177e4SLinus Torvalds unsigned long i = *ppos;
4091da177e4SLinus Torvalds const char __user *tmp = buf;
4101da177e4SLinus Torvalds
41196d4f267SLinus Torvalds if (!access_ok(buf, count))
4121da177e4SLinus Torvalds return -EFAULT;
4131da177e4SLinus Torvalds while (count-- > 0 && i < 65536) {
4141da177e4SLinus Torvalds char c;
4156a0061baSRob Ward
416c654d60eSJan Beulich if (__get_user(c, tmp)) {
417c654d60eSJan Beulich if (tmp > buf)
418c654d60eSJan Beulich break;
4191da177e4SLinus Torvalds return -EFAULT;
420c654d60eSJan Beulich }
4211da177e4SLinus Torvalds outb(c, i);
4221da177e4SLinus Torvalds i++;
4231da177e4SLinus Torvalds tmp++;
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds *ppos = i;
4261da177e4SLinus Torvalds return tmp-buf;
4271da177e4SLinus Torvalds }
428f0a816fbSNiklas Schnelle #endif
4291da177e4SLinus Torvalds
read_null(struct file * file,char __user * buf,size_t count,loff_t * ppos)4301da177e4SLinus Torvalds static ssize_t read_null(struct file *file, char __user *buf,
4311da177e4SLinus Torvalds size_t count, loff_t *ppos)
4321da177e4SLinus Torvalds {
4331da177e4SLinus Torvalds return 0;
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds
write_null(struct file * file,const char __user * buf,size_t count,loff_t * ppos)4361da177e4SLinus Torvalds static ssize_t write_null(struct file *file, const char __user *buf,
4371da177e4SLinus Torvalds size_t count, loff_t *ppos)
4381da177e4SLinus Torvalds {
4391da177e4SLinus Torvalds return count;
4401da177e4SLinus Torvalds }
4411da177e4SLinus Torvalds
read_iter_null(struct kiocb * iocb,struct iov_iter * to)442cd28e28dSAl Viro static ssize_t read_iter_null(struct kiocb *iocb, struct iov_iter *to)
443162934deSZach Brown {
444162934deSZach Brown return 0;
445162934deSZach Brown }
446162934deSZach Brown
write_iter_null(struct kiocb * iocb,struct iov_iter * from)447cd28e28dSAl Viro static ssize_t write_iter_null(struct kiocb *iocb, struct iov_iter *from)
448162934deSZach Brown {
449cd28e28dSAl Viro size_t count = iov_iter_count(from);
450cd28e28dSAl Viro iov_iter_advance(from, count);
451cd28e28dSAl Viro return count;
452162934deSZach Brown }
453162934deSZach Brown
pipe_to_null(struct pipe_inode_info * info,struct pipe_buffer * buf,struct splice_desc * sd)4541ebd32fcSJens Axboe static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,
4551ebd32fcSJens Axboe struct splice_desc *sd)
4561ebd32fcSJens Axboe {
4571ebd32fcSJens Axboe return sd->len;
4581ebd32fcSJens Axboe }
4591ebd32fcSJens Axboe
splice_write_null(struct pipe_inode_info * pipe,struct file * out,loff_t * ppos,size_t len,unsigned int flags)4601ebd32fcSJens Axboe static ssize_t splice_write_null(struct pipe_inode_info *pipe, struct file *out,
4611ebd32fcSJens Axboe loff_t *ppos, size_t len, unsigned int flags)
4621ebd32fcSJens Axboe {
4631ebd32fcSJens Axboe return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null);
4641ebd32fcSJens Axboe }
4651ebd32fcSJens Axboe
uring_cmd_null(struct io_uring_cmd * ioucmd,unsigned int issue_flags)46670752795SPaul Moore static int uring_cmd_null(struct io_uring_cmd *ioucmd, unsigned int issue_flags)
46770752795SPaul Moore {
46870752795SPaul Moore return 0;
46970752795SPaul Moore }
47070752795SPaul Moore
read_iter_zero(struct kiocb * iocb,struct iov_iter * iter)47113ba33e8SAl Viro static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter)
472162934deSZach Brown {
473162934deSZach Brown size_t written = 0;
474162934deSZach Brown
47513ba33e8SAl Viro while (iov_iter_count(iter)) {
47613ba33e8SAl Viro size_t chunk = iov_iter_count(iter), n;
4776a0061baSRob Ward
47813ba33e8SAl Viro if (chunk > PAGE_SIZE)
47913ba33e8SAl Viro chunk = PAGE_SIZE; /* Just for latency reasons */
48013ba33e8SAl Viro n = iov_iter_zero(chunk, iter);
48113ba33e8SAl Viro if (!n && iov_iter_count(iter))
482162934deSZach Brown return written ? written : -EFAULT;
48313ba33e8SAl Viro written += n;
48413ba33e8SAl Viro if (signal_pending(current))
48513ba33e8SAl Viro return written ? written : -ERESTARTSYS;
486e5f71d60SPavel Begunkov if (!need_resched())
487e5f71d60SPavel Begunkov continue;
488e5f71d60SPavel Begunkov if (iocb->ki_flags & IOCB_NOWAIT)
489e5f71d60SPavel Begunkov return written ? written : -EAGAIN;
49013ba33e8SAl Viro cond_resched();
49113ba33e8SAl Viro }
49213ba33e8SAl Viro return written;
493162934deSZach Brown }
494162934deSZach Brown
read_zero(struct file * file,char __user * buf,size_t count,loff_t * ppos)49599f66735SChristoph Hellwig static ssize_t read_zero(struct file *file, char __user *buf,
49699f66735SChristoph Hellwig size_t count, loff_t *ppos)
49799f66735SChristoph Hellwig {
49899f66735SChristoph Hellwig size_t cleared = 0;
49999f66735SChristoph Hellwig
50099f66735SChristoph Hellwig while (count) {
50199f66735SChristoph Hellwig size_t chunk = min_t(size_t, count, PAGE_SIZE);
502ab04de8eSChristoph Hellwig size_t left;
50399f66735SChristoph Hellwig
504ab04de8eSChristoph Hellwig left = clear_user(buf + cleared, chunk);
505ab04de8eSChristoph Hellwig if (unlikely(left)) {
506ab04de8eSChristoph Hellwig cleared += (chunk - left);
507ab04de8eSChristoph Hellwig if (!cleared)
508ab04de8eSChristoph Hellwig return -EFAULT;
509ab04de8eSChristoph Hellwig break;
510ab04de8eSChristoph Hellwig }
51199f66735SChristoph Hellwig cleared += chunk;
51299f66735SChristoph Hellwig count -= chunk;
51399f66735SChristoph Hellwig
51499f66735SChristoph Hellwig if (signal_pending(current))
515ab04de8eSChristoph Hellwig break;
51699f66735SChristoph Hellwig cond_resched();
51799f66735SChristoph Hellwig }
51899f66735SChristoph Hellwig
51999f66735SChristoph Hellwig return cleared;
52099f66735SChristoph Hellwig }
52199f66735SChristoph Hellwig
mmap_zero(struct file * file,struct vm_area_struct * vma)5221da177e4SLinus Torvalds static int mmap_zero(struct file *file, struct vm_area_struct *vma)
5231da177e4SLinus Torvalds {
524557ed1faSNick Piggin #ifndef CONFIG_MMU
525557ed1faSNick Piggin return -ENOSYS;
526557ed1faSNick Piggin #endif
5271da177e4SLinus Torvalds if (vma->vm_flags & VM_SHARED)
5281da177e4SLinus Torvalds return shmem_zero_setup(vma);
529bfd40eafSKirill A. Shutemov vma_set_anonymous(vma);
530557ed1faSNick Piggin return 0;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds
get_unmapped_area_zero(struct file * file,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)533c01d5b30SHugh Dickins static unsigned long get_unmapped_area_zero(struct file *file,
534c01d5b30SHugh Dickins unsigned long addr, unsigned long len,
535c01d5b30SHugh Dickins unsigned long pgoff, unsigned long flags)
536c01d5b30SHugh Dickins {
537c01d5b30SHugh Dickins #ifdef CONFIG_MMU
538c01d5b30SHugh Dickins if (flags & MAP_SHARED) {
539c01d5b30SHugh Dickins /*
540c01d5b30SHugh Dickins * mmap_zero() will call shmem_zero_setup() to create a file,
541c01d5b30SHugh Dickins * so use shmem's get_unmapped_area in case it can be huge;
542c01d5b30SHugh Dickins * and pass NULL for file as in mmap.c's get_unmapped_area(),
543c01d5b30SHugh Dickins * so as not to confuse shmem with our handle on "/dev/zero".
544c01d5b30SHugh Dickins */
545c01d5b30SHugh Dickins return shmem_get_unmapped_area(NULL, addr, len, pgoff, flags);
546c01d5b30SHugh Dickins }
547c01d5b30SHugh Dickins
548c01d5b30SHugh Dickins /* Otherwise flags & MAP_PRIVATE: with no shmem object beneath it */
549529ce23aSRick Edgecombe return mm_get_unmapped_area(current->mm, file, addr, len, pgoff, flags);
550c01d5b30SHugh Dickins #else
551c01d5b30SHugh Dickins return -ENOSYS;
552c01d5b30SHugh Dickins #endif
553c01d5b30SHugh Dickins }
554c01d5b30SHugh Dickins
write_full(struct file * file,const char __user * buf,size_t count,loff_t * ppos)5551da177e4SLinus Torvalds static ssize_t write_full(struct file *file, const char __user *buf,
5561da177e4SLinus Torvalds size_t count, loff_t *ppos)
5571da177e4SLinus Torvalds {
5581da177e4SLinus Torvalds return -ENOSPC;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds
5611da177e4SLinus Torvalds /*
5621da177e4SLinus Torvalds * Special lseek() function for /dev/null and /dev/zero. Most notably, you
5631da177e4SLinus Torvalds * can fopen() both devices with "a" now. This was previously impossible.
5641da177e4SLinus Torvalds * -- SRB.
5651da177e4SLinus Torvalds */
null_lseek(struct file * file,loff_t offset,int orig)5661da177e4SLinus Torvalds static loff_t null_lseek(struct file *file, loff_t offset, int orig)
5671da177e4SLinus Torvalds {
5681da177e4SLinus Torvalds return file->f_pos = 0;
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds
5711da177e4SLinus Torvalds /*
5721da177e4SLinus Torvalds * The memory devices use the full 32/64 bits of the offset, and so we cannot
5731da177e4SLinus Torvalds * check against negative addresses: they are ok. The return value is weird,
5741da177e4SLinus Torvalds * though, in that case (0).
5751da177e4SLinus Torvalds *
5761da177e4SLinus Torvalds * also note that seeking relative to the "end of file" isn't supported:
5771da177e4SLinus Torvalds * it has no meaning, so it returns -EINVAL.
5781da177e4SLinus Torvalds */
memory_lseek(struct file * file,loff_t offset,int orig)5791da177e4SLinus Torvalds static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
5801da177e4SLinus Torvalds {
5811da177e4SLinus Torvalds loff_t ret;
5821da177e4SLinus Torvalds
5835955102cSAl Viro inode_lock(file_inode(file));
5841da177e4SLinus Torvalds switch (orig) {
585dcefafb6SWu Fengguang case SEEK_CUR:
586dcefafb6SWu Fengguang offset += file->f_pos;
587df561f66SGustavo A. R. Silva fallthrough;
588dcefafb6SWu Fengguang case SEEK_SET:
589dcefafb6SWu Fengguang /* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */
590ecb63a1bSAndrzej Hajda if ((unsigned long long)offset >= -MAX_ERRNO) {
591dcefafb6SWu Fengguang ret = -EOVERFLOW;
592dcefafb6SWu Fengguang break;
593dcefafb6SWu Fengguang }
594dcefafb6SWu Fengguang file->f_pos = offset;
5951da177e4SLinus Torvalds ret = file->f_pos;
5961da177e4SLinus Torvalds force_successful_syscall_return();
5971da177e4SLinus Torvalds break;
5981da177e4SLinus Torvalds default:
5991da177e4SLinus Torvalds ret = -EINVAL;
6001da177e4SLinus Torvalds }
6015955102cSAl Viro inode_unlock(file_inode(file));
6021da177e4SLinus Torvalds return ret;
6031da177e4SLinus Torvalds }
6041da177e4SLinus Torvalds
open_port(struct inode * inode,struct file * filp)6051da177e4SLinus Torvalds static int open_port(struct inode *inode, struct file *filp)
6061da177e4SLinus Torvalds {
6073234ac66SDan Williams int rc;
6083234ac66SDan Williams
6099b9d8ddaSMatthew Garrett if (!capable(CAP_SYS_RAWIO))
6109b9d8ddaSMatthew Garrett return -EPERM;
6119b9d8ddaSMatthew Garrett
6123234ac66SDan Williams rc = security_locked_down(LOCKDOWN_DEV_MEM);
6133234ac66SDan Williams if (rc)
6143234ac66SDan Williams return rc;
6153234ac66SDan Williams
6163234ac66SDan Williams if (iminor(inode) != DEVMEM_MINOR)
6173234ac66SDan Williams return 0;
6183234ac66SDan Williams
6193234ac66SDan Williams /*
6203234ac66SDan Williams * Use a unified address space to have a single point to manage
6213234ac66SDan Williams * revocations when drivers want to take over a /dev/mem mapped
6223234ac66SDan Williams * range.
6233234ac66SDan Williams */
62471a1d8edSDaniel Vetter filp->f_mapping = iomem_get_mapping();
6253234ac66SDan Williams
6263234ac66SDan Williams return 0;
6271da177e4SLinus Torvalds }
6281da177e4SLinus Torvalds
6291da177e4SLinus Torvalds #define zero_lseek null_lseek
6301da177e4SLinus Torvalds #define full_lseek null_lseek
6311da177e4SLinus Torvalds #define write_zero write_null
632cd28e28dSAl Viro #define write_iter_zero write_iter_null
6331b057bd8SMax Kellermann #define splice_write_zero splice_write_null
6341da177e4SLinus Torvalds #define open_mem open_port
6351da177e4SLinus Torvalds
63673f0718eSRob Ward static const struct file_operations __maybe_unused mem_fops = {
6371da177e4SLinus Torvalds .llseek = memory_lseek,
6381da177e4SLinus Torvalds .read = read_mem,
6391da177e4SLinus Torvalds .write = write_mem,
6401da177e4SLinus Torvalds .mmap = mmap_mem,
6411da177e4SLinus Torvalds .open = open_mem,
642b4caecd4SChristoph Hellwig #ifndef CONFIG_MMU
6435da6185bSDavid Howells .get_unmapped_area = get_unmapped_area_mem,
644b4caecd4SChristoph Hellwig .mmap_capabilities = memory_mmap_capabilities,
645b4caecd4SChristoph Hellwig #endif
646*641bb439SChristian Brauner .fop_flags = FOP_UNSIGNED_OFFSET,
6471da177e4SLinus Torvalds };
6481da177e4SLinus Torvalds
64962322d25SArjan van de Ven static const struct file_operations null_fops = {
6501da177e4SLinus Torvalds .llseek = null_lseek,
6511da177e4SLinus Torvalds .read = read_null,
6521da177e4SLinus Torvalds .write = write_null,
653cd28e28dSAl Viro .read_iter = read_iter_null,
654cd28e28dSAl Viro .write_iter = write_iter_null,
6551ebd32fcSJens Axboe .splice_write = splice_write_null,
65670752795SPaul Moore .uring_cmd = uring_cmd_null,
6571da177e4SLinus Torvalds };
6581da177e4SLinus Torvalds
659f0a816fbSNiklas Schnelle #ifdef CONFIG_DEVPORT
660f0a816fbSNiklas Schnelle static const struct file_operations port_fops = {
6611da177e4SLinus Torvalds .llseek = memory_lseek,
6621da177e4SLinus Torvalds .read = read_port,
6631da177e4SLinus Torvalds .write = write_port,
6641da177e4SLinus Torvalds .open = open_port,
6651da177e4SLinus Torvalds };
666f0a816fbSNiklas Schnelle #endif
6671da177e4SLinus Torvalds
66862322d25SArjan van de Ven static const struct file_operations zero_fops = {
6691da177e4SLinus Torvalds .llseek = zero_lseek,
6701da177e4SLinus Torvalds .write = write_zero,
67113ba33e8SAl Viro .read_iter = read_iter_zero,
67299f66735SChristoph Hellwig .read = read_zero,
673cd28e28dSAl Viro .write_iter = write_iter_zero,
6741b057bd8SMax Kellermann .splice_read = copy_splice_read,
6751b057bd8SMax Kellermann .splice_write = splice_write_zero,
6761da177e4SLinus Torvalds .mmap = mmap_zero,
677c01d5b30SHugh Dickins .get_unmapped_area = get_unmapped_area_zero,
678b4caecd4SChristoph Hellwig #ifndef CONFIG_MMU
679b4caecd4SChristoph Hellwig .mmap_capabilities = zero_mmap_capabilities,
680b4caecd4SChristoph Hellwig #endif
6811da177e4SLinus Torvalds };
6821da177e4SLinus Torvalds
68362322d25SArjan van de Ven static const struct file_operations full_fops = {
6841da177e4SLinus Torvalds .llseek = full_lseek,
68513ba33e8SAl Viro .read_iter = read_iter_zero,
6861da177e4SLinus Torvalds .write = write_full,
6871b057bd8SMax Kellermann .splice_read = copy_splice_read,
6881da177e4SLinus Torvalds };
6891da177e4SLinus Torvalds
690389e0cb9SKay Sievers static const struct memdev {
691389e0cb9SKay Sievers const char *name;
692d6f47befSAdriano dos Santos Fernandes const struct file_operations *fops;
693b4caecd4SChristoph Hellwig fmode_t fmode;
694ed1af26cSAlexey Dobriyan umode_t mode;
695389e0cb9SKay Sievers } devlist[] = {
69673f0718eSRob Ward #ifdef CONFIG_DEVMEM
697*641bb439SChristian Brauner [DEVMEM_MINOR] = { "mem", &mem_fops, 0, 0 },
69873f0718eSRob Ward #endif
699ed1af26cSAlexey Dobriyan [3] = { "null", &null_fops, FMODE_NOWAIT, 0666 },
700d6f47befSAdriano dos Santos Fernandes #ifdef CONFIG_DEVPORT
701ed1af26cSAlexey Dobriyan [4] = { "port", &port_fops, 0, 0 },
702d6f47befSAdriano dos Santos Fernandes #endif
703ed1af26cSAlexey Dobriyan [5] = { "zero", &zero_fops, FMODE_NOWAIT, 0666 },
704ed1af26cSAlexey Dobriyan [7] = { "full", &full_fops, 0, 0666 },
705ed1af26cSAlexey Dobriyan [8] = { "random", &random_fops, FMODE_NOWAIT, 0666 },
706ed1af26cSAlexey Dobriyan [9] = { "urandom", &urandom_fops, FMODE_NOWAIT, 0666 },
7077f3a781dSKay Sievers #ifdef CONFIG_PRINTK
708ed1af26cSAlexey Dobriyan [11] = { "kmsg", &kmsg_fops, 0, 0644 },
7097f3a781dSKay Sievers #endif
710d6f47befSAdriano dos Santos Fernandes };
711d6f47befSAdriano dos Santos Fernandes
memory_open(struct inode * inode,struct file * filp)7121da177e4SLinus Torvalds static int memory_open(struct inode *inode, struct file *filp)
7131da177e4SLinus Torvalds {
714389e0cb9SKay Sievers int minor;
715389e0cb9SKay Sievers const struct memdev *dev;
716d6f47befSAdriano dos Santos Fernandes
717389e0cb9SKay Sievers minor = iminor(inode);
718389e0cb9SKay Sievers if (minor >= ARRAY_SIZE(devlist))
719205153aaSFrederic Weisbecker return -ENXIO;
720d6f47befSAdriano dos Santos Fernandes
721389e0cb9SKay Sievers dev = &devlist[minor];
722389e0cb9SKay Sievers if (!dev->fops)
723205153aaSFrederic Weisbecker return -ENXIO;
724d6f47befSAdriano dos Santos Fernandes
725389e0cb9SKay Sievers filp->f_op = dev->fops;
726b4caecd4SChristoph Hellwig filp->f_mode |= dev->fmode;
7274a3956c7SKAMEZAWA Hiroyuki
728389e0cb9SKay Sievers if (dev->fops->open)
729205153aaSFrederic Weisbecker return dev->fops->open(inode, filp);
730205153aaSFrederic Weisbecker
731205153aaSFrederic Weisbecker return 0;
7321da177e4SLinus Torvalds }
7331da177e4SLinus Torvalds
73462322d25SArjan van de Ven static const struct file_operations memory_fops = {
735389e0cb9SKay Sievers .open = memory_open,
7366038f373SArnd Bergmann .llseek = noop_llseek,
7371da177e4SLinus Torvalds };
7381da177e4SLinus Torvalds
mem_devnode(const struct device * dev,umode_t * mode)739ff62b8e6SGreg Kroah-Hartman static char *mem_devnode(const struct device *dev, umode_t *mode)
740e454cea2SKay Sievers {
741e454cea2SKay Sievers if (mode && devlist[MINOR(dev->devt)].mode)
742e454cea2SKay Sievers *mode = devlist[MINOR(dev->devt)].mode;
743e454cea2SKay Sievers return NULL;
744e454cea2SKay Sievers }
745e454cea2SKay Sievers
7467671284bSIvan Orlov static const struct class mem_class = {
7477671284bSIvan Orlov .name = "mem",
7487671284bSIvan Orlov .devnode = mem_devnode,
7497671284bSIvan Orlov };
7501da177e4SLinus Torvalds
chr_dev_init(void)7511da177e4SLinus Torvalds static int __init chr_dev_init(void)
7521da177e4SLinus Torvalds {
7537671284bSIvan Orlov int retval;
754389e0cb9SKay Sievers int minor;
7551da177e4SLinus Torvalds
7561da177e4SLinus Torvalds if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
7571da177e4SLinus Torvalds printk("unable to get major %d for memory devs\n", MEM_MAJOR);
7581da177e4SLinus Torvalds
7597671284bSIvan Orlov retval = class_register(&mem_class);
7607671284bSIvan Orlov if (retval)
7617671284bSIvan Orlov return retval;
7626e191f7bSAnton Blanchard
763389e0cb9SKay Sievers for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
764389e0cb9SKay Sievers if (!devlist[minor].name)
765389e0cb9SKay Sievers continue;
766e1612de9SHaren Myneni
767e1612de9SHaren Myneni /*
768e1612de9SHaren Myneni * Create /dev/port?
769e1612de9SHaren Myneni */
770e1612de9SHaren Myneni if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
771e1612de9SHaren Myneni continue;
772e1612de9SHaren Myneni
7737671284bSIvan Orlov device_create(&mem_class, NULL, MKDEV(MEM_MAJOR, minor),
774389e0cb9SKay Sievers NULL, devlist[minor].name);
775389e0cb9SKay Sievers }
7761da177e4SLinus Torvalds
77731d1d48eSDavid Howells return tty_init();
7781da177e4SLinus Torvalds }
7791da177e4SLinus Torvalds
7801da177e4SLinus Torvalds fs_initcall(chr_dev_init);
781