Return EIO when user space tries to read/write/mmap hwpoison pages via the /dev/mem interface. The approach: rename range_is_allowed() to devmem_check_pfn_range(), and add PageHWPoison() test in it. This function will be called for the whole mmap() range, or page by page for read()/write(). So it would fail the mmap() request as a whole, and return partial results for read()/write(). CC: Kelly Bowa CC: Greg KH CC: KAMEZAWA Hiroyuki Reviewed-by: Andi Kleen Signed-off-by: Wu Fengguang --- drivers/char/mem.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) --- linux-mm.orig/drivers/char/mem.c 2009-12-29 10:47:00.000000000 +0800 +++ linux-mm/drivers/char/mem.c 2009-12-29 10:54:07.000000000 +0800 @@ -89,31 +89,28 @@ static inline int valid_mmap_phys_addr_r } #endif -#ifdef CONFIG_STRICT_DEVMEM -static inline int range_is_allowed(unsigned long pfn, unsigned long size) +static int devmem_check_pfn_range(unsigned long pfn, unsigned long bytes) { u64 from = ((u64)pfn) << PAGE_SHIFT; - u64 to = from + size; + u64 to = from + bytes; u64 cursor = from; while (cursor < to) { +#ifdef CONFIG_STRICT_DEVMEM 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; + return -EPERM; } +#endif + if (pfn_valid(pfn) && PageHWPoison(pfn_to_page(pfn))) + return -EIO; cursor += PAGE_SIZE; pfn++; } - return 1; -} -#else -static inline int range_is_allowed(unsigned long pfn, unsigned long size) -{ - return 1; + return 0; } -#endif void __attribute__((weak)) unxlate_dev_mem_ptr(unsigned long phys, void *addr) { @@ -150,11 +147,13 @@ static ssize_t read_mem(struct file * fi while (count > 0) { unsigned long remaining; + int err; sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, count)) - return -EPERM; + err = devmem_check_pfn_range(p >> PAGE_SHIFT, count); + if (err) + return err; /* * On ia64 if a page has been mapped somewhere as @@ -184,9 +183,10 @@ static ssize_t write_mem(struct file * f size_t count, loff_t *ppos) { unsigned long p = *ppos; - ssize_t written, sz; unsigned long copied; + ssize_t written, sz; void *ptr; + int err; if (!valid_phys_addr_range(p, count)) return -EFAULT; @@ -208,8 +208,9 @@ static ssize_t write_mem(struct file * f while (count > 0) { sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, sz)) - return -EPERM; + err = devmem_check_pfn_range(p >> PAGE_SHIFT, sz); + if (err) + return err; /* * On ia64 if a page has been mapped somewhere as @@ -297,6 +298,7 @@ static const struct vm_operations_struct static int mmap_mem(struct file * file, struct vm_area_struct * vma) { size_t size = vma->vm_end - vma->vm_start; + int err; if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) return -EINVAL; @@ -304,8 +306,9 @@ static int mmap_mem(struct file * file, if (!private_mapping_ok(vma)) return -ENOSYS; - if (!range_is_allowed(vma->vm_pgoff, size)) - return -EPERM; + err = devmem_check_pfn_range(vma->vm_pgoff, size); + if (err) + return err; if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size, &vma->vm_page_prot)) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/