18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Written by Pekka Paalanen, 2008-2009 <pq@iki.fi> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/mmiotrace.h> 118c2ecf20Sopenharmony_ci#include <linux/security.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic unsigned long mmio_address; 148c2ecf20Sopenharmony_cimodule_param_hw(mmio_address, ulong, iomem, 0); 158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mmio_address, " Start address of the mapping of 16 kB " 168c2ecf20Sopenharmony_ci "(or 8 MB if read_far is non-zero)."); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic unsigned long read_far = 0x400100; 198c2ecf20Sopenharmony_cimodule_param(read_far, ulong, 0); 208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(read_far, " Offset of a 32-bit read within 8 MB " 218c2ecf20Sopenharmony_ci "(default: 0x400100)."); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic unsigned v16(unsigned i) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci return i * 12 + 7; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic unsigned v32(unsigned i) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return i * 212371 + 13; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void do_write_test(void __iomem *p) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci unsigned int i; 368c2ecf20Sopenharmony_ci pr_info("write test.\n"); 378c2ecf20Sopenharmony_ci mmiotrace_printk("Write test.\n"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) 408c2ecf20Sopenharmony_ci iowrite8(i, p + i); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci for (i = 1024; i < (5 * 1024); i += 2) 438c2ecf20Sopenharmony_ci iowrite16(v16(i), p + i); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci for (i = (5 * 1024); i < (16 * 1024); i += 4) 468c2ecf20Sopenharmony_ci iowrite32(v32(i), p + i); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void do_read_test(void __iomem *p) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci unsigned int i; 528c2ecf20Sopenharmony_ci unsigned errs[3] = { 0 }; 538c2ecf20Sopenharmony_ci pr_info("read test.\n"); 548c2ecf20Sopenharmony_ci mmiotrace_printk("Read test.\n"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) 578c2ecf20Sopenharmony_ci if (ioread8(p + i) != i) 588c2ecf20Sopenharmony_ci ++errs[0]; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci for (i = 1024; i < (5 * 1024); i += 2) 618c2ecf20Sopenharmony_ci if (ioread16(p + i) != v16(i)) 628c2ecf20Sopenharmony_ci ++errs[1]; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci for (i = (5 * 1024); i < (16 * 1024); i += 4) 658c2ecf20Sopenharmony_ci if (ioread32(p + i) != v32(i)) 668c2ecf20Sopenharmony_ci ++errs[2]; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci mmiotrace_printk("Read errors: 8-bit %d, 16-bit %d, 32-bit %d.\n", 698c2ecf20Sopenharmony_ci errs[0], errs[1], errs[2]); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void do_read_far_test(void __iomem *p) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci pr_info("read far test.\n"); 758c2ecf20Sopenharmony_ci mmiotrace_printk("Read far test.\n"); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci ioread32(p + read_far); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void do_test(unsigned long size) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci void __iomem *p = ioremap(mmio_address, size); 838c2ecf20Sopenharmony_ci if (!p) { 848c2ecf20Sopenharmony_ci pr_err("could not ioremap, aborting.\n"); 858c2ecf20Sopenharmony_ci return; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci mmiotrace_printk("ioremap returned %p.\n", p); 888c2ecf20Sopenharmony_ci do_write_test(p); 898c2ecf20Sopenharmony_ci do_read_test(p); 908c2ecf20Sopenharmony_ci if (read_far && read_far < size - 4) 918c2ecf20Sopenharmony_ci do_read_far_test(p); 928c2ecf20Sopenharmony_ci iounmap(p); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * Tests how mmiotrace behaves in face of multiple ioremap / iounmaps in 978c2ecf20Sopenharmony_ci * a short time. We had a bug in deferred freeing procedure which tried 988c2ecf20Sopenharmony_ci * to free this region multiple times (ioremap can reuse the same address 998c2ecf20Sopenharmony_ci * for many mappings). 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic void do_test_bulk_ioremapping(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci void __iomem *p; 1048c2ecf20Sopenharmony_ci int i; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < 10; ++i) { 1078c2ecf20Sopenharmony_ci p = ioremap(mmio_address, PAGE_SIZE); 1088c2ecf20Sopenharmony_ci if (p) 1098c2ecf20Sopenharmony_ci iounmap(p); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Force freeing. If it will crash we will know why. */ 1138c2ecf20Sopenharmony_ci synchronize_rcu(); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int __init init(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned long size = (read_far) ? (8 << 20) : (16 << 10); 1198c2ecf20Sopenharmony_ci int ret = security_locked_down(LOCKDOWN_MMIOTRACE); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (mmio_address == 0) { 1258c2ecf20Sopenharmony_ci pr_err("you have to use the module argument mmio_address.\n"); 1268c2ecf20Sopenharmony_ci pr_err("DO NOT LOAD THIS MODULE UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!\n"); 1278c2ecf20Sopenharmony_ci return -ENXIO; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci pr_warn("WARNING: mapping %lu kB @ 0x%08lx in PCI address space, " 1318c2ecf20Sopenharmony_ci "and writing 16 kB of rubbish in there.\n", 1328c2ecf20Sopenharmony_ci size >> 10, mmio_address); 1338c2ecf20Sopenharmony_ci do_test(size); 1348c2ecf20Sopenharmony_ci do_test_bulk_ioremapping(); 1358c2ecf20Sopenharmony_ci pr_info("All done.\n"); 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void __exit cleanup(void) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci pr_debug("unloaded.\n"); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cimodule_init(init); 1458c2ecf20Sopenharmony_cimodule_exit(cleanup); 1468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 147