| // SPDX-License-Identifier: GPL-2.0-only | |
| //Copyright(c) 2021 Intel Corporation. All rights reserved. | |
| #include <linux/libnvdimm.h> | |
| #include <linux/rculist.h> | |
| #include <linux/device.h> | |
| #include <linux/export.h> | |
| #include <linux/acpi.h> | |
| #include <linux/pci.h> | |
| #include <cxlmem.h> | |
| #include <cxlpci.h> | |
| #include "mock.h" | |
| static LIST_HEAD(mock); | |
| void register_cxl_mock_ops(struct cxl_mock_ops *ops) | |
| { | |
| list_add_rcu(&ops->list, &mock); | |
| } | |
| EXPORT_SYMBOL_GPL(register_cxl_mock_ops); | |
| DEFINE_STATIC_SRCU(cxl_mock_srcu); | |
| void unregister_cxl_mock_ops(struct cxl_mock_ops *ops) | |
| { | |
| list_del_rcu(&ops->list); | |
| synchronize_srcu(&cxl_mock_srcu); | |
| } | |
| EXPORT_SYMBOL_GPL(unregister_cxl_mock_ops); | |
| struct cxl_mock_ops *get_cxl_mock_ops(int *index) | |
| { | |
| *index = srcu_read_lock(&cxl_mock_srcu); | |
| return list_first_or_null_rcu(&mock, struct cxl_mock_ops, list); | |
| } | |
| EXPORT_SYMBOL_GPL(get_cxl_mock_ops); | |
| void put_cxl_mock_ops(int index) | |
| { | |
| srcu_read_unlock(&cxl_mock_srcu, index); | |
| } | |
| EXPORT_SYMBOL_GPL(put_cxl_mock_ops); | |
| bool __wrap_is_acpi_device_node(const struct fwnode_handle *fwnode) | |
| { | |
| struct acpi_device *adev = | |
| container_of(fwnode, struct acpi_device, fwnode); | |
| int index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| bool retval = false; | |
| if (ops) | |
| retval = ops->is_mock_adev(adev); | |
| if (!retval) | |
| retval = is_acpi_device_node(fwnode); | |
| put_cxl_mock_ops(index); | |
| return retval; | |
| } | |
| EXPORT_SYMBOL(__wrap_is_acpi_device_node); | |
| int __wrap_acpi_table_parse_cedt(enum acpi_cedt_type id, | |
| acpi_tbl_entry_handler_arg handler_arg, | |
| void *arg) | |
| { | |
| int index, rc; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops) | |
| rc = ops->acpi_table_parse_cedt(id, handler_arg, arg); | |
| else | |
| rc = acpi_table_parse_cedt(id, handler_arg, arg); | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_NS_GPL(__wrap_acpi_table_parse_cedt, "ACPI"); | |
| acpi_status __wrap_acpi_evaluate_integer(acpi_handle handle, | |
| acpi_string pathname, | |
| struct acpi_object_list *arguments, | |
| unsigned long long *data) | |
| { | |
| int index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| acpi_status status; | |
| if (ops) | |
| status = ops->acpi_evaluate_integer(handle, pathname, arguments, | |
| data); | |
| else | |
| status = acpi_evaluate_integer(handle, pathname, arguments, | |
| data); | |
| put_cxl_mock_ops(index); | |
| return status; | |
| } | |
| EXPORT_SYMBOL(__wrap_acpi_evaluate_integer); | |
| int __wrap_hmat_get_extended_linear_cache_size(struct resource *backing_res, | |
| int nid, | |
| resource_size_t *cache_size) | |
| { | |
| int index, rc; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops) | |
| rc = ops->hmat_get_extended_linear_cache_size(backing_res, nid, | |
| cache_size); | |
| else | |
| rc = hmat_get_extended_linear_cache_size(backing_res, nid, | |
| cache_size); | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_GPL(__wrap_hmat_get_extended_linear_cache_size); | |
| struct acpi_pci_root *__wrap_acpi_pci_find_root(acpi_handle handle) | |
| { | |
| int index; | |
| struct acpi_pci_root *root; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops) | |
| root = ops->acpi_pci_find_root(handle); | |
| else | |
| root = acpi_pci_find_root(handle); | |
| put_cxl_mock_ops(index); | |
| return root; | |
| } | |
| EXPORT_SYMBOL_GPL(__wrap_acpi_pci_find_root); | |
| struct nvdimm_bus * | |
| __wrap_nvdimm_bus_register(struct device *dev, | |
| struct nvdimm_bus_descriptor *nd_desc) | |
| { | |
| int index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops && ops->is_mock_dev(dev->parent->parent)) | |
| nd_desc->provider_name = "cxl_test"; | |
| put_cxl_mock_ops(index); | |
| return nvdimm_bus_register(dev, nd_desc); | |
| } | |
| EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register); | |
| int __wrap_devm_cxl_switch_port_decoders_setup(struct cxl_port *port) | |
| { | |
| int rc, index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops && ops->is_mock_port(port->uport_dev)) | |
| rc = ops->devm_cxl_switch_port_decoders_setup(port); | |
| else | |
| rc = devm_cxl_switch_port_decoders_setup(port); | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_switch_port_decoders_setup, "CXL"); | |
| int __wrap_devm_cxl_endpoint_decoders_setup(struct cxl_port *port) | |
| { | |
| int rc, index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops && ops->is_mock_port(port->uport_dev)) | |
| rc = ops->devm_cxl_endpoint_decoders_setup(port); | |
| else | |
| rc = devm_cxl_endpoint_decoders_setup(port); | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_endpoint_decoders_setup, "CXL"); | |
| int __wrap_cxl_await_media_ready(struct cxl_dev_state *cxlds) | |
| { | |
| int rc, index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops && ops->is_mock_dev(cxlds->dev)) | |
| rc = 0; | |
| else | |
| rc = cxl_await_media_ready(cxlds); | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_NS_GPL(__wrap_cxl_await_media_ready, "CXL"); | |
| struct cxl_dport *__wrap_devm_cxl_add_rch_dport(struct cxl_port *port, | |
| struct device *dport_dev, | |
| int port_id, | |
| resource_size_t rcrb) | |
| { | |
| int index; | |
| struct cxl_dport *dport; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops && ops->is_mock_port(dport_dev)) { | |
| dport = devm_cxl_add_dport(port, dport_dev, port_id, | |
| CXL_RESOURCE_NONE); | |
| if (!IS_ERR(dport)) { | |
| dport->rcrb.base = rcrb; | |
| dport->rch = true; | |
| } | |
| } else | |
| dport = devm_cxl_add_rch_dport(port, dport_dev, port_id, rcrb); | |
| put_cxl_mock_ops(index); | |
| return dport; | |
| } | |
| EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_rch_dport, "CXL"); | |
| void __wrap_cxl_endpoint_parse_cdat(struct cxl_port *port) | |
| { | |
| int index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev); | |
| if (ops && ops->is_mock_dev(cxlmd->dev.parent)) | |
| ops->cxl_endpoint_parse_cdat(port); | |
| else | |
| cxl_endpoint_parse_cdat(port); | |
| put_cxl_mock_ops(index); | |
| } | |
| EXPORT_SYMBOL_NS_GPL(__wrap_cxl_endpoint_parse_cdat, "CXL"); | |
| struct cxl_dport *__wrap_devm_cxl_add_dport_by_dev(struct cxl_port *port, | |
| struct device *dport_dev) | |
| { | |
| int index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| struct cxl_dport *dport; | |
| if (ops && ops->is_mock_port(port->uport_dev)) | |
| dport = ops->devm_cxl_add_dport_by_dev(port, dport_dev); | |
| else | |
| dport = devm_cxl_add_dport_by_dev(port, dport_dev); | |
| put_cxl_mock_ops(index); | |
| return dport; | |
| } | |
| EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_dport_by_dev, "CXL"); | |
| int __wrap_region_intersects(resource_size_t start, size_t size, | |
| unsigned long flags, unsigned long desc) | |
| { | |
| int rc = -1; | |
| int index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops) | |
| rc = ops->region_intersects(start, size, flags, desc); | |
| if (rc < 0) | |
| rc = region_intersects(start, size, flags, desc); | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_GPL(__wrap_region_intersects); | |
| int __wrap_region_intersects_soft_reserve(resource_size_t start, size_t size) | |
| { | |
| int rc = -1; | |
| int index; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (ops) | |
| rc = ops->region_intersects_soft_reserve(start, size); | |
| if (rc < 0) | |
| rc = region_intersects_soft_reserve(start, size); | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_GPL(__wrap_region_intersects_soft_reserve); | |
| int __wrap_walk_hmem_resources(struct device *host, walk_hmem_fn fn) | |
| { | |
| int index, rc = 0; | |
| bool is_mock = strcmp(dev_name(host), "hmem_platform.1") == 0; | |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); | |
| if (is_mock) { | |
| if (ops) | |
| rc = ops->walk_hmem_resources(host, fn); | |
| } else { | |
| rc = walk_hmem_resources(host, fn); | |
| } | |
| put_cxl_mock_ops(index); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_GPL(__wrap_walk_hmem_resources); | |
| MODULE_LICENSE("GPL v2"); | |
| MODULE_DESCRIPTION("cxl_test: emulation module"); | |
| MODULE_IMPORT_NS("ACPI"); | |
| MODULE_IMPORT_NS("CXL"); |