/dev/mem
Rev.1を表示中。最新版はこちら。
/dev/memはページ単位で以下の条件下で、カーネル空間のメモリーを参照するデバイスです。valid_phys_addr_range()で参照範囲がhigh_memory(0x20000:アーキテクチャ依存)以下なら無条件に参照可能で,それ以上の範囲だと、range_is_allowed()で参照範囲がiomem下でIORESOURCE_BUSY && IORESOURCE_EXCLUSIVEであるならエラー(EPERM)エラーとなります。IORESOURCE_BUSY && IORESOURCE_EXCLUSIVEでないiomemリソースでも、System RAM配下のリソースならエラー(EPERM)となります。なお、pagenr <= 256(1M:BIOS領域)なら参照OKです。従って00010000-0009f7ff範囲は、System RAMですが参照可能となります。
上記機能はlinux 2.6では実装されておらず、linux3.3でもCONFIG_STRICT_DEVMEMで構築したシステム下で実装されます。
static const struct file_operations mem_fops = { .llseek = memory_lseek, .read = read_mem, .write = write_mem, .mmap = mmap_mem, .open = open_mem, .get_unmapped_area = get_unmapped_area_mem, }; static ssize_t read_mem(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; ssize_t read, sz; char *ptr; if (!valid_phys_addr_range(p, count)) return -EFAULT; read = 0; while (count > 0) { unsigned long remaining; sz = size_inside_page(p, count); if (!range_is_allowed(p >> PAGE_SHIFT, count)) return -EPERM; ptr = xlate_dev_mem_ptr(p); if (!ptr) return -EFAULT; remaining = copy_to_user(buf, ptr, sz); unxlate_dev_mem_ptr(p, ptr); if (remaining) return -EFAULT; buf += sz; p += sz; count -= sz; read += sz; } *ppos += read; return read; } #define __pa(x) ((unsigned long) (x)) static inline int valid_phys_addr_range(unsigned long addr, size_t count) { return addr + count <= __pa(high_memory); } #ifdef CONFIG_STRICT_DEVMEM static inline int range_is_allowed(unsigned long pfn, unsigned long size) { u64 from = ((u64)pfn) << PAGE_SHIFT; u64 to = from + size; u64 cursor = from; while (cursor < to) { if (!devmem_is_allowed(pfn)) { printk(KERN_INFO "Program %s tried to access /dev/mem between %Lx->%Lx.\n", current->comm, from, to); return 0; } cursor += PAGE_SIZE; pfn++; } return 1; } #else static inline int range_is_allowed(unsigned long pfn, unsigned long size) { return 1; } #endif int devmem_is_allowed(unsigned long pagenr) { if (pagenr <= 256) return 1; if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) return 0; if (!page_is_ram(pagenr)) return 1; return 0; } int iomem_is_exclusive(u64 addr) { struct resource *p = &iomem_resource; int err = 0; loff_t l; int size = PAGE_SIZE; if (!strict_iomem_checks) return 0; addr = addr & PAGE_MASK; read_lock(&resource_lock); for (p = p->child; p ; p = r_next(NULL, p, &l)) { if (p->start >= addr + size) break; if (p->end < addr) continue; if (p->flags & IORESOURCE_BUSY && p->flags & IORESOURCE_EXCLUSIVE) { err = 1; break; } } read_unlock(&resource_lock); return err; } int __weak page_is_ram(unsigned long pfn) { return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1; } int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, void *arg, int (*func)(unsigned long, unsigned long, void *)) { struct resource res; unsigned long pfn, end_pfn; u64 orig_end; int ret = -1; res.start = (u64) start_pfn << PAGE_SHIFT; res.end = ((u64)(start_pfn + nr_pages) << PAGE_SHIFT) - 1; res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; orig_end = res.end; while ((res.start < res.end) && (find_next_system_ram(&res, "System RAM") >= 0)) { pfn = (res.start + PAGE_SIZE - 1) >> PAGE_SHIFT; end_pfn = (res.end + 1) >> PAGE_SHIFT; if (end_pfn > pfn) ret = (*func)(pfn, end_pfn - pfn, arg); if (ret) break; res.start = res.end + 1; res.end = orig_end; } return ret; }
検証サンプルiomemより
[root@localhost c]# cat iomem_reso.txt child :00000000-ffffffff : PCI mem: 1.0 [BUSY:0] [EXCLU:0] [MEM:1] child :00000000-0000ffff : reserved: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:00010000-0009f7ff : System RAM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:0009f800-0009ffff : reserved: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:000a0000-000bffff : PCI Bus 0000:00: PCI mem 1.1 [BUSY:0] [EXCLU:0] [MEM:1] child :000a0000-000bffff : Video RAM area: PCI Bus 0000:00 0.0 [BUSY:1] [EXCLU:0] [MEM:1] sibling:000c0000-000c7fff : Video ROM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:000ca000-000cafff : Adapter ROM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:000cb000-000cbfff : Adapter ROM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:000cc000-000cffff : PCI Bus 0000:00: PCI mem 0.1 [BUSY:0] [EXCLU:0] [MEM:1] sibling:000d0000-000d3fff : PCI Bus 0000:00: PCI mem 0.1 [BUSY:0] [EXCLU:0] [MEM:1] sibling:000d4000-000d7fff : PCI Bus 0000:00: PCI mem 0.1 [BUSY:0] [EXCLU:0] [MEM:1] sibling:000d8000-000dbfff : PCI Bus 0000:00: PCI mem 0.1 [BUSY:0] [EXCLU:0] [MEM:1] sibling:000dc000-000fffff : reserved: PCI mem 1.1 [BUSY:1] [EXCLU:0] [MEM:1] child :000f0000-000fffff : System ROM: reserved 0.0 [BUSY:1] [EXCLU:0] [MEM:1] sibling:00100000-1feeffff : System RAM: PCI mem 1.1 [BUSY:1] [EXCLU:0] [MEM:1] child :00400000-0093411e : Kernel code: System RAM 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:0093411f-00bb6e3f : Kernel data: System RAM 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:00c57000-00d4efff : Kernel bss: System RAM 0.0 [BUSY:1] [EXCLU:0] [MEM:1] sibling:1fef0000-1fefefff : ACPI Tables: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:1feff000-1fefffff : ACPI Non-volatile Storage: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:1ff00000-1fffffff : System RAM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] sibling:20000000-febfffff : PCI Bus 0000:00: PCI mem 1.1 [BUSY:0] [EXCLU:0] [MEM:1] child :20000000-20007fff : 0000:00:0f.0: PCI Bus 0000:00 0.1 [BUSY:0] [EXCLU:0] [MEM:1]
全iomemリソースの参照チェック
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int read_devmem(int fd, unsigned int addr); char *deverr_name(int err, char *buf); void get_adress(char *p, unsigned int *start, unsigned int *end) { sscanf(p + 8, "%x-%x", start, end); } int main(int argc,char **argv) { char buff[500], errname[10]; int fd, err; unsigned int start, end; FILE *fp; char *endp; long value; fd=open("/dev/mem",0); fp = fopen("iomem_reso.txt", "r"); while (fgets(buff, sizeof(buff), fp)) { get_adress(buff, &start, &end); err = read_devmem(fd, start + 0xfff); if (err) { printf("%s:%s", deverr_name(err, errname), buff); } if (start >= 0x20000000) { break; } } fclose(fp); }
int read_devmem(int fd, unsigned int addr) { int r, err = 0; char buf[1]; lseek(fd,addr, SEEK_SET); r=read(fd,buf,1); if(r<1) { err = errno; } return err; } char *deverr_name(int err, char *buf) { switch (err) { case EPERM: strcpy(buf, " EPERM"); break; case EFAULT: strcpy(buf, "EFAULT"); break; default: strcpy(buf, " OTHER"); break; } return buf; } [root@localhost c]# ./a.out EPERM:child :00400000-0093411e : Kernel code: System RAM 0.1 [BUSY:1] [EXCLU:0] [MEM:1] EPERM:sibling:0093411f-00bb6e3f : Kernel data: System RAM 0.1 [BUSY:1] [EXCLU:0] [MEM:1] EPERM:sibling:00c57000-00d4efff : Kernel bss: System RAM 0.0 [BUSY:1] [EXCLU:0] [MEM:1] EPERM:sibling:1ff00000-1fffffff : System RAM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] EFAULT:sibling:20000000-febfffff : PCI Bus 0000:00: PCI mem 1.1 [BUSY:0] [EXCLU:0] [MEM:1] [root@localhost c]# dmesg [ 918.089364] Program a.out tried to access /dev/mem between 400000->400001. [ 918.094529] Program a.out tried to access /dev/mem between 935000->935001. [ 918.094622] Program a.out tried to access /dev/mem between c57000->c57001. [ 918.095541] Program a.out tried to access /dev/mem between 1ff00000->1ff00001.
System RAMエリアチェックサンプル
#include <linux/module.h> #include <linux/kernel.h> struct file *fopen(char *file, int mode); int freads(char *buff, int size, struct file *fp); int fclose(struct file *fp); static int find_next_system_ram(struct resource *res, char *name); static int __is_ram(unsigned long pfn, unsigned long nr_pages, void *arg) { return 1; } int __weak page_is_ram(unsigned long pfn) { return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1; } int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, void *arg, int (*func)(unsigned long, unsigned long, void *)) { struct resource res; unsigned long pfn, end_pfn; u64 orig_end; int ret = -1; res.start = (u64) start_pfn << PAGE_SHIFT; res.end = ((u64)(start_pfn + nr_pages) << PAGE_SHIFT) - 1; res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; orig_end = res.end; while ((res.start < res.end) && (find_next_system_ram(&res, "System RAM") >= 0)) { pfn = (res.start + PAGE_SIZE - 1) >> PAGE_SHIFT; end_pfn = (res.end + 1) >> PAGE_SHIFT; if (end_pfn > pfn) ret = (*func)(pfn, end_pfn - pfn, arg); if (ret) break; res.start = res.end + 1; res.end = orig_end; } return ret; } static int find_next_system_ram(struct resource *res, char *name) { resource_size_t start, end; struct resource *p; start = res->start; end = res->end; for (p = iomem_resource.child; p ; p = p->sibling) { if (p->flags != res->flags) continue; if (name && strcmp(p->name, name)) continue; if (p->start > end) { p = NULL; break; } if ((p->end >= start) && (p->start < end)) break; } if (!p) return -1; if (res->start < p->start) res->start = p->start; if (res->end > p->end) res->end = p->end; return 0; } void get_adress(char *p, unsigned int *start, unsigned int *end) { sscanf(p + 8, "%x-%x", start, end); } static int __init babakaka_init( void ) { int ret; struct file *fp; char buff[200]; unsigned int start, end; fp = fopen("iomem_reso.txt", 0); if (fp) { printk("System ram\n"); while (freads(buff, sizeof(buff), fp)) { get_adress(buff, &start, &end); start += 0xfff; ret = page_is_ram(start >> PAGE_SHIFT); if (ret) { printk("%s\n", buff); } } fclose(fp); } return -1; } static void __exit babakaka_exit( void ) { } module_init(babakaka_init); module_exit(babakaka_exit);
[root@localhost lkm]# insmod babakaka.ko [root@localhost lkm]# dmesg [ 1861.432741] System ram [ 1861.434028] sibling:00010000-0009f7ff : System RAM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1] [ 1861.436773] sibling:00100000-1feeffff : System RAM: PCI mem 1.1 [BUSY:1] [EXCLU:0] [MEM:1] [ 1861.436954] child :00400000-0093411e : Kernel code: System RAM 0.1 [BUSY:1] [EXCLU:0] [MEM:1] [ 1861.437131] sibling:0093411f-00bb6e3f : Kernel data: System RAM 0.1 [BUSY:1] [EXCLU:0] [MEM:1] [ 1861.437307] sibling:00c57000-00d4efff : Kernel bss: System RAM 0.0 [BUSY:1] [EXCLU:0] [MEM:1] [ 1861.438594] sibling:1ff00000-1fffffff : System RAM: PCI mem 0.1 [BUSY:1] [EXCLU:0] [MEM:1]
追記
low_memoryはup to 20000000で、high_memory=20000000という事です。[root@localhost ~]# dmesg | grep "kernel direct mapping" [ 0.000000] kernel direct mapping tables up to 20000000 @ ffb000-1000000