| /* |
| * Copyright (c) 2023 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "acpi.h" |
| #include "accommon.h" |
| #include "acapps.h" |
| #include <aecommon.h> |
| |
| #include <zephyr/drivers/pcie/pcie.h> |
| #include <zephyr/acpi/acpi.h> |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(ACPI, CONFIG_ACPI_LOG_LEVEL); |
| |
| struct acpi { |
| struct acpi_dev child_dev[CONFIG_ACPI_DEV_MAX]; |
| int num_dev; |
| ACPI_PCI_ROUTING_TABLE pci_prt_table[CONFIG_ACPI_MAX_PRT_ENTRY]; |
| bool early_init; |
| int status; |
| }; |
| |
| static struct acpi bus_ctx = { |
| .status = AE_NOT_CONFIGURED, |
| }; |
| |
| static ACPI_TABLE_DESC acpi_tables[CONFIG_ACPI_MAX_INIT_TABLES]; |
| |
| static int acpi_init(void); |
| |
| static int check_init_status(void) |
| { |
| int ret; |
| |
| if (ACPI_SUCCESS(bus_ctx.status)) { |
| return 0; |
| } |
| |
| if (bus_ctx.status == AE_NOT_CONFIGURED) { |
| ret = acpi_init(); |
| } else { |
| LOG_ERR("ACPI init was not success\n"); |
| ret = -EIO; |
| } |
| return ret; |
| } |
| |
| static void notify_handler(ACPI_HANDLE device, UINT32 value, void *ctx) |
| { |
| ACPI_INFO(("Received a notify 0x%X", value)); |
| } |
| |
| static ACPI_STATUS install_handlers(void) |
| { |
| ACPI_STATUS status; |
| |
| /* Install global notify handler */ |
| status = AcpiInstallNotifyHandler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, notify_handler, |
| NULL); |
| if (ACPI_FAILURE(status)) { |
| ACPI_EXCEPTION((AE_INFO, status, "While installing Notify handler")); |
| goto exit; |
| } |
| |
| exit: |
| return status; |
| } |
| |
| static ACPI_STATUS initialize_acpica(void) |
| { |
| ACPI_STATUS status; |
| |
| /* Initialize the ACPI subsystem */ |
| status = AcpiInitializeSubsystem(); |
| if (ACPI_FAILURE(status)) { |
| ACPI_EXCEPTION((AE_INFO, status, "While initializing ACPI")); |
| goto exit; |
| } |
| |
| /* Initialize the ACPI Table Manager and get all ACPI tables */ |
| if (!bus_ctx.early_init) { |
| status = AcpiInitializeTables(NULL, 16, FALSE); |
| } else { |
| /* Copy the root table list to dynamic memory if already initialized */ |
| status = AcpiReallocateRootTable(); |
| } |
| if (ACPI_FAILURE(status)) { |
| ACPI_EXCEPTION((AE_INFO, status, "While initializing Table Manager")); |
| goto exit; |
| } |
| |
| /* Create the ACPI namespace from ACPI tables */ |
| status = AcpiLoadTables(); |
| if (ACPI_FAILURE(status)) { |
| ACPI_EXCEPTION((AE_INFO, status, "While loading ACPI tables")); |
| goto exit; |
| } |
| |
| /* Install local handlers */ |
| status = install_handlers(); |
| if (ACPI_FAILURE(status)) { |
| ACPI_EXCEPTION((AE_INFO, status, "While installing handlers")); |
| goto exit; |
| } |
| |
| /* Initialize the ACPI hardware */ |
| status = AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION); |
| if (ACPI_FAILURE(status)) { |
| ACPI_EXCEPTION((AE_INFO, status, "While enabling ACPI")); |
| goto exit; |
| } |
| |
| /* Complete the ACPI namespace object initialization */ |
| status = AcpiInitializeObjects(ACPI_FULL_INITIALIZATION); |
| if (ACPI_FAILURE(status)) { |
| ACPI_EXCEPTION((AE_INFO, status, "While initializing ACPI objects")); |
| } |
| exit: |
| |
| return status; |
| } |
| |
| static ACPI_NAMESPACE_NODE *acpi_name_lookup(char *name) |
| { |
| char *path; |
| ACPI_STATUS status; |
| ACPI_NAMESPACE_NODE *node; |
| |
| LOG_DBG(""); |
| |
| status = AcpiNsInternalizeName(name, &path); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("Invalid namestring: %s", name); |
| return NULL; |
| } |
| |
| status = AcpiNsLookup(NULL, path, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, |
| ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE, NULL, &node); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("Could not locate name: %s, %d", name, status); |
| node = NULL; |
| } |
| |
| ACPI_FREE(path); |
| return node; |
| } |
| |
| static ACPI_NAMESPACE_NODE *acpi_evaluate_method(char *bus_name, char *method) |
| { |
| ACPI_NAMESPACE_NODE *node; |
| ACPI_NAMESPACE_NODE *handle; |
| ACPI_NAMESPACE_NODE *prt_node = NULL; |
| |
| LOG_DBG("%s", bus_name); |
| |
| handle = acpi_name_lookup(bus_name); |
| if (!handle) { |
| LOG_ERR("No ACPI node with given name: %s", bus_name); |
| goto exit; |
| } |
| |
| if (handle->Type != ACPI_TYPE_DEVICE) { |
| LOG_ERR("No ACPI node foud with given name: %s", bus_name); |
| goto exit; |
| } |
| |
| node = ACPI_CAST_PTR(ACPI_NAMESPACE_NODE, handle); |
| |
| (void)AcpiGetHandle(node, method, ACPI_CAST_PTR(ACPI_HANDLE, &prt_node)); |
| |
| if (!prt_node) { |
| LOG_ERR("No entry for the ACPI node with given name: %s", bus_name); |
| goto exit; |
| } |
| return node; |
| exit: |
| return NULL; |
| } |
| |
| static ACPI_STATUS acpi_enable_pic_mode(void) |
| { |
| ACPI_STATUS status; |
| ACPI_OBJECT_LIST arg_list; |
| ACPI_OBJECT arg[1]; |
| |
| arg_list.Count = 1; |
| arg_list.Pointer = arg; |
| |
| arg[0].Type = ACPI_TYPE_INTEGER; |
| arg[0].Integer.Value = 1; |
| |
| status = AcpiEvaluateObject(NULL, "\\_PIC", &arg_list, NULL); |
| if (ACPI_FAILURE(status)) { |
| LOG_WRN("error While executing \\_pic method: %d", status); |
| } |
| |
| return status; |
| } |
| |
| static int acpi_get_irq_table(struct acpi *bus, char *bus_name, |
| ACPI_PCI_ROUTING_TABLE *rt_table, uint32_t rt_size) |
| { |
| ACPI_BUFFER rt_buffer; |
| ACPI_NAMESPACE_NODE *node; |
| int status; |
| |
| LOG_DBG("%s", bus_name); |
| |
| node = acpi_evaluate_method(bus_name, METHOD_NAME__PRT); |
| if (!node) { |
| LOG_ERR("Evaluation failed for given device: %s", bus_name); |
| return -ENODEV; |
| } |
| |
| rt_buffer.Pointer = rt_table; |
| rt_buffer.Length = rt_size; |
| |
| status = AcpiGetIrqRoutingTable(node, &rt_buffer); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("unable to retrieve IRQ Routing Table: %s", bus_name); |
| return -EIO; |
| } |
| |
| for (int i = 0; i < CONFIG_ACPI_MAX_PRT_ENTRY; i++) { |
| if (!bus->pci_prt_table[i].SourceIndex) { |
| break; |
| } |
| if (IS_ENABLED(CONFIG_X86_64)) { |
| /* mark the PRT irq numbers as reserved. */ |
| arch_irq_set_used(bus->pci_prt_table[i].SourceIndex); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int acpi_retrieve_legacy_irq(struct acpi *bus) |
| { |
| /* TODO: assume platform have only one PCH with single PCI bus (bus 0). */ |
| return acpi_get_irq_table(bus, CONFIG_ACPI_PRT_BUS_NAME, |
| bus->pci_prt_table, sizeof(bus->pci_prt_table)); |
| } |
| |
| static ACPI_STATUS dev_resource_enum_callback(ACPI_HANDLE obj_handle, UINT32 level, void *ctx, |
| void **ret_value) |
| { |
| ACPI_NAMESPACE_NODE *node; |
| ACPI_BUFFER rt_buffer; |
| struct acpi *bus = (struct acpi *)ctx; |
| struct acpi_dev *child_dev; |
| |
| node = ACPI_CAST_PTR(ACPI_NAMESPACE_NODE, obj_handle); |
| char *path_name; |
| int status; |
| ACPI_DEVICE_INFO *dev_info; |
| |
| LOG_DBG("%s %p\n", __func__, node); |
| |
| /* get device info such as HID, Class ID etc. */ |
| status = AcpiGetObjectInfo(obj_handle, &dev_info); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("AcpiGetObjectInfo failed: %s", AcpiFormatException(status)); |
| goto exit; |
| } |
| |
| if (bus->num_dev >= CONFIG_ACPI_DEV_MAX) { |
| return AE_NO_MEMORY; |
| } |
| |
| child_dev = (struct acpi_dev *)&bus->child_dev[bus->num_dev++]; |
| child_dev->handle = obj_handle; |
| child_dev->dev_info = dev_info; |
| |
| path_name = AcpiNsGetNormalizedPathname(node, TRUE); |
| if (!path_name) { |
| LOG_ERR("No memory for path_name\n"); |
| goto exit; |
| } else { |
| LOG_DBG("Device path: %s\n", path_name); |
| child_dev->path = path_name; |
| } |
| |
| rt_buffer.Pointer = NULL; |
| rt_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; |
| |
| status = AcpiGetCurrentResources(node, &rt_buffer); |
| if (ACPI_FAILURE(status)) { |
| LOG_DBG("AcpiGetCurrentResources failed: %s", AcpiFormatException(status)); |
| } else { |
| child_dev->res_lst = rt_buffer.Pointer; |
| } |
| |
| exit: |
| |
| return status; |
| } |
| |
| static int acpi_enum_devices(struct acpi *bus) |
| { |
| LOG_DBG(""); |
| |
| AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, |
| dev_resource_enum_callback, NULL, bus, NULL); |
| |
| return 0; |
| } |
| |
| static int acpi_init(void) |
| { |
| int status; |
| |
| LOG_DBG(""); |
| |
| if (bus_ctx.status != AE_NOT_CONFIGURED) { |
| LOG_DBG("acpi init already done"); |
| return bus_ctx.status; |
| } |
| |
| /* For debug version only */ |
| ACPI_DEBUG_INITIALIZE(); |
| |
| status = initialize_acpica(); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("Error in ACPI init:%d", status); |
| goto exit; |
| } |
| |
| /* Enable IO APIC mode */ |
| status = acpi_enable_pic_mode(); |
| if (ACPI_FAILURE(status)) { |
| LOG_WRN("Error in enable pic mode acpi method:%d", status); |
| } |
| |
| status = acpi_retrieve_legacy_irq(&bus_ctx); |
| if (status) { |
| LOG_ERR("Error in retrieve legacy interrupt info:%d", status); |
| goto exit; |
| } |
| |
| acpi_enum_devices(&bus_ctx); |
| |
| exit: |
| bus_ctx.status = status; |
| |
| return status; |
| } |
| |
| static int acpi_early_init(void) |
| { |
| ACPI_STATUS status; |
| |
| LOG_DBG(""); |
| |
| if (bus_ctx.early_init) { |
| LOG_DBG("acpi early init already done"); |
| return 0; |
| } |
| |
| status = AcpiInitializeTables(acpi_tables, CONFIG_ACPI_MAX_INIT_TABLES, TRUE); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("Error in acpi table init:%d", status); |
| return -EIO; |
| } |
| |
| bus_ctx.early_init = true; |
| |
| return 0; |
| } |
| |
| uint32_t acpi_legacy_irq_get(pcie_bdf_t bdf) |
| { |
| uint32_t slot = PCIE_BDF_TO_DEV(bdf), pin; |
| |
| LOG_DBG(""); |
| |
| if (check_init_status()) { |
| return UINT_MAX; |
| } |
| |
| pin = (pcie_conf_read(bdf, PCIE_CONF_INTR) >> 8) & 0x3; |
| |
| LOG_DBG("Device irq info: slot:%d pin:%d", slot, pin); |
| |
| for (int i = 0; i < CONFIG_ACPI_MAX_PRT_ENTRY; i++) { |
| if (((bus_ctx.pci_prt_table[i].Address >> 16) & 0xffff) == slot && |
| bus_ctx.pci_prt_table[i].Pin + 1 == pin) { |
| LOG_DBG("[%d]Device irq info: slot:%d pin:%d irq:%d", i, slot, pin, |
| bus_ctx.pci_prt_table[i].SourceIndex); |
| return bus_ctx.pci_prt_table[i].SourceIndex; |
| } |
| } |
| |
| return UINT_MAX; |
| } |
| |
| int acpi_current_resource_get(char *dev_name, ACPI_RESOURCE **res) |
| { |
| ACPI_BUFFER rt_buffer; |
| ACPI_NAMESPACE_NODE *node; |
| int status; |
| |
| LOG_DBG("%s", dev_name); |
| |
| status = check_init_status(); |
| if (status) { |
| return status; |
| } |
| |
| node = acpi_evaluate_method(dev_name, METHOD_NAME__CRS); |
| if (!node) { |
| LOG_ERR("Evaluation failed for given device: %s", dev_name); |
| status = -ENOTSUP; |
| goto exit; |
| } |
| |
| rt_buffer.Pointer = NULL; |
| rt_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; |
| |
| status = AcpiGetCurrentResources(node, &rt_buffer); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("AcpiGetCurrentResources failed: %s", AcpiFormatException(status)); |
| status = -ENOTSUP; |
| } else { |
| *res = rt_buffer.Pointer; |
| } |
| |
| exit: |
| |
| return status; |
| } |
| |
| int acpi_possible_resource_get(char *dev_name, ACPI_RESOURCE **res) |
| { |
| ACPI_BUFFER rt_buffer; |
| ACPI_NAMESPACE_NODE *node; |
| int status; |
| |
| LOG_DBG("%s", dev_name); |
| |
| status = check_init_status(); |
| if (status) { |
| return status; |
| } |
| |
| node = acpi_evaluate_method(dev_name, METHOD_NAME__PRS); |
| if (!node) { |
| LOG_ERR("Evaluation failed for given device: %s", dev_name); |
| status = -ENOTSUP; |
| goto exit; |
| } |
| |
| rt_buffer.Pointer = NULL; |
| rt_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; |
| |
| AcpiGetPossibleResources(node, &rt_buffer); |
| *res = rt_buffer.Pointer; |
| |
| exit: |
| |
| return status; |
| } |
| |
| int acpi_current_resource_free(ACPI_RESOURCE *res) |
| { |
| ACPI_FREE(res); |
| |
| return 0; |
| } |
| |
| int acpi_get_irq_routing_table(char *bus_name, |
| ACPI_PCI_ROUTING_TABLE *rt_table, size_t rt_size) |
| { |
| int ret; |
| |
| ret = check_init_status(); |
| if (ret) { |
| return ret; |
| } |
| |
| return acpi_get_irq_table(&bus_ctx, bus_name, rt_table, rt_size); |
| } |
| |
| ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type) |
| { |
| do { |
| if (!res->Length) { |
| LOG_DBG("Error: zero length found!\n"); |
| break; |
| } else if (res->Type == res_type) { |
| break; |
| } |
| res = ACPI_NEXT_RESOURCE(res); |
| } while (res->Type != ACPI_RESOURCE_TYPE_END_TAG); |
| |
| if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) { |
| return NULL; |
| } |
| |
| return res; |
| } |
| |
| static int acpi_res_type(ACPI_RESOURCE *res) |
| { |
| int type; |
| |
| switch (res->Type) { |
| case ACPI_RESOURCE_TYPE_IO: |
| type = ACPI_RESOURCE_TYPE_IO; |
| break; |
| case ACPI_RESOURCE_TYPE_FIXED_IO: |
| type = ACPI_RESOURCE_TYPE_FIXED_IO; |
| break; |
| case ACPI_RESOURCE_TYPE_MEMORY24: |
| type = ACPI_RESOURCE_TYPE_MEMORY24; |
| break; |
| case ACPI_RESOURCE_TYPE_MEMORY32: |
| type = ACPI_RESOURCE_TYPE_MEMORY32; |
| break; |
| case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: |
| type = ACPI_RESOURCE_TYPE_FIXED_MEMORY32; |
| break; |
| case ACPI_RESOURCE_TYPE_ADDRESS16: |
| type = ACPI_RESOURCE_TYPE_ADDRESS16; |
| break; |
| case ACPI_RESOURCE_TYPE_ADDRESS32: |
| type = ACPI_RESOURCE_TYPE_ADDRESS32; |
| break; |
| case ACPI_RESOURCE_TYPE_ADDRESS64: |
| type = ACPI_RESOURCE_TYPE_ADDRESS64; |
| break; |
| case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: |
| type = ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64; |
| break; |
| default: |
| type = ACPI_RESOURCE_TYPE_MAX; |
| } |
| |
| return type; |
| } |
| |
| int acpi_device_type_get(ACPI_RESOURCE *res) |
| { |
| int type = ACPI_RESOURCE_TYPE_MAX; |
| |
| do { |
| if (!res->Length) { |
| LOG_ERR("Error: zero length found!\n"); |
| break; |
| } |
| type = acpi_res_type(res); |
| if (type != ACPI_RESOURCE_TYPE_MAX) { |
| break; |
| } |
| res = ACPI_NEXT_RESOURCE(res); |
| } while (res->Type != ACPI_RESOURCE_TYPE_END_TAG); |
| |
| return type; |
| } |
| |
| struct acpi_dev *acpi_device_get(char *hid, int inst) |
| { |
| struct acpi_dev *child_dev; |
| int i = 0, inst_id; |
| |
| LOG_DBG(""); |
| |
| if (check_init_status()) { |
| return NULL; |
| } |
| |
| do { |
| child_dev = &bus_ctx.child_dev[i]; |
| if (!child_dev->path) { |
| LOG_DBG("NULL device path found\n"); |
| continue; |
| } |
| |
| if (!child_dev->res_lst || !child_dev->dev_info || |
| !child_dev->dev_info->HardwareId.Length) { |
| continue; |
| } |
| |
| if (!strcmp(hid, child_dev->dev_info->HardwareId.String)) { |
| if (child_dev->dev_info->UniqueId.Length) { |
| inst_id = atoi(child_dev->dev_info->UniqueId.String); |
| if (inst_id == inst) { |
| return child_dev; |
| } |
| } else { |
| return child_dev; |
| } |
| } |
| } while (i++ < bus_ctx.num_dev); |
| |
| return NULL; |
| } |
| |
| struct acpi_dev *acpi_device_by_index_get(int index) |
| { |
| return index < bus_ctx.num_dev ? &bus_ctx.child_dev[index] : NULL; |
| } |
| |
| void *acpi_table_get(char *signature, int inst) |
| { |
| int status; |
| ACPI_TABLE_HEADER *table; |
| |
| if (!bus_ctx.early_init) { |
| status = acpi_early_init(); |
| if (status) { |
| LOG_ERR("ACPI early init failed"); |
| return NULL; |
| } |
| } |
| |
| status = AcpiGetTable(signature, inst, &table); |
| if (ACPI_FAILURE(status)) { |
| LOG_ERR("ACPI get table failed: %d", status); |
| return NULL; |
| } |
| |
| return (void *)table; |
| } |
| |
| static uint32_t acpi_get_subtable_entry_num(int type, struct acpi_subtable_header *subtable, |
| uintptr_t offset, uintptr_t base, uint32_t madt_len) |
| { |
| uint32_t subtable_cnt = 0; |
| |
| while (offset < madt_len) { |
| if (type == subtable->Type) { |
| subtable_cnt++; |
| } |
| offset += subtable->Length; |
| subtable = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, base, offset); |
| |
| if (!subtable->Length) { |
| break; |
| } |
| } |
| |
| return subtable_cnt; |
| } |
| |
| int acpi_madt_entry_get(int type, struct acpi_subtable_header **tables, int *num_inst) |
| { |
| struct acpi_table_header *madt = acpi_table_get("APIC", 0); |
| uintptr_t base = POINTER_TO_UINT(madt); |
| uintptr_t offset = sizeof(ACPI_TABLE_MADT); |
| struct acpi_subtable_header *subtable; |
| |
| if (!madt) { |
| return -EIO; |
| } |
| |
| subtable = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, base, offset); |
| while (offset < madt->Length) { |
| |
| if (type == subtable->Type) { |
| *tables = subtable; |
| *num_inst = acpi_get_subtable_entry_num(type, subtable, offset, base, |
| madt->Length); |
| return 0; |
| } |
| |
| offset += subtable->Length; |
| subtable = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, base, offset); |
| } |
| |
| return -ENODEV; |
| } |
| |
| int acpi_dmar_entry_get(enum AcpiDmarType type, struct acpi_subtable_header **tables) |
| { |
| struct acpi_table_dmar *dmar = acpi_table_get("DMAR", 0); |
| uintptr_t base = POINTER_TO_UINT(dmar); |
| uintptr_t offset = sizeof(ACPI_TABLE_DMAR); |
| struct acpi_dmar_header *subtable; |
| |
| if (!dmar) { |
| LOG_ERR("error on get DMAR table\n"); |
| return -EIO; |
| } |
| |
| subtable = ACPI_ADD_PTR(ACPI_DMAR_HEADER, base, offset); |
| while (offset < dmar->Header.Length) { |
| if (type == subtable->Type) { |
| *tables = (struct acpi_subtable_header *)subtable; |
| return 0; |
| } |
| offset += subtable->Length; |
| subtable = ACPI_ADD_PTR(ACPI_DMAR_HEADER, base, offset); |
| } |
| |
| return -ENODEV; |
| } |
| |
| int acpi_drhd_get(enum AcpiDmarScopeType scope, struct acpi_dmar_device_scope *dev_scope, |
| union acpi_dmar_id *dmar_id, int *num_inst, int max_inst) |
| { |
| uintptr_t offset = sizeof(ACPI_DMAR_HARDWARE_UNIT); |
| uint32_t i = 0; |
| struct acpi_dmar_header *drdh; |
| struct acpi_dmar_device_scope *subtable; |
| struct acpi_dmar_pci_path *dev_path; |
| int ret; |
| uintptr_t base; |
| int scope_size; |
| |
| ret = acpi_dmar_entry_get(ACPI_DMAR_TYPE_HARDWARE_UNIT, |
| (struct acpi_subtable_header **)&drdh); |
| if (ret) { |
| LOG_ERR("Error on retrieve DMAR table\n"); |
| return ret; |
| } |
| |
| scope_size = drdh->Length - sizeof(ACPI_DMAR_HARDWARE_UNIT); |
| base = (uintptr_t)((uintptr_t)drdh + offset); |
| |
| offset = 0; |
| |
| while (scope_size) { |
| int num_path; |
| |
| subtable = ACPI_ADD_PTR(ACPI_DMAR_DEVICE_SCOPE, base, offset); |
| if (!subtable->Length) { |
| break; |
| } |
| |
| if (scope == subtable->EntryType) { |
| num_path = (subtable->Length - 6u) / 2u; |
| dev_path = ACPI_ADD_PTR(ACPI_DMAR_PCI_PATH, subtable, |
| sizeof(ACPI_DMAR_DEVICE_SCOPE)); |
| |
| while (num_path--) { |
| if (i >= max_inst) { |
| LOG_ERR("DHRD not enough buffer size\n"); |
| return -ENOBUFS; |
| } |
| dmar_id[i].bits.bus = subtable->Bus; |
| dmar_id[i].bits.device = dev_path[i].Device; |
| dmar_id[i].bits.function = dev_path[i].Function; |
| i++; |
| } |
| break; |
| } |
| |
| offset += subtable->Length; |
| |
| if (scope_size < subtable->Length) { |
| break; |
| } |
| scope_size -= subtable->Length; |
| } |
| |
| *num_inst = i; |
| if (!i) { |
| LOG_ERR("Error on retrieve DRHD Info\n"); |
| return -ENODEV; |
| } |
| |
| if (dev_scope && subtable) { |
| memcpy(dev_scope, subtable, sizeof(struct acpi_dmar_device_scope)); |
| } |
| |
| return 0; |
| } |
| |
| #define ACPI_CPU_FLAGS_ENABLED 0x01u |
| |
| struct acpi_madt_local_apic *acpi_local_apic_get(int cpu_num) |
| { |
| struct acpi_madt_local_apic *lapic; |
| int cpu_cnt; |
| int idx; |
| |
| if (acpi_madt_entry_get(ACPI_MADT_TYPE_LOCAL_APIC, (ACPI_SUBTABLE_HEADER **)&lapic, |
| &cpu_cnt)) { |
| /* Error on MAD table. */ |
| return NULL; |
| } |
| |
| for (idx = 0; cpu_num >= 0 && idx < cpu_cnt; idx++) { |
| if (lapic[idx].LapicFlags & ACPI_CPU_FLAGS_ENABLED) { |
| if (cpu_num == 0) { |
| return &lapic[idx]; |
| } |
| |
| cpu_num--; |
| } |
| } |
| |
| return NULL; |
| } |