diff options
Diffstat (limited to 'tools/testing/cxl')
| -rw-r--r-- | tools/testing/cxl/test/Kbuild | 2 | ||||
| -rw-r--r-- | tools/testing/cxl/test/accel.c | 66 | ||||
| -rw-r--r-- | tools/testing/cxl/test/cxl.c | 808 | ||||
| -rw-r--r-- | tools/testing/cxl/test/cxl_translate.c | 2 | ||||
| -rw-r--r-- | tools/testing/cxl/test/hmem_test.c | 3 | ||||
| -rw-r--r-- | tools/testing/cxl/test/mem.c | 63 | ||||
| -rw-r--r-- | tools/testing/cxl/test/mock.h | 2 |
7 files changed, 752 insertions, 194 deletions
diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild index c168e3c998a7..9a24ddc28488 100644 --- a/tools/testing/cxl/test/Kbuild +++ b/tools/testing/cxl/test/Kbuild @@ -5,10 +5,12 @@ obj-m += cxl_test.o obj-m += cxl_mock.o obj-m += cxl_mock_mem.o obj-m += cxl_translate.o +obj-m += cxl_mock_accel.o cxl_test-y := cxl.o cxl_test-y += hmem_test.o cxl_mock-y := mock.o cxl_mock_mem-y := mem.o +cxl_mock_accel-y := accel.o KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS)) diff --git a/tools/testing/cxl/test/accel.c b/tools/testing/cxl/test/accel.c new file mode 100644 index 000000000000..8e6f4687ca02 --- /dev/null +++ b/tools/testing/cxl/test/accel.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2026 Intel Corporation. All rights reserved. + +#include <linux/platform_device.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/sizes.h> +#include <cxl/mailbox.h> +#include <cxlmem.h> + +struct mock_cxl_accel { + struct cxl_dev_state cxlds; + struct cxl_memdev *cxlmd; +}; + +static int cxl_mock_accel_probe(struct platform_device *pdev) +{ + struct mock_cxl_accel *cxl_accel; + struct device *dev = &pdev->dev; + struct cxl_dev_state *cxlds; + struct cxl_memdev *cxlmd; + struct range mock_range; + int rc; + + cxl_accel = devm_cxl_dev_state_create(&pdev->dev, CXL_DEVTYPE_DEVMEM, + pdev->id + 1, 0, + struct mock_cxl_accel, cxlds, + false); + if (!cxl_accel) + return -ENOMEM; + + cxlds = &cxl_accel->cxlds; + cxlds->media_ready = true; + rc = cxl_set_capacity(cxlds, SZ_512M); + if (rc) + return rc; + + cxlmd = devm_cxl_probe_mem(cxlds, &mock_range); + if (IS_ERR(cxlmd)) + return PTR_ERR(cxlmd); + cxl_accel->cxlmd = cxlmd; + + dev_dbg(dev, "Probed mock accelerator with range %pra\n", &mock_range); + + return 0; +} + +static const struct platform_device_id cxl_mock_accel_ids[] = { + { .name = "cxl_type2_accel" }, + { } +}; +MODULE_DEVICE_TABLE(platform, cxl_mock_accel_ids); + +static struct platform_driver cxl_mock_accel_driver = { + .probe = cxl_mock_accel_probe, + .id_table = cxl_mock_accel_ids, + .driver = { + .name = KBUILD_MODNAME, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +module_platform_driver(cxl_mock_accel_driver); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("cxl_test: accelerator device mock module"); +MODULE_IMPORT_NS("CXL"); diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 296516eecfd6..62bd92b3be45 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -17,6 +17,7 @@ static int interleave_arithmetic; static bool extended_linear_cache; static bool fail_autoassemble; +static bool type2_test; #define FAKE_QTG_ID 42 @@ -27,6 +28,7 @@ static bool fail_autoassemble; #define NR_CXL_SWITCH_PORTS 2 #define NR_CXL_PORT_DECODERS 8 #define NR_BRIDGES (NR_CXL_HOST_BRIDGES + NR_CXL_SINGLE_HOST + NR_CXL_RCH) +#define NR_CXL_TYPE2_ACCEL 1 #define MOCK_AUTO_REGION_SIZE_DEFAULT SZ_512M static int mock_auto_region_size = MOCK_AUTO_REGION_SIZE_DEFAULT; @@ -318,7 +320,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, .qtg_id = FAKE_QTG_ID, - .window_size = SZ_256M, + .window_size = SZ_256M > PMD_SIZE ? SZ_256M : PMD_SIZE, }, .target = { 3 }, }, @@ -384,6 +386,19 @@ static struct { }, }; +static struct acpi_cedt_cfmws type2_cfmws0 = { + .header = { + .type = ACPI_CEDT_TYPE_CFMWS, + .length = sizeof(mock_cedt.cfmws0), + }, + .interleave_ways = 0, + .granularity = 4, + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_DEVMEM | + ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, + .qtg_id = FAKE_QTG_ID, + .window_size = SZ_256M * 4, +}; + struct acpi_cedt_cfmws *mock_cfmws[] = { [0] = &mock_cedt.cfmws0.cfmws, [1] = &mock_cedt.cfmws1.cfmws, @@ -433,12 +448,16 @@ static void depopulate_all_mock_resources(void) static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) { - struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL); struct genpool_data_align data = { .align = align, }; unsigned long phys; + struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res), + GFP_KERNEL); + if (!res) + return NULL; + INIT_LIST_HEAD(&res->list); phys = gen_pool_alloc_algo(cxl_mock_pool, size, gen_pool_first_fit_align, &data); @@ -453,7 +472,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) list_add(&res->list, &mock_res); mutex_unlock(&mock_res_lock); - return res; + return no_free_ptr(res); } /* Only update CFMWS0 as this is used by the auto region. */ @@ -472,6 +491,11 @@ static void cfmws_elc_update(struct acpi_cedt_cfmws *window, int index) window->window_size = mock_auto_region_size * 2; } +static void update_type2_cfmws(void) +{ + memcpy(&mock_cedt.cfmws0.cfmws, &type2_cfmws0, sizeof(type2_cfmws0)); +} + static int populate_cedt(void) { struct cxl_mock_res *res; @@ -493,11 +517,18 @@ static int populate_cedt(void) chbs->length = size; } + if (type2_test) + update_type2_cfmws(); + for (i = cfmws_start; i <= cfmws_end; i++) { struct acpi_cedt_cfmws *window = mock_cfmws[i]; + int align = SZ_256M; - cfmws_elc_update(window, i); - res = alloc_mock_res(window->window_size, SZ_256M); + if (i == 0 && !type2_test) + cfmws_elc_update(window, i); + if (window->restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE) + align = max_t(int, SZ_256M, PMD_SIZE); + res = alloc_mock_res(window->window_size, align); if (!res) return -ENOMEM; window->base_hpa = res->range.start; @@ -1053,74 +1084,26 @@ static int first_decoder(struct device *dev, const void *data) return 0; } -/* - * Initialize a decoder during HDM enumeration. - * - * If a saved registry entry exists: - * - enabled decoders are restored from the saved programming - * - disabled decoders are initialized in a clean disabled state - * - * If no registry entry exists the decoder follows the normal mock - * initialization path, including the special auto-region setup for - * the first endpoints under host-bridge0. - * - * Returns true if decoder state was restored from the registry. In - * that case the saved decode configuration (including target mapping) - * has already been applied and the map_targets() is skipped. - */ -static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) -{ - struct acpi_cedt_cfmws *window = mock_cfmws[0]; - struct platform_device *pdev = NULL; - struct cxl_endpoint_decoder *cxled; - struct cxl_switch_decoder *cxlsd; - struct cxl_port *port, *iter; - struct cxl_test_decoder *td; - struct cxl_memdev *cxlmd; - struct cxl_dport *dport; - struct device *dev; - bool hb0 = false; - u64 base; - int i; +enum cxld_init_type { + MOCK_DECODER_INIT_DEFAULT, + MOCK_DECODER_INIT_SAVED, + MOCK_DECODER_INIT_TYPE3_AUTO, + MOCK_DECODER_INIT_TYPE2_AUTO, +}; - if (is_endpoint_decoder(&cxld->dev)) { - cxled = to_cxl_endpoint_decoder(&cxld->dev); - cxlmd = cxled_to_memdev(cxled); - WARN_ON(!dev_is_platform(cxlmd->dev.parent)); - pdev = to_platform_device(cxlmd->dev.parent); +static enum cxld_init_type get_decoder_init_type(struct cxl_decoder *cxld, + struct platform_device *pdev, + bool hb0, + struct cxl_test_decoder **td) +{ + struct cxl_test_decoder *found_td = cxld_registry_find(cxld); - /* check is endpoint is attach to host-bridge0 */ - port = cxled_to_port(cxled); - do { - if (port->uport_dev == &cxl_host_bridge[0]->dev) { - hb0 = true; - break; - } - if (is_cxl_port(port->dev.parent)) - port = to_cxl_port(port->dev.parent); - else - port = NULL; - } while (port); - port = cxled_to_port(cxled); - } else { - port = to_cxl_port(cxld->dev.parent); + if (found_td) { + *td = found_td; + return MOCK_DECODER_INIT_SAVED; } - td = cxld_registry_find(cxld); - if (td) { - bool enabled; - - if (is_switch_decoder(&cxld->dev)) - enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE; - else - enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE; - - if (enabled) - return !cxld_registry_restore(cxld, td); - - init_disabled_mock_decoder(cxld); - return false; - } + *td = NULL; /* * The first decoder on the first 2 devices on the first switch @@ -1131,15 +1114,119 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) * See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4' */ if (!is_endpoint_decoder(&cxld->dev) || !hb0 || pdev->id % 4 || - pdev->id > 4 || cxld->id > 0) { - default_mock_decoder(cxld); - return false; - } + pdev->id > 4 || cxld->id > 0) + return MOCK_DECODER_INIT_DEFAULT; + + return type2_test ? MOCK_DECODER_INIT_TYPE2_AUTO : + MOCK_DECODER_INIT_TYPE3_AUTO; +} + +static bool mock_decoder_handle_saved(struct cxl_decoder *cxld, struct cxl_test_decoder *td) +{ + bool enabled; + + if (is_switch_decoder(&cxld->dev)) + enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE; + else + enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE; + + if (enabled) + return !cxld_registry_restore(cxld, td); + + init_disabled_mock_decoder(cxld); + return false; +} + +static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled, + struct cxl_port *port) +{ + struct acpi_cedt_cfmws *window = mock_cfmws[0]; + struct cxl_decoder *cxld = &cxled->cxld; + struct cxl_switch_decoder *cxlsd; + struct cxl_dport *dport; + struct cxl_port *root_port; + struct device *dev; + u64 base; + + base = window->base_hpa; + cxld->hpa_range = (struct range) { + .start = base, + .end = base + mock_auto_region_size - 1, + }; + + cxld->interleave_ways = 1; + eig_to_granularity(window->granularity, &cxld->interleave_granularity); + cxld->target_type = CXL_DECODER_DEVMEM; + cxld->flags = CXL_DECODER_F_ENABLE; + cxled->state = CXL_DECODER_STATE_AUTO; + port->commit_end = cxld->id; + devm_cxl_dpa_reserve(cxled, 0, + mock_auto_region_size / cxld->interleave_ways, 0); + cxld->commit = mock_decoder_commit; + cxld->reset = mock_decoder_reset; + + WARN_ON_ONCE(!cxld_registry_new(cxld)); + /* + * Now that endpoint decoder is set up, walk up the hierarchy + * and setup the root port decoder targeting @cxlmd. + */ + dport = port->parent_dport; + root_port = dport->port; + dev = device_find_child(&root_port->dev, NULL, first_decoder); + /* + * Ancestor ports are guaranteed to be enumerated before + * @port, and all ports have at least one decoder. + */ + if (WARN_ON(!dev)) + return; + + cxlsd = to_cxl_switch_decoder(dev); + cxld = &cxlsd->cxld; + cxld->target_type = CXL_DECODER_DEVMEM; + cxld->flags = CXL_DECODER_F_ENABLE; + root_port->commit_end = 0; + cxld->interleave_ways = 1; + cxld->interleave_granularity = 4096; + cxld->target_map[0] = dport->port_id; + cxld->hpa_range = (struct range) { + .start = base, + .end = base + mock_auto_region_size - 1, + }; + cxld->commit = mock_decoder_commit; + cxld->reset = mock_decoder_reset; + + /* + * Only target_map[] is programmed above, mimicking + * firmware. On real hardware target[] is populated as + * dports enumerate, via update_decoder_targets(). The + * mock's dports are already bound by now, so fire that + * resolution explicitly here rather than stamping + * target[] directly. + */ + cxl_port_update_decoder_targets(root_port, dport); + + cxld_registry_update(cxld); + put_device(dev); +} + +static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled, + struct cxl_port *port, + struct platform_device *pdev, + bool hb0) +{ + struct acpi_cedt_cfmws *window = mock_cfmws[0]; + struct cxl_decoder *cxld = &cxled->cxld; + struct cxl_switch_decoder *cxlsd; + struct cxl_dport *dport; + struct cxl_port *iter; + struct device *dev; + u64 base; + int i; /* Simulate missing cxl_mem.4 configuration */ if (hb0 && pdev->id == 4 && cxld->id == 0 && fail_autoassemble) { default_mock_decoder(cxld); - return false; + return; } base = window->base_hpa; @@ -1181,15 +1268,11 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxlsd = to_cxl_switch_decoder(dev); if (i == 0) { /* put cxl_mem.4 second in the decode order */ - if (pdev->id == 4) { - cxlsd->target[1] = dport; + if (pdev->id == 4) cxlsd->cxld.target_map[1] = dport->port_id; - } else { - cxlsd->target[0] = dport; + else cxlsd->cxld.target_map[0] = dport->port_id; - } } else { - cxlsd->target[0] = dport; cxlsd->cxld.target_map[0] = dport->port_id; } cxld = &cxlsd->cxld; @@ -1212,10 +1295,89 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxld->commit = mock_decoder_commit; cxld->reset = mock_decoder_reset; + /* + * Only target_map[] is programmed above, mimicking + * firmware. On real hardware target[] is populated as + * dports enumerate, via update_decoder_targets(). The + * mock's dports are already bound by now, so fire that + * resolution explicitly here rather than stamping + * target[] directly. + */ + cxl_port_update_decoder_targets(iter, dport); + cxld_registry_update(cxld); put_device(dev); } +} +/* + * Initialize a decoder during HDM enumeration. + * + * If a saved registry entry exists: + * - enabled decoders are restored from the saved programming + * - disabled decoders are initialized in a clean disabled state + * + * If no registry entry exists the decoder follows the normal mock + * initialization path, including the special auto-region setup for + * the first endpoints under host-bridge0. + * + * Returns true if decoder state was restored from the registry. In + * that case the saved decode configuration (including target mapping) + * has already been applied and the map_targets() is skipped. + */ +static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) +{ + struct cxl_endpoint_decoder *cxled = NULL; + struct platform_device *pdev = NULL; + struct cxl_test_decoder *td; + struct cxl_memdev *cxlmd; + struct cxl_port *port; + bool hb0 = false; + + if (is_endpoint_decoder(&cxld->dev)) { + cxled = to_cxl_endpoint_decoder(&cxld->dev); + cxlmd = cxled_to_memdev(cxled); + WARN_ON(!dev_is_platform(cxlmd->dev.parent)); + pdev = to_platform_device(cxlmd->dev.parent); + + /* check is endpoint is attach to host-bridge0 */ + port = cxled_to_port(cxled); + do { + if (port->uport_dev == &cxl_host_bridge[0]->dev) { + hb0 = true; + break; + } + if (is_cxl_port(port->dev.parent)) + port = to_cxl_port(port->dev.parent); + else + port = NULL; + } while (port); + port = cxled_to_port(cxled); + } else { + port = to_cxl_port(cxld->dev.parent); + } + + switch (get_decoder_init_type(cxld, pdev, hb0, &td)) { + case MOCK_DECODER_INIT_SAVED: + if (WARN_ON(!td)) + return false; + return mock_decoder_handle_saved(cxld, td); + case MOCK_DECODER_INIT_DEFAULT: + /* + * The default path picks up all the decoders that are not + * endpoint. + */ + default_mock_decoder(cxld); + return false; + case MOCK_DECODER_INIT_TYPE3_AUTO: + mock_init_hdm_type3_cxled(cxled, port, pdev, hb0); + return false; + case MOCK_DECODER_INIT_TYPE2_AUTO: + mock_init_hdm_type2_cxled(cxled, port); + return false; + default: + return false; + } return false; } @@ -1550,8 +1712,10 @@ static __init int cxl_rch_topo_init(void) struct platform_device *pdev; pdev = platform_device_alloc("cxl_host_bridge", idx); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_bridge; + } mock_companion(adev, &pdev->dev); rc = cxl_mock_platform_device_add(pdev, &cxl_rch[i]); @@ -1605,8 +1769,10 @@ static __init int cxl_single_topo_init(void) pdev = platform_device_alloc("cxl_host_bridge", NR_CXL_HOST_BRIDGES + i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_bridge; + } mock_companion(adev, &pdev->dev); rc = cxl_mock_platform_device_add(pdev, &cxl_hb_single[i]); @@ -1627,8 +1793,10 @@ static __init int cxl_single_topo_init(void) pdev = platform_device_alloc("cxl_root_port", NR_MULTI_ROOT + i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_port; + } pdev->dev.parent = &bridge->dev; rc = cxl_mock_platform_device_add(pdev, &cxl_root_single[i]); @@ -1642,8 +1810,10 @@ static __init int cxl_single_topo_init(void) pdev = platform_device_alloc("cxl_switch_uport", NR_MULTI_ROOT + i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_uport; + } pdev->dev.parent = &root_port->dev; rc = cxl_mock_platform_device_add(pdev, &cxl_swu_single[i]); @@ -1658,8 +1828,10 @@ static __init int cxl_single_topo_init(void) pdev = platform_device_alloc("cxl_switch_dport", i + NR_MEM_MULTI); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_dport; + } pdev->dev.parent = &uport->dev; rc = cxl_mock_platform_device_add(pdev, &cxl_swd_single[i]); @@ -1711,19 +1883,84 @@ static void cxl_single_topo_exit(void) } } -static void cxl_mem_exit(void) +static void cxl_type3_mem_exit(void) { + struct platform_device *pdev; int i; - for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) + for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) { + pdev = cxl_rcd[i]; + if (!pdev) + continue; platform_device_unregister(cxl_rcd[i]); - for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) + } + + for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) { + pdev = cxl_mem_single[i]; + if (!pdev) + continue; platform_device_unregister(cxl_mem_single[i]); - for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) + } + + for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) { + pdev = cxl_mem[i]; + if (!pdev) + continue; + platform_device_unregister(pdev); + } +} + +static void cxl_type2_mem_exit(void) +{ + for (int i = NR_CXL_TYPE2_ACCEL - 1; i >= 0; i--) { + struct platform_device *pdev = cxl_mem[i]; + + if (!pdev) + continue; + platform_device_unregister(pdev); + } +} + +static void cxl_mem_exit(void) +{ + if (type2_test) { + cxl_type2_mem_exit(); + return; + } + + cxl_type3_mem_exit(); +} + +static int cxl_type2_mem_init(void) +{ + int i, rc; + + for (i = 0; i < NR_CXL_TYPE2_ACCEL; i++) { + struct platform_device *dport = cxl_root_port[i]; + struct platform_device *pdev; + + pdev = platform_device_alloc("cxl_type2_accel", i); + if (!pdev) { + rc = -ENOMEM; + goto err_mem; + } + pdev->dev.parent = &dport->dev; + set_dev_node(&pdev->dev, i % 2); + + rc = cxl_mock_platform_device_add(pdev, &cxl_mem[i]); + if (rc) + goto err_mem; + } + + return 0; + +err_mem: + for (i = NR_CXL_TYPE2_ACCEL - 1; i >= 0; i--) platform_device_unregister(cxl_mem[i]); + return rc; } -static int cxl_mem_init(void) +static int cxl_type3_mem_init(void) { int i, rc; @@ -1732,8 +1969,10 @@ static int cxl_mem_init(void) struct platform_device *pdev; pdev = platform_device_alloc("cxl_mem", i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_mem; + } pdev->dev.parent = &dport->dev; set_dev_node(&pdev->dev, i % 2); @@ -1747,8 +1986,10 @@ static int cxl_mem_init(void) struct platform_device *pdev; pdev = platform_device_alloc("cxl_mem", NR_MEM_MULTI + i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_single; + } pdev->dev.parent = &dport->dev; set_dev_node(&pdev->dev, i % 2); @@ -1763,8 +2004,10 @@ static int cxl_mem_init(void) struct platform_device *pdev; pdev = platform_device_alloc("cxl_rcd", idx); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_rcd; + } pdev->dev.parent = &rch->dev; set_dev_node(&pdev->dev, i % 2); @@ -1787,6 +2030,13 @@ err_mem: return rc; } +static int cxl_mem_init(void) +{ + if (type2_test) + return cxl_type2_mem_init(); + return cxl_type3_mem_init(); +} + static ssize_t decoder_reset_preserve_registry_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1814,52 +2064,50 @@ static struct attribute *cxl_acpi_attrs[] = { }; ATTRIBUTE_GROUPS(cxl_acpi); -static __init int cxl_test_init(void) +static bool __init have_multiple_modparms(void) { - int rc, i; - struct range mappable; + int count = 0; - cxl_acpi_test(); - cxl_core_test(); - cxl_mem_test(); - cxl_pmem_test(); - cxl_port_test(); + if (interleave_arithmetic) + count++; + if (extended_linear_cache) + count++; + if (hmem_test) + count++; + if (type2_test) + count++; - register_cxl_mock_ops(&cxl_mock_ops); + return count > 1; +} - cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE); - if (!cxl_mock_pool) { - rc = -ENOMEM; - goto err_gen_pool_create; - } - mappable = mhp_get_pluggable_range(true); +static void host_bridges_remove(void) +{ + int i; - rc = gen_pool_add(cxl_mock_pool, - min(iomem_resource.end + 1 - SZ_64G, - mappable.end + 1 - SZ_64G), - SZ_64G, NUMA_NO_NODE); - if (rc) - goto err_gen_pool_add; + for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) { + struct platform_device *pdev = cxl_host_bridge[i]; - if (interleave_arithmetic == 1) { - cfmws_start = CFMWS_XOR_ARRAY_START; - cfmws_end = CFMWS_XOR_ARRAY_END; - } else { - cfmws_start = CFMWS_MOD_ARRAY_START; - cfmws_end = CFMWS_MOD_ARRAY_END; + if (!pdev) + continue; + + sysfs_remove_link(&pdev->dev.kobj, "physical_node"); + platform_device_unregister(cxl_host_bridge[i]); } +} - rc = populate_cedt(); - if (rc) - goto err_populate; +static int host_bridges_populate(void) +{ + int rc = 0; - for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) { + for (int i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) { struct acpi_device *adev = &host_bridge[i]; struct platform_device *pdev; pdev = platform_device_alloc("cxl_host_bridge", i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_bridge; + } mock_companion(adev, &pdev->dev); rc = cxl_mock_platform_device_add(pdev, &cxl_host_bridge[i]); @@ -1873,14 +2121,40 @@ static __init int cxl_test_init(void) goto err_bridge; } - for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) { + return 0; + +err_bridge: + host_bridges_remove(); + return rc; +} + +static void cxl_rootports_remove(void) +{ + for (int i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--) { + struct platform_device *pdev = cxl_root_port[i]; + + if (!pdev) + continue; + + platform_device_unregister(pdev); + } +} + +static int cxl_rootports_populate(void) +{ + int rc = 0; + + for (int i = 0; i < ARRAY_SIZE(cxl_root_port); i++) { struct platform_device *bridge = cxl_host_bridge[i % ARRAY_SIZE(cxl_host_bridge)]; struct platform_device *pdev; pdev = platform_device_alloc("cxl_root_port", i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_port; + } + pdev->dev.parent = &bridge->dev; rc = cxl_mock_platform_device_add(pdev, &cxl_root_port[i]); @@ -1888,14 +2162,39 @@ static __init int cxl_test_init(void) goto err_port; } - BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port)); - for (i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) { + return 0; + +err_port: + cxl_rootports_remove(); + return rc; +} + +static void cxl_usps_remove(void) +{ + for (int i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--) { + struct platform_device *pdev = cxl_switch_uport[i]; + + if (!pdev) + continue; + + platform_device_unregister(cxl_switch_uport[i]); + } +} + +static int cxl_usps_populate(void) +{ + int rc = 0; + + for (int i = 0; i < ARRAY_SIZE(cxl_switch_uport); i++) { struct platform_device *root_port = cxl_root_port[i]; struct platform_device *pdev; pdev = platform_device_alloc("cxl_switch_uport", i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_uport; + } + pdev->dev.parent = &root_port->dev; rc = cxl_mock_platform_device_add(pdev, &cxl_switch_uport[i]); @@ -1903,14 +2202,40 @@ static __init int cxl_test_init(void) goto err_uport; } - for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) { + return 0; + +err_uport: + cxl_usps_remove(); + return rc; +} + +static void cxl_dsps_remove(void) +{ + for (int i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--) { + struct platform_device *pdev = cxl_switch_dport[i]; + + if (!pdev) + continue; + + platform_device_unregister(cxl_switch_dport[i]); + } +} + + +static int cxl_dsps_populate(void) +{ + int rc = 0; + + for (int i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) { struct platform_device *uport = cxl_switch_uport[i % ARRAY_SIZE(cxl_switch_uport)]; struct platform_device *pdev; pdev = platform_device_alloc("cxl_switch_dport", i); - if (!pdev) + if (!pdev) { + rc = -ENOMEM; goto err_dport; + } pdev->dev.parent = &uport->dev; rc = cxl_mock_platform_device_add(pdev, &cxl_switch_dport[i]); @@ -1918,17 +2243,181 @@ static __init int cxl_test_init(void) goto err_dport; } + return 0; + +err_dport: + cxl_dsps_remove(); + return rc; +} + +static void cxl_switches_remove(void) +{ + cxl_dsps_remove(); + cxl_usps_remove(); +} + +static int cxl_switches_populate(void) +{ + int rc; + + BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port)); + rc = cxl_usps_populate(); + if (rc) + return rc; + + rc = cxl_dsps_populate(); + if (rc) { + cxl_usps_remove(); + return rc; + } + + return 0; +} + +static void cxl_type2_topo_exit(void) +{ + cxl_rootports_remove(); + host_bridges_remove(); +} + +static int cxl_type2_topo_init(void) +{ + int rc; + + rc = host_bridges_populate(); + if (rc) + return rc; + + rc = cxl_rootports_populate(); + if (rc) { + host_bridges_remove(); + return rc; + } + + return 0; +} + +static void cxl_type3_topo_exit(void) +{ + cxl_rch_topo_exit(); + cxl_single_topo_exit(); + cxl_switches_remove(); + cxl_rootports_remove(); + host_bridges_remove(); +} + +static int cxl_type3_topo_init(void) +{ + int rc; + + rc = host_bridges_populate(); + if (rc) + return rc; + + rc = cxl_rootports_populate(); + if (rc) + goto err_host_bridges; + + rc = cxl_switches_populate(); + if (rc) + goto err_root_ports; + rc = cxl_single_topo_init(); if (rc) - goto err_dport; + goto err_switches; rc = cxl_rch_topo_init(); if (rc) goto err_single; + return 0; + +err_single: + cxl_single_topo_exit(); +err_switches: + cxl_switches_remove(); +err_root_ports: + cxl_rootports_remove(); +err_host_bridges: + host_bridges_remove(); + return rc; +} + +static void cxl_topo_exit(void) +{ + if (type2_test) { + cxl_type2_topo_exit(); + return; + } + + cxl_type3_topo_exit(); +} + +static int cxl_topo_init(void) +{ + if (type2_test) + return cxl_type2_topo_init(); + return cxl_type3_topo_init(); +} + +static __init int cxl_test_init(void) +{ + struct range mappable; + int rc; + + /* Enforce a single module param active at a time */ + if (have_multiple_modparms()) + return -EINVAL; + + if (!IS_ALIGNED(mock_auto_region_size, PMD_SIZE)) { + pr_err_once("mock_auto_region_size %d must be PMD-aligned\n", + mock_auto_region_size); + return -EINVAL; + } + + cxl_acpi_test(); + cxl_core_test(); + cxl_mem_test(); + cxl_pmem_test(); + cxl_port_test(); + + register_cxl_mock_ops(&cxl_mock_ops); + + cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE); + if (!cxl_mock_pool) { + rc = -ENOMEM; + goto err_gen_pool_create; + } + mappable = mhp_get_pluggable_range(true); + + rc = gen_pool_add(cxl_mock_pool, + min(iomem_resource.end + 1 - SZ_64G, + mappable.end + 1 - SZ_64G), + SZ_64G, NUMA_NO_NODE); + if (rc) + goto err_gen_pool_add; + + if (interleave_arithmetic == 1) { + cfmws_start = CFMWS_XOR_ARRAY_START; + cfmws_end = CFMWS_XOR_ARRAY_END; + } else { + cfmws_start = CFMWS_MOD_ARRAY_START; + cfmws_end = CFMWS_MOD_ARRAY_END; + } + + rc = populate_cedt(); + if (rc) + goto err_populate; + + rc = cxl_topo_init(); + if (rc) + goto err_populate; + cxl_acpi = platform_device_alloc("cxl_acpi", 0); - if (!cxl_acpi) - goto err_rch; + if (!cxl_acpi) { + rc = -ENOMEM; + goto err_topo; + } mock_companion(&acpi0017_mock, &cxl_acpi->dev); acpi0017_mock.dev.bus = &platform_bus_type; @@ -1936,7 +2425,7 @@ static __init int cxl_test_init(void) rc = cxl_mock_platform_device_add(cxl_acpi, NULL); if (rc) - goto err_rch; + goto err_topo; rc = cxl_mem_init(); if (rc) @@ -1951,29 +2440,9 @@ static __init int cxl_test_init(void) err_mem: cxl_mem_exit(); err_root: - platform_device_put(cxl_acpi); -err_rch: - cxl_rch_topo_exit(); -err_single: - cxl_single_topo_exit(); -err_dport: - for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--) - platform_device_unregister(cxl_switch_dport[i]); -err_uport: - for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--) - platform_device_unregister(cxl_switch_uport[i]); -err_port: - for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--) - platform_device_unregister(cxl_root_port[i]); -err_bridge: - for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) { - struct platform_device *pdev = cxl_host_bridge[i]; - - if (!pdev) - continue; - sysfs_remove_link(&pdev->dev.kobj, "physical_node"); - platform_device_unregister(cxl_host_bridge[i]); - } + platform_device_unregister(cxl_acpi); +err_topo: + cxl_topo_exit(); err_populate: depopulate_all_mock_resources(); err_gen_pool_add: @@ -1996,27 +2465,10 @@ static void free_decoder_registry(void) static __exit void cxl_test_exit(void) { - int i; - hmem_test_exit(); cxl_mem_exit(); platform_device_unregister(cxl_acpi); - cxl_rch_topo_exit(); - cxl_single_topo_exit(); - for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--) - platform_device_unregister(cxl_switch_dport[i]); - for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--) - platform_device_unregister(cxl_switch_uport[i]); - for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--) - platform_device_unregister(cxl_root_port[i]); - for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) { - struct platform_device *pdev = cxl_host_bridge[i]; - - if (!pdev) - continue; - sysfs_remove_link(&pdev->dev.kobj, "physical_node"); - platform_device_unregister(cxl_host_bridge[i]); - } + cxl_topo_exit(); depopulate_all_mock_resources(); gen_pool_destroy(cxl_mock_pool); unregister_cxl_mock_ops(&cxl_mock_ops); @@ -2030,6 +2482,8 @@ module_param(extended_linear_cache, bool, 0444); MODULE_PARM_DESC(extended_linear_cache, "Enable extended linear cache support"); module_param(fail_autoassemble, bool, 0444); MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region"); +module_param(type2_test, bool, 0444); +MODULE_PARM_DESC(type2_test, "Enable type 2 support testing"); module_init(cxl_test_init); module_exit(cxl_test_exit); MODULE_LICENSE("GPL v2"); diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c index 16328b2112b2..25a27e01ac21 100644 --- a/tools/testing/cxl/test/cxl_translate.c +++ b/tools/testing/cxl/test/cxl_translate.c @@ -236,8 +236,8 @@ static int setup_xor_mapping(void) if (!cximsd) return -ENOMEM; - memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); cximsd->nr_maps = nr_maps; + memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); return 0; } diff --git a/tools/testing/cxl/test/hmem_test.c b/tools/testing/cxl/test/hmem_test.c index 3a1a089e1721..0fa00f7e16db 100644 --- a/tools/testing/cxl/test/hmem_test.c +++ b/tools/testing/cxl/test/hmem_test.c @@ -3,8 +3,9 @@ #include <linux/moduleparam.h> #include <linux/workqueue.h> #include "../../../drivers/dax/bus.h" +#include "mock.h" -static bool hmem_test; +bool hmem_test; static void hmem_test_work(struct work_struct *work) { diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 271c7ad8cc32..7b756000a1a6 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -2,7 +2,6 @@ // Copyright(c) 2021 Intel Corporation. All rights reserved. #include <linux/platform_device.h> -#include <linux/mod_devicetable.h> #include <linux/vmalloc.h> #include <linux/module.h> #include <linux/delay.h> @@ -312,12 +311,17 @@ static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd) static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_clear_event_payload *pl = cmd->payload_in; + struct cxl_mbox_clear_event_payload *pl; struct mock_event_log *log; - u8 log_type = pl->event_log; + u8 log_type; u16 handle; int nr; + if (cmd->size_in < sizeof(*pl)) + return -EINVAL; + + pl = cmd->payload_in; + log_type = pl->event_log; if (log_type >= CXL_EVENT_TYPE_MAX) return -EINVAL; @@ -574,14 +578,19 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd) static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd) { struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; - struct cxl_mbox_get_log *gl = cmd->payload_in; - u32 offset = le32_to_cpu(gl->offset); - u32 length = le32_to_cpu(gl->length); uuid_t uuid = DEFINE_CXL_CEL_UUID; + struct cxl_mbox_get_log *gl; void *data = &mock_cel; + u32 offset; + u32 length; if (cmd->size_in < sizeof(*gl)) return -EINVAL; + + gl = cmd->payload_in; + offset = le32_to_cpu(gl->offset); + length = le32_to_cpu(gl->length); + if (length > cxl_mbox->payload_size) return -EINVAL; if (offset + length > sizeof(mock_cel)) @@ -1053,7 +1062,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(get_lsa->offset); length = le32_to_cpu(get_lsa->length); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; if (length > cmd->size_out) return -EINVAL; @@ -1073,7 +1082,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(set_lsa->offset); length = cmd->size_in - sizeof(*set_lsa); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; memcpy(lsa + offset, &set_lsa->data[0], length); @@ -1336,10 +1345,14 @@ static int mock_fw_info(struct cxl_mockmem_data *mdata, static int mock_transfer_fw(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_transfer_fw *transfer = cmd->payload_in; + struct cxl_mbox_transfer_fw *transfer; void *fw = mdata->fw; size_t offset, length; + if (cmd->size_in < sizeof(*transfer)) + return -EINVAL; + + transfer = cmd->payload_in; offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT; length = cmd->size_in - sizeof(*transfer); if (offset + length > FW_SIZE) @@ -1415,11 +1428,18 @@ static int mock_get_test_feature(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { struct vendor_test_feat *output = cmd->payload_out; - struct cxl_mbox_get_feat_in *input = cmd->payload_in; - u16 offset = le16_to_cpu(input->offset); - u16 count = le16_to_cpu(input->count); + struct cxl_mbox_get_feat_in *input; + u16 offset; + u16 count; u8 *ptr; + if (cmd->size_in < sizeof(*input)) + return -EINVAL; + + input = cmd->payload_in; + offset = le16_to_cpu(input->offset); + count = le16_to_cpu(input->count); + if (offset > sizeof(*output)) { cmd->return_code = CXL_MBOX_CMD_RC_INPUT; return -EINVAL; @@ -1693,6 +1713,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) struct cxl_mockmem_data *mdata; struct cxl_mailbox *cxl_mbox; struct cxl_dpa_info range_info = { 0 }; + u64 serial; int rc; /* Increase async probe race window */ @@ -1703,7 +1724,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, mdata); - mdata->lsa = vmalloc(LSA_SIZE); + mdata->lsa = vzalloc(LSA_SIZE); if (!mdata->lsa) return -ENOMEM; mdata->fw = vmalloc(FW_SIZE); @@ -1719,7 +1740,19 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) if (rc) return rc; - mds = cxl_memdev_state_create(dev, pdev->id + 1, 0); + /* + * Mock serials have historically been derived from pdev->id and stayed + * single-digit, so they never exercised either decimal-vs-hex key + * lookup or unsigned formatting. Give one mock device a full-width + * serial with bit 63 set, matching real hardware such as Montage CXL + * devices. pdev->id 7 is unused by the auto-region topology. + */ + if (pdev->id == 7) + serial = 0x8a34567890abcdef; + else + serial = pdev->id + 1; + + mds = cxl_memdev_state_create(dev, serial, 0); if (IS_ERR(mds)) return PTR_ERR(mds); @@ -1769,7 +1802,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) cxl_mock_add_event_logs(&mdata->mes); - cxlmd = devm_cxl_add_memdev(cxlds, NULL); + cxlmd = devm_cxl_add_classdev(cxlds); if (IS_ERR(cxlmd)) return PTR_ERR(cxlmd); diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h index 4f57dc80ae7d..846d7c5d6eaa 100644 --- a/tools/testing/cxl/test/mock.h +++ b/tools/testing/cxl/test/mock.h @@ -5,6 +5,8 @@ #include <linux/dax.h> #include <cxl.h> +extern bool hmem_test; + struct cxl_mock_ops { struct list_head list; bool (*is_mock_adev)(struct acpi_device *dev); |
