xref: /openbmc/linux/arch/x86/mm/testmmiotrace.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ff3a3e9bSPekka Paalanen /*
3fab852aaSPekka Paalanen  * Written by Pekka Paalanen, 2008-2009 <pq@iki.fi>
4ff3a3e9bSPekka Paalanen  */
53c355863SJoe Perches 
63c355863SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
73c355863SJoe Perches 
8ff3a3e9bSPekka Paalanen #include <linux/module.h>
9970e6fa0SPekka Paalanen #include <linux/io.h>
109e57fb35SPekka Paalanen #include <linux/mmiotrace.h>
11906357f7SDavid Howells #include <linux/security.h>
12ff3a3e9bSPekka Paalanen 
13ff3a3e9bSPekka Paalanen static unsigned long mmio_address;
143c2e2e68SDavid Howells module_param_hw(mmio_address, ulong, iomem, 0);
155ff93697SPekka Paalanen MODULE_PARM_DESC(mmio_address, " Start address of the mapping of 16 kB "
165ff93697SPekka Paalanen 				"(or 8 MB if read_far is non-zero).");
175ff93697SPekka Paalanen 
185ff93697SPekka Paalanen static unsigned long read_far = 0x400100;
195ff93697SPekka Paalanen module_param(read_far, ulong, 0);
205ff93697SPekka Paalanen MODULE_PARM_DESC(read_far, " Offset of a 32-bit read within 8 MB "
215ff93697SPekka Paalanen 				"(default: 0x400100).");
22ff3a3e9bSPekka Paalanen 
v16(unsigned i)23fab852aaSPekka Paalanen static unsigned v16(unsigned i)
24fab852aaSPekka Paalanen {
25fab852aaSPekka Paalanen 	return i * 12 + 7;
26fab852aaSPekka Paalanen }
27fab852aaSPekka Paalanen 
v32(unsigned i)28fab852aaSPekka Paalanen static unsigned v32(unsigned i)
29fab852aaSPekka Paalanen {
30fab852aaSPekka Paalanen 	return i * 212371 + 13;
31fab852aaSPekka Paalanen }
32fab852aaSPekka Paalanen 
do_write_test(void __iomem * p)33ff3a3e9bSPekka Paalanen static void do_write_test(void __iomem *p)
34ff3a3e9bSPekka Paalanen {
35ff3a3e9bSPekka Paalanen 	unsigned int i;
363c355863SJoe Perches 	pr_info("write test.\n");
379e57fb35SPekka Paalanen 	mmiotrace_printk("Write test.\n");
38fab852aaSPekka Paalanen 
39ff3a3e9bSPekka Paalanen 	for (i = 0; i < 256; i++)
40ff3a3e9bSPekka Paalanen 		iowrite8(i, p + i);
41fab852aaSPekka Paalanen 
42ff3a3e9bSPekka Paalanen 	for (i = 1024; i < (5 * 1024); i += 2)
43fab852aaSPekka Paalanen 		iowrite16(v16(i), p + i);
44fab852aaSPekka Paalanen 
45ff3a3e9bSPekka Paalanen 	for (i = (5 * 1024); i < (16 * 1024); i += 4)
46fab852aaSPekka Paalanen 		iowrite32(v32(i), p + i);
47ff3a3e9bSPekka Paalanen }
48ff3a3e9bSPekka Paalanen 
do_read_test(void __iomem * p)49ff3a3e9bSPekka Paalanen static void do_read_test(void __iomem *p)
50ff3a3e9bSPekka Paalanen {
51ff3a3e9bSPekka Paalanen 	unsigned int i;
52fab852aaSPekka Paalanen 	unsigned errs[3] = { 0 };
533c355863SJoe Perches 	pr_info("read test.\n");
549e57fb35SPekka Paalanen 	mmiotrace_printk("Read test.\n");
55fab852aaSPekka Paalanen 
56ff3a3e9bSPekka Paalanen 	for (i = 0; i < 256; i++)
57fab852aaSPekka Paalanen 		if (ioread8(p + i) != i)
58fab852aaSPekka Paalanen 			++errs[0];
59fab852aaSPekka Paalanen 
60ff3a3e9bSPekka Paalanen 	for (i = 1024; i < (5 * 1024); i += 2)
61fab852aaSPekka Paalanen 		if (ioread16(p + i) != v16(i))
62fab852aaSPekka Paalanen 			++errs[1];
63fab852aaSPekka Paalanen 
64ff3a3e9bSPekka Paalanen 	for (i = (5 * 1024); i < (16 * 1024); i += 4)
65fab852aaSPekka Paalanen 		if (ioread32(p + i) != v32(i))
66fab852aaSPekka Paalanen 			++errs[2];
67fab852aaSPekka Paalanen 
68fab852aaSPekka Paalanen 	mmiotrace_printk("Read errors: 8-bit %d, 16-bit %d, 32-bit %d.\n",
69fab852aaSPekka Paalanen 						errs[0], errs[1], errs[2]);
70ff3a3e9bSPekka Paalanen }
71ff3a3e9bSPekka Paalanen 
do_read_far_test(void __iomem * p)725ff93697SPekka Paalanen static void do_read_far_test(void __iomem *p)
73ff3a3e9bSPekka Paalanen {
743c355863SJoe Perches 	pr_info("read far test.\n");
755ff93697SPekka Paalanen 	mmiotrace_printk("Read far test.\n");
765ff93697SPekka Paalanen 
775ff93697SPekka Paalanen 	ioread32(p + read_far);
785ff93697SPekka Paalanen }
795ff93697SPekka Paalanen 
do_test(unsigned long size)805ff93697SPekka Paalanen static void do_test(unsigned long size)
815ff93697SPekka Paalanen {
82*4bdc0d67SChristoph Hellwig 	void __iomem *p = ioremap(mmio_address, size);
83ff3a3e9bSPekka Paalanen 	if (!p) {
843c355863SJoe Perches 		pr_err("could not ioremap, aborting.\n");
85ff3a3e9bSPekka Paalanen 		return;
86ff3a3e9bSPekka Paalanen 	}
879e57fb35SPekka Paalanen 	mmiotrace_printk("ioremap returned %p.\n", p);
88ff3a3e9bSPekka Paalanen 	do_write_test(p);
89ff3a3e9bSPekka Paalanen 	do_read_test(p);
905ff93697SPekka Paalanen 	if (read_far && read_far < size - 4)
915ff93697SPekka Paalanen 		do_read_far_test(p);
92ff3a3e9bSPekka Paalanen 	iounmap(p);
93ff3a3e9bSPekka Paalanen }
94ff3a3e9bSPekka Paalanen 
958b8f79b9SMarcin Slusarz /*
968b8f79b9SMarcin Slusarz  * Tests how mmiotrace behaves in face of multiple ioremap / iounmaps in
978b8f79b9SMarcin Slusarz  * a short time. We had a bug in deferred freeing procedure which tried
988b8f79b9SMarcin Slusarz  * to free this region multiple times (ioremap can reuse the same address
998b8f79b9SMarcin Slusarz  * for many mappings).
1008b8f79b9SMarcin Slusarz  */
do_test_bulk_ioremapping(void)1018b8f79b9SMarcin Slusarz static void do_test_bulk_ioremapping(void)
1028b8f79b9SMarcin Slusarz {
1038b8f79b9SMarcin Slusarz 	void __iomem *p;
1048b8f79b9SMarcin Slusarz 	int i;
1058b8f79b9SMarcin Slusarz 
1068b8f79b9SMarcin Slusarz 	for (i = 0; i < 10; ++i) {
107*4bdc0d67SChristoph Hellwig 		p = ioremap(mmio_address, PAGE_SIZE);
1088b8f79b9SMarcin Slusarz 		if (p)
1098b8f79b9SMarcin Slusarz 			iounmap(p);
1108b8f79b9SMarcin Slusarz 	}
1118b8f79b9SMarcin Slusarz 
1128b8f79b9SMarcin Slusarz 	/* Force freeing. If it will crash we will know why. */
1138b8f79b9SMarcin Slusarz 	synchronize_rcu();
1148b8f79b9SMarcin Slusarz }
1158b8f79b9SMarcin Slusarz 
init(void)116ff3a3e9bSPekka Paalanen static int __init init(void)
117ff3a3e9bSPekka Paalanen {
1185ff93697SPekka Paalanen 	unsigned long size = (read_far) ? (8 << 20) : (16 << 10);
119906357f7SDavid Howells 	int ret = security_locked_down(LOCKDOWN_MMIOTRACE);
120906357f7SDavid Howells 
121906357f7SDavid Howells 	if (ret)
122906357f7SDavid Howells 		return ret;
1235ff93697SPekka Paalanen 
124ff3a3e9bSPekka Paalanen 	if (mmio_address == 0) {
1253c355863SJoe Perches 		pr_err("you have to use the module argument mmio_address.\n");
1263c355863SJoe Perches 		pr_err("DO NOT LOAD THIS MODULE UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!\n");
127ff3a3e9bSPekka Paalanen 		return -ENXIO;
128ff3a3e9bSPekka Paalanen 	}
129ff3a3e9bSPekka Paalanen 
1308d3bcc44SKefeng Wang 	pr_warn("WARNING: mapping %lu kB @ 0x%08lx in PCI address space, "
1313c355863SJoe Perches 		"and writing 16 kB of rubbish in there.\n",
1325ff93697SPekka Paalanen 		size >> 10, mmio_address);
1335ff93697SPekka Paalanen 	do_test(size);
1348b8f79b9SMarcin Slusarz 	do_test_bulk_ioremapping();
1353c355863SJoe Perches 	pr_info("All done.\n");
136ff3a3e9bSPekka Paalanen 	return 0;
137ff3a3e9bSPekka Paalanen }
138ff3a3e9bSPekka Paalanen 
cleanup(void)139ff3a3e9bSPekka Paalanen static void __exit cleanup(void)
140ff3a3e9bSPekka Paalanen {
1413c355863SJoe Perches 	pr_debug("unloaded.\n");
142ff3a3e9bSPekka Paalanen }
143ff3a3e9bSPekka Paalanen 
144ff3a3e9bSPekka Paalanen module_init(init);
145ff3a3e9bSPekka Paalanen module_exit(cleanup);
146ff3a3e9bSPekka Paalanen MODULE_LICENSE("GPL");
147