cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cxl.c (18478B)


      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 <cxlmem.h>
     12#include "mock.h"
     13
     14#define NR_CXL_HOST_BRIDGES 2
     15#define NR_CXL_ROOT_PORTS 2
     16#define NR_CXL_SWITCH_PORTS 2
     17#define NR_CXL_PORT_DECODERS 2
     18
     19static struct platform_device *cxl_acpi;
     20static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES];
     21static struct platform_device
     22	*cxl_root_port[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
     23static struct platform_device
     24	*cxl_switch_uport[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
     25static struct platform_device
     26	*cxl_switch_dport[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS *
     27			  NR_CXL_SWITCH_PORTS];
     28struct platform_device
     29	*cxl_mem[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS * NR_CXL_SWITCH_PORTS];
     30
     31static struct acpi_device acpi0017_mock;
     32static struct acpi_device host_bridge[NR_CXL_HOST_BRIDGES] = {
     33	[0] = {
     34		.handle = &host_bridge[0],
     35	},
     36	[1] = {
     37		.handle = &host_bridge[1],
     38	},
     39};
     40
     41static bool is_mock_dev(struct device *dev)
     42{
     43	int i;
     44
     45	for (i = 0; i < ARRAY_SIZE(cxl_mem); i++)
     46		if (dev == &cxl_mem[i]->dev)
     47			return true;
     48	if (dev == &cxl_acpi->dev)
     49		return true;
     50	return false;
     51}
     52
     53static bool is_mock_adev(struct acpi_device *adev)
     54{
     55	int i;
     56
     57	if (adev == &acpi0017_mock)
     58		return true;
     59
     60	for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
     61		if (adev == &host_bridge[i])
     62			return true;
     63
     64	return false;
     65}
     66
     67static struct {
     68	struct acpi_table_cedt cedt;
     69	struct acpi_cedt_chbs chbs[NR_CXL_HOST_BRIDGES];
     70	struct {
     71		struct acpi_cedt_cfmws cfmws;
     72		u32 target[1];
     73	} cfmws0;
     74	struct {
     75		struct acpi_cedt_cfmws cfmws;
     76		u32 target[2];
     77	} cfmws1;
     78	struct {
     79		struct acpi_cedt_cfmws cfmws;
     80		u32 target[1];
     81	} cfmws2;
     82	struct {
     83		struct acpi_cedt_cfmws cfmws;
     84		u32 target[2];
     85	} cfmws3;
     86} __packed mock_cedt = {
     87	.cedt = {
     88		.header = {
     89			.signature = "CEDT",
     90			.length = sizeof(mock_cedt),
     91			.revision = 1,
     92		},
     93	},
     94	.chbs[0] = {
     95		.header = {
     96			.type = ACPI_CEDT_TYPE_CHBS,
     97			.length = sizeof(mock_cedt.chbs[0]),
     98		},
     99		.uid = 0,
    100		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
    101	},
    102	.chbs[1] = {
    103		.header = {
    104			.type = ACPI_CEDT_TYPE_CHBS,
    105			.length = sizeof(mock_cedt.chbs[0]),
    106		},
    107		.uid = 1,
    108		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
    109	},
    110	.cfmws0 = {
    111		.cfmws = {
    112			.header = {
    113				.type = ACPI_CEDT_TYPE_CFMWS,
    114				.length = sizeof(mock_cedt.cfmws0),
    115			},
    116			.interleave_ways = 0,
    117			.granularity = 4,
    118			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
    119					ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
    120			.qtg_id = 0,
    121			.window_size = SZ_256M,
    122		},
    123		.target = { 0 },
    124	},
    125	.cfmws1 = {
    126		.cfmws = {
    127			.header = {
    128				.type = ACPI_CEDT_TYPE_CFMWS,
    129				.length = sizeof(mock_cedt.cfmws1),
    130			},
    131			.interleave_ways = 1,
    132			.granularity = 4,
    133			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
    134					ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
    135			.qtg_id = 1,
    136			.window_size = SZ_256M * 2,
    137		},
    138		.target = { 0, 1, },
    139	},
    140	.cfmws2 = {
    141		.cfmws = {
    142			.header = {
    143				.type = ACPI_CEDT_TYPE_CFMWS,
    144				.length = sizeof(mock_cedt.cfmws2),
    145			},
    146			.interleave_ways = 0,
    147			.granularity = 4,
    148			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
    149					ACPI_CEDT_CFMWS_RESTRICT_PMEM,
    150			.qtg_id = 2,
    151			.window_size = SZ_256M,
    152		},
    153		.target = { 0 },
    154	},
    155	.cfmws3 = {
    156		.cfmws = {
    157			.header = {
    158				.type = ACPI_CEDT_TYPE_CFMWS,
    159				.length = sizeof(mock_cedt.cfmws3),
    160			},
    161			.interleave_ways = 1,
    162			.granularity = 4,
    163			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
    164					ACPI_CEDT_CFMWS_RESTRICT_PMEM,
    165			.qtg_id = 3,
    166			.window_size = SZ_256M * 2,
    167		},
    168		.target = { 0, 1, },
    169	},
    170};
    171
    172struct acpi_cedt_cfmws *mock_cfmws[4] = {
    173	[0] = &mock_cedt.cfmws0.cfmws,
    174	[1] = &mock_cedt.cfmws1.cfmws,
    175	[2] = &mock_cedt.cfmws2.cfmws,
    176	[3] = &mock_cedt.cfmws3.cfmws,
    177};
    178
    179struct cxl_mock_res {
    180	struct list_head list;
    181	struct range range;
    182};
    183
    184static LIST_HEAD(mock_res);
    185static DEFINE_MUTEX(mock_res_lock);
    186static struct gen_pool *cxl_mock_pool;
    187
    188static void depopulate_all_mock_resources(void)
    189{
    190	struct cxl_mock_res *res, *_res;
    191
    192	mutex_lock(&mock_res_lock);
    193	list_for_each_entry_safe(res, _res, &mock_res, list) {
    194		gen_pool_free(cxl_mock_pool, res->range.start,
    195			      range_len(&res->range));
    196		list_del(&res->list);
    197		kfree(res);
    198	}
    199	mutex_unlock(&mock_res_lock);
    200}
    201
    202static struct cxl_mock_res *alloc_mock_res(resource_size_t size)
    203{
    204	struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
    205	struct genpool_data_align data = {
    206		.align = SZ_256M,
    207	};
    208	unsigned long phys;
    209
    210	INIT_LIST_HEAD(&res->list);
    211	phys = gen_pool_alloc_algo(cxl_mock_pool, size,
    212				   gen_pool_first_fit_align, &data);
    213	if (!phys)
    214		return NULL;
    215
    216	res->range = (struct range) {
    217		.start = phys,
    218		.end = phys + size - 1,
    219	};
    220	mutex_lock(&mock_res_lock);
    221	list_add(&res->list, &mock_res);
    222	mutex_unlock(&mock_res_lock);
    223
    224	return res;
    225}
    226
    227static int populate_cedt(void)
    228{
    229	struct cxl_mock_res *res;
    230	int i;
    231
    232	for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
    233		struct acpi_cedt_chbs *chbs = &mock_cedt.chbs[i];
    234		resource_size_t size;
    235
    236		if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL20)
    237			size = ACPI_CEDT_CHBS_LENGTH_CXL20;
    238		else
    239			size = ACPI_CEDT_CHBS_LENGTH_CXL11;
    240
    241		res = alloc_mock_res(size);
    242		if (!res)
    243			return -ENOMEM;
    244		chbs->base = res->range.start;
    245		chbs->length = size;
    246	}
    247
    248	for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
    249		struct acpi_cedt_cfmws *window = mock_cfmws[i];
    250
    251		res = alloc_mock_res(window->window_size);
    252		if (!res)
    253			return -ENOMEM;
    254		window->base_hpa = res->range.start;
    255	}
    256
    257	return 0;
    258}
    259
    260/*
    261 * WARNING, this hack assumes the format of 'struct
    262 * cxl_cfmws_context' and 'struct cxl_chbs_context' share the property that
    263 * the first struct member is the device being probed by the cxl_acpi
    264 * driver.
    265 */
    266struct cxl_cedt_context {
    267	struct device *dev;
    268};
    269
    270static int mock_acpi_table_parse_cedt(enum acpi_cedt_type id,
    271				      acpi_tbl_entry_handler_arg handler_arg,
    272				      void *arg)
    273{
    274	struct cxl_cedt_context *ctx = arg;
    275	struct device *dev = ctx->dev;
    276	union acpi_subtable_headers *h;
    277	unsigned long end;
    278	int i;
    279
    280	if (dev != &cxl_acpi->dev)
    281		return acpi_table_parse_cedt(id, handler_arg, arg);
    282
    283	if (id == ACPI_CEDT_TYPE_CHBS)
    284		for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
    285			h = (union acpi_subtable_headers *)&mock_cedt.chbs[i];
    286			end = (unsigned long)&mock_cedt.chbs[i + 1];
    287			handler_arg(h, arg, end);
    288		}
    289
    290	if (id == ACPI_CEDT_TYPE_CFMWS)
    291		for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
    292			h = (union acpi_subtable_headers *) mock_cfmws[i];
    293			end = (unsigned long) h + mock_cfmws[i]->header.length;
    294			handler_arg(h, arg, end);
    295		}
    296
    297	return 0;
    298}
    299
    300static bool is_mock_bridge(struct device *dev)
    301{
    302	int i;
    303
    304	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++)
    305		if (dev == &cxl_host_bridge[i]->dev)
    306			return true;
    307	return false;
    308}
    309
    310static bool is_mock_port(struct device *dev)
    311{
    312	int i;
    313
    314	if (is_mock_bridge(dev))
    315		return true;
    316
    317	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++)
    318		if (dev == &cxl_root_port[i]->dev)
    319			return true;
    320
    321	for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++)
    322		if (dev == &cxl_switch_uport[i]->dev)
    323			return true;
    324
    325	for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++)
    326		if (dev == &cxl_switch_dport[i]->dev)
    327			return true;
    328
    329	if (is_cxl_memdev(dev))
    330		return is_mock_dev(dev->parent);
    331
    332	return false;
    333}
    334
    335static int host_bridge_index(struct acpi_device *adev)
    336{
    337	return adev - host_bridge;
    338}
    339
    340static struct acpi_device *find_host_bridge(acpi_handle handle)
    341{
    342	int i;
    343
    344	for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
    345		if (handle == host_bridge[i].handle)
    346			return &host_bridge[i];
    347	return NULL;
    348}
    349
    350static acpi_status
    351mock_acpi_evaluate_integer(acpi_handle handle, acpi_string pathname,
    352			   struct acpi_object_list *arguments,
    353			   unsigned long long *data)
    354{
    355	struct acpi_device *adev = find_host_bridge(handle);
    356
    357	if (!adev || strcmp(pathname, METHOD_NAME__UID) != 0)
    358		return acpi_evaluate_integer(handle, pathname, arguments, data);
    359
    360	*data = host_bridge_index(adev);
    361	return AE_OK;
    362}
    363
    364static struct pci_bus mock_pci_bus[NR_CXL_HOST_BRIDGES];
    365static struct acpi_pci_root mock_pci_root[NR_CXL_HOST_BRIDGES] = {
    366	[0] = {
    367		.bus = &mock_pci_bus[0],
    368	},
    369	[1] = {
    370		.bus = &mock_pci_bus[1],
    371	},
    372};
    373
    374static bool is_mock_bus(struct pci_bus *bus)
    375{
    376	int i;
    377
    378	for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
    379		if (bus == &mock_pci_bus[i])
    380			return true;
    381	return false;
    382}
    383
    384static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle)
    385{
    386	struct acpi_device *adev = find_host_bridge(handle);
    387
    388	if (!adev)
    389		return acpi_pci_find_root(handle);
    390	return &mock_pci_root[host_bridge_index(adev)];
    391}
    392
    393static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port)
    394{
    395	struct cxl_hdm *cxlhdm = devm_kzalloc(&port->dev, sizeof(*cxlhdm), GFP_KERNEL);
    396
    397	if (!cxlhdm)
    398		return ERR_PTR(-ENOMEM);
    399
    400	cxlhdm->port = port;
    401	return cxlhdm;
    402}
    403
    404static int mock_cxl_add_passthrough_decoder(struct cxl_port *port)
    405{
    406	dev_err(&port->dev, "unexpected passthrough decoder for cxl_test\n");
    407	return -EOPNOTSUPP;
    408}
    409
    410
    411struct target_map_ctx {
    412	int *target_map;
    413	int index;
    414	int target_count;
    415};
    416
    417static int map_targets(struct device *dev, void *data)
    418{
    419	struct platform_device *pdev = to_platform_device(dev);
    420	struct target_map_ctx *ctx = data;
    421
    422	ctx->target_map[ctx->index++] = pdev->id;
    423
    424	if (ctx->index > ctx->target_count) {
    425		dev_WARN_ONCE(dev, 1, "too many targets found?\n");
    426		return -ENXIO;
    427	}
    428
    429	return 0;
    430}
    431
    432static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
    433{
    434	struct cxl_port *port = cxlhdm->port;
    435	struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
    436	int target_count, i;
    437
    438	if (is_cxl_endpoint(port))
    439		target_count = 0;
    440	else if (is_cxl_root(parent_port))
    441		target_count = NR_CXL_ROOT_PORTS;
    442	else
    443		target_count = NR_CXL_SWITCH_PORTS;
    444
    445	for (i = 0; i < NR_CXL_PORT_DECODERS; i++) {
    446		int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
    447		struct target_map_ctx ctx = {
    448			.target_map = target_map,
    449			.target_count = target_count,
    450		};
    451		struct cxl_decoder *cxld;
    452		int rc;
    453
    454		if (target_count)
    455			cxld = cxl_switch_decoder_alloc(port, target_count);
    456		else
    457			cxld = cxl_endpoint_decoder_alloc(port);
    458		if (IS_ERR(cxld)) {
    459			dev_warn(&port->dev,
    460				 "Failed to allocate the decoder\n");
    461			return PTR_ERR(cxld);
    462		}
    463
    464		cxld->decoder_range = (struct range) {
    465			.start = 0,
    466			.end = -1,
    467		};
    468
    469		cxld->flags = CXL_DECODER_F_ENABLE;
    470		cxld->interleave_ways = min_not_zero(target_count, 1);
    471		cxld->interleave_granularity = SZ_4K;
    472		cxld->target_type = CXL_DECODER_EXPANDER;
    473
    474		if (target_count) {
    475			rc = device_for_each_child(port->uport, &ctx,
    476						   map_targets);
    477			if (rc) {
    478				put_device(&cxld->dev);
    479				return rc;
    480			}
    481		}
    482
    483		rc = cxl_decoder_add_locked(cxld, target_map);
    484		if (rc) {
    485			put_device(&cxld->dev);
    486			dev_err(&port->dev, "Failed to add decoder\n");
    487			return rc;
    488		}
    489
    490		rc = cxl_decoder_autoremove(&port->dev, cxld);
    491		if (rc)
    492			return rc;
    493		dev_dbg(&cxld->dev, "Added to port %s\n", dev_name(&port->dev));
    494	}
    495
    496	return 0;
    497}
    498
    499static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
    500{
    501	struct device *dev = &port->dev;
    502	struct platform_device **array;
    503	int i, array_size;
    504
    505	if (port->depth == 1) {
    506		array_size = ARRAY_SIZE(cxl_root_port);
    507		array = cxl_root_port;
    508	} else if (port->depth == 2) {
    509		array_size = ARRAY_SIZE(cxl_switch_dport);
    510		array = cxl_switch_dport;
    511	} else {
    512		dev_WARN_ONCE(&port->dev, 1, "unexpected depth %d\n",
    513			      port->depth);
    514		return -ENXIO;
    515	}
    516
    517	for (i = 0; i < array_size; i++) {
    518		struct platform_device *pdev = array[i];
    519		struct cxl_dport *dport;
    520
    521		if (pdev->dev.parent != port->uport)
    522			continue;
    523
    524		dport = devm_cxl_add_dport(port, &pdev->dev, pdev->id,
    525					   CXL_RESOURCE_NONE);
    526
    527		if (IS_ERR(dport)) {
    528			dev_err(dev, "failed to add dport: %s (%ld)\n",
    529				dev_name(&pdev->dev), PTR_ERR(dport));
    530			return PTR_ERR(dport);
    531		}
    532
    533		dev_dbg(dev, "add dport%d: %s\n", pdev->id,
    534			dev_name(&pdev->dev));
    535	}
    536
    537	return 0;
    538}
    539
    540static struct cxl_mock_ops cxl_mock_ops = {
    541	.is_mock_adev = is_mock_adev,
    542	.is_mock_bridge = is_mock_bridge,
    543	.is_mock_bus = is_mock_bus,
    544	.is_mock_port = is_mock_port,
    545	.is_mock_dev = is_mock_dev,
    546	.acpi_table_parse_cedt = mock_acpi_table_parse_cedt,
    547	.acpi_evaluate_integer = mock_acpi_evaluate_integer,
    548	.acpi_pci_find_root = mock_acpi_pci_find_root,
    549	.devm_cxl_port_enumerate_dports = mock_cxl_port_enumerate_dports,
    550	.devm_cxl_setup_hdm = mock_cxl_setup_hdm,
    551	.devm_cxl_add_passthrough_decoder = mock_cxl_add_passthrough_decoder,
    552	.devm_cxl_enumerate_decoders = mock_cxl_enumerate_decoders,
    553	.list = LIST_HEAD_INIT(cxl_mock_ops.list),
    554};
    555
    556static void mock_companion(struct acpi_device *adev, struct device *dev)
    557{
    558	device_initialize(&adev->dev);
    559	fwnode_init(&adev->fwnode, NULL);
    560	dev->fwnode = &adev->fwnode;
    561	adev->fwnode.dev = dev;
    562}
    563
    564#ifndef SZ_64G
    565#define SZ_64G (SZ_32G * 2)
    566#endif
    567
    568#ifndef SZ_512G
    569#define SZ_512G (SZ_64G * 8)
    570#endif
    571
    572static struct platform_device *alloc_memdev(int id)
    573{
    574	struct resource res[] = {
    575		[0] = {
    576			.flags = IORESOURCE_MEM,
    577		},
    578		[1] = {
    579			.flags = IORESOURCE_MEM,
    580			.desc = IORES_DESC_PERSISTENT_MEMORY,
    581		},
    582	};
    583	struct platform_device *pdev;
    584	int i, rc;
    585
    586	for (i = 0; i < ARRAY_SIZE(res); i++) {
    587		struct cxl_mock_res *r = alloc_mock_res(SZ_256M);
    588
    589		if (!r)
    590			return NULL;
    591		res[i].start = r->range.start;
    592		res[i].end = r->range.end;
    593	}
    594
    595	pdev = platform_device_alloc("cxl_mem", id);
    596	if (!pdev)
    597		return NULL;
    598
    599	rc = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
    600	if (rc)
    601		goto err;
    602
    603	return pdev;
    604
    605err:
    606	platform_device_put(pdev);
    607	return NULL;
    608}
    609
    610static __init int cxl_test_init(void)
    611{
    612	int rc, i;
    613
    614	register_cxl_mock_ops(&cxl_mock_ops);
    615
    616	cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
    617	if (!cxl_mock_pool) {
    618		rc = -ENOMEM;
    619		goto err_gen_pool_create;
    620	}
    621
    622	rc = gen_pool_add(cxl_mock_pool, SZ_512G, SZ_64G, NUMA_NO_NODE);
    623	if (rc)
    624		goto err_gen_pool_add;
    625
    626	rc = populate_cedt();
    627	if (rc)
    628		goto err_populate;
    629
    630	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
    631		struct acpi_device *adev = &host_bridge[i];
    632		struct platform_device *pdev;
    633
    634		pdev = platform_device_alloc("cxl_host_bridge", i);
    635		if (!pdev)
    636			goto err_bridge;
    637
    638		mock_companion(adev, &pdev->dev);
    639		rc = platform_device_add(pdev);
    640		if (rc) {
    641			platform_device_put(pdev);
    642			goto err_bridge;
    643		}
    644
    645		cxl_host_bridge[i] = pdev;
    646		rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
    647				       "physical_node");
    648		if (rc)
    649			goto err_bridge;
    650	}
    651
    652	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
    653		struct platform_device *bridge =
    654			cxl_host_bridge[i % ARRAY_SIZE(cxl_host_bridge)];
    655		struct platform_device *pdev;
    656
    657		pdev = platform_device_alloc("cxl_root_port", i);
    658		if (!pdev)
    659			goto err_port;
    660		pdev->dev.parent = &bridge->dev;
    661
    662		rc = platform_device_add(pdev);
    663		if (rc) {
    664			platform_device_put(pdev);
    665			goto err_port;
    666		}
    667		cxl_root_port[i] = pdev;
    668	}
    669
    670	BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
    671	for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) {
    672		struct platform_device *root_port = cxl_root_port[i];
    673		struct platform_device *pdev;
    674
    675		pdev = platform_device_alloc("cxl_switch_uport", i);
    676		if (!pdev)
    677			goto err_port;
    678		pdev->dev.parent = &root_port->dev;
    679
    680		rc = platform_device_add(pdev);
    681		if (rc) {
    682			platform_device_put(pdev);
    683			goto err_uport;
    684		}
    685		cxl_switch_uport[i] = pdev;
    686	}
    687
    688	for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
    689		struct platform_device *uport =
    690			cxl_switch_uport[i % ARRAY_SIZE(cxl_switch_uport)];
    691		struct platform_device *pdev;
    692
    693		pdev = platform_device_alloc("cxl_switch_dport", i);
    694		if (!pdev)
    695			goto err_port;
    696		pdev->dev.parent = &uport->dev;
    697
    698		rc = platform_device_add(pdev);
    699		if (rc) {
    700			platform_device_put(pdev);
    701			goto err_dport;
    702		}
    703		cxl_switch_dport[i] = pdev;
    704	}
    705
    706	BUILD_BUG_ON(ARRAY_SIZE(cxl_mem) != ARRAY_SIZE(cxl_switch_dport));
    707	for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) {
    708		struct platform_device *dport = cxl_switch_dport[i];
    709		struct platform_device *pdev;
    710
    711		pdev = alloc_memdev(i);
    712		if (!pdev)
    713			goto err_mem;
    714		pdev->dev.parent = &dport->dev;
    715		set_dev_node(&pdev->dev, i % 2);
    716
    717		rc = platform_device_add(pdev);
    718		if (rc) {
    719			platform_device_put(pdev);
    720			goto err_mem;
    721		}
    722		cxl_mem[i] = pdev;
    723	}
    724
    725	cxl_acpi = platform_device_alloc("cxl_acpi", 0);
    726	if (!cxl_acpi)
    727		goto err_mem;
    728
    729	mock_companion(&acpi0017_mock, &cxl_acpi->dev);
    730	acpi0017_mock.dev.bus = &platform_bus_type;
    731
    732	rc = platform_device_add(cxl_acpi);
    733	if (rc)
    734		goto err_add;
    735
    736	return 0;
    737
    738err_add:
    739	platform_device_put(cxl_acpi);
    740err_mem:
    741	for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
    742		platform_device_unregister(cxl_mem[i]);
    743err_dport:
    744	for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
    745		platform_device_unregister(cxl_switch_dport[i]);
    746err_uport:
    747	for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
    748		platform_device_unregister(cxl_switch_uport[i]);
    749err_port:
    750	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
    751		platform_device_unregister(cxl_root_port[i]);
    752err_bridge:
    753	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
    754		struct platform_device *pdev = cxl_host_bridge[i];
    755
    756		if (!pdev)
    757			continue;
    758		sysfs_remove_link(&pdev->dev.kobj, "physical_node");
    759		platform_device_unregister(cxl_host_bridge[i]);
    760	}
    761err_populate:
    762	depopulate_all_mock_resources();
    763err_gen_pool_add:
    764	gen_pool_destroy(cxl_mock_pool);
    765err_gen_pool_create:
    766	unregister_cxl_mock_ops(&cxl_mock_ops);
    767	return rc;
    768}
    769
    770static __exit void cxl_test_exit(void)
    771{
    772	int i;
    773
    774	platform_device_unregister(cxl_acpi);
    775	for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
    776		platform_device_unregister(cxl_mem[i]);
    777	for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--)
    778		platform_device_unregister(cxl_switch_dport[i]);
    779	for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--)
    780		platform_device_unregister(cxl_switch_uport[i]);
    781	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
    782		platform_device_unregister(cxl_root_port[i]);
    783	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) {
    784		struct platform_device *pdev = cxl_host_bridge[i];
    785
    786		if (!pdev)
    787			continue;
    788		sysfs_remove_link(&pdev->dev.kobj, "physical_node");
    789		platform_device_unregister(cxl_host_bridge[i]);
    790	}
    791	depopulate_all_mock_resources();
    792	gen_pool_destroy(cxl_mock_pool);
    793	unregister_cxl_mock_ops(&cxl_mock_ops);
    794}
    795
    796module_init(cxl_test_init);
    797module_exit(cxl_test_exit);
    798MODULE_LICENSE("GPL v2");
    799MODULE_IMPORT_NS(ACPI);
    800MODULE_IMPORT_NS(CXL);