
/* * devmem_is_allowed() 检查对某个地址的 /dev/mem 访问是否有效。 * 其参数是一个物理页号。 * * 在 x86 上必须允许访问 RAM 的第一个兆字节因为该区域 * 传统上包含 X、dosemu 以及类似应用程序所使用的 BIOS 代码和 * 数据区域。由于它们会映射整个内存范围因此必须允许映射 * 整个范围但那些本应被禁止的区域则被标记为零填充而不是 * 直接拒绝。同样也必须允许访问非内核 RAM 区域这些区域 * 包含 PCI MMIO 资源以及可能的 BIOS/ACPI 数据区域。 */devmem_is_allowed()是 Linux 内核中用于控制/dev/mem设备访问权限的核心函数。它决定了一个给定的物理页帧pagenr是否允许通过/dev/mem被用户空间直接访问。核心作用安全过滤/dev/mem是一个危险的设备——它允许用户空间直接读写物理内存。如果没有任何限制恶意程序可以轻易破坏内核或窃取敏感数据。devmem_is_allowed()就是内核设置的“安检门”根据页帧的类型和位置返回不同的许可级别。返回值语义这个函数的返回值有明确的含义返回值含义1完全允许用户空间可以直接读写该页。0完全禁止用户空间访问该页将失败返回-EPERM。2特殊允许允许访问但该页会被显示为全零零页实际内容被隐藏。逻辑解析函数的判断分为两个阶段第一阶段System RAM 检查if (region_intersects(PFN_PHYS(pagenr), PAGE_SIZE, IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) ! REGION_DISJOINT) { if (pagenr 256) return 2; // 低1MB的RAM区域显示为零页 return 0; // 其他RAM区域完全禁止 }通过region_intersects()检查该页是否属于System RAM区域。如果是 System RAM则禁止直接访问因为 RAM 中可能包含内核数据、用户密码等敏感信息。但有一个例外低 1MBpagenr 256的 RAM 区域返回2表示允许访问但内容被替换为零页。这是为了兼容一些依赖低端内存的旧程序同时又不泄露真实数据。第二阶段ioremap 独占检查if (iomem_is_exclusive(pagenr PAGE_SHIFT)) { if (pagenr 256) return 1; // 低1MB绕过ioremap限制 return 0; // 其他禁止 }如果该页不属于 System RAM再检查它是否被ioremap标记为独占IORESOURCE_EXCLUSIVE。如果被独占通常禁止访问但低 1MB 仍然允许返回1。这是因为低 1MB 是传统 ISA 设备的保留区域很多调试工具需要访问它。如果既不是 System RAM也不是独占的 ioremap 区域则允许访问返回1例如 PCI BAR 映射的 MMIO 区域。与CONFIG_STRICT_DEVMEM的关系这个函数的行为受CONFIG_STRICT_DEVMEM内核配置选项控制启用STRICT_DEVMEM默认System RAM 被严格保护devmem_is_allowed()会阻止对 RAM 的访问。禁用STRICT_DEVMEM函数会变得宽松允许访问更多区域。代码注释中提到的“System RAM is considered a restricted resource under CONFIG_STRICT_DEVMEM”正是指这一点。唯一的调用点range_is_allowed()devmem_is_allowed()的调用点非常集中它只在一个地方被调用drivers/char/mem.c文件中的range_is_allowed()函数。当对/dev/mem设备进行read()、write()或mmap()操作时内核会调用range_is_allowed()来检查你试图访问的物理地址范围是否合法。它的调用逻辑大致如下static inline int range_is_allowed(unsigned long pfn, unsigned long nr) { u64 from ((u64)pfn) PAGE_SHIFT; u64 to from nr; 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; }这个函数会逐页遍历你请求的物理地址范围对每一页调用devmem_is_allowed()进行检查。只要有一页不被允许整个访问请求就会被拒绝并在内核日志中打印一条警告信息包含发起访问的程序名和地址范围。与CONFIG_STRICT_DEVMEM的关系这个调用路径是CONFIG_STRICT_DEVMEM内核配置选项生效的关键机制。当该选项被启用时range_is_allowed()会通过devmem_is_allowed()来严格过滤对 System RAM 和独占 I/O 内存的访问如果该选项未启用range_is_allowed()可能会直接返回允许不进行这些检查。所以devmem_is_allowed()是/dev/mem设备驱动内部的一个“安检员”它的所有决策都发生在用户空间试图通过这个设备直接触碰物理内存的瞬间。设计意图这个函数体现了内核在兼容性与安全性之间的精细权衡低 1MB 的特殊处理是为了兼容旧硬件和调试工具而对 System RAM 的严格保护则是为了防止敏感数据泄露和系统被破坏。