1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Written by Pekka Paalanen, 2008-2009 <pq@iki.fi> 4 */ 5 6 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7 8 #include <linux/module.h> 9 #include <linux/io.h> 10 #include <linux/mmiotrace.h> 11 #include <linux/security.h> 12 13 static unsigned long mmio_address; 14 module_param_hw(mmio_address, ulong, iomem, 0); 15 MODULE_PARM_DESC(mmio_address, " Start address of the mapping of 16 kB " 16 "(or 8 MB if read_far is non-zero)."); 17 18 static unsigned long read_far = 0x400100; 19 module_param(read_far, ulong, 0); 20 MODULE_PARM_DESC(read_far, " Offset of a 32-bit read within 8 MB " 21 "(default: 0x400100)."); 22 23 static unsigned v16(unsigned i) 24 { 25 return i * 12 + 7; 26 } 27 28 static unsigned v32(unsigned i) 29 { 30 return i * 212371 + 13; 31 } 32 33 static void do_write_test(void __iomem *p) 34 { 35 unsigned int i; 36 pr_info("write test.\n"); 37 mmiotrace_printk("Write test.\n"); 38 39 for (i = 0; i < 256; i++) 40 iowrite8(i, p + i); 41 42 for (i = 1024; i < (5 * 1024); i += 2) 43 iowrite16(v16(i), p + i); 44 45 for (i = (5 * 1024); i < (16 * 1024); i += 4) 46 iowrite32(v32(i), p + i); 47 } 48 49 static void do_read_test(void __iomem *p) 50 { 51 unsigned int i; 52 unsigned errs[3] = { 0 }; 53 pr_info("read test.\n"); 54 mmiotrace_printk("Read test.\n"); 55 56 for (i = 0; i < 256; i++) 57 if (ioread8(p + i) != i) 58 ++errs[0]; 59 60 for (i = 1024; i < (5 * 1024); i += 2) 61 if (ioread16(p + i) != v16(i)) 62 ++errs[1]; 63 64 for (i = (5 * 1024); i < (16 * 1024); i += 4) 65 if (ioread32(p + i) != v32(i)) 66 ++errs[2]; 67 68 mmiotrace_printk("Read errors: 8-bit %d, 16-bit %d, 32-bit %d.\n", 69 errs[0], errs[1], errs[2]); 70 } 71 72 static void do_read_far_test(void __iomem *p) 73 { 74 pr_info("read far test.\n"); 75 mmiotrace_printk("Read far test.\n"); 76 77 ioread32(p + read_far); 78 } 79 80 static void do_test(unsigned long size) 81 { 82 void __iomem *p = ioremap(mmio_address, size); 83 if (!p) { 84 pr_err("could not ioremap, aborting.\n"); 85 return; 86 } 87 mmiotrace_printk("ioremap returned %p.\n", p); 88 do_write_test(p); 89 do_read_test(p); 90 if (read_far && read_far < size - 4) 91 do_read_far_test(p); 92 iounmap(p); 93 } 94 95 /* 96 * Tests how mmiotrace behaves in face of multiple ioremap / iounmaps in 97 * a short time. We had a bug in deferred freeing procedure which tried 98 * to free this region multiple times (ioremap can reuse the same address 99 * for many mappings). 100 */ 101 static void do_test_bulk_ioremapping(void) 102 { 103 void __iomem *p; 104 int i; 105 106 for (i = 0; i < 10; ++i) { 107 p = ioremap(mmio_address, PAGE_SIZE); 108 if (p) 109 iounmap(p); 110 } 111 112 /* Force freeing. If it will crash we will know why. */ 113 synchronize_rcu(); 114 } 115 116 static int __init init(void) 117 { 118 unsigned long size = (read_far) ? (8 << 20) : (16 << 10); 119 int ret = security_locked_down(LOCKDOWN_MMIOTRACE); 120 121 if (ret) 122 return ret; 123 124 if (mmio_address == 0) { 125 pr_err("you have to use the module argument mmio_address.\n"); 126 pr_err("DO NOT LOAD THIS MODULE UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!\n"); 127 return -ENXIO; 128 } 129 130 pr_warn("WARNING: mapping %lu kB @ 0x%08lx in PCI address space, " 131 "and writing 16 kB of rubbish in there.\n", 132 size >> 10, mmio_address); 133 do_test(size); 134 do_test_bulk_ioremapping(); 135 pr_info("All done.\n"); 136 return 0; 137 } 138 139 static void __exit cleanup(void) 140 { 141 pr_debug("unloaded.\n"); 142 } 143 144 module_init(init); 145 module_exit(cleanup); 146 MODULE_LICENSE("GPL"); 147
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.