1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Test cases for API provided by resource.c and ioport.h 4 */ 5 6 #include <kunit/test.h> 7 #include <linux/ioport.h> 8 #include <linux/kernel.h> 9 #include <linux/string.h> 10 #include <linux/sizes.h> 11 #include <linux/mm.h> 12 13 #define R0_START 0x0000 14 #define R0_END 0xffff 15 #define R1_START 0x1234 16 #define R1_END 0x2345 17 #define R2_START 0x4567 18 #define R2_END 0x5678 19 #define R3_START 0x6789 20 #define R3_END 0x789a 21 #define R4_START 0x2000 22 #define R4_END 0x7000 23 24 static struct resource r0 = { .start = R0_START, .end = R0_END }; 25 static struct resource r1 = { .start = R1_START, .end = R1_END }; 26 static struct resource r2 = { .start = R2_START, .end = R2_END }; 27 static struct resource r3 = { .start = R3_START, .end = R3_END }; 28 static struct resource r4 = { .start = R4_START, .end = R4_END }; 29 30 struct result { 31 struct resource *r1; 32 struct resource *r2; 33 struct resource r; 34 bool ret; 35 }; 36 37 static struct result results_for_union[] = { 38 { 39 .r1 = &r1, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true, 40 }, { 41 .r1 = &r2, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true, 42 }, { 43 .r1 = &r3, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true, 44 }, { 45 .r1 = &r4, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true, 46 }, { 47 .r1 = &r2, .r2 = &r1, .ret = false, 48 }, { 49 .r1 = &r3, .r2 = &r1, .ret = false, 50 }, { 51 .r1 = &r4, .r2 = &r1, .r.start = R1_START, .r.end = R4_END, .ret = true, 52 }, { 53 .r1 = &r2, .r2 = &r3, .ret = false, 54 }, { 55 .r1 = &r2, .r2 = &r4, .r.start = R4_START, .r.end = R4_END, .ret = true, 56 }, { 57 .r1 = &r3, .r2 = &r4, .r.start = R4_START, .r.end = R3_END, .ret = true, 58 }, 59 }; 60 61 static struct result results_for_intersection[] = { 62 { 63 .r1 = &r1, .r2 = &r0, .r.start = R1_START, .r.end = R1_END, .ret = true, 64 }, { 65 .r1 = &r2, .r2 = &r0, .r.start = R2_START, .r.end = R2_END, .ret = true, 66 }, { 67 .r1 = &r3, .r2 = &r0, .r.start = R3_START, .r.end = R3_END, .ret = true, 68 }, { 69 .r1 = &r4, .r2 = &r0, .r.start = R4_START, .r.end = R4_END, .ret = true, 70 }, { 71 .r1 = &r2, .r2 = &r1, .ret = false, 72 }, { 73 .r1 = &r3, .r2 = &r1, .ret = false, 74 }, { 75 .r1 = &r4, .r2 = &r1, .r.start = R4_START, .r.end = R1_END, .ret = true, 76 }, { 77 .r1 = &r2, .r2 = &r3, .ret = false, 78 }, { 79 .r1 = &r2, .r2 = &r4, .r.start = R2_START, .r.end = R2_END, .ret = true, 80 }, { 81 .r1 = &r3, .r2 = &r4, .r.start = R3_START, .r.end = R4_END, .ret = true, 82 }, 83 }; 84 85 static void resource_do_test(struct kunit *test, bool ret, struct resource *r, 86 bool exp_ret, struct resource *exp_r, 87 struct resource *r1, struct resource *r2) 88 { 89 KUNIT_EXPECT_EQ_MSG(test, ret, exp_ret, "Resources %pR %pR", r1, r2); 90 KUNIT_EXPECT_EQ_MSG(test, r->start, exp_r->start, "Start elements are not equal"); 91 KUNIT_EXPECT_EQ_MSG(test, r->end, exp_r->end, "End elements are not equal"); 92 } 93 94 static void resource_do_union_test(struct kunit *test, struct result *r) 95 { 96 struct resource result; 97 bool ret; 98 99 memset(&result, 0, sizeof(result)); 100 ret = resource_union(r->r1, r->r2, &result); 101 resource_do_test(test, ret, &result, r->ret, &r->r, r->r1, r->r2); 102 103 memset(&result, 0, sizeof(result)); 104 ret = resource_union(r->r2, r->r1, &result); 105 resource_do_test(test, ret, &result, r->ret, &r->r, r->r2, r->r1); 106 } 107 108 static void resource_test_union(struct kunit *test) 109 { 110 struct result *r = results_for_union; 111 unsigned int i = 0; 112 113 do { 114 resource_do_union_test(test, &r[i]); 115 } while (++i < ARRAY_SIZE(results_for_union)); 116 } 117 118 static void resource_do_intersection_test(struct kunit *test, struct result *r) 119 { 120 struct resource result; 121 bool ret; 122 123 memset(&result, 0, sizeof(result)); 124 ret = resource_intersection(r->r1, r->r2, &result); 125 resource_do_test(test, ret, &result, r->ret, &r->r, r->r1, r->r2); 126 127 memset(&result, 0, sizeof(result)); 128 ret = resource_intersection(r->r2, r->r1, &result); 129 resource_do_test(test, ret, &result, r->ret, &r->r, r->r2, r->r1); 130 } 131 132 static void resource_test_intersection(struct kunit *test) 133 { 134 struct result *r = results_for_intersection; 135 unsigned int i = 0; 136 137 do { 138 resource_do_intersection_test(test, &r[i]); 139 } while (++i < ARRAY_SIZE(results_for_intersection)); 140 } 141 142 /* 143 * The test resource tree for region_intersects() test: 144 * 145 * BASE-BASE+1M-1 : Test System RAM 0 146 * # hole 0 (BASE+1M-BASE+2M) 147 * BASE+2M-BASE+3M-1 : Test CXL Window 0 148 * BASE+3M-BASE+4M-1 : Test System RAM 1 149 * BASE+4M-BASE+7M-1 : Test CXL Window 1 150 * BASE+4M-BASE+5M-1 : Test System RAM 2 151 * BASE+4M+128K-BASE+4M+256K-1: Test Code 152 * BASE+5M-BASE+6M-1 : Test System RAM 3 153 */ 154 #define RES_TEST_RAM0_OFFSET 0 155 #define RES_TEST_RAM0_SIZE SZ_1M 156 #define RES_TEST_HOLE0_OFFSET (RES_TEST_RAM0_OFFSET + RES_TEST_RAM0_SIZE) 157 #define RES_TEST_HOLE0_SIZE SZ_1M 158 #define RES_TEST_WIN0_OFFSET (RES_TEST_HOLE0_OFFSET + RES_TEST_HOLE0_SIZE) 159 #define RES_TEST_WIN0_SIZE SZ_1M 160 #define RES_TEST_RAM1_OFFSET (RES_TEST_WIN0_OFFSET + RES_TEST_WIN0_SIZE) 161 #define RES_TEST_RAM1_SIZE SZ_1M 162 #define RES_TEST_WIN1_OFFSET (RES_TEST_RAM1_OFFSET + RES_TEST_RAM1_SIZE) 163 #define RES_TEST_WIN1_SIZE (SZ_1M * 3) 164 #define RES_TEST_RAM2_OFFSET RES_TEST_WIN1_OFFSET 165 #define RES_TEST_RAM2_SIZE SZ_1M 166 #define RES_TEST_CODE_OFFSET (RES_TEST_RAM2_OFFSET + SZ_128K) 167 #define RES_TEST_CODE_SIZE SZ_128K 168 #define RES_TEST_RAM3_OFFSET (RES_TEST_RAM2_OFFSET + RES_TEST_RAM2_SIZE) 169 #define RES_TEST_RAM3_SIZE SZ_1M 170 #define RES_TEST_TOTAL_SIZE ((RES_TEST_WIN1_OFFSET + RES_TEST_WIN1_SIZE)) 171 172 KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *); 173 174 static void remove_free_resource(void *ctx) 175 { 176 struct resource *res = (struct resource *)ctx; 177 178 remove_resource(res); 179 kfree(res); 180 } 181 182 static void resource_test_add_action_or_abort( 183 struct kunit *test, void (*action)(void *), void *ctx) 184 { 185 KUNIT_ASSERT_EQ_MSG(test, 0, 186 kunit_add_action_or_reset(test, action, ctx), 187 "Fail to add action"); 188 } 189 190 static void resource_test_request_region(struct kunit *test, struct resource *parent, 191 resource_size_t start, resource_size_t size, 192 const char *name, unsigned long flags) 193 { 194 struct resource *res; 195 196 res = __request_region(parent, start, size, name, flags); 197 KUNIT_ASSERT_NOT_NULL(test, res); 198 resource_test_add_action_or_abort(test, remove_free_resource, res); 199 } 200 201 static void resource_test_insert_resource(struct kunit *test, struct resource *parent, 202 resource_size_t start, resource_size_t size, 203 const char *name, unsigned long flags) 204 { 205 struct resource *res; 206 207 res = kzalloc(sizeof(*res), GFP_KERNEL); 208 KUNIT_ASSERT_NOT_NULL(test, res); 209 210 res->name = name; 211 res->start = start; 212 res->end = start + size - 1; 213 res->flags = flags; 214 if (insert_resource(parent, res)) { 215 resource_test_add_action_or_abort(test, kfree_wrapper, res); 216 KUNIT_FAIL_AND_ABORT(test, "Fail to insert resource %pR\n", res); 217 } 218 219 resource_test_add_action_or_abort(test, remove_free_resource, res); 220 } 221 222 static void resource_test_region_intersects(struct kunit *test) 223 { 224 unsigned long flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; 225 struct resource *parent; 226 resource_size_t start; 227 228 /* Find an iomem_resource hole to hold test resources */ 229 parent = alloc_free_mem_region(&iomem_resource, RES_TEST_TOTAL_SIZE, SZ_1M, 230 "test resources"); 231 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent); 232 start = parent->start; 233 resource_test_add_action_or_abort(test, remove_free_resource, parent); 234 235 resource_test_request_region(test, parent, start + RES_TEST_RAM0_OFFSET, 236 RES_TEST_RAM0_SIZE, "Test System RAM 0", flags); 237 resource_test_insert_resource(test, parent, start + RES_TEST_WIN0_OFFSET, 238 RES_TEST_WIN0_SIZE, "Test CXL Window 0", 239 IORESOURCE_MEM); 240 resource_test_request_region(test, parent, start + RES_TEST_RAM1_OFFSET, 241 RES_TEST_RAM1_SIZE, "Test System RAM 1", flags); 242 resource_test_insert_resource(test, parent, start + RES_TEST_WIN1_OFFSET, 243 RES_TEST_WIN1_SIZE, "Test CXL Window 1", 244 IORESOURCE_MEM); 245 resource_test_request_region(test, parent, start + RES_TEST_RAM2_OFFSET, 246 RES_TEST_RAM2_SIZE, "Test System RAM 2", flags); 247 resource_test_insert_resource(test, parent, start + RES_TEST_CODE_OFFSET, 248 RES_TEST_CODE_SIZE, "Test Code", flags); 249 resource_test_request_region(test, parent, start + RES_TEST_RAM3_OFFSET, 250 RES_TEST_RAM3_SIZE, "Test System RAM 3", flags); 251 kunit_release_action(test, remove_free_resource, parent); 252 253 KUNIT_EXPECT_EQ(test, REGION_INTERSECTS, 254 region_intersects(start + RES_TEST_RAM0_OFFSET, PAGE_SIZE, 255 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 256 KUNIT_EXPECT_EQ(test, REGION_INTERSECTS, 257 region_intersects(start + RES_TEST_RAM0_OFFSET + 258 RES_TEST_RAM0_SIZE - PAGE_SIZE, 2 * PAGE_SIZE, 259 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 260 KUNIT_EXPECT_EQ(test, REGION_DISJOINT, 261 region_intersects(start + RES_TEST_HOLE0_OFFSET, PAGE_SIZE, 262 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 263 KUNIT_EXPECT_EQ(test, REGION_DISJOINT, 264 region_intersects(start + RES_TEST_HOLE0_OFFSET + 265 RES_TEST_HOLE0_SIZE - PAGE_SIZE, 2 * PAGE_SIZE, 266 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 267 KUNIT_EXPECT_EQ(test, REGION_MIXED, 268 region_intersects(start + RES_TEST_WIN0_OFFSET + 269 RES_TEST_WIN0_SIZE - PAGE_SIZE, 2 * PAGE_SIZE, 270 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 271 KUNIT_EXPECT_EQ(test, REGION_INTERSECTS, 272 region_intersects(start + RES_TEST_RAM1_OFFSET + 273 RES_TEST_RAM1_SIZE - PAGE_SIZE, 2 * PAGE_SIZE, 274 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 275 KUNIT_EXPECT_EQ(test, REGION_INTERSECTS, 276 region_intersects(start + RES_TEST_RAM2_OFFSET + 277 RES_TEST_RAM2_SIZE - PAGE_SIZE, 2 * PAGE_SIZE, 278 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 279 KUNIT_EXPECT_EQ(test, REGION_INTERSECTS, 280 region_intersects(start + RES_TEST_CODE_OFFSET, PAGE_SIZE, 281 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 282 KUNIT_EXPECT_EQ(test, REGION_INTERSECTS, 283 region_intersects(start + RES_TEST_RAM2_OFFSET, 284 RES_TEST_RAM2_SIZE + PAGE_SIZE, 285 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 286 KUNIT_EXPECT_EQ(test, REGION_MIXED, 287 region_intersects(start + RES_TEST_RAM3_OFFSET, 288 RES_TEST_RAM3_SIZE + PAGE_SIZE, 289 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE)); 290 } 291 292 static struct kunit_case resource_test_cases[] = { 293 KUNIT_CASE(resource_test_union), 294 KUNIT_CASE(resource_test_intersection), 295 KUNIT_CASE(resource_test_region_intersects), 296 {} 297 }; 298 299 static struct kunit_suite resource_test_suite = { 300 .name = "resource", 301 .test_cases = resource_test_cases, 302 }; 303 kunit_test_suite(resource_test_suite); 304 305 MODULE_DESCRIPTION("I/O Port & Memory Resource manager unit tests"); 306 MODULE_LICENSE("GPL"); 307