xref: /openbmc/linux/tools/testing/cxl/test/cxl.c (revision 7a6ee0bb)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright(c) 2021 Intel Corporation. All rights reserved.
3 
4 #include <linux/platform_device.h>
5 #include <linux/genalloc.h>
6 #include <linux/module.h>
7 #include <linux/mutex.h>
8 #include <linux/acpi.h>
9 #include <linux/pci.h>
10 #include <linux/mm.h>
11 #include "mock.h"
12 
13 #define NR_CXL_HOST_BRIDGES 4
14 #define NR_CXL_ROOT_PORTS 2
15 
16 static struct platform_device *cxl_acpi;
17 static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES];
18 static struct platform_device
19 	*cxl_root_port[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
20 struct platform_device *cxl_mem[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
21 
22 static struct acpi_device acpi0017_mock;
23 static struct acpi_device host_bridge[NR_CXL_HOST_BRIDGES] = {
24 	[0] = {
25 		.handle = &host_bridge[0],
26 	},
27 	[1] = {
28 		.handle = &host_bridge[1],
29 	},
30 	[2] = {
31 		.handle = &host_bridge[2],
32 	},
33 	[3] = {
34 		.handle = &host_bridge[3],
35 	},
36 };
37 
38 static bool is_mock_dev(struct device *dev)
39 {
40 	int i;
41 
42 	for (i = 0; i < ARRAY_SIZE(cxl_mem); i++)
43 		if (dev == &cxl_mem[i]->dev)
44 			return true;
45 	if (dev == &cxl_acpi->dev)
46 		return true;
47 	return false;
48 }
49 
50 static bool is_mock_adev(struct acpi_device *adev)
51 {
52 	int i;
53 
54 	if (adev == &acpi0017_mock)
55 		return true;
56 
57 	for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
58 		if (adev == &host_bridge[i])
59 			return true;
60 
61 	return false;
62 }
63 
64 static struct {
65 	struct acpi_table_cedt cedt;
66 	struct acpi_cedt_chbs chbs[NR_CXL_HOST_BRIDGES];
67 	struct {
68 		struct acpi_cedt_cfmws cfmws;
69 		u32 target[1];
70 	} cfmws0;
71 	struct {
72 		struct acpi_cedt_cfmws cfmws;
73 		u32 target[4];
74 	} cfmws1;
75 	struct {
76 		struct acpi_cedt_cfmws cfmws;
77 		u32 target[1];
78 	} cfmws2;
79 	struct {
80 		struct acpi_cedt_cfmws cfmws;
81 		u32 target[4];
82 	} cfmws3;
83 } __packed mock_cedt = {
84 	.cedt = {
85 		.header = {
86 			.signature = "CEDT",
87 			.length = sizeof(mock_cedt),
88 			.revision = 1,
89 		},
90 	},
91 	.chbs[0] = {
92 		.header = {
93 			.type = ACPI_CEDT_TYPE_CHBS,
94 			.length = sizeof(mock_cedt.chbs[0]),
95 		},
96 		.uid = 0,
97 		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
98 	},
99 	.chbs[1] = {
100 		.header = {
101 			.type = ACPI_CEDT_TYPE_CHBS,
102 			.length = sizeof(mock_cedt.chbs[0]),
103 		},
104 		.uid = 1,
105 		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
106 	},
107 	.chbs[2] = {
108 		.header = {
109 			.type = ACPI_CEDT_TYPE_CHBS,
110 			.length = sizeof(mock_cedt.chbs[0]),
111 		},
112 		.uid = 2,
113 		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
114 	},
115 	.chbs[3] = {
116 		.header = {
117 			.type = ACPI_CEDT_TYPE_CHBS,
118 			.length = sizeof(mock_cedt.chbs[0]),
119 		},
120 		.uid = 3,
121 		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
122 	},
123 	.cfmws0 = {
124 		.cfmws = {
125 			.header = {
126 				.type = ACPI_CEDT_TYPE_CFMWS,
127 				.length = sizeof(mock_cedt.cfmws0),
128 			},
129 			.interleave_ways = 0,
130 			.granularity = 4,
131 			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
132 					ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
133 			.qtg_id = 0,
134 			.window_size = SZ_256M,
135 		},
136 		.target = { 0 },
137 	},
138 	.cfmws1 = {
139 		.cfmws = {
140 			.header = {
141 				.type = ACPI_CEDT_TYPE_CFMWS,
142 				.length = sizeof(mock_cedt.cfmws1),
143 			},
144 			.interleave_ways = 2,
145 			.granularity = 4,
146 			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
147 					ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
148 			.qtg_id = 1,
149 			.window_size = SZ_256M * 4,
150 		},
151 		.target = { 0, 1, 2, 3 },
152 	},
153 	.cfmws2 = {
154 		.cfmws = {
155 			.header = {
156 				.type = ACPI_CEDT_TYPE_CFMWS,
157 				.length = sizeof(mock_cedt.cfmws2),
158 			},
159 			.interleave_ways = 0,
160 			.granularity = 4,
161 			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
162 					ACPI_CEDT_CFMWS_RESTRICT_PMEM,
163 			.qtg_id = 2,
164 			.window_size = SZ_256M,
165 		},
166 		.target = { 0 },
167 	},
168 	.cfmws3 = {
169 		.cfmws = {
170 			.header = {
171 				.type = ACPI_CEDT_TYPE_CFMWS,
172 				.length = sizeof(mock_cedt.cfmws3),
173 			},
174 			.interleave_ways = 2,
175 			.granularity = 4,
176 			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
177 					ACPI_CEDT_CFMWS_RESTRICT_PMEM,
178 			.qtg_id = 3,
179 			.window_size = SZ_256M * 4,
180 		},
181 		.target = { 0, 1, 2, 3 },
182 	},
183 };
184 
185 struct acpi_cedt_cfmws *mock_cfmws[4] = {
186 	[0] = &mock_cedt.cfmws0.cfmws,
187 	[1] = &mock_cedt.cfmws1.cfmws,
188 	[2] = &mock_cedt.cfmws2.cfmws,
189 	[3] = &mock_cedt.cfmws3.cfmws,
190 };
191 
192 struct cxl_mock_res {
193 	struct list_head list;
194 	struct range range;
195 };
196 
197 static LIST_HEAD(mock_res);
198 static DEFINE_MUTEX(mock_res_lock);
199 static struct gen_pool *cxl_mock_pool;
200 
201 static void depopulate_all_mock_resources(void)
202 {
203 	struct cxl_mock_res *res, *_res;
204 
205 	mutex_lock(&mock_res_lock);
206 	list_for_each_entry_safe(res, _res, &mock_res, list) {
207 		gen_pool_free(cxl_mock_pool, res->range.start,
208 			      range_len(&res->range));
209 		list_del(&res->list);
210 		kfree(res);
211 	}
212 	mutex_unlock(&mock_res_lock);
213 }
214 
215 static struct cxl_mock_res *alloc_mock_res(resource_size_t size)
216 {
217 	struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
218 	struct genpool_data_align data = {
219 		.align = SZ_256M,
220 	};
221 	unsigned long phys;
222 
223 	INIT_LIST_HEAD(&res->list);
224 	phys = gen_pool_alloc_algo(cxl_mock_pool, size,
225 				   gen_pool_first_fit_align, &data);
226 	if (!phys)
227 		return NULL;
228 
229 	res->range = (struct range) {
230 		.start = phys,
231 		.end = phys + size - 1,
232 	};
233 	mutex_lock(&mock_res_lock);
234 	list_add(&res->list, &mock_res);
235 	mutex_unlock(&mock_res_lock);
236 
237 	return res;
238 }
239 
240 static int populate_cedt(void)
241 {
242 	struct cxl_mock_res *res;
243 	int i;
244 
245 	for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
246 		struct acpi_cedt_chbs *chbs = &mock_cedt.chbs[i];
247 		resource_size_t size;
248 
249 		if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL20)
250 			size = ACPI_CEDT_CHBS_LENGTH_CXL20;
251 		else
252 			size = ACPI_CEDT_CHBS_LENGTH_CXL11;
253 
254 		res = alloc_mock_res(size);
255 		if (!res)
256 			return -ENOMEM;
257 		chbs->base = res->range.start;
258 		chbs->length = size;
259 	}
260 
261 	for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
262 		struct acpi_cedt_cfmws *window = mock_cfmws[i];
263 
264 		res = alloc_mock_res(window->window_size);
265 		if (!res)
266 			return -ENOMEM;
267 		window->base_hpa = res->range.start;
268 	}
269 
270 	return 0;
271 }
272 
273 /*
274  * WARNING, this hack assumes the format of 'struct
275  * cxl_cfmws_context' and 'struct cxl_chbs_context' share the property that
276  * the first struct member is the device being probed by the cxl_acpi
277  * driver.
278  */
279 struct cxl_cedt_context {
280 	struct device *dev;
281 };
282 
283 static int mock_acpi_table_parse_cedt(enum acpi_cedt_type id,
284 				      acpi_tbl_entry_handler_arg handler_arg,
285 				      void *arg)
286 {
287 	struct cxl_cedt_context *ctx = arg;
288 	struct device *dev = ctx->dev;
289 	union acpi_subtable_headers *h;
290 	unsigned long end;
291 	int i;
292 
293 	if (dev != &cxl_acpi->dev)
294 		return acpi_table_parse_cedt(id, handler_arg, arg);
295 
296 	if (id == ACPI_CEDT_TYPE_CHBS)
297 		for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
298 			h = (union acpi_subtable_headers *)&mock_cedt.chbs[i];
299 			end = (unsigned long)&mock_cedt.chbs[i + 1];
300 			handler_arg(h, arg, end);
301 		}
302 
303 	if (id == ACPI_CEDT_TYPE_CFMWS)
304 		for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
305 			h = (union acpi_subtable_headers *) mock_cfmws[i];
306 			end = (unsigned long) h + mock_cfmws[i]->header.length;
307 			handler_arg(h, arg, end);
308 		}
309 
310 	return 0;
311 }
312 
313 static bool is_mock_bridge(struct device *dev)
314 {
315 	int i;
316 
317 	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++)
318 		if (dev == &cxl_host_bridge[i]->dev)
319 			return true;
320 
321 	return false;
322 }
323 
324 static int host_bridge_index(struct acpi_device *adev)
325 {
326 	return adev - host_bridge;
327 }
328 
329 static struct acpi_device *find_host_bridge(acpi_handle handle)
330 {
331 	int i;
332 
333 	for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
334 		if (handle == host_bridge[i].handle)
335 			return &host_bridge[i];
336 	return NULL;
337 }
338 
339 static acpi_status
340 mock_acpi_evaluate_integer(acpi_handle handle, acpi_string pathname,
341 			   struct acpi_object_list *arguments,
342 			   unsigned long long *data)
343 {
344 	struct acpi_device *adev = find_host_bridge(handle);
345 
346 	if (!adev || strcmp(pathname, METHOD_NAME__UID) != 0)
347 		return acpi_evaluate_integer(handle, pathname, arguments, data);
348 
349 	*data = host_bridge_index(adev);
350 	return AE_OK;
351 }
352 
353 static struct pci_bus mock_pci_bus[NR_CXL_HOST_BRIDGES];
354 static struct acpi_pci_root mock_pci_root[NR_CXL_HOST_BRIDGES] = {
355 	[0] = {
356 		.bus = &mock_pci_bus[0],
357 	},
358 	[1] = {
359 		.bus = &mock_pci_bus[1],
360 	},
361 	[2] = {
362 		.bus = &mock_pci_bus[2],
363 	},
364 	[3] = {
365 		.bus = &mock_pci_bus[3],
366 	},
367 };
368 
369 static struct platform_device *mock_cxl_root_port(struct pci_bus *bus, int index)
370 {
371 	int i;
372 
373 	for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
374 		if (bus == &mock_pci_bus[i])
375 			return cxl_root_port[index + i * NR_CXL_ROOT_PORTS];
376 	return NULL;
377 }
378 
379 static bool is_mock_port(struct platform_device *pdev)
380 {
381 	int i;
382 
383 	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++)
384 		if (pdev == cxl_root_port[i])
385 			return true;
386 	return false;
387 }
388 
389 static bool is_mock_bus(struct pci_bus *bus)
390 {
391 	int i;
392 
393 	for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
394 		if (bus == &mock_pci_bus[i])
395 			return true;
396 	return false;
397 }
398 
399 static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle)
400 {
401 	struct acpi_device *adev = find_host_bridge(handle);
402 
403 	if (!adev)
404 		return acpi_pci_find_root(handle);
405 	return &mock_pci_root[host_bridge_index(adev)];
406 }
407 
408 static struct cxl_mock_ops cxl_mock_ops = {
409 	.is_mock_adev = is_mock_adev,
410 	.is_mock_bridge = is_mock_bridge,
411 	.is_mock_bus = is_mock_bus,
412 	.is_mock_port = is_mock_port,
413 	.is_mock_dev = is_mock_dev,
414 	.mock_port = mock_cxl_root_port,
415 	.acpi_table_parse_cedt = mock_acpi_table_parse_cedt,
416 	.acpi_evaluate_integer = mock_acpi_evaluate_integer,
417 	.acpi_pci_find_root = mock_acpi_pci_find_root,
418 	.list = LIST_HEAD_INIT(cxl_mock_ops.list),
419 };
420 
421 static void mock_companion(struct acpi_device *adev, struct device *dev)
422 {
423 	device_initialize(&adev->dev);
424 	fwnode_init(&adev->fwnode, NULL);
425 	dev->fwnode = &adev->fwnode;
426 	adev->fwnode.dev = dev;
427 }
428 
429 #ifndef SZ_64G
430 #define SZ_64G (SZ_32G * 2)
431 #endif
432 
433 #ifndef SZ_512G
434 #define SZ_512G (SZ_64G * 8)
435 #endif
436 
437 static struct platform_device *alloc_memdev(int id)
438 {
439 	struct resource res[] = {
440 		[0] = {
441 			.flags = IORESOURCE_MEM,
442 		},
443 		[1] = {
444 			.flags = IORESOURCE_MEM,
445 			.desc = IORES_DESC_PERSISTENT_MEMORY,
446 		},
447 	};
448 	struct platform_device *pdev;
449 	int i, rc;
450 
451 	for (i = 0; i < ARRAY_SIZE(res); i++) {
452 		struct cxl_mock_res *r = alloc_mock_res(SZ_256M);
453 
454 		if (!r)
455 			return NULL;
456 		res[i].start = r->range.start;
457 		res[i].end = r->range.end;
458 	}
459 
460 	pdev = platform_device_alloc("cxl_mem", id);
461 	if (!pdev)
462 		return NULL;
463 
464 	rc = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
465 	if (rc)
466 		goto err;
467 
468 	return pdev;
469 
470 err:
471 	platform_device_put(pdev);
472 	return NULL;
473 }
474 
475 static __init int cxl_test_init(void)
476 {
477 	int rc, i;
478 
479 	register_cxl_mock_ops(&cxl_mock_ops);
480 
481 	cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
482 	if (!cxl_mock_pool) {
483 		rc = -ENOMEM;
484 		goto err_gen_pool_create;
485 	}
486 
487 	rc = gen_pool_add(cxl_mock_pool, SZ_512G, SZ_64G, NUMA_NO_NODE);
488 	if (rc)
489 		goto err_gen_pool_add;
490 
491 	rc = populate_cedt();
492 	if (rc)
493 		goto err_populate;
494 
495 	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
496 		struct acpi_device *adev = &host_bridge[i];
497 		struct platform_device *pdev;
498 
499 		pdev = platform_device_alloc("cxl_host_bridge", i);
500 		if (!pdev)
501 			goto err_bridge;
502 
503 		mock_companion(adev, &pdev->dev);
504 		rc = platform_device_add(pdev);
505 		if (rc) {
506 			platform_device_put(pdev);
507 			goto err_bridge;
508 		}
509 		cxl_host_bridge[i] = pdev;
510 	}
511 
512 	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
513 		struct platform_device *bridge =
514 			cxl_host_bridge[i / NR_CXL_ROOT_PORTS];
515 		struct platform_device *pdev;
516 
517 		pdev = platform_device_alloc("cxl_root_port", i);
518 		if (!pdev)
519 			goto err_port;
520 		pdev->dev.parent = &bridge->dev;
521 
522 		rc = platform_device_add(pdev);
523 		if (rc) {
524 			platform_device_put(pdev);
525 			goto err_port;
526 		}
527 		cxl_root_port[i] = pdev;
528 	}
529 
530 	BUILD_BUG_ON(ARRAY_SIZE(cxl_mem) != ARRAY_SIZE(cxl_root_port));
531 	for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) {
532 		struct platform_device *port = cxl_root_port[i];
533 		struct platform_device *pdev;
534 
535 		pdev = alloc_memdev(i);
536 		if (!pdev)
537 			goto err_mem;
538 		pdev->dev.parent = &port->dev;
539 
540 		rc = platform_device_add(pdev);
541 		if (rc) {
542 			platform_device_put(pdev);
543 			goto err_mem;
544 		}
545 		cxl_mem[i] = pdev;
546 	}
547 
548 	cxl_acpi = platform_device_alloc("cxl_acpi", 0);
549 	if (!cxl_acpi)
550 		goto err_mem;
551 
552 	mock_companion(&acpi0017_mock, &cxl_acpi->dev);
553 	acpi0017_mock.dev.bus = &platform_bus_type;
554 
555 	rc = platform_device_add(cxl_acpi);
556 	if (rc)
557 		goto err_add;
558 
559 	return 0;
560 
561 err_add:
562 	platform_device_put(cxl_acpi);
563 err_mem:
564 	for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
565 		platform_device_unregister(cxl_mem[i]);
566 err_port:
567 	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
568 		platform_device_unregister(cxl_root_port[i]);
569 err_bridge:
570 	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--)
571 		platform_device_unregister(cxl_host_bridge[i]);
572 err_populate:
573 	depopulate_all_mock_resources();
574 err_gen_pool_add:
575 	gen_pool_destroy(cxl_mock_pool);
576 err_gen_pool_create:
577 	unregister_cxl_mock_ops(&cxl_mock_ops);
578 	return rc;
579 }
580 
581 static __exit void cxl_test_exit(void)
582 {
583 	int i;
584 
585 	platform_device_unregister(cxl_acpi);
586 	for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
587 		platform_device_unregister(cxl_mem[i]);
588 	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
589 		platform_device_unregister(cxl_root_port[i]);
590 	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--)
591 		platform_device_unregister(cxl_host_bridge[i]);
592 	depopulate_all_mock_resources();
593 	gen_pool_destroy(cxl_mock_pool);
594 	unregister_cxl_mock_ops(&cxl_mock_ops);
595 }
596 
597 module_init(cxl_test_init);
598 module_exit(cxl_test_exit);
599 MODULE_LICENSE("GPL v2");
600 MODULE_IMPORT_NS(ACPI);
601