// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2024-2025, Ventana Micro Systems Inc * Author: Sunil V L * */ #define pr_fmt(fmt) "ACPI: RIMT: " fmt #include #include #include #include #include #include #include "init.h" struct rimt_fwnode { struct list_head list; struct acpi_rimt_node *rimt_node; struct fwnode_handle *fwnode; }; static LIST_HEAD(rimt_fwnode_list); static DEFINE_SPINLOCK(rimt_fwnode_lock); #define RIMT_TYPE_MASK(type) (1 << (type)) #define RIMT_IOMMU_TYPE BIT(0) /* Root pointer to the mapped RIMT table */ static struct acpi_table_header *rimt_table; /** * rimt_set_fwnode() - Create rimt_fwnode and use it to register * iommu data in the rimt_fwnode_list * * @rimt_node: RIMT table node associated with the IOMMU * @fwnode: fwnode associated with the RIMT node * * Returns: 0 on success * <0 on failure */ static int rimt_set_fwnode(struct acpi_rimt_node *rimt_node, struct fwnode_handle *fwnode) { struct rimt_fwnode *np; np = kzalloc(sizeof(*np), GFP_ATOMIC); if (WARN_ON(!np)) return -ENOMEM; INIT_LIST_HEAD(&np->list); np->rimt_node = rimt_node; np->fwnode = fwnode; spin_lock(&rimt_fwnode_lock); list_add_tail(&np->list, &rimt_fwnode_list); spin_unlock(&rimt_fwnode_lock); return 0; } /** * rimt_get_fwnode() - Retrieve fwnode associated with an RIMT node * * @node: RIMT table node to be looked-up * * Returns: fwnode_handle pointer on success, NULL on failure */ static struct fwnode_handle *rimt_get_fwnode(struct acpi_rimt_node *node) { struct fwnode_handle *fwnode = NULL; struct rimt_fwnode *curr; spin_lock(&rimt_fwnode_lock); list_for_each_entry(curr, &rimt_fwnode_list, list) { if (curr->rimt_node == node) { fwnode = curr->fwnode; break; } } spin_unlock(&rimt_fwnode_lock); return fwnode; } static acpi_status rimt_match_node_callback(struct acpi_rimt_node *node, void *context) { acpi_status status = AE_NOT_FOUND; struct device *dev = context; if (node->type == ACPI_RIMT_NODE_TYPE_IOMMU) { struct acpi_rimt_iommu *iommu_node = (struct acpi_rimt_iommu *)&node->node_data; if (dev_is_pci(dev)) { struct pci_dev *pdev; u16 bdf; pdev = to_pci_dev(dev); bdf = PCI_DEVID(pdev->bus->number, pdev->devfn); if ((pci_domain_nr(pdev->bus) == iommu_node->pcie_segment_number) && bdf == iommu_node->pcie_bdf) { status = AE_OK; } else { status = AE_NOT_FOUND; } } else { struct platform_device *pdev = to_platform_device(dev); struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res && res->start == iommu_node->base_address) status = AE_OK; else status = AE_NOT_FOUND; } } else if (node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) { struct acpi_rimt_pcie_rc *pci_rc; struct pci_bus *bus; bus = to_pci_bus(dev); pci_rc = (struct acpi_rimt_pcie_rc *)node->node_data; /* * It is assumed that PCI segment numbers maps one-to-one * with root complexes. Each segment number can represent only * one root complex. */ status = pci_rc->pcie_segment_number == pci_domain_nr(bus) ? AE_OK : AE_NOT_FOUND; } else if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE) { struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_rimt_platform_device *ncomp; struct device *plat_dev = dev; struct acpi_device *adev; /* * Walk the device tree to find a device with an * ACPI companion; there is no point in scanning * RIMT for a device matching a platform device if * the device does not have an ACPI companion to * start with. */ do { adev = ACPI_COMPANION(plat_dev); if (adev) break; plat_dev = plat_dev->parent; } while (plat_dev); if (!adev) return status; status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); if (ACPI_FAILURE(status)) { dev_warn(plat_dev, "Can't get device full path name\n"); return status; } ncomp = (struct acpi_rimt_platform_device *)node->node_data; status = !strcmp(ncomp->device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND; acpi_os_free(buf.pointer); } return status; } static struct acpi_rimt_node *rimt_scan_node(enum acpi_rimt_node_type type, void *context) { struct acpi_rimt_node *rimt_node, *rimt_end; struct acpi_table_rimt *rimt; int i; if (!rimt_table) return NULL; /* Get the first RIMT node */ rimt = (struct acpi_table_rimt *)rimt_table; rimt_node = ACPI_ADD_PTR(struct acpi_rimt_node, rimt, rimt->node_offset); rimt_end = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_table, rimt_table->length); for (i = 0; i < rimt->num_nodes; i++) { if (WARN_TAINT(rimt_node >= rimt_end, TAINT_FIRMWARE_WORKAROUND, "RIMT node pointer overflows, bad table!\n")) return NULL; if (rimt_node->type == type && ACPI_SUCCESS(rimt_match_node_callback(rimt_node, context))) return rimt_node; rimt_node = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_node, rimt_node->length); } return NULL; } static bool rimt_pcie_rc_supports_ats(struct acpi_rimt_node *node) { struct acpi_rimt_pcie_rc *pci_rc; pci_rc = (struct acpi_rimt_pcie_rc *)node->node_data; return pci_rc->flags & ACPI_RIMT_PCIE_ATS_SUPPORTED; } static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32 deviceid) { struct fwnode_handle *rimt_fwnode; if (!node) return -ENODEV; rimt_fwnode = rimt_get_fwnode(node); /* * The IOMMU drivers may not be probed yet. * Defer the IOMMU configuration */ if (!rimt_fwnode) return -EPROBE_DEFER; return acpi_iommu_fwspec_init(dev, deviceid, rimt_fwnode); } struct rimt_pci_alias_info { struct device *dev; struct acpi_rimt_node *node; const struct iommu_ops *ops; }; static int rimt_id_map(struct acpi_rimt_id_mapping *map, u8 type, u32 rid_in, u32 *rid_out) { if (rid_in < map->source_id_base || (rid_in > map->source_id_base + map->num_ids)) return -ENXIO; *rid_out = map->dest_id_base + (rid_in - map->source_id_base); return 0; } static struct acpi_rimt_node *rimt_node_get_id(struct acpi_rimt_node *node, u32 *id_out, int index) { struct acpi_rimt_platform_device *plat_node; u32 id_mapping_offset, num_id_mapping; struct acpi_rimt_pcie_rc *pci_node; struct acpi_rimt_id_mapping *map; struct acpi_rimt_node *parent; if (node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) { pci_node = (struct acpi_rimt_pcie_rc *)&node->node_data; id_mapping_offset = pci_node->id_mapping_offset; num_id_mapping = pci_node->num_id_mappings; } else if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE) { plat_node = (struct acpi_rimt_platform_device *)&node->node_data; id_mapping_offset = plat_node->id_mapping_offset; num_id_mapping = plat_node->num_id_mappings; } else { return NULL; } if (!id_mapping_offset || !num_id_mapping || index >= num_id_mapping) return NULL; map = ACPI_ADD_PTR(struct acpi_rimt_id_mapping, node, id_mapping_offset + index * sizeof(*map)); /* Firmware bug! */ if (!map->dest_offset) { pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", node, node->type); return NULL; } parent = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_table, map->dest_offset); if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE || node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) { *id_out = map->dest_id_base; return parent; } return NULL; } /* * RISC-V supports IOMMU as a PCI device or a platform device. * When it is a platform device, there should be a namespace device as * well along with RIMT. To create the link between RIMT information and * the platform device, the IOMMU driver should register itself with the * RIMT module. This is true for PCI based IOMMU as well. */ int rimt_iommu_register(struct device *dev) { struct fwnode_handle *rimt_fwnode; struct acpi_rimt_node *node; node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_IOMMU, dev); if (!node) { pr_err("Could not find IOMMU node in RIMT\n"); return -ENODEV; } if (dev_is_pci(dev)) { rimt_fwnode = acpi_alloc_fwnode_static(); if (!rimt_fwnode) return -ENOMEM; rimt_fwnode->dev = dev; if (!dev->fwnode) dev->fwnode = rimt_fwnode; rimt_set_fwnode(node, rimt_fwnode); } else { rimt_set_fwnode(node, dev->fwnode); } return 0; } #ifdef CONFIG_IOMMU_API static struct acpi_rimt_node *rimt_node_map_id(struct acpi_rimt_node *node, u32 id_in, u32 *id_out, u8 type_mask) { struct acpi_rimt_platform_device *plat_node; u32 id_mapping_offset, num_id_mapping; struct acpi_rimt_pcie_rc *pci_node; u32 id = id_in; /* Parse the ID mapping tree to find specified node type */ while (node) { struct acpi_rimt_id_mapping *map; int i, rc = 0; u32 map_id = id; if (RIMT_TYPE_MASK(node->type) & type_mask) { if (id_out) *id_out = id; return node; } if (node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) { pci_node = (struct acpi_rimt_pcie_rc *)&node->node_data; id_mapping_offset = pci_node->id_mapping_offset; num_id_mapping = pci_node->num_id_mappings; } else if (node->type == ACPI_RIMT_NODE_TYPE_PLAT_DEVICE) { plat_node = (struct acpi_rimt_platform_device *)&node->node_data; id_mapping_offset = plat_node->id_mapping_offset; num_id_mapping = plat_node->num_id_mappings; } else { goto fail_map; } if (!id_mapping_offset || !num_id_mapping) goto fail_map; map = ACPI_ADD_PTR(struct acpi_rimt_id_mapping, node, id_mapping_offset); /* Firmware bug! */ if (!map->dest_offset) { pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n", node, node->type); goto fail_map; } /* Do the ID translation */ for (i = 0; i < num_id_mapping; i++, map++) { rc = rimt_id_map(map, node->type, map_id, &id); if (!rc) break; } if (i == num_id_mapping) goto fail_map; node = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_table, rc ? 0 : map->dest_offset); } fail_map: /* Map input ID to output ID unchanged on mapping failure */ if (id_out) *id_out = id_in; return NULL; } static struct acpi_rimt_node *rimt_node_map_platform_id(struct acpi_rimt_node *node, u32 *id_out, u8 type_mask, int index) { struct acpi_rimt_node *parent; u32 id; parent = rimt_node_get_id(node, &id, index); if (!parent) return NULL; if (!(RIMT_TYPE_MASK(parent->type) & type_mask)) parent = rimt_node_map_id(parent, id, id_out, type_mask); else if (id_out) *id_out = id; return parent; } static int rimt_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) { struct rimt_pci_alias_info *info = data; struct acpi_rimt_node *parent; u32 deviceid; parent = rimt_node_map_id(info->node, alias, &deviceid, RIMT_IOMMU_TYPE); return rimt_iommu_xlate(info->dev, parent, deviceid); } static int rimt_plat_iommu_map(struct device *dev, struct acpi_rimt_node *node) { struct acpi_rimt_node *parent; int err = -ENODEV, i = 0; u32 deviceid = 0; do { parent = rimt_node_map_platform_id(node, &deviceid, RIMT_IOMMU_TYPE, i++); if (parent) err = rimt_iommu_xlate(dev, parent, deviceid); } while (parent && !err); return err; } static int rimt_plat_iommu_map_id(struct device *dev, struct acpi_rimt_node *node, const u32 *in_id) { struct acpi_rimt_node *parent; u32 deviceid; parent = rimt_node_map_id(node, *in_id, &deviceid, RIMT_IOMMU_TYPE); if (parent) return rimt_iommu_xlate(dev, parent, deviceid); return -ENODEV; } /** * rimt_iommu_configure_id - Set-up IOMMU configuration for a device. * * @dev: device to configure * @id_in: optional input id const value pointer * * Returns: 0 on success, <0 on failure */ int rimt_iommu_configure_id(struct device *dev, const u32 *id_in) { struct acpi_rimt_node *node; int err = -ENODEV; if (dev_is_pci(dev)) { struct iommu_fwspec *fwspec; struct pci_bus *bus = to_pci_dev(dev)->bus; struct rimt_pci_alias_info info = { .dev = dev }; node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX, &bus->dev); if (!node) return -ENODEV; info.node = node; err = pci_for_each_dma_alias(to_pci_dev(dev), rimt_pci_iommu_init, &info); fwspec = dev_iommu_fwspec_get(dev); if (fwspec && rimt_pcie_rc_supports_ats(node)) fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS; } else { node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_PLAT_DEVICE, dev); if (!node) return -ENODEV; err = id_in ? rimt_plat_iommu_map_id(dev, node, id_in) : rimt_plat_iommu_map(dev, node); } return err; } #endif void __init riscv_acpi_rimt_init(void) { acpi_status status; /* rimt_table will be used at runtime after the rimt init, * so we don't need to call acpi_put_table() to release * the RIMT table mapping. */ status = acpi_get_table(ACPI_SIG_RIMT, 0, &rimt_table); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) { const char *msg = acpi_format_exception(status); pr_err("Failed to get table, %s\n", msg); } return; } }