xref: /openbmc/linux/tools/testing/cxl/test/cxl.c (revision ee8ec048)
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 cxl_mock_res {
186 	struct list_head list;
187 	struct range range;
188 };
189 
190 static LIST_HEAD(mock_res);
191 static DEFINE_MUTEX(mock_res_lock);
192 static struct gen_pool *cxl_mock_pool;
193 
194 static void depopulate_all_mock_resources(void)
195 {
196 	struct cxl_mock_res *res, *_res;
197 
198 	mutex_lock(&mock_res_lock);
199 	list_for_each_entry_safe(res, _res, &mock_res, list) {
200 		gen_pool_free(cxl_mock_pool, res->range.start,
201 			      range_len(&res->range));
202 		list_del(&res->list);
203 		kfree(res);
204 	}
205 	mutex_unlock(&mock_res_lock);
206 }
207 
208 static struct cxl_mock_res *alloc_mock_res(resource_size_t size)
209 {
210 	struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
211 	struct genpool_data_align data = {
212 		.align = SZ_256M,
213 	};
214 	unsigned long phys;
215 
216 	INIT_LIST_HEAD(&res->list);
217 	phys = gen_pool_alloc_algo(cxl_mock_pool, size,
218 				   gen_pool_first_fit_align, &data);
219 	if (!phys)
220 		return NULL;
221 
222 	res->range = (struct range) {
223 		.start = phys,
224 		.end = phys + size - 1,
225 	};
226 	mutex_lock(&mock_res_lock);
227 	list_add(&res->list, &mock_res);
228 	mutex_unlock(&mock_res_lock);
229 
230 	return res;
231 }
232 
233 static int populate_cedt(void)
234 {
235 	struct acpi_cedt_cfmws *cfmws[4] = {
236 		[0] = &mock_cedt.cfmws0.cfmws,
237 		[1] = &mock_cedt.cfmws1.cfmws,
238 		[2] = &mock_cedt.cfmws2.cfmws,
239 		[3] = &mock_cedt.cfmws3.cfmws,
240 	};
241 	struct cxl_mock_res *res;
242 	int i;
243 
244 	for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
245 		struct acpi_cedt_chbs *chbs = &mock_cedt.chbs[i];
246 		resource_size_t size;
247 
248 		if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL20)
249 			size = ACPI_CEDT_CHBS_LENGTH_CXL20;
250 		else
251 			size = ACPI_CEDT_CHBS_LENGTH_CXL11;
252 
253 		res = alloc_mock_res(size);
254 		if (!res)
255 			return -ENOMEM;
256 		chbs->base = res->range.start;
257 		chbs->length = size;
258 	}
259 
260 	for (i = 0; i < ARRAY_SIZE(cfmws); i++) {
261 		struct acpi_cedt_cfmws *window = cfmws[i];
262 
263 		res = alloc_mock_res(window->window_size);
264 		if (!res)
265 			return -ENOMEM;
266 		window->base_hpa = res->range.start;
267 	}
268 
269 	return 0;
270 }
271 
272 static acpi_status mock_acpi_get_table(char *signature, u32 instance,
273 				       struct acpi_table_header **out_table)
274 {
275 	if (instance < U32_MAX || strcmp(signature, ACPI_SIG_CEDT) != 0)
276 		return acpi_get_table(signature, instance, out_table);
277 
278 	*out_table = (struct acpi_table_header *) &mock_cedt;
279 	return AE_OK;
280 }
281 
282 static void mock_acpi_put_table(struct acpi_table_header *table)
283 {
284 	if (table == (struct acpi_table_header *) &mock_cedt)
285 		return;
286 	acpi_put_table(table);
287 }
288 
289 static bool is_mock_bridge(struct device *dev)
290 {
291 	int i;
292 
293 	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++)
294 		if (dev == &cxl_host_bridge[i]->dev)
295 			return true;
296 
297 	return false;
298 }
299 
300 static int host_bridge_index(struct acpi_device *adev)
301 {
302 	return adev - host_bridge;
303 }
304 
305 static struct acpi_device *find_host_bridge(acpi_handle handle)
306 {
307 	int i;
308 
309 	for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
310 		if (handle == host_bridge[i].handle)
311 			return &host_bridge[i];
312 	return NULL;
313 }
314 
315 static acpi_status
316 mock_acpi_evaluate_integer(acpi_handle handle, acpi_string pathname,
317 			   struct acpi_object_list *arguments,
318 			   unsigned long long *data)
319 {
320 	struct acpi_device *adev = find_host_bridge(handle);
321 
322 	if (!adev || strcmp(pathname, METHOD_NAME__UID) != 0)
323 		return acpi_evaluate_integer(handle, pathname, arguments, data);
324 
325 	*data = host_bridge_index(adev);
326 	return AE_OK;
327 }
328 
329 static struct pci_bus mock_pci_bus[NR_CXL_HOST_BRIDGES];
330 static struct acpi_pci_root mock_pci_root[NR_CXL_HOST_BRIDGES] = {
331 	[0] = {
332 		.bus = &mock_pci_bus[0],
333 	},
334 	[1] = {
335 		.bus = &mock_pci_bus[1],
336 	},
337 	[2] = {
338 		.bus = &mock_pci_bus[2],
339 	},
340 	[3] = {
341 		.bus = &mock_pci_bus[3],
342 	},
343 };
344 
345 static struct platform_device *mock_cxl_root_port(struct pci_bus *bus, int index)
346 {
347 	int i;
348 
349 	for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
350 		if (bus == &mock_pci_bus[i])
351 			return cxl_root_port[index + i * NR_CXL_ROOT_PORTS];
352 	return NULL;
353 }
354 
355 static bool is_mock_port(struct platform_device *pdev)
356 {
357 	int i;
358 
359 	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++)
360 		if (pdev == cxl_root_port[i])
361 			return true;
362 	return false;
363 }
364 
365 static bool is_mock_bus(struct pci_bus *bus)
366 {
367 	int i;
368 
369 	for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
370 		if (bus == &mock_pci_bus[i])
371 			return true;
372 	return false;
373 }
374 
375 static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle)
376 {
377 	struct acpi_device *adev = find_host_bridge(handle);
378 
379 	if (!adev)
380 		return acpi_pci_find_root(handle);
381 	return &mock_pci_root[host_bridge_index(adev)];
382 }
383 
384 static struct cxl_mock_ops cxl_mock_ops = {
385 	.is_mock_adev = is_mock_adev,
386 	.is_mock_bridge = is_mock_bridge,
387 	.is_mock_bus = is_mock_bus,
388 	.is_mock_port = is_mock_port,
389 	.is_mock_dev = is_mock_dev,
390 	.mock_port = mock_cxl_root_port,
391 	.acpi_get_table = mock_acpi_get_table,
392 	.acpi_put_table = mock_acpi_put_table,
393 	.acpi_evaluate_integer = mock_acpi_evaluate_integer,
394 	.acpi_pci_find_root = mock_acpi_pci_find_root,
395 	.list = LIST_HEAD_INIT(cxl_mock_ops.list),
396 };
397 
398 static void mock_companion(struct acpi_device *adev, struct device *dev)
399 {
400 	device_initialize(&adev->dev);
401 	fwnode_init(&adev->fwnode, NULL);
402 	dev->fwnode = &adev->fwnode;
403 	adev->fwnode.dev = dev;
404 }
405 
406 #ifndef SZ_64G
407 #define SZ_64G (SZ_32G * 2)
408 #endif
409 
410 #ifndef SZ_512G
411 #define SZ_512G (SZ_64G * 8)
412 #endif
413 
414 static struct platform_device *alloc_memdev(int id)
415 {
416 	struct resource res[] = {
417 		[0] = {
418 			.flags = IORESOURCE_MEM,
419 		},
420 		[1] = {
421 			.flags = IORESOURCE_MEM,
422 			.desc = IORES_DESC_PERSISTENT_MEMORY,
423 		},
424 	};
425 	struct platform_device *pdev;
426 	int i, rc;
427 
428 	for (i = 0; i < ARRAY_SIZE(res); i++) {
429 		struct cxl_mock_res *r = alloc_mock_res(SZ_256M);
430 
431 		if (!r)
432 			return NULL;
433 		res[i].start = r->range.start;
434 		res[i].end = r->range.end;
435 	}
436 
437 	pdev = platform_device_alloc("cxl_mem", id);
438 	if (!pdev)
439 		return NULL;
440 
441 	rc = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
442 	if (rc)
443 		goto err;
444 
445 	return pdev;
446 
447 err:
448 	platform_device_put(pdev);
449 	return NULL;
450 }
451 
452 static __init int cxl_test_init(void)
453 {
454 	int rc, i;
455 
456 	register_cxl_mock_ops(&cxl_mock_ops);
457 
458 	cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
459 	if (!cxl_mock_pool) {
460 		rc = -ENOMEM;
461 		goto err_gen_pool_create;
462 	}
463 
464 	rc = gen_pool_add(cxl_mock_pool, SZ_512G, SZ_64G, NUMA_NO_NODE);
465 	if (rc)
466 		goto err_gen_pool_add;
467 
468 	rc = populate_cedt();
469 	if (rc)
470 		goto err_populate;
471 
472 	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
473 		struct acpi_device *adev = &host_bridge[i];
474 		struct platform_device *pdev;
475 
476 		pdev = platform_device_alloc("cxl_host_bridge", i);
477 		if (!pdev)
478 			goto err_bridge;
479 
480 		mock_companion(adev, &pdev->dev);
481 		rc = platform_device_add(pdev);
482 		if (rc) {
483 			platform_device_put(pdev);
484 			goto err_bridge;
485 		}
486 		cxl_host_bridge[i] = pdev;
487 	}
488 
489 	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
490 		struct platform_device *bridge =
491 			cxl_host_bridge[i / NR_CXL_ROOT_PORTS];
492 		struct platform_device *pdev;
493 
494 		pdev = platform_device_alloc("cxl_root_port", i);
495 		if (!pdev)
496 			goto err_port;
497 		pdev->dev.parent = &bridge->dev;
498 
499 		rc = platform_device_add(pdev);
500 		if (rc) {
501 			platform_device_put(pdev);
502 			goto err_port;
503 		}
504 		cxl_root_port[i] = pdev;
505 	}
506 
507 	BUILD_BUG_ON(ARRAY_SIZE(cxl_mem) != ARRAY_SIZE(cxl_root_port));
508 	for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) {
509 		struct platform_device *port = cxl_root_port[i];
510 		struct platform_device *pdev;
511 
512 		pdev = alloc_memdev(i);
513 		if (!pdev)
514 			goto err_mem;
515 		pdev->dev.parent = &port->dev;
516 
517 		rc = platform_device_add(pdev);
518 		if (rc) {
519 			platform_device_put(pdev);
520 			goto err_mem;
521 		}
522 		cxl_mem[i] = pdev;
523 	}
524 
525 	cxl_acpi = platform_device_alloc("cxl_acpi", 0);
526 	if (!cxl_acpi)
527 		goto err_mem;
528 
529 	mock_companion(&acpi0017_mock, &cxl_acpi->dev);
530 	acpi0017_mock.dev.bus = &platform_bus_type;
531 
532 	rc = platform_device_add(cxl_acpi);
533 	if (rc)
534 		goto err_add;
535 
536 	return 0;
537 
538 err_add:
539 	platform_device_put(cxl_acpi);
540 err_mem:
541 	for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
542 		platform_device_unregister(cxl_mem[i]);
543 err_port:
544 	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
545 		platform_device_unregister(cxl_root_port[i]);
546 err_bridge:
547 	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--)
548 		platform_device_unregister(cxl_host_bridge[i]);
549 err_populate:
550 	depopulate_all_mock_resources();
551 err_gen_pool_add:
552 	gen_pool_destroy(cxl_mock_pool);
553 err_gen_pool_create:
554 	unregister_cxl_mock_ops(&cxl_mock_ops);
555 	return rc;
556 }
557 
558 static __exit void cxl_test_exit(void)
559 {
560 	int i;
561 
562 	platform_device_unregister(cxl_acpi);
563 	for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
564 		platform_device_unregister(cxl_mem[i]);
565 	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
566 		platform_device_unregister(cxl_root_port[i]);
567 	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--)
568 		platform_device_unregister(cxl_host_bridge[i]);
569 	depopulate_all_mock_resources();
570 	gen_pool_destroy(cxl_mock_pool);
571 	unregister_cxl_mock_ops(&cxl_mock_ops);
572 }
573 
574 module_init(cxl_test_init);
575 module_exit(cxl_test_exit);
576 MODULE_LICENSE("GPL v2");
577