之前有一个需求是uboot 将一段key写到memory中,然后要求kernel reserve 这段memory,最后user space 通过打开/dev/mem 来映射这段memory,这样就可以将kernel space的内容和user space共享.
所以我们来看看kernel中第/dev/mem的实现。
/dev/mem 是一个字符设备,因此源码放在char/mem.c 中.
843 static int __init chr_dev_init(void)
844 {
845 int minor;
846
847 if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
848 printk("unable to get major %d for memory devs\n", MEM_MAJOR);
849
850 mem_class = class_create(THIS_MODULE, "mem");
851 if (IS_ERR(mem_class))
852 return PTR_ERR(mem_class);
853
854 mem_class->devnode = mem_devnode;
855 for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
856 if (!devlist[minor].name)
857 continue;
858
859 /*
860 * Create /dev/port?
861 */
862 if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
863 continue;
864
865 device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
866 NULL, devlist[minor].name);
867 }
868
869 return tty_init();
870 }
871
872 fs_initcall(chr_dev_init);
kernel 在启动的最后阶段会调用fs_initcall。因此/dev/mem 是由kernel 自己建立的.而且建立的字符设备不知一个。具体如devlist所示:
782 static const struct memdev {
783 const char *name;
784 umode_t mode;
785 const struct file_operations *fops;
786 fmode_t fmode;
787 } devlist[] = {
788 #ifdef CONFIG_DEVMEM
789 [1] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
790 #endif
791 #ifdef CONFIG_DEVKMEM
792 [2] = { "kmem", 0, &kmem_fops, FMODE_UNSIGNED_OFFSET },
793 #endif
794 [3] = { "null", 0666, &null_fops, 0 },
795 #ifdef CONFIG_DEVPORT
796 [4] = { "port", 0, &port_fops, 0 },
797 #endif
798 [5] = { "zero", 0666, &zero_fops, 0 },
799 [7] = { "full", 0666, &full_fops, 0 },
800 [8] = { "random", 0666, &random_fops, 0 },
801 [9] = { "urandom", 0666, &urandom_fops, 0 },
802 #ifdef CONFIG_PRINTK
803 [11] = { "kmsg", 0644, &kmsg_fops, 0 },
804 #endif
805 };
可见dev/mem,dev/kmem,dev/null,dev/port,dev/zero,dev/full,dev/random,/dev/urandom,dev/kmsg都是在这个文件中实现的且major number都是一样的。
我们来看看/dev/mem的fops
725 static const struct file_operations __maybe_unused mem_fops = {
726 .llseek = memory_lseek,
727 .read = read_mem,
728 .write = write_mem,
729 .mmap = mmap_mem,
730 .open = open_mem,
731 #ifndef CONFIG_MMU
732 .get_unmapped_area = get_unmapped_area_mem,
733 .mmap_capabilities = memory_mmap_capabilities,
734 #endif
735 };
上层一般会先open 这个字符设备后,调用mmap来做映射。
fm = open("/dev/mem", O_RDWR|O_SYNC);
mmap((void *)addr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fm, pfn<
其中open 就调用的是open_mem
2 #define open_mem open_port
713 static int open_port(struct inode *inode, struct file *filp)
714 {
715 return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
716 }
仅仅是检查一下权限设定而已,
我们来看看mmap对应的实现。
321 static int mmap_mem(struct file *file, struct vm_area_struct *vma)
322 {
323 size_t size = vma->vm_end - vma->vm_start;
324
325 if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
326 return -EINVAL;
327
328 if (!private_mapping_ok(vma))
329 return -ENOSYS;
330
331 if (!range_is_allowed(vma->vm_pgoff, size))
332 return -EPERM;
333
334 if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
335 &vma->vm_page_prot))
336 return -EINVAL;
337
338 vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
339 size,
340 vma->vm_page_prot);
341
342 vma->vm_ops = &mmap_mem_ops;
343
344 /* Remap-pfn-range will mark the range VM_IO */
345 if (remap_pfn_range(vma,
346 vma->vm_start,
347 vma->vm_pgoff,
348 size,
349 vma->vm_page_prot)) {
350 return -EAGAIN;
351 }
352 return 0;
353 }
这个函数从321~345都是做一些检测或者赋值。最重要的是345行调用remap_pfn_range来做实际的映射,这样user space 就可以看到kernel space memory中的内容了.其中vma->vm_start 就对应mmap的第一个参数addr.