1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * imr_selftest.c -- Intel Isolated Memory Region self-test driver |
4 | * |
5 | * Copyright(c) 2013 Intel Corporation. |
6 | * Copyright(c) 2015 Bryan O'Donoghue <pure.logic@nexus-software.ie> |
7 | * |
8 | * IMR self test. The purpose of this module is to run a set of tests on the |
9 | * IMR API to validate its sanity. We check for overlapping, reserved |
10 | * addresses and setup/teardown sanity. |
11 | * |
12 | */ |
13 | |
14 | #include <asm-generic/sections.h> |
15 | #include <asm/cpu_device_id.h> |
16 | #include <asm/imr.h> |
17 | #include <asm/io.h> |
18 | |
19 | #include <linux/init.h> |
20 | #include <linux/mm.h> |
21 | #include <linux/types.h> |
22 | |
23 | #define SELFTEST KBUILD_MODNAME ": " |
24 | /** |
25 | * imr_self_test_result - Print result string for self test. |
26 | * |
27 | * @res: result code - true if test passed false otherwise. |
28 | * @fmt: format string. |
29 | * ... variadic argument list. |
30 | */ |
31 | static __printf(2, 3) |
32 | void __init imr_self_test_result(int res, const char *fmt, ...) |
33 | { |
34 | va_list vlist; |
35 | |
36 | /* Print pass/fail. */ |
37 | if (res) |
38 | pr_info(SELFTEST "pass " ); |
39 | else |
40 | pr_info(SELFTEST "fail " ); |
41 | |
42 | /* Print variable string. */ |
43 | va_start(vlist, fmt); |
44 | vprintk(fmt, args: vlist); |
45 | va_end(vlist); |
46 | |
47 | /* Optional warning. */ |
48 | WARN(res == 0, "test failed" ); |
49 | } |
50 | #undef SELFTEST |
51 | |
52 | /** |
53 | * imr_self_test |
54 | * |
55 | * Verify IMR self_test with some simple tests to verify overlap, |
56 | * zero sized allocations and 1 KiB sized areas. |
57 | * |
58 | */ |
59 | static void __init imr_self_test(void) |
60 | { |
61 | phys_addr_t base = virt_to_phys(address: &_text); |
62 | size_t size = virt_to_phys(address: &__end_rodata) - base; |
63 | const char *fmt_over = "overlapped IMR @ (0x%08lx - 0x%08lx)\n" ; |
64 | int ret; |
65 | |
66 | /* Test zero zero. */ |
67 | ret = imr_add_range(base: 0, size: 0, rmask: 0, wmask: 0); |
68 | imr_self_test_result(res: ret < 0, fmt: "zero sized IMR\n" ); |
69 | |
70 | /* Test exact overlap. */ |
71 | ret = imr_add_range(base, size, IMR_CPU, IMR_CPU); |
72 | imr_self_test_result(res: ret < 0, fmt: fmt_over, __va(base), __va(base + size)); |
73 | |
74 | /* Test overlap with base inside of existing. */ |
75 | base += size - IMR_ALIGN; |
76 | ret = imr_add_range(base, size, IMR_CPU, IMR_CPU); |
77 | imr_self_test_result(res: ret < 0, fmt: fmt_over, __va(base), __va(base + size)); |
78 | |
79 | /* Test overlap with end inside of existing. */ |
80 | base -= size + IMR_ALIGN * 2; |
81 | ret = imr_add_range(base, size, IMR_CPU, IMR_CPU); |
82 | imr_self_test_result(res: ret < 0, fmt: fmt_over, __va(base), __va(base + size)); |
83 | |
84 | /* Test that a 1 KiB IMR @ zero with read/write all will bomb out. */ |
85 | ret = imr_add_range(base: 0, IMR_ALIGN, IMR_READ_ACCESS_ALL, |
86 | IMR_WRITE_ACCESS_ALL); |
87 | imr_self_test_result(res: ret < 0, fmt: "1KiB IMR @ 0x00000000 - access-all\n" ); |
88 | |
89 | /* Test that a 1 KiB IMR @ zero with CPU only will work. */ |
90 | ret = imr_add_range(base: 0, IMR_ALIGN, IMR_CPU, IMR_CPU); |
91 | imr_self_test_result(res: ret >= 0, fmt: "1KiB IMR @ 0x00000000 - cpu-access\n" ); |
92 | if (ret >= 0) { |
93 | ret = imr_remove_range(base: 0, IMR_ALIGN); |
94 | imr_self_test_result(res: ret == 0, fmt: "teardown - cpu-access\n" ); |
95 | } |
96 | |
97 | /* Test 2 KiB works. */ |
98 | size = IMR_ALIGN * 2; |
99 | ret = imr_add_range(base: 0, size, IMR_READ_ACCESS_ALL, IMR_WRITE_ACCESS_ALL); |
100 | imr_self_test_result(res: ret >= 0, fmt: "2KiB IMR @ 0x00000000\n" ); |
101 | if (ret >= 0) { |
102 | ret = imr_remove_range(base: 0, size); |
103 | imr_self_test_result(res: ret == 0, fmt: "teardown 2KiB\n" ); |
104 | } |
105 | } |
106 | |
107 | static const struct x86_cpu_id imr_ids[] __initconst = { |
108 | X86_MATCH_VENDOR_FAM_MODEL(INTEL, 5, INTEL_FAM5_QUARK_X1000, NULL), |
109 | {} |
110 | }; |
111 | |
112 | /** |
113 | * imr_self_test_init - entry point for IMR driver. |
114 | * |
115 | * return: -ENODEV for no IMR support 0 if good to go. |
116 | */ |
117 | static int __init imr_self_test_init(void) |
118 | { |
119 | if (x86_match_cpu(match: imr_ids)) |
120 | imr_self_test(); |
121 | return 0; |
122 | } |
123 | |
124 | /** |
125 | * imr_self_test_exit - exit point for IMR code. |
126 | * |
127 | * return: |
128 | */ |
129 | device_initcall(imr_self_test_init); |
130 | |