diff --git a/benchmark/benchmark.c b/benchmark/benchmark.c index 4839fb18e..d93cedfbb 100644 --- a/benchmark/benchmark.c +++ b/benchmark/benchmark.c @@ -356,6 +356,68 @@ seL4_Bool fault(microkit_child id, microkit_msginfo msginfo, microkit_msginfo *r break; } case seL4_Fault_UserException: { + sddf_printf("Registers: \n"); + sddf_printf("rip : "); + sddf_printf("0x%lx", regs.rip); + sddf_printf("\n"); + sddf_printf("rsp: "); + sddf_printf("0x%lx", regs.rsp); + sddf_printf("\n"); + sddf_printf("rflags : "); + sddf_printf("0x%lx", regs.rflags); + sddf_printf("\n"); + sddf_printf("rax : "); + sddf_printf("0x%lx", regs.rax); + sddf_printf("\n"); + sddf_printf("rbx : "); + sddf_printf("0x%lx", regs.rbx); + sddf_printf("\n"); + sddf_printf("rcx : "); + sddf_printf("0x%lx", regs.rcx); + sddf_printf("\n"); + sddf_printf("rdx : "); + sddf_printf("0x%lx", regs.rdx); + sddf_printf("\n"); + sddf_printf("rsi : "); + sddf_printf("0x%lx", regs.rsi); + sddf_printf("\n"); + sddf_printf("rdi : "); + sddf_printf("0x%lx", regs.rdi); + sddf_printf("\n"); + sddf_printf("rbp : "); + sddf_printf("0x%lx", regs.rbp); + sddf_printf("\n"); + sddf_printf("r8 : "); + sddf_printf("0x%lx", regs.r8); + sddf_printf("\n"); + sddf_printf("r9 : "); + sddf_printf("0x%lx", regs.r9); + sddf_printf("\n"); + sddf_printf("r10 : "); + sddf_printf("0x%lx", regs.r10); + sddf_printf("\n"); + sddf_printf("r11 : "); + sddf_printf("0x%lx", regs.r11); + sddf_printf("\n"); + sddf_printf("r12 : "); + sddf_printf("0x%lx", regs.r12); + sddf_printf("\n"); + sddf_printf("r13 : "); + sddf_printf("0x%lx", regs.r13); + sddf_printf("\n"); + sddf_printf("r14 : "); + sddf_printf("0x%lx", regs.r14); + sddf_printf("\n"); + sddf_printf("r15 : "); + sddf_printf("0x%lx", regs.r15); + sddf_printf("\n"); + sddf_printf("fs_base : "); + sddf_printf("0x%lx", regs.fs_base); + sddf_printf("\n"); + sddf_printf("gs_base : "); + sddf_printf("0x%lx", regs.gs_base); + sddf_printf("\n"); + sddf_printf("UserException\n"); break; } diff --git a/docs/acpi/acpi.md b/docs/acpi/acpi.md new file mode 100644 index 000000000..59bc6ff0b --- /dev/null +++ b/docs/acpi/acpi.md @@ -0,0 +1,80 @@ + +# sDDF ACPI Subsystem + +On x86, Intel introduced the Advanced Configuration and Power Interface (ACPI) +to standardise the communication mechanism between the firmware and the host OS. +The firmware updates the platform configurations and power interface data in the +ACPI tables during the initialisation. This means the resources cannot be known +and allocated for the subsystems at build time like what is done on ARM and +RISCV. + +The Root System Description Pointer (RSDP) of these tables can either be +obtained from the boot loader (in UEFI-based systems) or at fixed address (in +legacy BIOS systems). In UEFI-based systems, the RSDP and other ACPI tables can +be anywhere in the memory, and can only be known at run time. + +## Dynamic ACPI tables mapping + +So far, the seL4 kernel does not parse all the ACPI tables but passes RSDP as a +bootinfo block to the root task. The bootinfo pre-filling to user's memory was +partially supported by the rust capDL initialiser but not Microkit. This +[Microkit PR](https://github.com/seL4/microkit/pull/536) adds the support by +extending the pre-filling type of `MemoryRegion`. + +To self-map the ACPI tables, the ACPI driver needs to receive all the remaining +untypeds from the root task. The [draft +implementation](https://github.com/seL4/rust-sel4/pull/322) is done by @syzmon, +but it is a need to make an RFC to the capDL repository. + +In this typical system, the untyped capabilities flow from the capDL initialiser +to the ACPI driver, and then some of them are handed off to the PCIe driver. +The existing `cap_sharing` feature in Microkit allows a PD to access to another +PD's CSpace, but it is obviously not safe to give the unrestricted access for a +specific use case. The solution here is to add a tag `cnode` that defines a +user-managed CNode and can be mapped to one or more PDs. There are already +[some discussions](https://github.com/seL4/microkit/pull/539) on this aggressive +change, and it still needs more input from the verification team. + + +## AML interpreter + +The ACPI tables are compiled in ACPI Machine Language (AML) and stored as byte +code in memory, so another primary functionality of ACPI driver is parsing and +extracting required configuration information from the byte code. For the +verifiability and minimality of the ACPI driver, the solution that might be +silly but necessary is to implement a simplified interpreter rather than to +integrate a third-party one. + +The custom interpreter is implemented with a few simplified data structures: +- A finite state machine that manages the parsing state of current `operation`, +each `operation` having a known list of definition blocks. +- A state stack that saves the hierarchy of current parsing states. +- An object tree that connects all the parsed AML `operations`. + +The ACPI driver firstly runs the interpreter on all the DSDT and SSDT tables +in `scan` mode and form a tree of AML objects. After finding the target object, +it runs the interpreter in `evaluation` mode to extract the data. The primary +reasons of doing this way are that there are some across-defined objects among +the tables, and the evaluation results depend on the `MethodOperation` execution +order and arguments. + +## Dynamic resource mapping for subsystems + +After the extraction, the ACPI driver places the subsystem-related resource +information in the shared memory and copies the corresponding capabilities to +the shared CNode, so the downstream subsystems can start its lifecylce with +everthing prepared. For now, we have only PCIe subsystem connected to the ACPI +driver. Other subsystems, such as timer, serial, power management, etc. should +be properly integrated on x86 as well. + +## TODO List + +- [ ] ACPI driver works as a [Post-initialiser](https://github.com/seL4/rfcs) +- [ ] Properly receive leftover untypeds from the capDL initialiser (RFC TBD) +- [ ] Passing IRQ routing tables to or set up IRQs for all the subsystems +- [ ] Self-map ACPI tables without untypeds for paging structures (by taking +advantages of driver image mapping) diff --git a/docs/pci/pci.md b/docs/pci/pci.md new file mode 100644 index 000000000..ff2f2fa5e --- /dev/null +++ b/docs/pci/pci.md @@ -0,0 +1,31 @@ + +# sDDF PCIe Subsystem + +The Enhanced Configuration Access Method (ECAM) space is the interface for +software to negotiate the PCIe-related settings, such as memory BARs and IRQs. +Giving the device drivers the access to ECAM apparently breaks the isolation +principle, as they would be able to mess things up. + +The solution is to have PCIe driver as a centralised server that address all +the requests from the device drivers and set things up for them. The simplified +implementation of this mechanism is to patch the requests to the PCIe driver at +build time, so the PCIe driver can allocates and configures resources for them +once it receives the available resource window from the ACPI driver. + +## Progress + +The PCIe driver is currently in an experimental phase, which has proved the +feasibility of dynamically allocating and mapping IRQs and memroy for PCIe +device drivers, so the PCIe device drivers will have required resources ready +when they are scheduled. + +## TODO + +- [ ] Resource requests patched by metaprogram for PCIe device drivers +- [ ] Centralised MSI/MSI-X interrupt management +- [ ] Separate IRQ node for restricted access to PCIe device driver's CSpace +- [ ] Work as a post-initialiser diff --git a/drivers/acpi/acpi.c b/drivers/acpi/acpi.c new file mode 100644 index 000000000..ba3362af1 --- /dev/null +++ b/drivers/acpi/acpi.c @@ -0,0 +1,502 @@ +/* + * Copyright 2026, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +#include "acpi.h" + +uintptr_t remaining_untypeds_vaddr; +typedef struct { + /* seL4_CNode untyped_cnode_cptr; */ + seL4_SlotRegion untypeds; + seL4_UntypedDesc untypedList[CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS]; +} capDLBootInfo_t; + +const char acpi_str_xsdt[] = {'X', 'S', 'D', 'T', 0}; +const char acpi_str_rsdt[] = {'R', 'S', 'D', 'T', 0}; +const char acpi_str_dsdt[] = {'D', 'S', 'D', 'T', 0}; +const char acpi_str_ssdt[] = {'S', 'S', 'D', 'T', 0}; +const char acpi_str_fadt[] = {'F', 'A', 'C', 'P', 0}; +const char acpi_str_mcfg[] = {'M', 'C', 'F', 'G', 0}; +const char aml_str_hid[] = {'_', 'H', 'I', 'D', 0}; // Hardware ID +const char aml_str_adr[] = {'_', 'A', 'D', 'R', 0}; // Address +const char aml_str_crs[] = {'_', 'C', 'R', 'S', 0}; // Current Resource Settings +const char aml_str_prt[] = {'_', 'P', 'R', 'T', 0}; // PCI Routing Table +const char aml_str_pic[] = {'_', 'P', 'I', 'C', 0}; // PIC mode method +const char eisaid_str_pcie[] = {'P', 'N', 'P', '0', 'A', '0', '8', 0}; // PCI Express Bus + +capDLBootInfo_t *capDLBootInfo; +uintptr_t aml_object_pool_start = 0x30000000; +uintptr_t aml_object_pool_size = 0x100000; +pci_resources_t *pci_resources = (pci_resources_t *)0x60000000; +uintptr_t ecam_base_vaddr = 0x20000000; + +seL4_CPtr vspace_cptr_pci_driver; +seL4_CPtr cnode_cptr_remaining_untypeds; +seL4_CPtr cnode_cptr_pci_resources; +uintptr_t bootinfo_remaining_untypeds; +uintptr_t bootinfo_rsdp; + +uintptr_t acpi_vaddr = 0x4000000; +uintptr_t ecam_base_paddr; + +cnode_specs_t post_boot_cnode; +cnode_specs_t *pci_resources_cnode; + +mempool_t aml_namespace_mempool; +aml_namespace_node_t namespace_root; + +// Lookup results of AML namespace nodes +#define MAX_NUM_LOOKUP_NODES 128 +aml_namespace_node_t *lookup_results[MAX_NUM_LOOKUP_NODES]; + +__attribute__((__aligned__(0x1000))) __attribute__((__section__(".acpi_tables"))) uint8_t acpi_tables[1000000]; +__attribute__((__section__(".acpi_tables_summary"))) acpi_tables_summary_t acpi_tables_summary; + +void pass_resource_with_range(uint8_t resource_type, uint64_t min_addr, uint64_t max_addr) +{ + switch (resource_type) { + case 0: { + pass_ut_with_range(pci_resources_cnode, &post_boot_cnode, min_addr, max_addr); + sddf_dprintf("Memory "); + break; + } + case 1: { + sddf_dprintf("IO "); + break; + } + case 2: { + sddf_dprintf("Bus "); + break; + } + } + sddf_dprintf(": [0x%lx-0x%lx]\n", min_addr, max_addr); +} + +// Section 6.4 +void pass_crs_and_caps(aml_data_t crs_data, uint32_t bridge_idx) +{ + uint8_t *buf_cur = (uint8_t *)crs_data.value; + uint8_t *crs_data_end = (uint8_t *)crs_data.value + crs_data.length; + + // TODO: deal with WORD_IO and WORD_BUS + sddf_dprintf("=====pass CRS untypeds=====\n"); + while (buf_cur < crs_data_end) { + uint8_t new_res_idx = pci_resources->bridges[bridge_idx].num_dev_resources; + device_resource_t *dev_res = (device_resource_t *)&pci_resources->bridges[bridge_idx].dev_resources[new_res_idx]; + + switch (buf_cur[0]) { + case WORD_AS_DESCRIPTOR: { + acpi_word_address_space_t *word_as = (acpi_word_address_space_t *)buf_cur; + dev_res->min_addr = word_as->min_address; + dev_res->max_addr = word_as->min_address + word_as->address_length; + dev_res->type = word_as->resource_type; + + sddf_dprintf("Word "); + pass_resource_with_range(dev_res->type, dev_res->min_addr, dev_res->max_addr); + + pci_resources->bridges[bridge_idx].num_dev_resources++; + break; + } + case DWORD_AS_DESCRIPTOR: { + acpi_dword_address_space_t *dword_as = (acpi_dword_address_space_t *)buf_cur; + dev_res->min_addr = dword_as->min_address; + dev_res->max_addr = dword_as->min_address + dword_as->address_length; + dev_res->type = dword_as->resource_type; + + sddf_dprintf("DWord "); + pass_resource_with_range(dev_res->type, dev_res->min_addr, dev_res->max_addr); + + pci_resources->bridges[bridge_idx].num_dev_resources++; + break; + } + case QWORD_AS_DESCRIPTOR: { + acpi_qword_address_space_t *qword_as = (acpi_qword_address_space_t *)buf_cur; + dev_res->min_addr = qword_as->min_address; + dev_res->max_addr = qword_as->min_address + qword_as->address_length; + dev_res->type = qword_as->resource_type; + + sddf_dprintf("QWord "); + pass_resource_with_range(dev_res->type, dev_res->min_addr, dev_res->max_addr); + + pci_resources->bridges[bridge_idx].num_dev_resources++; + break; + } + case IO_PORT_DESCRIPTOR: { + acpi_io_port_t *io_port = (acpi_io_port_t *)buf_cur; + dev_res->min_addr = io_port->min_address; + dev_res->max_addr = io_port->min_address + io_port->address_length; + + sddf_dprintf("I/O Port "); + pass_resource_with_range(1, dev_res->min_addr, dev_res->max_addr); + break; + } + case END_TAG: { + sddf_dprintf("end_tag\n"); + // TODO: checksum + break; + } + default: { + sddf_dprintf("Resource type 0x%02x parsing is not implemented\n", buf_cur[0]); + } + } + + if (buf_cur[0] & 0x80) { + // Large Resource Data Length + buf_cur += 3 + buf_cur[1] + (buf_cur[2] << 8); + } else { + // Small Resource Data Length: Byte0[2:0] + buf_cur += (buf_cur[0] & 0x7) + 1; + } + } + sddf_dprintf("=====Finish CRS untypeds passing=====\n"); +} + +// TODO: this should be in interpreter.c + +bool validate_acpi_table_signature(acpi_header_t *header, const char *signature) +{ + sddf_dprintf("Signature: %c%c%c%c\n", + header->signature[0], + header->signature[1], + header->signature[2], + header->signature[3]); + + assert(header->signature[0] == signature[0]); + assert(header->signature[1] == signature[1]); + assert(header->signature[2] == signature[2]); + assert(header->signature[3] == signature[3]); + return true; +} + +bool map_acpi_table_header(uintptr_t paddr, acpi_header_t *header) +{ + return map_memory_region(&post_boot_cnode, paddr, sizeof(acpi_header_t), (uintptr_t)header); +} + +bool map_acpi_table_content(uintptr_t paddr, acpi_header_t *header) +{ + uintptr_t mapped_paddr_end = ROUND_UP(paddr + sizeof(acpi_header_t), PAGE_SIZE); + uintptr_t mapped_vaddr_end = ROUND_UP((uintptr_t)header + sizeof(acpi_header_t), PAGE_SIZE); + uintptr_t acpi_table_paddr_end = paddr + header->length; + return map_memory_region(&post_boot_cnode, mapped_paddr_end, acpi_table_paddr_end - mapped_paddr_end, mapped_vaddr_end); +} + +void backup_acpi_table(acpi_header_t *header) +{ + uintptr_t backup_table_vaddr = ROUND_UP((uintptr_t)&acpi_tables + acpi_tables_summary.tables_end, ACPI_TABLES_ALIGNMENT); + sddf_dprintf("backup_table_vaddr: 0x%lx, len: 0x%x, end: 0x%lx\n", backup_table_vaddr, header->length, backup_table_vaddr + header->length); + assert(backup_table_vaddr + header->length < acpi_tables_summary.mem_end); + memcpy((void *)backup_table_vaddr, (void *)header, header->length); + + uint32_t table_idx = acpi_tables_summary.num_tables; + acpi_tables_summary.tables_offset[table_idx] = backup_table_vaddr - (uintptr_t)&acpi_tables; + acpi_tables_summary.num_tables++; + acpi_tables_summary.tables_end = backup_table_vaddr + header->length - (uintptr_t)&acpi_tables; + sddf_dprintf("update tables_end to 0x%lx\n", acpi_tables_summary.tables_end); +} + +acpi_header_t *find_first_acpi_header_by_signature(const char *signature) +{ + for (int i = 0; i < acpi_tables_summary.num_tables; i++) { + sddf_dprintf("acpi start: 0x%lx, table pointer: 0x%lx\n", (uintptr_t)&acpi_tables, acpi_tables_summary.tables_offset[i]); + acpi_header_t *header = (acpi_header_t *)(acpi_tables_summary.tables_offset[i] + (uintptr_t)&acpi_tables); + sddf_dprintf("Signature: %c%c%c%c\n", + header->signature[0], + header->signature[1], + header->signature[2], + header->signature[3]); + + if (strncmp(header->signature, signature, 4) == 0) { + return header; + } + } + return NULL; +} + +void load_acpi_tables() +{ + // Read RSDP to locate RSDT + bootinfo_rsdp_t *bi_rsdp = (bootinfo_rsdp_t *)bootinfo_rsdp; + sddf_dprintf("revision: %d, rsdt_addr: 0x%x, xsdt_addr: 0x%lx\n", + bi_rsdp->content.revision, + bi_rsdp->content.rsdt_address, + bi_rsdp->content.xsdt_address); + uintptr_t rsdt_paddr = bi_rsdp->content.rsdt_address; + if (bi_rsdp->content.revision > 1) { + rsdt_paddr = bi_rsdp->content.xsdt_address; + } + + // Map all the frames covering the RSDT table + acpi_header_t *rsdt_header = (acpi_header_t *)(acpi_vaddr + PAGE_OFFSET(rsdt_paddr)); + assert(map_acpi_table_header(rsdt_paddr, rsdt_header)); + if (bi_rsdp->content.revision >= 2) { + validate_acpi_table_signature(rsdt_header, acpi_str_xsdt); + } else { + validate_acpi_table_signature(rsdt_header, acpi_str_rsdt); + } + + assert(map_acpi_table_content(rsdt_paddr, rsdt_header)); + backup_acpi_table(rsdt_header); + assert(cnode_untypeds_revoke(&post_boot_cnode) == seL4_NoError); + + acpi_header_t *acpi_rsdt_header; + if (bi_rsdp->content.revision >= 2) { + acpi_rsdt_header = find_first_acpi_header_by_signature(acpi_str_xsdt); + } else { + acpi_rsdt_header = find_first_acpi_header_by_signature(acpi_str_rsdt); + } + assert(acpi_rsdt_header != NULL); + + acpi_rsdt_t *acpi_rsdt = (acpi_rsdt_t *)acpi_rsdt_header; + // TODO: XSDT has different struct size + uint32_t num_entries = (acpi_rsdt->header.length - sizeof(acpi_rsdt->header)) / sizeof(uint32_t); + sddf_dprintf("rsdt: 0x%lx, entries: %d, length: %d\n", (uintptr_t)acpi_rsdt_header, num_entries, acpi_rsdt->header.length); + uint32_t *table_entries = (uint32_t *)&acpi_rsdt->entry; + + // Look up entries in RSDT + for (int i = 0; i < num_entries; i++) { + acpi_header_t *header = (acpi_header_t *)(acpi_vaddr + (table_entries[i] & 0xfff)); + assert(map_acpi_table_header(table_entries[i], header)); + + sddf_dprintf("Signature: %c%c%c%c\n", + header->signature[0], + header->signature[1], + header->signature[2], + header->signature[3]); + + if (strncmp(header->signature, acpi_str_fadt, 4) == 0) { + assert(map_acpi_table_content(table_entries[i], header)); + + acpi_fadt_t *fadt_table = (acpi_fadt_t *)header; + sddf_dprintf("DSDT address: 0x%x\n", fadt_table->dsdt); + uintptr_t acpi_dsdt_paddr = fadt_table->dsdt; + + assert(cnode_untypeds_revoke(&post_boot_cnode) == seL4_NoError); + // FADT table has been unmapped, so it's no longer readable + + acpi_header_t *dsdt_header = (acpi_header_t *)(acpi_vaddr + (acpi_dsdt_paddr & 0xfff)); + assert(map_acpi_table_header(acpi_dsdt_paddr, dsdt_header)); + validate_acpi_table_signature(dsdt_header, acpi_str_dsdt); + + // Map and backup the DSDT table + assert(map_acpi_table_content(acpi_dsdt_paddr, dsdt_header)); + backup_acpi_table(dsdt_header); + + } else if (strncmp(header->signature, acpi_str_mcfg, 4) == 0) { + // Map and backup the MCFG table + assert(map_acpi_table_content(table_entries[i], header)); + backup_acpi_table(header); + + } else if (strncmp(header->signature, acpi_str_ssdt, 4) == 0) { + // Map and backup the SSDT table + assert(map_acpi_table_content(table_entries[i], header)); + backup_acpi_table(header); + } + + assert(cnode_untypeds_revoke(&post_boot_cnode) == seL4_NoError); + } +} + +void init(void) +{ + // Init the CNode specs that record all the untypeds passed from the capDL initialiser + capDLBootInfo = (capDLBootInfo_t*)bootinfo_remaining_untypeds; + post_boot_cnode.cptr = cnode_cptr_remaining_untypeds; + post_boot_cnode.start = capDLBootInfo->untypeds.start; + // TODO: is end empty? + for (uint64_t i = capDLBootInfo->untypeds.start; i < capDLBootInfo->untypeds.end; i++) { + post_boot_cnode.caps[i].base_addr = capDLBootInfo->untypedList[i].paddr; + post_boot_cnode.caps[i].end_addr = post_boot_cnode.caps[i].base_addr + (1ULL << capDLBootInfo->untypedList[i].sizeBits); + post_boot_cnode.caps[i].is_device = capDLBootInfo->untypedList[i].isDevice; + post_boot_cnode.caps[i].object_type = seL4_UntypedObject; + post_boot_cnode.end = i + 1; + sddf_dprintf("i: %lu, 0x%lx-0x%lx: device? %d\n", i, post_boot_cnode.caps[i].base_addr, post_boot_cnode.caps[i].end_addr, post_boot_cnode.caps[i].is_device); + } + update_active_ut_idx(&post_boot_cnode); + sddf_dprintf("cnode start: %d\n", post_boot_cnode.start); + + // Init the CNodes that is shared between ACPI and PCIe driver + pci_resources_cnode = &pci_resources->cnode_specs; + pci_resources_cnode->cptr = cnode_cptr_pci_resources; + // TODO: this should be passed by capDL loader + seL4_Error error = seL4_CNode_Move(pci_resources_cnode->cptr, 1, 58, post_boot_cnode.cptr, 1, 58); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to copy a the IRQControl Capability\n"); + return; + } + pci_resources_cnode->start = 2; + pci_resources_cnode->end = 3; + + sddf_dprintf("ACPI tables summary:\n"); + sddf_dprintf(" num_tables: %d\n", acpi_tables_summary.num_tables); + sddf_dprintf(" mem_size: %lu\n", acpi_tables_summary.mem_end); + sddf_dprintf(" tables_end: %lu\n", acpi_tables_summary.tables_end); + + if (acpi_tables_summary.num_tables == 0) { + load_acpi_tables(); + } else { + sddf_dprintf("Test tables are found, skip loading the ACPI tables from the host machine\n"); + } + + sddf_dprintf("======MAP ======\n"); + + acpi_mcfg_t *mcfg_table = (acpi_mcfg_t *)find_first_acpi_header_by_signature(acpi_str_mcfg); + assert(mcfg_table != NULL); + + uint32_t num_pci_seg_grps = (mcfg_table->header.length - sizeof(acpi_header_t)) / sizeof(pci_seg_group_t); + sddf_dprintf("num_pci: %u\n", num_pci_seg_grps); + for (int i = 0; i < num_pci_seg_grps; i++) { + void *src_table = (void *)&mcfg_table->pci_seg_group[i]; + void *dst_table = (void *)&pci_resources->pci_seg_groups[pci_resources->num_pci_groups]; + memcpy(dst_table, src_table, sizeof(pci_seg_group_t)); + pci_resources->num_pci_groups++; + } + assert(pci_resources->num_pci_groups > 0); + // ACPI ASL assumes that Segment 0 is used for PCI_Config in OpRegion + ecam_base_paddr = pci_resources->pci_seg_groups[0].base_addr; + + sddf_dprintf("===============Scanning DSDT and SSDT===============\n"); + aml_namespace_mempool.start = (void *)aml_object_pool_start; + aml_namespace_mempool.next = (void *)aml_object_pool_start; + aml_namespace_mempool.end = (void *)aml_object_pool_start + aml_object_pool_size; + + acpi_dsdt_t *dsdt_table = (acpi_dsdt_t *)find_first_acpi_header_by_signature(acpi_str_dsdt); + assert(dsdt_table != NULL); + uint8_t *dsdt_table_end = (uint8_t *)dsdt_table + dsdt_table->header.length; + set_scanner_to((uint8_t *)&dsdt_table->content[0]); + namespace_root.pkt_start = scanner.current; + namespace_root.op_code = NULL_OP; + namespace_root.name[0] = '\\'; + sddf_dprintf("Scan DSDT, start at: 0x%lx\n", (uintptr_t)scanner.current); + scan_namespace_tree(&namespace_root, dsdt_table_end); + + for (int i = 0; i < acpi_tables_summary.num_tables; i++) { + acpi_header_t *header = (acpi_header_t *)((uintptr_t)&acpi_tables + acpi_tables_summary.tables_offset[i]); + if (strncmp(header->signature, acpi_str_ssdt, 4) == 0) { + sddf_dprintf("i: %d, Scan SSDT at offset 0x%lx, acpi_start: 0x%lx\n", i, (uintptr_t)acpi_tables_summary.tables_offset[i], (uintptr_t)&acpi_tables); + acpi_dsdt_t *ssdt_table = (acpi_dsdt_t *)header; + uint8_t *ssdt_table_end = (uint8_t *)ssdt_table + ssdt_table->header.length; + set_scanner_to((uint8_t *)&ssdt_table->content[0]); + scan_namespace_tree(&namespace_root, ssdt_table_end); + } + } + + // Look for _PIC method + uint8_t num_results = find_decendant_nodes_by_name(&namespace_root, aml_str_pic, lookup_results, 0); + assert(num_results == 1 && num_results < MAX_NUM_LOOKUP_NODES); // There must be only one _PIC method + sddf_dprintf("Found _PIC method! num: %u\n", num_results); + + // Enable APIC mode: pass 1 to method "_PIC" + aml_data_t pic_method_arg = {1, DATA_OBJ_QWORD, 0}; + // TODO: fix ret_type + eval_namespace_node(lookup_results[0], 1, &pic_method_arg); + + // Extract _CRS and _PRT + sddf_dprintf("===============Extract _CRS===============\n"); + num_results = find_decendant_nodes_by_name(&namespace_root, aml_str_hid, lookup_results, 0); + sddf_dprintf("num_results: %d\n", num_results); + + for (uint32_t i = 0; i < num_results; i++) { + aml_namespace_node_t *node = lookup_results[i]; + char eisa_id[10]; + read_eisa_id(node, eisa_id); + if (!strcmp(eisa_id, eisaid_str_pcie)) { + sddf_dprintf("=====Found PCIe Bus\n"); + aml_namespace_node_t *crs_node = find_child_node_by_name(node->parent, aml_str_crs); + assert(crs_node != NULL); + + aml_namespace_node_t *prt_node = find_child_node_by_name(node->parent, aml_str_prt); + assert(prt_node != NULL); + + aml_data_t prt_data = eval_namespace_node(prt_node, 0, NULL); + sddf_dprintf("value: 0x%lx, type: %u, length: %u\n", prt_data.value, prt_data.type, prt_data.length); + parse_prt_package(prt_node, prt_data, pci_resources->num_bridges); + pci_resources->num_bridges++; + sddf_dprintf("======Finish _PRT parsing\n"); + + aml_namespace_node_t *child_node = node->parent->child; + while(child_node) { + aml_namespace_node_t *child_adr_node = find_child_node_by_name(child_node, aml_str_adr); + aml_namespace_node_t *child_prt_node = find_child_node_by_name(child_node, aml_str_prt); + + if (child_node->op_code == DEVICE_OP && child_adr_node && child_prt_node) { + aml_data_t child_prt_data = eval_namespace_node(child_prt_node, 0, NULL); + sddf_dprintf("name: %s, adr_node: 0x%lx, prt_node: 0x%lx, num_bridge: 0x%x\n", child_node->name, child_adr_node, child_prt_node, pci_resources->num_bridges); + parse_prt_package(child_prt_node, child_prt_data, pci_resources->num_bridges); + + aml_data_t child_adr_data = eval_namespace_node(child_adr_node, 0, NULL); + sddf_dprintf("ADR: 0x%lx\n", child_adr_data.value); + pci_resources->bridges[pci_resources->num_bridges].adr = child_adr_data.value; + + pci_resources->num_bridges++; + } + child_node = child_node->next; + } + + // TODO: fix ret_type + aml_data_t crs_data = eval_namespace_node(crs_node, 0, NULL); + sddf_dprintf("CRS: 0x%lx, len: 0x%x\n", crs_data.value, crs_data.length); + // TODO: fix register reading during PRT parsing by reusing paging structures + pass_crs_and_caps(crs_data, pci_resources->num_bridges); + sddf_dprintf("======Finish _CRS parsing\n"); + + } + } + // TODO: Num of PCIe bridges should be matched in MCFG and DSDT + /* assert(pci_resources->num_bridges == num_pci_seg_grps); */ + + // Map ECAM space for PCIe driver + for (int i = 0; i < pci_resources->num_pci_groups; i++) { + sddf_dprintf("PCI segment group: %u, base addr: 0x%lx, bus_range: [%u-%u]\n", + pci_resources->pci_seg_groups[i].group_id, + pci_resources->pci_seg_groups[i].base_addr, + pci_resources->pci_seg_groups[i].bus_start, + pci_resources->pci_seg_groups[i].bus_end); + uint32_t ecam_size = (1 + pci_resources->pci_seg_groups[i].bus_end - pci_resources->pci_seg_groups[i].bus_start) * (1 << 20); + uintptr_t end_paddr = pci_resources->pci_seg_groups[i].base_addr + ecam_size; + uintptr_t cur_paddr = pci_resources->pci_seg_groups[i].base_addr; + uintptr_t cur_vaddr = ecam_base_vaddr; + while (cur_paddr < end_paddr) { + error = retype_and_map_frame(&post_boot_cnode, cur_paddr, cur_vaddr, vspace_cptr_pci_driver, seL4_X86_LargePageObject, seL4_ReadWrite); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to retype or map a frame.\n"); + return; + } + cur_paddr += (1 << seL4_LargePageBits); + cur_vaddr += (1 << seL4_LargePageBits); + } + + pci_resources->pci_seg_groups[i].base_addr = ecam_base_vaddr; + ecam_base_vaddr = cur_vaddr; + } + + sddf_dprintf("Finished ECAM mapping!\n"); + + sddf_dprintf("active ut: 0x%lx-0x%lx\n", post_boot_cnode.caps[post_boot_cnode.active_ut_idx].base_addr, post_boot_cnode.caps[post_boot_cnode.active_ut_idx].end_addr); + error = seL4_CNode_Copy(pci_resources_cnode->cptr, 2, 58, post_boot_cnode.cptr, post_boot_cnode.active_ut_idx, 58, seL4_ReadWrite); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to copy a the IRQControl Capability\n"); + return; + } + pci_resources_cnode->caps[2].base_addr = post_boot_cnode.caps[post_boot_cnode.active_ut_idx].base_addr; + pci_resources_cnode->caps[2].end_addr = post_boot_cnode.caps[post_boot_cnode.active_ut_idx].end_addr; + pci_resources_cnode->active_ut_idx = 2; + + sddf_deferred_notify(0); +} + +void notified(microkit_channel ch) +{ +} diff --git a/drivers/acpi/acpi.h b/drivers/acpi/acpi.h new file mode 100644 index 000000000..f2160ef19 --- /dev/null +++ b/drivers/acpi/acpi.h @@ -0,0 +1,369 @@ +/* + * Copyright 2026, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +#define HEX_TO_CHAR(hex) ((hex) < 10) ? ((hex) + '0') : ((hex) - 10 + 'A') +#define ACPI_TABLES_ALIGNMENT 0x1000 + +// A system could have up to 65536 PCI Segment Groups in theory, but 16 is +// sufficient in our use cases. +#define MAX_NUM_PCI_SEG_GROUP 16 +#define MAX_BYTES_DSDT 10000 + +/* Root System Descriptor Pointer */ +typedef struct acpi_rsdp { + char signature[8]; + uint8_t checksum; + char oem_id[6]; + uint8_t revision; + uint32_t rsdt_address; + uint32_t length; + uint64_t xsdt_address; + uint8_t extended_checksum; + char reserved[3]; +} __attribute__((packed)) acpi_rsdp_t; + +/* Generic System Descriptor Table Header */ +typedef struct acpi_header { + char signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + char oem_id[6]; + char oem_table_id[8]; + uint32_t oem_revision; + char creater_id[4]; + uint32_t creater_revision; +} __attribute__((packed)) acpi_header_t; + +/* Root System Descriptor Table */ +typedef struct acpi_rsdt { + acpi_header_t header; + uint32_t entry[1]; +} __attribute__((packed)) acpi_rsdt_t; + +typedef struct acpi_fadt { + acpi_header_t header; + uint32_t fw_ctrl; + uint32_t dsdt; +} __attribute__((packed)) acpi_fadt_t; + +typedef struct pci_seg_group { + uint64_t base_addr; + uint16_t group_id; + uint8_t bus_start; + uint8_t bus_end; + uint8_t reserved[4]; +} __attribute__((packed)) pci_seg_group_t; + +typedef struct acpi_mcfg { + acpi_header_t header; + uint8_t reserved[8]; + pci_seg_group_t pci_seg_group[MAX_NUM_PCI_SEG_GROUP]; +} __attribute__((packed)) acpi_mcfg_t; + +typedef struct acpi_dsdt { + acpi_header_t header; + uint8_t content[MAX_BYTES_DSDT]; +} __attribute__((packed)) acpi_dsdt_t; + +typedef struct bootinfo_rsdp { + seL4_BootInfoHeader header; + acpi_rsdp_t content; +} __attribute__((packed)) bootinfo_rsdp_t; + +enum aml_encoding_value { + ZERO_OP = 0x00, + ONE_OP = 0x01, + NULL_OP = 0x02, + ALIAS_OP = 0x06, + NAME_OP = 0x08, + BYTE_PREFIX = 0x0A, + WORD_PREFIX = 0x0B, + DWORD_PREFIX = 0x0C, + STRING_PREFIX = 0x0D, + QWORD_PREFIX = 0x0E, + SCOPE_OP = 0x10, + BUFFER_PREFIX = 0x11, + PACKAGE_PREFIX = 0x12, + METHOD_OP = 0x14, + EXT_OP_PREFIX = 0x5B, + MUTEX_OP = 0x5B01, + EVENT_OP = 0x5B02, + CREATE_FIELD_OP = 0x5B13, + OP_REGION_OP = 0x5B80, + FIELD_OP = 0x5B81, + DEVICE_OP = 0x5B82, + PROCESSOR_OP = 0x5B83, + POWER_RESOURCE_OP = 0x5B84, + THERMAL_ZONE_OP = 0x5B85, + INDEX_FIELD_OP = 0x5B86, + // Something more + LOCAL0_OP = 0x60, + LOCAL1_OP = 0x61, + LOCAL2_OP = 0x62, + LOCAL3_OP = 0x63, + LOCAL4_OP = 0x64, + LOCAL5_OP = 0x65, + LOCAL6_OP = 0x66, + LOCAL7_OP = 0x67, + ARG0_OP = 0x68, + ARG1_OP = 0x69, + ARG2_OP = 0x6A, + ARG3_OP = 0x6B, + ARG4_OP = 0x6C, + ARG5_OP = 0x6D, + ARG6_OP = 0x6E, + STORE_OP = 0x70, + ADD_OP = 0x72, + SUBTRACT_OP = 0x74, + SHIFT_LEFT_OP = 0x79, + SHIFT_RIGHT_OP = 0x7A, + AND_OP = 0x7B, + DEREF_OF_OP = 0x83, + INDEX_OP = 0x88, + CREATE_BIT_FIELD_OP = 0x8D, + CREATE_BYTE_FIELD_OP = 0x8C, + CREATE_WORD_FIELD_OP = 0x8B, + CREATE_DWORD_FIELD_OP = 0x8A, + CREATE_QWORD_FIELD_OP = 0x8F, + LNOT_EQUAL_OP = 0x9293, + LLESS_EQUAL_OP = 0x9294, + LGREATER_EQUAL_OP = 0x9295, + LEQUAL_OP = 0x93, + IF_OP = 0xA0, + ELSE_OP = 0xA1, + RETURN_OP = 0xA4, + // Custom Ops + PRT_PACKAGE = 0xFE01, +}; + +typedef enum aml_data_type { + DATA_OBJ_ZERO = 0x00, + DATA_OBJ_ONE = 0x01, + DATA_OBJ_BYTE = 0x0A, + DATA_OBJ_WORD = 0x0B, + DATA_OBJ_DWORD = 0x0C, + DATA_OBJ_STRING = 0x0D, + DATA_OBJ_QWORD = 0x0E, + DATA_OBJ_BUFFER = 0x11, + DATA_OBJ_PACKAGE = 0x12, + DATA_OBJ_NODE = 0xFE, // custom type: node + DATA_OBJ_RET = 0xFF, // custom type: ret_data +} aml_data_type_t; + +enum aml_data_resource_type { + EXTENDED_IRQ_DESCRIPTOR = 0x89, + IO_PORT_DESCRIPTOR = 0x47, + END_TAG = 0x79, + DWORD_AS_DESCRIPTOR = 0x87, + WORD_AS_DESCRIPTOR = 0x88, + QWORD_AS_DESCRIPTOR = 0x8A, +}; + +#define MAX_NUM_AS_RESOURCES 10 +#define MAX_NUM_PRT_ENTRIES 256 + +enum device_resource_type { + IO_PORT = 0, + DWORD_MEMORY, + DWORD_IO, + DWORD_BUS, + WORD_MEMORY, + WORD_IO, + WORD_BUS, + QWORD_MEMORY, + QWORD_IO, + QWORD_BUS, +}; + +typedef struct { + enum device_resource_type type; + uintptr_t min_addr; + uintptr_t max_addr; +} device_resource_t; + +typedef struct { + uint32_t address; + uint8_t pin; + uint8_t gsi; +} pci_prt_t; + +typedef struct { + uint32_t bus_start; + uint32_t bus_end; + uintptr_t adr; + device_resource_t dev_resources[MAX_NUM_AS_RESOURCES]; + uint8_t num_dev_resources; + pci_prt_t prt_entries[MAX_NUM_PRT_ENTRIES]; + uint8_t num_prt_entries; + uint8_t segment_id; +} pci_bridge_t; + +typedef struct { + pci_seg_group_t pci_seg_groups[MAX_NUM_PCI_SEG_GROUP]; + uint32_t num_pci_groups; + pci_bridge_t bridges[30]; // Host bridges + uint32_t num_bridges; + cnode_specs_t cnode_specs; +} pci_resources_t; + +typedef struct { + uint8_t tag; // 0x89 + uint16_t length; // Length of data (usually 13 bytes) + uint8_t vector_flags; // + uint8_t table_len; // + uint8_t irq_num; // + // Optional 'Resource Source' string could follow here +} __attribute__((packed)) acpi_ext_irq_t; + +typedef struct { + uint8_t tag; // 0x88 + uint16_t length; // Length of data (usually 13 bytes) + uint8_t resource_type; // 0=Memory, 1=IO, 2=BusNumber + uint8_t flags; // General flags (Dec, Min, Max, etc.) + uint8_t type_flags; // Type-specific flags + uint16_t granularity; // Address granularity + uint16_t min_address; // Range minimum + uint16_t max_address; // Range maximum + uint16_t translation; // Address translation offset + uint16_t address_length; // Length of the address range + // Optional 'Resource Source' string could follow here +} __attribute__((packed)) acpi_word_address_space_t; + +typedef struct { + uint8_t tag; // 0x87 + uint16_t length; // 0x0017 (23 bytes) + uint8_t resource_type; // 0=Memory, 1=IO, 2=BusNumber + uint8_t flags; // General Flags (Producer, Decode, etc.) + uint8_t type_flags; // Type-specific flags (e.g., Cacheable) + uint32_t granularity; // Address Granularity + uint32_t min_address; // Address Minimum + uint32_t max_address; // Address Maximum + uint32_t translation; // Address Translation Offset + uint32_t address_length; // Address Length + // Optional: Resource Source Index and String could follow +} __attribute__((packed)) acpi_dword_address_space_t; + +typedef struct { + uint8_t tag; // 0x8A + uint16_t length; // 0x002B (43 bytes) + uint8_t resource_type; // 0=Memory, 1=IO, 2=BusNumber + uint8_t flags; // General Flags (Producer, Decode, etc.) + uint8_t type_flags; // Type-specific flags + uint64_t granularity; // Address Granularity + uint64_t min_address; // Address Minimum + uint64_t max_address; // Address Maximum + uint64_t translation; // Address Translation Offset + uint64_t address_length; // Address Length + // Optional: Resource Source Index and String follow if length > 43 +} __attribute__((packed)) acpi_qword_address_space_t; + +typedef struct { + uint8_t tag; // 0x47 (Type 0x08, Length 7) + uint8_t info; // Flags (16-bit decode, etc.) + uint16_t min_address; // Minimum I/O address + uint16_t max_address; // Maximum I/O address + uint8_t alignment; // Alignment requirement + uint8_t address_length; // Number of ports used +} __attribute__((packed)) acpi_io_port_t; + +typedef struct acpi_crs_list { + enum aml_data_resource_type resource_type; + uintptr_t data_addr; + struct acpi_crs_list *next; +} __attribute__((packed)) acpi_crs_list_t; + +typedef struct { + uint8_t* start; + uint8_t* current; +} scanner_t; + +extern scanner_t scanner; +extern pci_resources_t *pci_resources; + +// ====== Refactor ===== +typedef struct { + void *start; + void *next; + void *end; +} mempool_t; + +typedef struct { + uint64_t value; + aml_data_type_t type; + uint32_t length; +} aml_data_t; + +typedef struct { + aml_data_t address; + aml_data_t pin; + aml_data_t source; + aml_data_t source_index; +} aml_prt_package_t; + +typedef struct aml_namespace_node { + uint8_t *pkt_start; + uint8_t *pkt_end; + struct aml_namespace_node *parent; // parent + struct aml_namespace_node *child; // first child object + struct aml_namespace_node *next; // siblings + char name[5]; // Name Segment + enum aml_encoding_value op_code; + aml_data_t data; // Store evaluation results + bool evaluated; // If this has been evaluated +} aml_namespace_node_t; + +typedef struct parse_state { + struct parse_state *parent; + uint8_t *node_start; + uint8_t *pkt_end; + aml_namespace_node_t *node; + uint16_t op_code; + uint8_t stage_idx; + uint8_t num_args; + aml_data_t arguments[10]; + bool evaluation; + bool if_condition; +} parse_state_t; + +#define MAX_NUM_ACPI_TABLES 20 + +typedef enum { + ACPI_TABLE_TYPE_FADT, + ACPI_TABLE_TYPE_DSDT, + ACPI_TABLE_TYPE_SSDT, + ACPI_TABLE_TYPE_MCFG, +} acpi_table_type_t; + +typedef struct { + uintptr_t tables_offset[MAX_NUM_ACPI_TABLES]; + uintptr_t tables_end; + uintptr_t mem_end; + uint32_t alignment; + uint32_t num_tables; +} acpi_tables_summary_t; + +extern mempool_t aml_namespace_mempool; +extern aml_namespace_node_t namespace_root; +extern uintptr_t ecam_base_paddr; +extern cnode_specs_t post_boot_cnode; + +void scan_namespace_tree(aml_namespace_node_t *namespace, uint8_t *namespace_end); +aml_namespace_node_t *find_child_node_by_name(aml_namespace_node_t *node, const char *name_segment); +uint8_t find_decendant_nodes_by_name(aml_namespace_node_t *node, const char *name_segment, aml_namespace_node_t **lookup_results, uint8_t num_results); +void read_eisa_id(aml_namespace_node_t *node, char *eisa_id_str); +aml_data_t eval_namespace_node(aml_namespace_node_t *node, uint8_t num_args, aml_data_t argv[]); +uint8_t advance(); +void set_scanner_to(uint8_t *start); +uint8_t *get_pkt_end(); +void parse_prt_package(aml_namespace_node_t *prt_node, aml_data_t prt_data, uint32_t bridge_idx); diff --git a/drivers/acpi/acpi_driver.mk b/drivers/acpi/acpi_driver.mk new file mode 100644 index 000000000..efa692db1 --- /dev/null +++ b/drivers/acpi/acpi_driver.mk @@ -0,0 +1,27 @@ +# +# Copyright 2026, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Include this snippet in your project Makefile to build +# the ACPI driver +# +# NOTES: +# Generates acpi_driver.elf +# Expects libsddf_util_debug.a to be in ${LIBS} + +ACPI_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +acpi_driver.elf: acpi/acpi.o acpi/interpreter.o + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +acpi/%.o: ${ACPI_DIR}/%.c ${ACPI_DIR}/interpreter.o ${CHECK_FLAGS_BOARD_MD5} |acpi $(SDDF_LIBC_INCLUDE) + ${CC} ${CFLAGS} -o $@ -c $^ + +acpi: + mkdir -p acpi + +clean:: + rm -rf acpi +clobber:: + rm -f acpi_driver.elf diff --git a/drivers/acpi/interpreter.c b/drivers/acpi/interpreter.c new file mode 100644 index 000000000..6995d7dc1 --- /dev/null +++ b/drivers/acpi/interpreter.c @@ -0,0 +1,1415 @@ + +#include +#include "acpi.h" +// =========================== Refactor ========================= + +const char acpi_str_adr[] = {'_', 'A', 'D', 'R', 0}; // Address +const char acpi_str_bbn[] = {'_', 'B', 'B', 'N', 0}; // BIOS Bus Number +// TODO: share signatures +const char test_aml_str_crs[] = {'_', 'C', 'R', 'S', 0}; // Current Resource Settings + +typedef enum { + INIT = 0, + PKT_LEN, + OBJECT_NAME_STRING, // Name String used for creating objects + NAME_STRING, // Name String referring to a namespace node + TERM_LIST, + FIELD_LIST, + TERM_INTEGER, + BYTE_DATA, + WORD_DATA, + DWORD_DATA, + QWORD_DATA, + STRING_DATA, + BUFFER_DATA, + PACKAGE_DATA, + DATA_OBJECT, + COMPLETE, +} parse_stage_t; + +#define MAX_OPCODE 256 // 1-byte AML opcode +#define MAX_OP_STAGES 8 + +parse_stage_t op_stage_table[MAX_OPCODE][MAX_OP_STAGES] = { + [NULL_OP] = { INIT, TERM_LIST, COMPLETE }, + [RETURN_OP] = { INIT, DATA_OBJECT, COMPLETE }, + [SCOPE_OP] = { INIT, PKT_LEN, OBJECT_NAME_STRING, TERM_LIST, COMPLETE }, + [METHOD_OP] = { INIT, PKT_LEN, OBJECT_NAME_STRING, BYTE_DATA, TERM_LIST, COMPLETE }, + [NAME_OP] = { INIT, OBJECT_NAME_STRING, DATA_OBJECT, COMPLETE}, + [STORE_OP] = { INIT, DATA_OBJECT, NAME_STRING, COMPLETE }, + [IF_OP] = { INIT, PKT_LEN, TERM_INTEGER, TERM_LIST, COMPLETE }, + [ELSE_OP] = { INIT, PKT_LEN, TERM_LIST, COMPLETE }, + [ALIAS_OP] = { INIT, NAME_STRING, NAME_STRING, COMPLETE }, + [CREATE_BIT_FIELD_OP] = { INIT, BUFFER_DATA, TERM_INTEGER, OBJECT_NAME_STRING, COMPLETE }, + [CREATE_BYTE_FIELD_OP] = { INIT, BUFFER_DATA, TERM_INTEGER, OBJECT_NAME_STRING, COMPLETE }, + [CREATE_WORD_FIELD_OP] = { INIT, BUFFER_DATA, TERM_INTEGER, OBJECT_NAME_STRING, COMPLETE }, + [CREATE_DWORD_FIELD_OP] = { INIT, BUFFER_DATA, TERM_INTEGER, OBJECT_NAME_STRING, COMPLETE }, + [CREATE_QWORD_FIELD_OP] = { INIT, BUFFER_DATA, TERM_INTEGER, OBJECT_NAME_STRING, COMPLETE }, + [LEQUAL_OP] = { INIT, TERM_INTEGER, TERM_INTEGER, COMPLETE }, + [AND_OP] = { INIT, TERM_INTEGER, TERM_INTEGER, NAME_STRING, COMPLETE }, + [ADD_OP] = { INIT, TERM_INTEGER, TERM_INTEGER, NAME_STRING, COMPLETE }, + [SUBTRACT_OP] = { INIT, TERM_INTEGER, TERM_INTEGER, NAME_STRING, COMPLETE }, + [SHIFT_LEFT_OP] = { INIT, TERM_INTEGER, TERM_INTEGER, NAME_STRING, COMPLETE }, + [SHIFT_RIGHT_OP] = { INIT, TERM_INTEGER, TERM_INTEGER, NAME_STRING, COMPLETE }, + [BYTE_PREFIX] = { INIT, BYTE_DATA, COMPLETE }, + [WORD_PREFIX] = { INIT, WORD_DATA, COMPLETE }, + [DWORD_PREFIX] = { INIT, DWORD_DATA, COMPLETE }, + [QWORD_PREFIX] = { INIT, QWORD_DATA, COMPLETE }, + [STRING_PREFIX] = { INIT, STRING_DATA, COMPLETE }, + [BUFFER_PREFIX] = { INIT, PKT_LEN, TERM_INTEGER, BUFFER_DATA, COMPLETE }, + [PACKAGE_PREFIX] = { INIT, PKT_LEN, COMPLETE }, + [DEREF_OF_OP] = { INIT, TERM_INTEGER, COMPLETE }, + [INDEX_OP] = { INIT, DATA_OBJECT, TERM_INTEGER, NAME_STRING, COMPLETE }, +}; + +parse_stage_t op_stage_5b_table[MAX_OPCODE][MAX_OP_STAGES] = { + [FIELD_OP & 0xFF] = { INIT, PKT_LEN, OBJECT_NAME_STRING, BYTE_DATA, FIELD_LIST, COMPLETE}, + [CREATE_FIELD_OP & 0xFF] = { INIT, BUFFER_DATA, TERM_INTEGER, TERM_INTEGER, OBJECT_NAME_STRING, COMPLETE }, + [INDEX_FIELD_OP & 0xFF] = { INIT, PKT_LEN, COMPLETE}, + [OP_REGION_OP & 0xFF] = { INIT, OBJECT_NAME_STRING, BYTE_DATA, TERM_INTEGER, TERM_INTEGER, COMPLETE}, + [DEVICE_OP & 0xFF] = { INIT, PKT_LEN, OBJECT_NAME_STRING, TERM_LIST, COMPLETE }, + [MUTEX_OP & 0xFF] = { INIT, NAME_STRING, BYTE_DATA, COMPLETE }, + [POWER_RESOURCE_OP & 0xFF] = { INIT, PKT_LEN, NAME_STRING, BYTE_DATA, WORD_DATA, TERM_LIST, COMPLETE }, + [PROCESSOR_OP & 0xFF] = { INIT, PKT_LEN, COMPLETE }, + [THERMAL_ZONE_OP & 0xFF] = { INIT, PKT_LEN, COMPLETE }, +}; + +parse_stage_t op_stage_lnot_table[MAX_OPCODE][MAX_OP_STAGES] = { + [LNOT_EQUAL_OP & 0xFF] = { INIT, TERM_INTEGER, TERM_INTEGER, COMPLETE }, + [LLESS_EQUAL_OP & 0xFF] = { INIT, TERM_INTEGER, TERM_INTEGER, COMPLETE }, + [LGREATER_EQUAL_OP & 0xFF] = { INIT, TERM_INTEGER, TERM_INTEGER, COMPLETE }, +}; + +parse_stage_t op_stage_custom[MAX_OPCODE][MAX_OP_STAGES] = { + [PRT_PACKAGE & 0xFF] = { INIT, PKT_LEN, BYTE_DATA, TERM_INTEGER, TERM_INTEGER, DATA_OBJECT, TERM_INTEGER, COMPLETE }, +}; + +parse_state_t *current_state; + +mempool_t state_stack_mempool = { + .start = (void *)0x50000000, + .next = (void *)0x50000000, + .end = (void *)0x50010000, +}; + +#define READ_BITS(val, m, n) (((val) >> (m)) & ((1ULL << (n)) - 1)) + +scanner_t scanner; + +// =============== Memory Pool ============= + +void *mempool_alloc(mempool_t *mempool, uint32_t mem_size) +{ + if (mempool->next + mem_size >= mempool->end) { + // Error: Out of memory for AML objects + return 0; + } + + void *allocated_mem = mempool->next; + mempool->next = mempool->next + mem_size; + + for (uint8_t *clear_byte = allocated_mem; clear_byte < (uint8_t *)mempool->next; clear_byte++) { + *clear_byte = 0; + } + return allocated_mem; +} + +void mempool_rc(mempool_t *mempool, void *addr, uint32_t mem_size) +{ + if (addr + mem_size < mempool->next) { + sddf_dprintf("[Error] failed to release memory [0x%lx-0x%lx] from allocated memory pool [0x%lx-0x%lx]\n", (uintptr_t)addr, (uintptr_t)(addr + mem_size), (uintptr_t)mempool->start, (uintptr_t)mempool->next); + return; + } + + mempool->next = addr; +} + +// =============== Namespace Node ============== + +// Return object pointer if already exists +aml_namespace_node_t *find_local_variable_in_namespace(aml_namespace_node_t *node, uint8_t op_code) +{ + if (node == NULL) return NULL; + if (node->child == NULL) return NULL; + + aml_namespace_node_t *child = node->child; + while (child) { + if (child->op_code == op_code) return child; + child = child->next; + } + + return NULL; +} + +// Return object pointer if already exists +aml_namespace_node_t *find_child_node_by_name(aml_namespace_node_t *node, const char *name_segment) +{ + if (node == NULL) return NULL; + if (node->child == NULL) return NULL; + + aml_namespace_node_t *child = node->child; + while (child) { + if (!strcmp(child->name, name_segment)) return child; + if (child->op_code == OP_REGION_OP) { + aml_namespace_node_t *field_node = find_child_node_by_name(child, name_segment); + if (field_node) { + return field_node; + } + } + child = child->next; + } + + return NULL; +} + +uint8_t find_decendant_nodes_by_name(aml_namespace_node_t *node, const char *name_segment, aml_namespace_node_t **lookup_results, uint8_t num_results) +{ + if (!strcmp(node->name, name_segment)) { + lookup_results[num_results] = node; + num_results++; + } + aml_namespace_node_t *child = node->child; + while (child) { + num_results = find_decendant_nodes_by_name(child, name_segment, lookup_results, num_results); + child = child->next; + } + + return num_results; +} + +aml_namespace_node_t *find_namespace_node_by_name(aml_namespace_node_t *node, const char *name_segment) +{ + aml_namespace_node_t *parent = node; + while (parent) { + aml_namespace_node_t *target = find_child_node_by_name(parent, name_segment); + if (target) { + return target; + } + parent = parent->parent; + } + return NULL; +} + +aml_namespace_node_t *namespace_insert_child_node(aml_namespace_node_t *namespace, const char *name_segment, enum aml_encoding_value op_code) +{ + aml_namespace_node_t *child_node = (aml_namespace_node_t *)mempool_alloc(&aml_namespace_mempool, sizeof(aml_namespace_node_t)); + if (child_node == NULL) { + sddf_dprintf("Failed to create a new namespace node: Out of Memory\n"); + return NULL; + } + + child_node->pkt_start = current_state->node_start; + child_node->parent = namespace; + child_node->op_code = op_code; + if (name_segment != NULL) { + memcpy(&child_node->name, name_segment, 4); + child_node->name[4] = '\0'; + /* sddf_dprintf("Create a type 0x%02X object: %s at 0x%lx, parent: %s\n", op_code, name_segment, (uintptr_t)scanner.current, namespace->name); */ + } else { + /* sddf_dprintf("Create a type 0x%02X object\n", op_code); */ + } + + // Insert the new node into the front of list + if (namespace->child) { + child_node->next = namespace->child; + } + + namespace->child = child_node; + return child_node; +} + +// =============== State Stack =============== + +parse_stage_t get_op_stage() +{ + if (current_state->op_code == NULL_OP) { + return INIT; + } + + if ((current_state->op_code & 0xFF00) == 0x5B00) { + uint8_t second_op_code = current_state->op_code & 0xFF; + return op_stage_5b_table[second_op_code][current_state->stage_idx]; + } else if ((current_state->op_code & 0xFF00) == 0x9200) { + uint8_t second_op_code = current_state->op_code & 0xFF; + return op_stage_lnot_table[second_op_code][current_state->stage_idx]; + } else if ((current_state->op_code & 0xFF00) == 0xFE00) { + uint8_t second_op_code = current_state->op_code & 0xFF; + return op_stage_custom[second_op_code][current_state->stage_idx]; + } + + return op_stage_table[current_state->op_code][current_state->stage_idx]; +} + +void state_stack_create(uint16_t op_code, bool evaluation) +{ + current_state = (parse_state_t *)mempool_alloc(&state_stack_mempool, sizeof(parse_state_t)); + current_state->op_code = op_code; + current_state->stage_idx = 0; + current_state->evaluation = evaluation; + current_state->node_start = scanner.current - 1; + if ((op_code & 0x5B00) == 0x5B00) { + current_state->node_start = scanner.current - 2; + } +} + +void state_stack_push(uint16_t op_code, bool evaluation) +{ + parse_state_t *reserved_state = current_state; + + state_stack_create(op_code, evaluation); + current_state->parent = reserved_state; + current_state->node = current_state->parent->node; // used for looking up namespace nodes +} + +void state_stack_add_argument(aml_data_t argument) +{ + /* sddf_dprintf("add argument(%u): {0x%lx, %u, %u} to op 0x%04x\n", current_state->num_args, argument.value, argument.type, argument.length, current_state->op_code); */ + current_state->arguments[current_state->num_args].value = argument.value; + current_state->arguments[current_state->num_args].type = argument.type; + current_state->arguments[current_state->num_args].length = argument.length; + current_state->num_args++; +} + +void write_to_buffer(uint64_t buf_addr, uint64_t value, uint8_t bytes) +{ + sddf_dprintf("update FIELD(%u bytes) to 0x%lx at 0x%lx\n", + bytes, + value, + buf_addr); + + uint8_t *buffer = (uint8_t *)buf_addr; + while (bytes--) { + *buffer = value & 0xFF; + value = value >> 8; + buffer++; + } +} + +void store_op_evaluation() +{ + assert(current_state->num_args == 2); + assert(current_state->arguments[1].type == DATA_OBJ_NODE); + aml_namespace_node_t *target_node = (aml_namespace_node_t *)current_state->arguments[1].value; + if (target_node) { + // TODO: distinguish bitIndex and ByteIndex + switch (target_node->op_code) { + case CREATE_BIT_FIELD_OP: { + sddf_dprintf("update BIT_FIELD %s to 0x%lx at 0x%lx\n", + target_node->name, + current_state->arguments[0].value, + target_node->data.value); + uint8_t *buf_to_update = (uint8_t *)target_node->data.value; + // bitOffset in byte is stored in data.length + uint8_t bit_value = 0; + if (current_state->arguments[0].value) { + bit_value = 0xFF; + } else { + bit_value = ~(1 << (target_node->data.length % 8)); + } + // TODO: improve this + *buf_to_update = (*buf_to_update) & bit_value; + break; + } + case CREATE_BYTE_FIELD_OP: { + write_to_buffer(target_node->data.value, current_state->arguments[0].value, 1); + break; + } + case CREATE_WORD_FIELD_OP: { + write_to_buffer(target_node->data.value, current_state->arguments[0].value, 2); + break; + } + case CREATE_DWORD_FIELD_OP: { + write_to_buffer(target_node->data.value, current_state->arguments[0].value, 4); + break; + } + case CREATE_QWORD_FIELD_OP: { + write_to_buffer(target_node->data.value, current_state->arguments[0].value, 8); + break; + } + default: { + target_node->data = current_state->arguments[0]; + target_node->evaluated = true; + sddf_dprintf("save value %lu to node\n", target_node->data.value); + } + } + } else { + sddf_dprintf("target node is invalid\n"); + } +} + +void state_stack_update(); +void state_stack_pop() +{ + aml_data_t ret_data; + ret_data.type = DATA_OBJ_QWORD; + if (current_state && current_state->num_args > 0) { + ret_data = current_state->arguments[0]; + } + + if (current_state && current_state->evaluation) { + switch (current_state->op_code) { + case STORE_OP: { + store_op_evaluation(); + break; + } + case LEQUAL_OP: { + assert(current_state->num_args == 2); + sddf_dprintf("Equal: %lu == %lu\n", current_state->arguments[0].value, current_state->arguments[1].value); + ret_data.value = current_state->arguments[0].value == current_state->arguments[1].value; + break; + } + case LNOT_EQUAL_OP: { + assert(current_state->num_args == 2); + sddf_dprintf("Equal: %lu != %lu\n", current_state->arguments[0].value, current_state->arguments[1].value); + ret_data.value = current_state->arguments[0].value != current_state->arguments[1].value; + break; + } + case LLESS_EQUAL_OP: { + assert(current_state->num_args == 2); + sddf_dprintf("Equal: %lu <= %lu\n", current_state->arguments[0].value, current_state->arguments[1].value); + ret_data.value = current_state->arguments[0].value <= current_state->arguments[1].value; + break; + } + case LGREATER_EQUAL_OP: { + assert(current_state->num_args == 2); + sddf_dprintf("Equal: %lu >= %lu\n", current_state->arguments[0].value, current_state->arguments[1].value); + ret_data.value = current_state->arguments[0].value >= current_state->arguments[1].value; + break; + } + case AND_OP: { + assert(current_state->num_args == 3); + assert(current_state->arguments[2].type == DATA_OBJ_NODE); + ret_data.value = current_state->arguments[0].value & current_state->arguments[1].value; + sddf_dprintf("and: 0x%lx & 0x%lx = 0x%lx\n", current_state->arguments[0].value, current_state->arguments[1].value, ret_data.value); + aml_namespace_node_t *supername_node = (aml_namespace_node_t *)current_state->arguments[2].value; + if (supername_node) { + supername_node->data = ret_data; + supername_node->evaluated = true; + sddf_dprintf("save value %lu to node %s\n", supername_node->data.value, supername_node->name); + } + break; + } + case ADD_OP: { + assert(current_state->num_args == 3); + assert(current_state->arguments[2].type == DATA_OBJ_NODE); + ret_data.value = current_state->arguments[0].value + current_state->arguments[1].value; + sddf_dprintf("add: 0x%lx + 0x%lx = 0x%lx\n", current_state->arguments[0].value, current_state->arguments[1].value, ret_data.value); + aml_namespace_node_t *supername_node = (aml_namespace_node_t *)current_state->arguments[2].value; + if (supername_node) { + supername_node->data = ret_data; + supername_node->evaluated = true; + sddf_dprintf("save value %lu to node %s\n", supername_node->data.value, supername_node->name); + } + break; + } + case SUBTRACT_OP: { + assert(current_state->num_args == 3); + assert(current_state->arguments[2].type == DATA_OBJ_NODE); + ret_data.value = current_state->arguments[0].value - current_state->arguments[1].value; + sddf_dprintf("subtract: 0x%lx - 0x%lx = 0x%lx\n", current_state->arguments[0].value, current_state->arguments[1].value, ret_data.value); + aml_namespace_node_t *supername_node = (aml_namespace_node_t *)current_state->arguments[2].value; + if (supername_node) { + supername_node->data = ret_data; + supername_node->evaluated = true; + sddf_dprintf("save value %lu to node %s\n", supername_node->data.value, supername_node->name); + } + break; + } + case SHIFT_LEFT_OP: { + assert(current_state->num_args == 3); + assert(current_state->arguments[2].type == DATA_OBJ_NODE); + sddf_dprintf("argument0: 0x%lx, argument1: 0x%lx\n", current_state->arguments[0].value, current_state->arguments[1].value); + ret_data.value = (current_state->arguments[0].value) << (current_state->arguments[1].value); + aml_namespace_node_t *supername_node = (aml_namespace_node_t *)current_state->arguments[2].value; + if (supername_node) { + supername_node->data.value = ret_data.value; + supername_node->evaluated = true; + sddf_dprintf("save value %lu to node %s\n", supername_node->data.value, supername_node->name); + } + break; + } + case SHIFT_RIGHT_OP: { + assert(current_state->num_args == 3); + assert(current_state->arguments[2].type == DATA_OBJ_NODE); + ret_data.value = (current_state->arguments[0].value) >> (current_state->arguments[1].value); + sddf_dprintf("argument0: 0x%lx, argument1: 0x%lx, ret_val: 0x%lx\n", current_state->arguments[0].value, current_state->arguments[1].value, ret_data.value); + aml_namespace_node_t *supername_node = (aml_namespace_node_t *)current_state->arguments[2].value; + if (supername_node) { + supername_node->data.value = ret_data.value; + supername_node->evaluated = true; + sddf_dprintf("save value %lu to node %s\n", supername_node->data.value, supername_node->name); + } + break; + } + case BUFFER_PREFIX: { + assert(current_state->num_args == 1); + ret_data.value = (uint64_t)current_state->pkt_end - current_state->arguments[0].value; + ret_data.type = DATA_OBJ_BUFFER; + ret_data.length = current_state->arguments[0].value; + sddf_dprintf("return buffer prefix: 0x%lx, len: 0x%x\n", ret_data.value, ret_data.length); + break; + } + case PACKAGE_PREFIX: { + assert(current_state->num_args == 1); + ret_data.value = current_state->arguments[0].value; + ret_data.length = (uint64_t)current_state->pkt_end - current_state->arguments[0].value; + ret_data.type = DATA_OBJ_PACKAGE; + sddf_dprintf("return package prefix: 0x%lx\n", ret_data.value); + break; + } + case NAME_OP: { + assert(current_state->num_args == 2); + assert(current_state->arguments[0].type == DATA_OBJ_RET); + sddf_dprintf("complete NameOp %s: addr: 0x%lx, ret_buf = %u\n", current_state->node->name, (uintptr_t)current_state->arguments[0].value, (uint32_t)current_state->arguments[2].value); + // TODO: check ret_type + aml_data_t *eval_ret = (aml_data_t *)current_state->arguments[0].value; + *eval_ret = current_state->arguments[1]; + ret_data = current_state->arguments[1]; + if (current_state->node) { + current_state->node->data = current_state->arguments[1]; + current_state->node->evaluated = true; + } + break; + } + case RETURN_OP: { + assert(current_state->num_args == 1); + sddf_dprintf("complete MethodOp: %s, ret_buf = 0x%lx\n", current_state->parent->node->name, (uint64_t)current_state->arguments[0].value); + aml_data_t method_ret = current_state->arguments[0]; + while (current_state && current_state->op_code != METHOD_OP) { + parse_state_t *completed_state = current_state; + current_state = current_state->parent; + mempool_rc(&state_stack_mempool, (void *)completed_state, sizeof(parse_state_t)); + } + if (current_state) { + aml_data_t *eval_ret = (aml_data_t *)current_state->arguments[0].value; + *eval_ret = method_ret; + current_state->stage_idx += 1; // MethodOp completes + } + break; + } + case PRT_PACKAGE: { + assert(current_state->num_args == 5); + assert(current_state->arguments[0].type == DATA_OBJ_RET); + aml_prt_package_t *prt = (aml_prt_package_t *)current_state->arguments[0].value; + prt->address = current_state->arguments[1]; + prt->pin = current_state->arguments[2]; + prt->source = current_state->arguments[3]; + prt->source_index = current_state->arguments[4]; + break; + } + case OP_REGION_OP: { + assert(current_state->num_args == 4); + assert(current_state->arguments[0].type == DATA_OBJ_RET); + uint8_t region_space = current_state->arguments[1].value; + uintptr_t region_offset = current_state->arguments[2].value; + uint64_t region_length = current_state->arguments[3].value; + assert(current_state->arguments[0].value % 8 == 0); + aml_data_t *eval_ret = (aml_data_t *)current_state->arguments[0].value; + + uintptr_t field_register = region_offset; + if (region_space == 0x00) { + + } else if (region_space == 0x02) { + aml_namespace_node_t *adr_node = find_namespace_node_by_name(current_state->node, acpi_str_adr); + // TODO: this might be 64-bit + uint64_t address; + aml_data_t addr_eval_ret = eval_namespace_node(adr_node, 0, NULL); + address = addr_eval_ret.value; + sddf_dprintf("address node name: %s, addr: 0x%lx\n", adr_node->name, address); + + uint64_t bus; + aml_namespace_node_t *bbn_node = find_namespace_node_by_name(current_state->node, acpi_str_bbn); + aml_data_t bus_eval_ret = eval_namespace_node(bbn_node, 0, NULL); + bus = bus_eval_ret.value; + sddf_dprintf("bus node name: %s, bus: 0x%lx\n", bbn_node->name, bus); + + // TODO: use of region_offset and region_length + sddf_dprintf("field_reg: 0x%lx, bus: 0x%lx, address: 0x%lx, region_offset: 0x%lx\n", field_register, bus, address, region_offset); + field_register = ecam_base_paddr + field_register + (bus << 20) + address; + } else { + sddf_dprintf("Region space 0x%x is not implemneted\n", region_space); + } + + sddf_dprintf("field_register: 0x%lx\n", field_register); + eval_ret->value = field_register; + eval_ret->length = region_length; + ret_data = *eval_ret; + + /* sddf_dprintf("complete OpRegionOp: %s, ret_buf = %lu\n", current_state->node->name, eval_ret->value); */ + break; + } + case CREATE_BIT_FIELD_OP: + case CREATE_BYTE_FIELD_OP: + case CREATE_WORD_FIELD_OP: + case CREATE_DWORD_FIELD_OP: + case CREATE_QWORD_FIELD_OP: { + assert(current_state->num_args == 2); + assert(current_state->arguments[0].type == DATA_OBJ_BUFFER); + aml_data_t field_buffer = current_state->arguments[0]; + uint64_t index = current_state->arguments[1].value; + if (current_state->op_code == CREATE_BIT_FIELD_OP) { + // 2nd argument is bitIndex in CreateBitFieldOp + field_buffer.value = field_buffer.value + index / 8; + // use buffer_field.length as bit offset + field_buffer.length = index % 8; + } else { + // 2nd argument is byteIndex + field_buffer.value = field_buffer.value + index; + } + current_state->node->data = field_buffer; + current_state->node->evaluated = true; + /* sddf_dprintf("CreateFieldOp: {0x%lx, %u, %u}, name: %s\n", field_buffer.value, field_buffer.type, field_buffer.length, current_state->node->name); */ + break; + } + } + } + + // Update + if ((current_state->parent == NULL || current_state->parent->node != current_state->node) && current_state->node && current_state->node->pkt_end == 0) { + current_state->node->pkt_end = scanner.current; + /* sddf_dprintf("save pkt_end 0x%lx to node %s\n", (uintptr_t)scanner.current, current_state->node->name); */ + } + + /* sddf_dprintf("Stack pop Op 0x%04x, current: 0x%lx, pkt_end: 0x%lx, parent: 0x%lx\n", current_state->op_code, (uintptr_t)scanner.current, (uintptr_t)current_state->pkt_end, (uintptr_t)current_state->parent); */ + parse_state_t *completed_state = current_state; + current_state = current_state->parent; + mempool_rc(&state_stack_mempool, (void *)completed_state, sizeof(parse_state_t)); + + if (current_state != NULL) { + parse_stage_t op_stage = get_op_stage(); + if (op_stage == TERM_INTEGER || op_stage == BUFFER_DATA || op_stage == DATA_OBJECT) { + state_stack_add_argument(ret_data); + /* sddf_dprintf("after argument adding: Op 0x%04x, idx: %u, current: 0x%lx, pkt_end: 0x%lx\n", current_state->op_code, current_state->stage_idx, (uintptr_t)scanner.current, (uintptr_t)current_state->pkt_end); */ + state_stack_update(); + } + } + + if (current_state != NULL) { + parse_stage_t op_stage = get_op_stage(); + if ((current_state->pkt_end && scanner.current >= current_state->pkt_end) || op_stage == COMPLETE) { + /* sddf_dprintf("pop at end current: 0x%lx, pkt_end: 0x%lx\n", (uintptr_t)scanner.current, (uintptr_t)current_state->pkt_end); */ + state_stack_pop(); + } + } +} + +void state_stack_update() +{ + parse_stage_t op_stage = get_op_stage(); + if (!current_state->evaluation && (current_state->op_code == IF_OP || current_state->op_code == ELSE_OP) && op_stage == PKT_LEN) { + // TODO: This should be removed once real-time value reading is implemented + if (current_state->evaluation == false) { + current_state->stage_idx = 4; + } + } else if (!current_state->evaluation && current_state->op_code == IF_OP && op_stage == TERM_INTEGER) { + if (current_state->num_args == 1 && current_state->arguments[0].value == 0) { + current_state->stage_idx += 2; + } + } else if (!current_state->evaluation && current_state->op_code == METHOD_OP && op_stage == OBJECT_NAME_STRING) { + current_state->stage_idx += 3; + scanner.current = current_state->pkt_end; + } else if (current_state->evaluation && current_state->op_code == IF_OP && op_stage == TERM_INTEGER) { + if (current_state->num_args == 1 && current_state->arguments[0].value == 0) { + current_state->parent->if_condition = false; + current_state->stage_idx += 2; + } else { + current_state->parent->if_condition = true; + } + } else if (current_state->evaluation && current_state->op_code == ELSE_OP && op_stage == PKT_LEN) { + if (current_state->parent->if_condition) { + sddf_dprintf("Skip Elseif to 0x%lx\n", (uintptr_t)current_state->pkt_end); + current_state->stage_idx += 2; + } else { + current_state->stage_idx += 1; + } + } else if (current_state->op_code == BUFFER_PREFIX && op_stage == TERM_INTEGER) { + /* sddf_dprintf("buffer pkt_start: 0x%lx, pkt_end: 0x%lx, buffer_size: 0x%lx\n", */ + /* (uintptr_t)current_state->node->pkt_start, */ + /* (uintptr_t)current_state->pkt_end, */ + /* current_state->arguments[0].value); */ + current_state->stage_idx += 2; + } else if (current_state->op_code == PACKAGE_PREFIX && op_stage == PKT_LEN) { + aml_data_t package_start = {(uint64_t)scanner.current, DATA_OBJ_QWORD, 0}; + state_stack_add_argument(package_start); + current_state->stage_idx += 1; + } else if (op_stage != TERM_LIST) { + current_state->stage_idx += 1; + } + + /* sddf_dprintf("current op_code: 0x%04x, idx: %u, stage: %u, num_args: %u\n", current_state->op_code, current_state->stage_idx, get_op_stage(), current_state->num_args); */ + + // Check if the Op has been completely parsed + op_stage = get_op_stage(); + if (op_stage == COMPLETE) { + if (current_state->pkt_end != 0) { + scanner.current = current_state->pkt_end; + } + state_stack_pop(); + } +} + +// ======================= AML Parser ==================== + +void set_scanner_to(uint8_t *start) +{ + scanner.current = start; +} + +uint8_t advance() { + scanner.current++; + return scanner.current[-1]; +} + +// scanner.current should be at start of pktLength when invoked +// PktLength consists of LeadByte followed by variable-length bytes, see more in Section 20.2.4 +uint8_t *get_pkt_end() +{ + uint8_t lead_byte = advance(); + uint8_t extra_bytes_len = lead_byte >> 6; + + // pktLength encoded in bits 5-0 if one byte long + if (extra_bytes_len == 0) { + return scanner.current + (lead_byte & 0x3F) - 1; + } + + uint32_t pkt_len = (lead_byte & 0xF) + (advance() << 4); + if (extra_bytes_len > 1) pkt_len += (advance() << 12); + if (extra_bytes_len > 2) pkt_len += (advance() << 20); + + return scanner.current + pkt_len - extra_bytes_len - 1; +} + +// See more in Section 20.2.2 +void skip_name_string() +{ + uint8_t name_type = advance(); + + if ((name_type >= 'A' && name_type < 'Z') || name_type == '_') { + // Name Segment + scanner.current += 3; + } else if (name_type == '\\' || name_type == '^') { + // Root Path + skip_name_string(); + } else if (name_type == 0x2E) { + // Dual Name Segment + scanner.current += 8; + } else if (name_type == 0x2F) { + // Multiple Name Segment + uint8_t seg_cnt = advance(); + scanner.current += (4 * seg_cnt); + } else { + scanner.current--; + } +} + +// Parse the compressed EISA ID to readable characters +// see 19.3.4 ASL Macros, EISAID +void read_eisa_id(aml_namespace_node_t *node, char *eisa_id_str) +{ + scanner.current = node->pkt_start + 1; // First byte for NAME_OP + skip_name_string(); + + uint8_t eisa_type = advance(); + + if (eisa_type == DATA_OBJ_DWORD) { + // Combine the first two bytes in little-endian + uint16_t char_word = advance() << 8; + char_word |= advance(); + + // Extract the 3 characters + // Bit mapping: 0-4 (Char 3), 5-9 (Char 2), 10-14 (Char 1) + eisa_id_str[0] = (char)(((char_word >> 10) & 0x1F) + 0x40); + eisa_id_str[1] = (char)(((char_word >> 5) & 0x1F) + 0x40); + eisa_id_str[2] = (char)((char_word & 0x1F) + 0x40); + + // Extract four Hex ID from the last two bytes + uint8_t hex_hi = advance(); + eisa_id_str[3] = (char)(HEX_TO_CHAR(hex_hi >> 4)); + eisa_id_str[4] = (char)(HEX_TO_CHAR(hex_hi & 0xF)); + uint8_t hex_lo = advance(); + eisa_id_str[5] = (char)(HEX_TO_CHAR(hex_lo >> 4)); + eisa_id_str[6] = (char)(HEX_TO_CHAR(hex_lo & 0xF)); + eisa_id_str[7] = '\0'; + } else if (eisa_type == DATA_OBJ_STRING){ + char c; + uint8_t i = 0; + while ((c = advance())) { + eisa_id_str[i] = c; + i++; + } + eisa_id_str[i] = '\0'; + } +} + +void read_name_segment(char *name_segment) +{ + name_segment[0] = (char)advance(); + name_segment[1] = (char)advance(); + name_segment[2] = (char)advance(); + name_segment[3] = (char)advance(); + name_segment[4] = '\0'; +} + +aml_namespace_node_t *find_node_by_name_string(aml_namespace_node_t *parent_node, uint8_t left_num_segments) +{ + char name_segment[5]; + uint8_t name_type = advance(); + aml_namespace_node_t *node = NULL; + + /* sddf_dprintf("name_type: 0x%x, current: 0x%lx, parent: %s\n", name_type, (uintptr_t)scanner.current, parent_node->name); */ + if (name_type == 0x00) { + /* sddf_dprintf("Null Name\n"); */ + return NULL; + } + + if (name_type >= LOCAL0_OP && name_type <= LOCAL7_OP) { + scanner.current--; + aml_namespace_node_t *local_variable = find_local_variable_in_namespace(parent_node, name_type); + if (local_variable) { + return local_variable; + } + + return namespace_insert_child_node(parent_node, NULL, name_type); + } + + if (name_type >= ARG0_OP && name_type <= ARG6_OP) { + scanner.current--; + aml_namespace_node_t *local_variable = find_local_variable_in_namespace(parent_node, name_type); + if (local_variable) { + return local_variable; + } + + sddf_dprintf("[Error] node ARG%u is not found\n", name_type - ARG0_OP); + return NULL; + } + + if ((name_type >= 'A' && name_type < 'Z') || name_type == '_') { + // Name Segment + scanner.current--; + read_name_segment(name_segment); + node = find_namespace_node_by_name(current_state->node, name_segment); + left_num_segments--; + /* sddf_dprintf(" node: 0x%lx, current: 0x%lx, segment: %s, parent: %s\n", (uintptr_t)node, (uintptr_t)scanner.current, name_segment, parent_node->name); */ + } else if (name_type == '\\') { + // Root Path + node = &namespace_root; + } else if (name_type == 0x2E) { + // Dual Name Segment + left_num_segments = 2; + node = parent_node; + } else if (name_type == 0x2F) { + // Multiple Name Segment + uint8_t seg_cnt = advance(); + left_num_segments = seg_cnt; + node = parent_node; + } else { + sddf_dprintf("Not a NameString at 0x%lx\n", (uintptr_t)scanner.current); + scanner.current--; + return NULL; + } + + if (node == NULL) { + return NULL; + } + + if (left_num_segments > 0) { + /* sddf_dprintf(" Parent: %s, Name segment: %s, left_num_segments: %u\n", node->name, name_segment, left_num_segments); */ + return find_node_by_name_string(node, left_num_segments); + } + + if (node->op_code == NAME_OP || node->op_code == METHOD_OP || node->op_code == DEVICE_OP + || node->op_code == OP_REGION_OP || node->op_code == FIELD_OP || node->op_code == CREATE_DWORD_FIELD_OP + || node->op_code == CREATE_QWORD_FIELD_OP || node->op_code == CREATE_WORD_FIELD_OP) { + return node; + } else { + sddf_dprintf("Object \'%s\' has invalid OpCode: 0x%x, try parsing the following name segment at 0x%lx\n", node->name, node->op_code, (uintptr_t)scanner.current); + return find_node_by_name_string(node, 1); + } + + return NULL; +} + +uint8_t *get_data_end() +{ + uint8_t first_byte = advance(); + switch (first_byte) { + case DATA_OBJ_ZERO: + case DATA_OBJ_ONE: + return scanner.current; + case DATA_OBJ_BYTE: + return scanner.current + 1; + case DATA_OBJ_WORD: + return scanner.current + 2; + case DATA_OBJ_DWORD: + return scanner.current + 4; + case DATA_OBJ_QWORD: + return scanner.current + 8; + case DATA_OBJ_STRING: { + while (advance()); + return scanner.current; + case DATA_OBJ_BUFFER: + return get_pkt_end(); + case DATA_OBJ_PACKAGE: + return get_pkt_end(); + default: + sddf_dprintf("Unkown prefix: 0x%x\n", first_byte); + } + } + return 0; +} + +aml_namespace_node_t *make_namespace_node(aml_namespace_node_t *namespace, uint16_t op_code) +{ + uint8_t name_type = advance(); + + if (name_type == '\\') { + namespace = &namespace_root; + name_type = advance(); + } + + if (name_type == '^') { + namespace = namespace->parent; + name_type = advance(); + } + + if (name_type == 0x00) { + return namespace; + } + + // Local variable [Local0Op, Local7Op] or ARGs [ARG0, ARG6] + if ((op_code >= LOCAL0_OP && op_code <= LOCAL7_OP) || (op_code >= ARG0_OP && op_code <= ARG6_OP)) { + scanner.current--; + aml_namespace_node_t *local_variable = find_local_variable_in_namespace(namespace, op_code); + if (local_variable) { + return local_variable; + } + return namespace_insert_child_node(namespace, NULL, op_code); + } + + if ((name_type >= 'A' && name_type <= 'Z') || name_type == '_') { + scanner.current--; + char name_segment[5]; + read_name_segment(name_segment); + aml_namespace_node_t *existing_node = find_child_node_by_name(namespace, name_segment); + if (existing_node) { + return existing_node; + } + + return namespace_insert_child_node(namespace, name_segment, op_code); + } + + uint8_t name_segment_cnt = 0; + if (name_type == 0x2E) { + name_segment_cnt = 2; + } else if (name_type == 0x2F) { + name_segment_cnt = advance(); + } else { + sddf_dprintf("[Error] invalid encoding \'0x%02x\' for expected NameString at 0x%lx\n", name_type, (uintptr_t)scanner.current); + return NULL; + } + + while (--name_segment_cnt) { + namespace = make_namespace_node(namespace, NULL_OP); + } + + return make_namespace_node(namespace, op_code); +} + +void parse_field_list() +{ + assert(current_state->arguments[0].type == DATA_OBJ_BYTE); + uint8_t field_flags = current_state->arguments[0].value; + // AccessType: bit 0-3 in FieldFlags + uint8_t access_type = field_flags & 0xF; + // Save 4-bit AccessType as data type of FieldOp to be used as alignment size + aml_data_type_t access_data_type = DATA_OBJ_ZERO; + switch (access_type) { + case 0: { access_data_type = DATA_OBJ_ZERO; break; } // DATA_OBJ_ZERO indicates AnyAcc + case 1: { access_data_type = DATA_OBJ_BYTE; break; } + case 2: { access_data_type = DATA_OBJ_WORD; break; } + case 3: { access_data_type = DATA_OBJ_DWORD; break; } + case 4: { access_data_type = DATA_OBJ_QWORD; break; } + case 5: { access_data_type = DATA_OBJ_BUFFER; break; } + default: { + sddf_dprintf("[Error] Unsupported AccessType: 0x%x\n", access_type); + } + } + + uint32_t bit_offset = 0; + while (scanner.current < current_state->pkt_end) { + uint8_t byte = advance(); + if ((byte >= 'A' && byte <= 'Z') || byte == '_') { + scanner.current--; + // Create FieldOps as direct Children of OpRegionOp + aml_namespace_node_t *field_node = make_namespace_node(current_state->node, FIELD_OP); + uint8_t *field_element_start = scanner.current; + uint32_t bit_width = get_pkt_end() - field_element_start; + // Save "| 56-bit offset | 8-bit width |" in data of FieldOp + field_node->data.value = ((uint64_t)bit_offset << 8) | bit_width; + field_node->data.type = access_data_type; + /* sddf_dprintf("field name: %s, bit_width: %u, bit_offset: 0x%x\n", field_node->name, bit_width, bit_offset); */ + bit_offset += bit_width; + } else if (byte == 0x00) { + uint8_t *field_element_start = scanner.current; + uint8_t *reserved_pkt_end = get_pkt_end(); + uint32_t padding_bits = (uint32_t)(reserved_pkt_end - field_element_start); + bit_offset += padding_bits; + /* sddf_dprintf("Reserved: current: 0x%lx, reserved_pkt_end: 0x%lx, width: 0x%x\n", (uintptr_t)scanner.current, (uintptr_t)reserved_pkt_end, padding_bits); */ + /* sddf_dprintf("bit_offset: 0x%x\n", bit_offset); */ + } else if (byte == 0x01) { + advance(); // Type + advance(); // Attrib + /* sddf_dprintf("Access field - type: 0x%x, attrib: 0x%x\n", type, attrib); */ + } else { + sddf_dprintf("Error: unknown prefix - 0x%x\n", byte); + } + } +} + +aml_data_t read_field_value(aml_namespace_node_t *field_node) +{ + sddf_dprintf("Try evaluating FieldOp: %s\n", field_node->name); + + // TODO: should be DWORD_DATA + aml_data_t op_region = eval_namespace_node(field_node->parent, 0, NULL); + sddf_dprintf("name: %s, ret_value: 0x%lx, op_code: 0x%x\n", field_node->parent->name, op_region.value, field_node->parent->op_code); + + // Decode bit_offset and bit_width from data of FieldOp + uint64_t field_bit_offset = field_node->data.value >> 8; + uint8_t field_bit_width = field_node->data.value & 0xFF; + sddf_dprintf("Field offset: 0x%lx, bit_offset: 0x%lx, width: %u\n", field_bit_offset / 8, field_bit_offset % 8, field_bit_width); + + // Align the field address to a 1-byte boundary + uintptr_t field_paddr = op_region.value + (field_bit_offset / 8); + uint8_t bit_offset = field_bit_offset % 8; + + // TODO: replace this constant value with a macro; + uintptr_t field_vaddr = 0x4000000 + PAGE_OFFSET(field_paddr);; + map_memory_region(&post_boot_cnode, field_paddr, op_region.length, field_vaddr); + + uint64_t final_field_value = 0; + uint8_t remaining_bit_width = field_bit_width; + // TODO: BufferAcc? + while (remaining_bit_width) { + uint8_t bit_width_to_read = 0; + uint64_t reg_val = 0; + switch (field_node->data.type) { + case DATA_OBJ_ZERO: + case DATA_OBJ_BYTE: { + bit_width_to_read = MIN(8 - bit_offset, remaining_bit_width); + + uint8_t *read_field_reg = (uint8_t *)field_vaddr; + reg_val = (uint64_t)*read_field_reg; + reg_val = READ_BITS(reg_val, bit_offset, bit_width_to_read); + + field_paddr += 1; + field_vaddr += 1; + break; + } + case DATA_OBJ_WORD: { + // Adjust to 2-byte alignment + bit_offset = bit_offset + (field_paddr % 2) * 8; + field_paddr = ROUND_DOWN(field_paddr, 2); + bit_width_to_read = MIN(2 * 8 - bit_offset, remaining_bit_width); + + uint16_t *read_field_reg = (uint16_t *)field_vaddr; + reg_val = (uint64_t)*read_field_reg; + reg_val = READ_BITS(reg_val, bit_offset, bit_width_to_read); + + field_paddr += 2; + field_vaddr += 2; + break; + } + case DATA_OBJ_DWORD: { + // Adjust to 4-byte alignment + bit_offset = bit_offset + (field_paddr % 4) * 8; + field_paddr = ROUND_DOWN(field_paddr, 4); + field_vaddr = ROUND_DOWN(field_vaddr, 4); + bit_width_to_read = MIN(4 * 8 - bit_offset, remaining_bit_width); + + uint32_t *read_field_reg = (uint32_t *)field_vaddr; + reg_val = (uint64_t)*read_field_reg; + reg_val = READ_BITS(reg_val, bit_offset, bit_width_to_read); + + field_paddr += 4; + field_vaddr += 4; + break; + } + case DATA_OBJ_QWORD: { + // Adjust to 8-byte alignment + bit_offset = bit_offset + (field_paddr % 8) * 8; + field_paddr = ROUND_DOWN(field_paddr, 8); + field_vaddr = ROUND_DOWN(field_vaddr, 8); + bit_width_to_read = MIN(8 * 8 - bit_offset, remaining_bit_width); + + uint64_t *read_field_reg = (uint64_t *)field_vaddr; + reg_val = (uint64_t)*read_field_reg; + reg_val = READ_BITS(reg_val, bit_offset, bit_width_to_read); + + field_paddr += 8; + field_vaddr += 8; + break; + } + default: { + sddf_dprintf("[Error] Unsupported AccessType: 0x%x\n", field_node->data.type); + } + } + + sddf_dprintf("[READ] paddr: 0x%lx, bit_offset: %u, bit_width: %u, read: 0x%lx, final: 0x%lx\n", field_paddr, bit_offset, bit_width_to_read, reg_val, final_field_value); + final_field_value = final_field_value + (reg_val << (field_bit_width - remaining_bit_width)); + bit_offset = 0; // no offset since round 2 + remaining_bit_width -= bit_width_to_read; + } + + // Unmap the mapped region + assert(cnode_untypeds_revoke(&post_boot_cnode) == seL4_NoError); + + sddf_dprintf("read field %s value: 0x%lx\n", field_node->name, final_field_value); + + aml_data_t field_data = {final_field_value, DATA_OBJ_QWORD, 0}; + return field_data; +} + +void parse_namespace_node(bool evaluation) +{ + /* sddf_dprintf("Evaluation? %s\n", evaluation ? "true" : "false"); */ + + uint16_t op_code = 0; + uint8_t *namespace_end = current_state->pkt_end; + /* sddf_dprintf("scanner.current: 0x%lx, end: 0x%lx\n", (uintptr_t)scanner.current, (uintptr_t)namespace_end); */ + + while (scanner.current < namespace_end) { + if (current_state == NULL) return; + uint8_t op_stage = get_op_stage(); + if (op_stage == PKT_LEN) { + current_state->pkt_end = get_pkt_end(); + /* } else if (!evaluation && op_stage == DATA_OBJECT) { */ + /* scanner.current = get_data_end(); */ + } else if (op_stage == OBJECT_NAME_STRING) { + aml_namespace_node_t *new_node = make_namespace_node(current_state->parent->node, current_state->op_code); + current_state->node = new_node; + + if (new_node->op_code != OP_REGION_OP) { + new_node->pkt_start = current_state->node_start; + if (current_state->pkt_end != 0) { + new_node->pkt_end = current_state->pkt_end; + } + } + } else if (op_stage == NAME_STRING) { + if (evaluation) { + sddf_dprintf("Need to read the value of node at 0x%lx, %s\n", (uintptr_t)scanner.current, current_state->node->name); + aml_namespace_node_t *node = find_node_by_name_string(current_state->node, 1); + aml_data_t argument = {(uint64_t)node, DATA_OBJ_NODE, 0}; + state_stack_add_argument(argument); + } else { + /* sddf_dprintf("Skip Name String\n"); */ + skip_name_string(); + } + } else if (op_stage == FIELD_LIST) { + parse_field_list(); + } else if (op_stage == BYTE_DATA) { + aml_data_t argument = {advance(), DATA_OBJ_BYTE, 0}; + state_stack_add_argument(argument); + } else if (op_stage == WORD_DATA) { + uint16_t data = advance(); + data |= (advance() << 8); + aml_data_t argument = {data, DATA_OBJ_WORD, 0}; + state_stack_add_argument(argument); + } else if (op_stage == DWORD_DATA) { + uint32_t data = advance(); + data |= ((uint32_t)advance() << 8); + data |= ((uint32_t)advance() << 16); + data |= ((uint32_t)advance() << 24); + aml_data_t argument = {data, DATA_OBJ_DWORD, 0}; + state_stack_add_argument(argument); + } else if (op_stage == QWORD_DATA) { + uint64_t data = advance(); + data |= ((uint64_t)advance() << 8); + data |= ((uint64_t)advance() << 16); + data |= ((uint64_t)advance() << 24); + data |= ((uint64_t)advance() << 32); + data |= ((uint64_t)advance() << 40); + data |= ((uint64_t)advance() << 48); + data |= ((uint64_t)advance() << 56); + aml_data_t argument = {data, DATA_OBJ_DWORD, 0}; + state_stack_add_argument(argument); + } else if (op_stage == STRING_DATA) { + while (advance()); + } else { + op_code = op_code | advance(); + if (op_code == 0x5B || op_code == 0x92) { + op_code = op_code << 8; + continue; + } + + switch (op_code) { + case ZERO_OP: { + aml_data_t argument = {0, DATA_OBJ_ZERO, 0}; + state_stack_add_argument(argument); + break; + } + case ONE_OP: { + aml_data_t argument = {1, DATA_OBJ_ONE, 0}; + state_stack_add_argument(argument); + break; + } + case ARG0_OP: + case ARG1_OP: + case ARG2_OP: + case ARG3_OP: + case ARG4_OP: + case ARG5_OP: + case ARG6_OP: { + sddf_dprintf("name: %s\n", current_state->node->name); + aml_namespace_node_t *arg_node = find_local_variable_in_namespace(current_state->node, op_code); + if (arg_node == NULL) { + sddf_dprintf("[Error] No arg node found\n"); + } + sddf_dprintf("Found arg 0x%lx\n", arg_node->data.value); + state_stack_add_argument(arg_node->data); + break; + } + case LOCAL0_OP: + case LOCAL1_OP: + case LOCAL2_OP: + case LOCAL3_OP: + case LOCAL4_OP: + case LOCAL5_OP: + case LOCAL6_OP: + case LOCAL7_OP: { + aml_namespace_node_t *local_node = make_namespace_node(current_state->node, op_code); + sddf_dprintf("Local variable\n"); + if (local_node && local_node->evaluated) { + state_stack_add_argument(local_node->data); + } else { + sddf_dprintf("Local%u is not found or evaluated\n", op_code - LOCAL0_OP); + } + break; + } + case BYTE_PREFIX: + case WORD_PREFIX: + case DWORD_PREFIX: + case QWORD_PREFIX: + case BUFFER_PREFIX: + case PACKAGE_PREFIX: + case STRING_PREFIX: + case ADD_OP: + case SUBTRACT_OP: + case SHIFT_LEFT_OP: + case SHIFT_RIGHT_OP: + case AND_OP: + case ALIAS_OP: + case SCOPE_OP: + case METHOD_OP: + case NAME_OP: + case MUTEX_OP: + case EVENT_OP: + case FIELD_OP: + case INDEX_FIELD_OP: + case DEREF_OF_OP: + case INDEX_OP: + case IF_OP: + case ELSE_OP: + case STORE_OP: + case LEQUAL_OP: + case OP_REGION_OP: + case CREATE_FIELD_OP: + case CREATE_BIT_FIELD_OP: + case CREATE_BYTE_FIELD_OP: + case CREATE_WORD_FIELD_OP: + case CREATE_DWORD_FIELD_OP: + case CREATE_QWORD_FIELD_OP: + case LNOT_EQUAL_OP: + case LLESS_EQUAL_OP: + case LGREATER_EQUAL_OP: + case POWER_RESOURCE_OP: + case PROCESSOR_OP: + case THERMAL_ZONE_OP: + case DEVICE_OP: + case RETURN_OP: { + if (evaluation) { + /* sddf_dprintf("stack push 0x%04x\n", op_code); */ + } + state_stack_push(op_code, evaluation); + break; + } + default: { + scanner.current--; + if (evaluation) { + // Try looking up the object by name string by name string by name string by name string + aml_namespace_node_t *node = find_node_by_name_string(current_state->node, 1); + if (node) { + /* sddf_dprintf("Found node %s\n", node->name); */ + + aml_data_t eval_ret = eval_namespace_node(node, 0, NULL); + state_stack_add_argument(eval_ret); + } else { + sddf_dprintf("[Error] Op \'0x%04x\' is not implemented\n", op_code); + } + } else { + /* sddf_dprintf("skip_name_string, op_code: 0x%x at 0x%lx\n", op_code, (uintptr_t)scanner.current); */ + skip_name_string(); + } + } + } + } + + state_stack_update(); + op_code = 0; + } +} + +void scan_namespace_tree(aml_namespace_node_t *namespace, uint8_t *namespace_end) +{ + state_stack_create(NULL_OP, false); + current_state->node_start = scanner.current; + current_state->pkt_end = namespace_end; + current_state->node = namespace; + + parse_namespace_node(false); +} + +aml_data_t eval_namespace_node(aml_namespace_node_t *node, uint8_t num_args, aml_data_t argv[]) +{ + aml_data_t eval_ret; + + if (node->evaluated) { + eval_ret = node->data; + return eval_ret; + } + + parse_state_t *recovery_state = current_state; + uint8_t *recovery_location = scanner.current; + + if (node->op_code == FIELD_OP) { + eval_ret = read_field_value(node); + current_state = recovery_state; + scanner.current = recovery_location; + return eval_ret; + } + + /* sddf_dprintf("Eval node %s, Op: 0x%x, end: 0x%lx\n", node->name, node->op_code, (uintptr_t)node->pkt_end); */ + + state_stack_create(node->op_code, true); + current_state->node = node; + current_state->node_start = node->pkt_start; + current_state->pkt_end = node->pkt_end; + current_state->stage_idx = 0; + + aml_data_t ret_buf = {(uintptr_t)&eval_ret, DATA_OBJ_RET, 0}; + state_stack_add_argument(ret_buf); // First argument as address of return buffer + + if (node->op_code == METHOD_OP) { + // redirect scanner to TERM_LIST + scanner.current = node->pkt_start + 1; + get_pkt_end(); // PKT_LEN + skip_name_string(); // NAME STRING + advance(); // METHOD_FLAGS + current_state->stage_idx = 4; + + // Add ARGn Ops + for (int i = 0; i < num_args; i++) { + aml_namespace_node_t *arg_node = make_namespace_node(current_state->node, ARG0_OP + i); + arg_node->data = argv[i]; + } + } else if (node->op_code == NAME_OP) { + // redirect scanner to TERM_LIST + scanner.current = node->pkt_start + 1; + skip_name_string(); // NAME STRING + current_state->stage_idx = 2; + } else if (node->op_code == OP_REGION_OP) { + // redirect scanner to region_space + scanner.current = node->pkt_start + 2; + skip_name_string(); // NAME STRING + current_state->stage_idx = 2; + } else { + /* sddf_dprintf("Evaluation of op 0x%04x is not implmented, return node\n", node->op_code); */ + state_stack_pop(); + eval_ret = (aml_data_t){(uintptr_t)node, DATA_OBJ_NODE, 0}; + current_state = recovery_state; + scanner.current = recovery_location; + return eval_ret; + } + + parse_namespace_node(true); + + /* sddf_dprintf("Finish Eval node %s, Op: 0x%x\n", node->name, node->op_code); */ + current_state = recovery_state; + scanner.current = recovery_location; + + return eval_ret; +} + +void eval_data_object(aml_namespace_node_t *prt_node, aml_prt_package_t *prt, uint8_t *pkt_end) +{ + aml_data_t ret_buf = {(uintptr_t)prt, DATA_OBJ_RET, 0}; + + // Make a PRT_PACJAGE, and extract PRT data + state_stack_create(PRT_PACKAGE, true); + current_state->node = prt_node; + current_state->node_start = 0; + current_state->pkt_end = pkt_end; + current_state->stage_idx = 3; + + state_stack_add_argument(ret_buf); // First argument as address of return buffer + + parse_namespace_node(true); +} + +void parse_prt_package(aml_namespace_node_t *prt_node, aml_data_t prt_data, uint32_t bridge_idx) +{ + // DefPackage := PackageOp PkgLength NumElements PackageElementList + if (prt_data.type != DATA_OBJ_PACKAGE) { + sddf_dprintf("[Error] not a package data given\n"); + return; + } + + set_scanner_to((uint8_t *)prt_data.value); + uint8_t *package_end = (uint8_t *)prt_data.value + prt_data.length; + pci_bridge_t *pci_bridge_resource = &pci_resources->bridges[pci_resources->num_bridges]; + + uint8_t num_elements = advance(); + sddf_dprintf("num_elements: %u\n", num_elements); + + while (scanner.current < package_end) { + // Check if element is also Package Object + if (advance() != PACKAGE_PREFIX) return; + + uint8_t *element_pkt_end = get_pkt_end(); + uint32_t element_num_elements = advance(); + + // Check if num of elements is 4 + if (element_num_elements != 4) return; + + pci_prt_t *pci_prt = &pci_bridge_resource->prt_entries[pci_bridge_resource->num_prt_entries]; + aml_prt_package_t prt_package; + eval_data_object(prt_node, &prt_package, element_pkt_end); + + /* sddf_dprintf("==========\n"); */ + /* sddf_dprintf("address: 0x%lx, data_type: 0x%x\n", prt_package.address.value, prt_package.address.type); */ + /* sddf_dprintf("pin: 0x%lx, data_type: 0x%x\n", prt_package.pin.value, prt_package.pin.type); */ + /* sddf_dprintf("source: 0x%lx, data_type: 0x%x\n", prt_package.source.value, prt_package.source.type); */ + /* sddf_dprintf("sourceIndex: 0x%lx, data_type: 0x%x\n", prt_package.source_index.value, prt_package.source_index.type); */ + pci_prt->address = (uint32_t)prt_package.address.value; + pci_prt->pin = (uint32_t)prt_package.pin.value; + if (prt_package.source.type == DATA_OBJ_NODE) { + aml_namespace_node_t *gsi_node = (aml_namespace_node_t *)prt_package.source.value; + aml_namespace_node_t *crs_node = find_child_node_by_name(gsi_node, test_aml_str_crs); + aml_data_t gsi_crs = eval_namespace_node(crs_node, 0, NULL); + assert(gsi_crs.type == DATA_OBJ_BUFFER); + uint8_t *irq_descriptor = (uint8_t *)gsi_crs.value; + assert(irq_descriptor[0] == EXTENDED_IRQ_DESCRIPTOR); + uint32_t gsi = 0; + gsi |= (uint32_t)irq_descriptor[5] << 0; + gsi |= (uint32_t)irq_descriptor[6] << 8; + gsi |= (uint32_t)irq_descriptor[7] << 16; + gsi |= (uint32_t)irq_descriptor[8] << 24; + pci_prt->gsi = gsi; + // TODO: edge/level, assumes there is only one IRQ for now + } else { + pci_prt->gsi = (uint32_t)prt_package.source_index.value; + } + pci_bridge_resource->num_prt_entries++; + sddf_dprintf("{ address: 0x%X, pin: 0x%x, gsi: 0x%x}\n", pci_prt->address, pci_prt->pin, pci_prt->gsi); + } +} diff --git a/drivers/network/ixgbe/eth_driver.mk b/drivers/network/ixgbe/eth_driver.mk new file mode 100644 index 000000000..3b6157934 --- /dev/null +++ b/drivers/network/ixgbe/eth_driver.mk @@ -0,0 +1,30 @@ +# +# Copyright 2024, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Include this snippet in your project Makefile to build +# the IXGBE NIC driver +# +# NOTES +# Generates eth_driver.elf (alternative unique name eth_driver_ixgbe.elf) +# Expects libsddf_util_debug.a to be in LIBS + +ETHERNET_DRIVER_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +CHECK_NETDRV_FLAGS_MD5:=.netdrv_cflags-$(shell echo -- ${CFLAGS} ${CFLAGS_network} | shasum | sed 's/ *-//') +# This ethernet driver needs a configured timer driver +NET_NEED_TIMER := 1 + +${CHECK_NETDRV_FLAGS_MD5}: + -rm -f .netdrv_cflags-* + touch $@ + +eth_driver_ixgbe.elf: network/ixgbe/ethernet.o + $(LD) $(LDFLAGS) $< $(LIBS) -o $@ + +network/ixgbe/ethernet.o: ${ETHERNET_DRIVER_DIR}/ethernet.c ${CHECK_NETDRV_FLAGS_MD5} + mkdir -p network/ixgbe + ${CC} -c ${CFLAGS} ${CFLAGS_network} -I ${ETHERNET_DRIVER_DIR} -o $@ $< + + +-include ixgbe/ethernet.d diff --git a/drivers/network/ixgbe/ethernet.c b/drivers/network/ixgbe/ethernet.c new file mode 100644 index 000000000..ef3b93b13 --- /dev/null +++ b/drivers/network/ixgbe/ethernet.c @@ -0,0 +1,525 @@ +/* + * Copyright 2026, UNSW + * SPDX-License-Identifier: BSD-2-Clause + * + * Intel Ethernet Controller X550 Datasheet: + * https://cdrdv2-public.intel.com/333369/333369_X550_Datasheet_Rev2.7.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ethernet.h" + +__attribute__((__section__(".device_resources"))) device_resources_t device_resources; +__attribute__((__section__(".timer_client_config"))) timer_client_config_t timer_config; + +__attribute__((__section__(".net_driver_config"))) net_driver_config_t config; + +#define RX_IRQ_VECTOR 0 +#define TX_IRQ_VECTOR 1 + +#define IRQ_INTERVAL 40 + +const uint64_t hw_rx_ring_paddr = 0x10000000; +const uint64_t hw_rx_ring_vaddr = 0x2400000; +const uint64_t hw_tx_ring_paddr = 0x10004000; +const uint64_t hw_tx_ring_vaddr = 0x2404000; + +#define NUM_TX_DESCS 512llu +#define NUM_RX_DESCS 512llu +#define TX_CLEAN_BATCH 32llu + +struct ixgbe_device { + volatile ixgbe_adv_rx_desc_t *rx_ring; + size_t rx_head, rx_tail; + volatile ixgbe_adv_tx_desc_t *tx_ring; + size_t tx_head, tx_tail; + net_buff_desc_t rx_desc_mdata[NUM_RX_DESCS]; + net_buff_desc_t tx_desc_mdata[NUM_TX_DESCS]; + int init_stage; +} device; + +/* HW ring descriptor (shared with device) */ +struct descriptor { + uint16_t len; + uint16_t stat; + uint32_t addr; +}; + +/* HW ring buffer data type */ +typedef struct { + uint32_t tail; /* index to insert at */ + uint32_t head; /* index to remove from */ + uint32_t capacity; /* capacity of the ring */ + volatile struct descriptor *desc; /* buffer descriptor array */ +} hw_ring_t; + +net_queue_handle_t rx_queue; +net_queue_handle_t tx_queue; + +#define MAX_PACKET_SIZE 1536 + +volatile eth_regs_t *eth_regs; + +static inline bool hw_tx_ring_empty(void) +{ + return device.tx_head == device.tx_tail; +} + +static inline bool hw_tx_ring_full(void) +{ + return (device.tx_tail + 2) % NUM_TX_DESCS == device.tx_head; +} + +static inline bool hw_rx_ring_empty(void) +{ + return device.rx_head == device.rx_tail; +} + +static inline bool hw_rx_ring_full(void) +{ + return (device.rx_tail + 2) % NUM_RX_DESCS == device.rx_head; +} + +void clear_interrupts(void) +{ + (void)eth_regs->eicr; +} + +void disable_interrupts(void) +{ + eth_regs->eimc = IXGBE_IRQ_CLEAR_MASK; + clear_interrupts(); +} + +void enable_interrupts(void) +{ + // Section 8.2.2.6.10 + // - Bit[5:0] vector number for RX_QUEUE 0, BIT(vector number) is set on EICR if triggered + // - Bit[7] enable IRQ for RX_QUEUE 0 + // - Bit[13:8] vector number for TX_QUEUE 0 + // - Bit[15] enable IRQ for TX_QUEUE 0 + eth_regs->ivar[0] = RX_IRQ_VECTOR | BIT(7) | (TX_IRQ_VECTOR << 8) | BIT(15); + + // Section 7.3.1.6 - No need to enable auto-clear + eth_regs->eiac = 0; + + // Section 8.2.2.6.4 + // - Bits[11:3] Minimum inter-interrupt interval specified in 2.048us units + // at 1 GbE and 10 GbE link + eth_regs->eitr[0] = IXGBE_EITR_ITR_INTERVAL * IRQ_INTERVAL; + clear_interrupts(); + + // bit 15:0 for Receive/Transmit Queue Interrupts. We only enable those IRQs + // because the driver doesn't know how to handle IRQs caused by other reasons. + eth_regs->eims = 0xFF; +} + +void get_mac_addr(uint8_t mac[6]) +{ + uint64_t low = eth_regs->rx_addr[0].lo; + uint64_t high = eth_regs->rx_addr[0].hi; + + mac[0] = low & 0xff; + mac[1] = low >> 8 & 0xff; + mac[2] = low >> 16 & 0xff; + mac[3] = low >> 24; + mac[4] = high & 0xff; + mac[5] = high >> 8 & 0xff; +} + +uint32_t get_link_speed(void) +{ + uint32_t speed = eth_regs->links; + if ((speed & IXGBE_LINKS_UP) == 0) { + return 0; + } + switch (speed & IXGBE_LINKS_SPEED_82599) { + case IXGBE_LINKS_SPEED_100_82599: + return 100; + case IXGBE_LINKS_SPEED_1G_82599: + return 1000; + case IXGBE_LINKS_SPEED_10G_82599: + return 10000; + default: + return 0; + } +} + +void rx_provide(void) +{ + bool reprocess = true; + while (reprocess) { + bool provided = false; + + while (!hw_rx_ring_full() && !net_queue_empty_free(&rx_queue)) { + net_buff_desc_t buffer; + int err = net_dequeue_free(&rx_queue, &buffer); + assert(!err); + + volatile ixgbe_adv_rx_desc_t *desc = &device.rx_ring[device.rx_tail]; + desc->read.pkt_addr = buffer.io_or_offset; + desc->read.hdr_addr = 0; + + device.rx_desc_mdata[device.rx_tail] = buffer; + + device.rx_tail = (device.rx_tail + 1) % NUM_RX_DESCS; + provided = true; + } + + if (provided) { + THREAD_MEMORY_RELEASE(); + // Update tail if filled hardware ring with empty descriptors + eth_regs->rx_dma[0].rdt = device.rx_tail; + } + + /* Only request a notification from multiplexer if HW ring is empty */ + if (hw_rx_ring_empty()) { + net_request_signal_free(&rx_queue); + } else { + net_cancel_signal_free(&rx_queue); + } + reprocess = false; + + if (!net_queue_empty_free(&rx_queue) && !hw_rx_ring_full()) { + net_cancel_signal_free(&rx_queue); + reprocess = true; + } + } +} + +static void rx_return(void) +{ + bool packets_transferred = false; + while (!hw_rx_ring_empty()) { + ixgbe_adv_rx_desc_wb_t desc = device.rx_ring[device.rx_head].wb; + if ((desc.upper.status_error & IXGBE_RXDADV_STAT_DD) == 0) { + // The desciptor hasn't been used by hardware, implying no more available packets received + break; + } + if ((desc.upper.status_error & IXGBE_RXDADV_STAT_EOP) == 0) { + // See Table 7-16: DD=1 and EOP=0 + sddf_dprintf("ETH|ERROR: The packet spans across multiple descriptors.\n"); + break; + } + + // The access to `status_error` field should be ordered before the access to the `length` field + THREAD_MEMORY_ACQUIRE(); + + net_buff_desc_t buffer = device.rx_desc_mdata[device.rx_head]; + buffer.len = desc.upper.length; + int err = net_enqueue_active(&rx_queue, buffer); + assert(!err); + + packets_transferred = true; + device.rx_head = (device.rx_head + 1) % NUM_RX_DESCS; + } + + if (packets_transferred && net_require_signal_active(&rx_queue)) { + net_cancel_signal_active(&rx_queue); + microkit_notify(config.virt_rx.id); + } +} + +void tx_provide(void) +{ + bool reprocess = true; + while (reprocess) { + bool provided = false; + + while (!(hw_tx_ring_full()) && !net_queue_empty_active(&tx_queue)) { + + net_buff_desc_t buffer; + int err = net_dequeue_active(&tx_queue, &buffer); + assert(!err); + + volatile ixgbe_adv_tx_desc_t *desc = &device.tx_ring[device.tx_tail]; + desc->read.buffer_addr = buffer.io_or_offset; + desc->read.cmd_type_len = IXGBE_ADVTXD_DCMD_EOP | IXGBE_ADVTXD_DCMD_RS | IXGBE_ADVTXD_DCMD_IFCS + | IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_DATA | (uint32_t)buffer.len; + desc->read.olinfo_status = ((uint32_t)buffer.len << IXGBE_ADVTXD_PAYLEN_SHIFT); + + device.tx_desc_mdata[device.tx_tail] = buffer; + + device.tx_tail = (device.tx_tail + 1) % NUM_TX_DESCS; + provided = true; + } + + if (provided) { + THREAD_MEMORY_RELEASE(); + eth_regs->tx_dma[0].tdt = device.tx_tail; + eth_regs->tx_dma[0].tdt; // Write flush + } + + net_request_signal_active(&tx_queue); + reprocess = false; + + if (!hw_tx_ring_full() && !net_queue_empty_active(&tx_queue)) { + net_cancel_signal_active(&tx_queue); + reprocess = true; + } + } +} + +void tx_return(void) +{ + bool enqueued = false; + while (!hw_tx_ring_empty()) { + /* check if this buffer has been sent by the device */ + ixgbe_adv_tx_desc_wb_t hw_desc = device.tx_ring[device.tx_head].wb; + + if ((hw_desc.status & IXGBE_ADVTXD_STAT_DD) == 0) + break; + + net_buff_desc_t desc_mdata = device.tx_desc_mdata[device.tx_head]; + int err = net_enqueue_free(&tx_queue, desc_mdata); + assert(!err); + enqueued = true; + + device.tx_head = (device.tx_head + 1) % NUM_TX_DESCS; + } + + if (enqueued && net_require_signal_free(&tx_queue)) { + net_cancel_signal_free(&tx_queue); + microkit_notify(config.virt_tx.id); + } +} + +void init(void) +{ + eth_regs = (eth_regs_t *)0x2000000; + + // see PCI Express Technology 3.0 Chapter 17 for more details. + // =====Uncomment the below code snippet to use MSI interrupts======== + /* set_flags16(PCI_COMMAND_16, BIT(10)); */ + /* set_flags16(PCI_MSI_MESSAGE_CONTROL_16, BIT(0)); */ + /* clear_flags16(PCI_MSI_MESSAGE_CONTROL_16, BIT(4) | BIT(5) | BIT(6)); */ + /* set_reg(PCI_MSI_MESSAGE_ADDRESS_LOW, 0xFEEu << 20); */ + /* set_reg(PCI_MSI_MESSAGE_ADDRESS_HIGH, 0); */ + /* set_reg16(PCI_MSI_MESSAGE_DATA_16, 0x31); */ + /* clear_flags16(PCI_MSI_MASK, BIT(0)); */ + + // see PCI Express Technology 3.0 Chapter 17 for more details. + // =====Uncomment the below code snippet to use MSI-X interrupts====== + /* // Disable legacy interrupts. */ + /* set_flags16(PCI_COMMAND_16, BIT(10)); */ + /* // Set vector message address to Local APIC of CPU0 */ + /* set_reg(DEVICE_MSIX_TABLE + 0x0, 0xFEEu << 20); */ + /* set_reg(DEVICE_MSIX_TABLE + 0x4, 0); */ + /* // Set vector data to Interrupt Vector */ + /* set_reg(DEVICE_MSIX_TABLE + 0x8, 0x32); */ + /* // Unmask vector 0 to enable interrupts through it */ + /* set_reg(DEVICE_MSIX_TABLE + 0xC, 0xFFFFFFFE); */ + /* // Enable MSI-X. */ + /* set_flags(PCI_MSIX_CTRL, BIT(31)); */ + + device.rx_ring = (void *)hw_rx_ring_vaddr; + device.tx_ring = (void *)hw_tx_ring_vaddr; + + net_queue_init(&rx_queue, config.virt_rx.free_queue.vaddr, config.virt_rx.active_queue.vaddr, + config.virt_rx.num_buffers); + net_queue_init(&tx_queue, config.virt_tx.free_queue.vaddr, config.virt_tx.active_queue.vaddr, + config.virt_tx.num_buffers); + + // Disable Interrupts, see Section 4.6.3.1 + disable_interrupts(); + + // Master disable prior to link reset, see Section 4.2.1.7 + eth_regs->ctrl = IXGBE_CTRL_PCIE_MASTER_DISABLE; + while (eth_regs->status & IXGBE_STATUS_PCIE_MASTER_STATUS); + + + // Global Reset and General Configuration, see Section 4.6.3.2 + eth_regs->ctrl |= IXGBE_CTRL_RST; + while ((eth_regs->ctrl & IXGBE_CTRL_RST_MASK) != 0); + + // Wait at least 10ms + sddf_timer_set_timeout(timer_config.driver_id, 100 * NS_IN_MS); +} + +void init_1(void) +{ + device.init_stage = 1; + // section 4.6.3.1 - disable interrupts again after reset + disable_interrupts(); + + uint8_t mac[6]; + get_mac_addr(mac); + + // section 4.6.3 - wait for EEPROM auto read completion + while((eth_regs->eec & IXGBE_EEC_ARD) != IXGBE_EEC_ARD); + + // section 4.6.3 - wait for dma initialization done + while ((eth_regs->rdrxctl & IXGBE_RDRXCTL_DMAIDONE) != IXGBE_RDRXCTL_DMAIDONE); + + // section 4.6.4 - initialize link (auto negotiation) + // link auto-configuration register should have been set correctly + + // negotiate link + /* datasheet wants us to wait for the link here, but we can continue and wait afterwards */ + + // section 4.6.5 - statistical counters + // Initialise the Rx statistic registers. + + // section 4.6.5 - statistical counters + // Initialise the Rx statistic registers. + eth_regs->rqsmr[0] = 0; + // reset-on-read registers, just read them once + eth_regs->gprc; + eth_regs->gptc; + eth_regs->gorcl; + eth_regs->gorch; + eth_regs->gotcl; + eth_regs->gotch; + + // section 4.6.7 - init rx + { + // disable rx while re-configuring it + eth_regs->rxctrl &= (~IXGBE_RXCTRL_RXEN); + + // set buffer size for only RX queue 0 + eth_regs->rxpbsize[0] = IXGBE_RXPBSIZE_128KB; + for (int i = 1; i < 8; i++) { + eth_regs->rxpbsize[i] = 0; + } + + // enable CRC offloading + eth_regs->hlreg0 |= IXGBE_HLREG0_RXCRCSTRP; + eth_regs->rdrxctl |= IXGBE_RDRXCTL_CRCSTRIP; + + // accept broadcast packets, promiscuous + eth_regs->fctrl |= IXGBE_FCTRL_BAM | IXGBE_FCTRL_MPE | IXGBE_FCTRL_UPE; + + // use only queue 0 + eth_regs->rx_dma[0].srrctl &= ~IXGBE_SRRCTL_DESCTYPE_MASK; + eth_regs->rx_dma[0].srrctl |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF | IXGBE_SRRCTL_DROP_EN; + eth_regs->rx_dma[0].rdbal = hw_rx_ring_paddr & 0xFFFFFFFFull; + eth_regs->rx_dma[0].rdbah = hw_rx_ring_paddr >> 32; + eth_regs->rx_dma[0].rdlen = NUM_RX_DESCS * sizeof(ixgbe_adv_rx_desc_t); + eth_regs->rx_dma[0].rdh = 0; + eth_regs->rx_dma[0].rdt = 0; + + eth_regs->ctrl_ext = IXGBE_CTRL_EXT_NS_DIS; + eth_regs->rxctrl |= IXGBE_RXCTRL_RXEN; + eth_regs->rx_dma[0].rxdctl = IXGBE_RXDCTL_ENABLE; + while ((eth_regs->rx_dma[0].rxdctl & IXGBE_RXDCTL_ENABLE) == 0); + } + + // section 4.6.8 - init tx + { + // set buffer size for only TX queue 0 + eth_regs->txpbsize[0] = IXGBE_TXPBSIZE_40KB; + for (int i = 1; i < 8; i++) { + eth_regs->txpbsize[i] = 0; + } + + // TODO: why? + eth_regs->txpbthresh[0] = 0xA0; + for (int i = 1; i < 8; i++) { + eth_regs->txpbthresh[i] = 0; + } + + eth_regs->tx_dma[0].tdbal = hw_tx_ring_paddr & 0xFFFFFFFFull; + eth_regs->tx_dma[0].tdbah = hw_tx_ring_paddr >> 32; + eth_regs->tx_dma[0].tdh = 0; + eth_regs->tx_dma[0].tdt = 0; + + eth_regs->tx_dma[0].tdlen = NUM_TX_DESCS * sizeof(ixgbe_adv_tx_desc_t); + + // descriptor writeback magic values, important to get good performance and low PCIe overhead + // see 7.2.3.4.1 and 7.2.3.5 for an explanation of these values and how to find good ones + // we just use the defaults from DPDK here, but this is a potentially interesting point for optimizations + // let mut txdctl = self.read_reg_idx(IxgbeArrayRegs::Txdctl, i); + // there are no defines for this in ixgbe.rs for some reason + // pthresh: 6:0, hthresh: 14:8, wthresh: 22:16 + + eth_regs->tx_dma[0].txdctl &= ~(0x7F | (0x7F << 8) | (0x7F << 16)); // clear bits + eth_regs->tx_dma[0].txdctl |= (36 | (8 << 8) | (4 << 16)); // from DPDK + + // final step: enable DMA + eth_regs->dmatxctl = IXGBE_DMATXCTL_TE; + eth_regs->tx_dma[0].txdctl = IXGBE_TXDCTL_ENABLE; + while ((eth_regs->tx_dma[0].txdctl & IXGBE_TXDCTL_ENABLE) == 0); + } + + // wait some time for the link to come up + sddf_timer_set_timeout(timer_config.driver_id, 100 * NS_IN_MS); +} + +void init_2(void) +{ + uint32_t speed = get_link_speed(); + if (speed == 0) { + sddf_timer_set_timeout(timer_config.driver_id, 100 * NS_IN_MS); + return; + } + + device.init_stage = 2; + + // sleep for 10 seconds. Just stabilize the hardware + // Well. this ugliness costed us two days of debugging. + // https://github.com/mars-research/atmosphere/blob/bd485f22f1d5e4d1623e133700dc233086059603/ixgbe_driver/src/device.rs#L258 + sddf_timer_set_timeout(timer_config.driver_id, 10 * NS_IN_S); +} + +void init_3(void) +{ + device.init_stage = 3; + + rx_provide(); + tx_provide(); + + enable_interrupts(); + + sddf_dprintf("Finish NIC reset\n"); + device.init_stage = 4; +} + +void notified(microkit_channel ch) +{ + if (ch == timer_config.driver_id) { + if (device.init_stage == 0) { + init_1(); + } else if (device.init_stage == 1) { + init_2(); + } else if (device.init_stage == 2) { + init_3(); + } + /* } else if (device.init_stage != 4 && ch == device_resources.irqs[0].id) { */ + } else if (device.init_stage != 4 && ch == 16) { + microkit_deferred_irq_ack(ch); + } else if (device.init_stage == 4) { + /* if (ch == device_resources.irqs[0].id) { */ + if (ch == 16) { + // read-to-clear + uint32_t cause = eth_regs->eicr; + if (cause & BIT(RX_IRQ_VECTOR)) { + rx_return(); + rx_provide(); + } else if (cause & BIT(TX_IRQ_VECTOR)) { + tx_return(); + tx_provide(); + } + + /* + * Delay calling into the kernel to ack the IRQ until the next loop + * in the event handler loop. + */ + sddf_deferred_irq_ack(ch); + } else if (ch == config.virt_tx.id) { + tx_provide(); + } else if (ch == config.virt_rx.id) { + rx_provide(); + } + } +} diff --git a/drivers/network/ixgbe/ethernet.h b/drivers/network/ixgbe/ethernet.h new file mode 100644 index 000000000..320fecf14 --- /dev/null +++ b/drivers/network/ixgbe/ethernet.h @@ -0,0 +1,342 @@ +/* + * Copyright 2026, UNSW + * SPDX-License-Identifier: BSD-2-Clause + * + * Intel Ethernet Controller X550 datasheet: + * https://www.intel.com/content/www/us/en/content-details/333369/intel-ethernet-controller-x550-datasheet.html + */ +#pragma once + +#include +#include + +#define PCIE_CONFIG_BASE 0x3000000lu +#define DEVICE_BASE 0x2000000lu +#define DEVICE_MSIX_TABLE 0x4000000lu + +const uint64_t IXGBE_CTRL_LNK_RST = 0x00000008; /* Link Reset. Resets everything. */ +const uint64_t IXGBE_CTRL_RST = 0x04000000; /* Reset (SW) */ +const uint64_t IXGBE_CTRL_RST_MASK = IXGBE_CTRL_LNK_RST | IXGBE_CTRL_RST; +const uint64_t IXGBE_CTRL_PCIE_MASTER_DISABLE = 1 << 2; + +const uint64_t IXGBE_STATUS_PCIE_MASTER_STATUS = 1 << 19; +const uint64_t IXGBE_CTRL_EXT_DRV_LOAD = 1 << 28; + +const uint64_t IXGBE_EEC_ARD = 0x00000200; /* EEPROM Auto Read Done */ +const uint64_t IXGBE_RDRXCTL_DMAIDONE = 0x00000008; /* DMA init cycle done */ + +const uint64_t IXGBE_AUTOC_LMS_SHIFT = 13; +const uint64_t IXGBE_AUTOC_LMS_MASK = 0x7 << IXGBE_AUTOC_LMS_SHIFT; +const uint64_t IXGBE_AUTOC_LMS_10G_SERIAL = 0x3 << IXGBE_AUTOC_LMS_SHIFT; +const uint64_t IXGBE_AUTOC_10G_PMA_PMD_MASK = 0x00000180; +const uint64_t IXGBE_AUTOC_10G_PMA_PMD_SHIFT = 7; +const uint64_t IXGBE_AUTOC_10G_XAUI = 0x0 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT; +const uint64_t IXGBE_AUTOC_AN_RESTART = 0x00001000; + +const uint64_t IXGBE_RXCTRL_RXEN = 0x00000001; /* Enable Receiver */ + +const uint64_t IXGBE_RXPBSIZE_128KB = 0x00020000; /* 128KB Packet Buffer */ + +const uint64_t IXGBE_HLREG0_RXCRCSTRP = 0x00000002; /* bit 1 */ +const uint64_t IXGBE_HLREG0_LPBK = 1 << 15; +const uint64_t IXGBE_RDRXCTL_CRCSTRIP = 0x00000002; /* CRC Strip */ + +const uint64_t IXGBE_FCTRL_BAM = 0x00000400; /* Broadcast Accept Mode */ + +const uint64_t IXGBE_CTRL_EXT_NS_DIS = 0x00010000; /* No Snoop disable */ + +const uint64_t IXGBE_HLREG0_TXCRCEN = 0x00000001; /* bit 0 */ +const uint64_t IXGBE_HLREG0_TXPADEN = 0x00000400; /* bit 10 */ + +const uint64_t IXGBE_TXPBSIZE_40KB = 0x0000A000; /* 40KB Packet Buffer */ +const uint64_t IXGBE_RTTDCS_ARBDIS = 0x00000040; /* DCB arbiter disable */ + +const uint64_t IXGBE_DMATXCTL_TE = 0x1; /* Transmit Enable */ + +const uint64_t IXGBE_RXDCTL_ENABLE = 0x02000000; /* Ena specific Rx Queue, bit 25 */ +const uint64_t IXGBE_TXDCTL_ENABLE = 0x02000000; /* Ena specific Tx Queue, bit 25 */ +const uint64_t IXGBE_RSCINT_RSCEN = 0x00000001; /* RSC Enable */ +const uint64_t IXGBE_RSCCTL_RSCEN = 0x00000001; /* RSC Enable */ +/* RSCCTL bit 3:2 Maximum descriptors per large receive */ +const uint64_t IXGBE_RSCCTL_MAXDESC_1 = 0x0; /* 00b = Maximum Descriptors 1 */ +const uint64_t IXGBE_RSCCTL_MAXDESC_4 = 0x4; /* 01b = Maximum Descriptors 4 */ +const uint64_t IXGBE_RSCCTL_MAXDESC_8 = 0x8; /* 10b = Maximum Descriptors 8 */ +const uint64_t IXGBE_RSCCTL_MAXDESC_16 = 0xc; /* 11b = Maximum Descriptors 16 */ +const uint64_t IXGBE_EITR_ITR_INTERVAL = 0x00000008; /* bit 3 */ + +const uint64_t IXGBE_FCTRL_MPE = 0x00000100; /* Multicast Promiscuous Ena*/ +const uint64_t IXGBE_FCTRL_UPE = 0x00000200; /* Unicast Promiscuous Ena */ + +const uint64_t IXGBE_LINKS_UP = 0x40000000; +const uint64_t IXGBE_LINKS_SPEED_82599 = 0x30000000; +const uint64_t IXGBE_LINKS_SPEED_100_82599 = 0x10000000; +const uint64_t IXGBE_LINKS_SPEED_1G_82599 = 0x20000000; +const uint64_t IXGBE_LINKS_SPEED_10G_82599 = 0x30000000; + +const uint32_t IXGBE_IVAR_ALLOC_VAL = 0x80; /* Interrupt Allocation valid */ +const uint64_t IXGBE_EICR_RTX_QUEUE = 0x0000FFFF; /* RTx Queue Interrupt */ + +/* Interrupt clear mask */ +const uint64_t IXGBE_IRQ_CLEAR_MASK = 0xFFFFFFFF; + +const uint64_t IXGBE_GPIE_MSIX_MODE = 0x00000010; /* MSI-X mode */ +const uint64_t IXGBE_GPIE_OCD = 0x00000020; /* Other Clear Disable */ +const uint64_t IXGBE_GPIE_EIMEN = 0x00000040; /* Immediate Interrupt Enable */ +const uint64_t IXGBE_GPIE_EIAME = 0x40000000; +const uint64_t IXGBE_GPIE_PBA_SUPPORT = 0x80000000; + +const uint64_t SRRCTL_BSIZEHEADER_MASK = 0b11111100000000; +const uint64_t IXGBE_SRRCTL_DESCTYPE_MASK = 0x0E000000; +const uint64_t IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF = 0x02000000; +const uint64_t IXGBE_SRRCTL_DROP_EN = 0x10000000; + +const uint32_t IXGBE_RXD_STAT_DD = 0x01; /* Descriptor Done */ +const uint32_t IXGBE_RXD_STAT_EOP = 0x02; /* End of Packet */ +const uint32_t IXGBE_RXDADV_STAT_DD = IXGBE_RXD_STAT_DD; /* Done */ +const uint32_t IXGBE_RXDADV_STAT_EOP = IXGBE_RXD_STAT_EOP; /* End of Packet */ + +const uint32_t IXGBE_ADVTXD_PAYLEN_SHIFT = 14; /* Adv desc PAYLEN shift */ +const uint32_t IXGBE_TXD_CMD_EOP = 0x01000000; /* End of Packet */ +const uint32_t IXGBE_ADVTXD_DCMD_EOP = IXGBE_TXD_CMD_EOP; /* End of Packet */ +const uint32_t IXGBE_TXD_CMD_RS = 0x08000000; /* Report Status */ +const uint32_t IXGBE_ADVTXD_DCMD_RS = IXGBE_TXD_CMD_RS; /* Report Status */ +const uint32_t IXGBE_TXD_CMD_IFCS = 0x02000000; /* Insert FCS (Ethernet CRC) */ +const uint32_t IXGBE_ADVTXD_DCMD_IFCS = IXGBE_TXD_CMD_IFCS; /* Insert FCS */ +const uint32_t IXGBE_TXD_CMD_DEXT = 0x20000000; /* Desc extension (0 = legacy) */ +const uint32_t IXGBE_ADVTXD_DTYP_DATA = 0x00300000; /* Adv Data Descriptor */ +const uint32_t IXGBE_ADVTXD_DCMD_DEXT = IXGBE_TXD_CMD_DEXT; /* Desc ext 1=Adv */ +const uint32_t IXGBE_TXD_STAT_DD = 0x00000001; /* Descriptor Done */ +const uint32_t IXGBE_ADVTXD_STAT_DD = IXGBE_TXD_STAT_DD; /* Descriptor Done */ + + +#define IXGBE_TXPBSIZE_MAX 0x00028000 /* 160KB, section 7.2.1.2.2 */ + +// bit 15:0, Receive/Transmit Queue Interrupts, activated on receive/transmit +// events.The mapping of queue to the RTxQ bits is done by the IVAR registers +const uint64_t IXGBE_EICR_RTXQ_BASE = 1; +// Missed packet interrupt is activated for each received packet that +// overflows the Rx packet buffer (overrun) +const uint64_t IXGBE_EICR_RX_MISS = 1 << 17; + +typedef struct { + uint64_t pkt_addr; // Packet buffer address + uint64_t hdr_addr; // Header buffer address +} ixgbe_adv_rx_desc_read_t; + +/* Receive Descriptor - Advanced */ +typedef struct { + uint16_t pkt_info; // RSS, Pkt type + uint16_t hdr_info; // Splithdr, hdrlen +} ixgbe_adv_rx_desc_wb_lower_lo_dword_hs_rss_t; + +typedef union { + uint32_t data; + ixgbe_adv_rx_desc_wb_lower_lo_dword_hs_rss_t hs_rss; +} ixgbe_adv_rx_desc_wb_lower_lo_dword_t; + +typedef struct { + uint16_t ip_id; // IP id + uint16_t csum; // Packet Checksum +} ixgbe_adv_rx_desc_wb_lower_hi_dword_csum_ip_t; + +typedef union { + uint32_t rss; // RSS Hash + ixgbe_adv_rx_desc_wb_lower_hi_dword_csum_ip_t csum_ip; +} ixgbe_adv_rx_desc_wb_lower_hi_dword_t; + +typedef struct { + ixgbe_adv_rx_desc_wb_lower_lo_dword_t lo_dword; + ixgbe_adv_rx_desc_wb_lower_hi_dword_t hi_dword; +} ixgbe_adv_rx_desc_wb_lower_t; + +typedef struct { + uint32_t status_error; // ext status/error + uint16_t length; // Packet length + uint16_t vlan; // VLAN tag +} ixgbe_adv_rx_desc_wb_upper_t; + +typedef struct { + ixgbe_adv_rx_desc_wb_lower_t lower; + ixgbe_adv_rx_desc_wb_upper_t upper; +} ixgbe_adv_rx_desc_wb_t; + +typedef union { + ixgbe_adv_rx_desc_read_t read; + ixgbe_adv_rx_desc_wb_t wb; // writeback +} ixgbe_adv_rx_desc_t; + +/* Transmit Descriptor - Advanced */ +typedef struct { + uint64_t buffer_addr; // Address of descriptor's data buf + uint32_t cmd_type_len; + uint32_t olinfo_status; +} ixgbe_adv_tx_desc_read_t; + +typedef struct { + uint64_t rsvd; // Reserved + uint32_t nxtseq_seed; + uint32_t status; +} ixgbe_adv_tx_desc_wb_t; + +typedef union { + ixgbe_adv_tx_desc_read_t read; + ixgbe_adv_tx_desc_wb_t wb; +} ixgbe_adv_tx_desc_t; + +typedef struct { + uint32_t lo; + uint32_t hi; +} rx_addr_t; + +typedef struct { + uint32_t rdbal; // 0x00001000 + 0x40*n Receive Descriptor Base Address Low + uint32_t rdbah; // 0x00001004 + 0x40*n Receive Descriptor Base Address High + uint32_t rdlen; // 0x00001008 + 0x40*n Receive Descriptor Length + uint8_t unused1[4]; // 0x0000100C + 0x40*n + uint32_t rdh; // 0x00001010 + 0x40*n Receive Descriptor Head + uint32_t srrctl; // 0x00001014 + 0x40*n Split Receive Control Registers + uint32_t rdt; // 0x00001018 + 0x40*n Receive Descriptor Tail + uint8_t unused2[12]; // 0x0000101C + 0x40*n + uint32_t rxdctl; // 0x00001028 + 0x40*n Receive Descriptor Control + uint32_t rscctl; // 0x0000102C + 0x40*n RSC Control + uint8_t unused3[16]; // 0x00001030 + 0x40*n +} rx_dma_regs_t; + +typedef struct { + uint32_t tdbal; // 0x00006000 + 0x40*n Transmit Descriptor Base Address Low + uint32_t tdbah; // 0x00006004 + 0x40*n Transmit Descriptor Base Address High + uint32_t tdlen; // 0x00006008 + 0x40*n Transmit Descriptor Length + uint8_t unused1[4]; // 0x0000600C + 0x40*n + uint32_t tdh; // 0x00006010 + 0x40*n Transmit Descriptor Head + uint8_t unused2[4]; // 0x00006014 + 0x40*n + uint32_t tdt; // 0x00006018 + 0x40*n Transmit Descriptor Tail + uint8_t unused3[12]; // 0x0000601C + 0x40*n + uint32_t txdctl; // 0x00006028 + 0x40*n Transmit Descriptor Control + uint8_t unused4[12]; // 0x0000602C + 0x40*n + uint32_t tdwbal; // 0x00006038 + 0x40*n Tx Descriptor Completion Write Back Address Low + uint32_t tdwbah; // 0x0000603C + 0x40*n Tx Descriptor Completion Write Back Address High +} tx_dma_regs_t; + +typedef struct { + uint32_t ctrl; // 0x00000 Device Control Register + uint8_t unused1[4]; // 0x00004 + uint32_t status; // 0x00008 Device Status Register + uint8_t unused2[12]; // 0x0000C + uint32_t ctrl_ext; // 0x00018 Extended Device Control Register + uint8_t unused3[2020]; // 0x0001C + + uint32_t eicr; // 0x00800 Extended Interrupt Cause Register + uint8_t unused4[4]; // 0x00804 + uint32_t eics; // 0x00808 Extended Interrupt Cause Set Register + uint8_t unused5[4]; // 0x0080C + uint32_t eiac; // 0x00810 Extended Interrupt Auto Clear Register + uint8_t unused6[12]; // 0x00814 + + uint32_t eitr[24]; // 0x00820 + 0x4*n Extended Interrupt Throttle Registers + uint32_t eims; // 0x00880 Extended Interrupt Mask Set/Read Register + uint8_t unused7[4]; // 0x00884 + uint32_t eimc; // 0x00888 Extended Interrupt Mask Clear Register + uint8_t unused8[12]; // 0x0088C + uint32_t gpie; // 0x00898 General Purpose Interrupt Enable + uint8_t unused9[100]; // 0x0089C + + uint32_t ivar[64]; // 0x00900 + 0x4*n Interrupt Vector Allocation Registers + uint8_t unused10[1536]; // 0x00A00 + + rx_dma_regs_t rx_dma[64]; // 0x01000 Receive DMA Registers + uint8_t unused11[768]; // 0x02000 + + uint32_t rqsmr[32]; // 0x02300 + 0x4*n Receive Queue Statistic Mapping Registers + uint8_t unused12[2944]; // 0x02380 + + uint32_t rdrxctl; // 0x02F00 Receive DMA Control Register + uint8_t unused13[252]; // 0x02F04 + + uint32_t rxctrl; // 0x03000 Receive Control Register + uint8_t unused14[3068]; // 0x03004 + + uint32_t rxpbsize[8]; // 0x03C00 + 0x4*n Receive Packet Buffer Size + uint8_t unused15[1108]; // 0x03C20 + + uint32_t gprc; // 0x04074 Good Packets Received Count + uint8_t unused16[8]; // 0x04078 + uint32_t gptc; // 0x04080 Good Packets Transmitted Count + uint8_t unused17[4]; // 0x04084 + uint32_t gorcl; // 0x04088 Good Octets Received Count Low + uint32_t gorch; // 0x0408C Good Octets Received Count High + uint32_t gotcl; // 0x04090 Good Octets Transmitted Count Low + uint32_t gotch; // 0x04094 Good Octets Transmitted Count High + uint8_t unused18[424]; // 0x04098 + + uint32_t hlreg0; // 0x04240 Highlander Control 0 Register + uint8_t unused19[96]; // 0x04244 + uint32_t links; // 0x042A4 Link Status Register + uint8_t unused20[1704]; // 0x042A8 + + uint32_t txpbthresh[8]; // 0x04950 + 0x4*n Tx Packet Buffer Threshold + uint8_t unused21[272]; // 0x04970 + + uint32_t dmatxctl; // 0x04A80 DMA Tx Control + uint8_t unused22[1532]; // 0x04A84 + + uint32_t fctrl; // 0x05080 Filter Control Register + uint8_t unused23[3964]; // 0x05084 + + tx_dma_regs_t tx_dma[64]; // 0x06000 Transmite Registers + uint8_t unused24[4352]; // 0x07000 + + uint32_t dtxmxszrq; // 0x08100 DMA Tx TCP Max Allow Size Requests + uint8_t unused25[1692]; // 0x08104 + + uint32_t txdgpc; // 0x087A0 DMA Good Tx Packet Counter + uint32_t txdgbcl; // 0x087A4 DMA Good Tx Byte Counter Low + uint32_t txdgbch; // 0x087A8 DMA Good Tx Byte Counter High + uint8_t unused26[6740]; // 0x087AC + + rx_addr_t rx_addr[128]; // 0x0A200 + 0x8*n Receive Address + uint8_t unused27[9728]; // 0x0A600 + + uint32_t txpbsize[8]; // 0x0CC00 + 0x4*n Transmit Packet Buffer Size + uint8_t unused28[13296]; // 0x0CC20 + + uint32_t eec; // 0x10010 EEPROM Mode Control Register + uint8_t unused29[316]; // 0x10014 + uint32_t factps; // 0x10150 Function Active and Power State to Manageability +} eth_regs_t; + +struct pci_config_space { + // Device Identification + uint16_t vendor_id; // 0x00: Vendor ID + uint16_t device_id; // 0x02: Device ID + uint16_t command; // 0x04: Command Register + uint16_t status; // 0x06: Status Register + uint8_t revision_id; // 0x08: Revision ID + uint8_t prog_if; // 0x09: Programming Interface + uint8_t subclass; // 0x0A: Sub Class Code + uint8_t class_code; // 0x0B: Base Class Code + uint8_t cache_line_size; // 0x0C: Cache Line Size + uint8_t latency_timer; // 0x0D: Latency Timer + uint8_t header_type; // 0x0E: Header Type + uint8_t bist; // 0x0F: Built-in Self Test + + // Base Address Registers (BARs) + uint32_t bar[6]; // 0x10-0x27: Base Address Registers + + // Subsystem Information + uint32_t cardbus_cis_ptr; // 0x28: CardBus CIS Pointer + uint16_t subsystem_vendor_id; // 0x2C: Subsystem Vendor ID + uint16_t subsystem_device_id; // 0x2E: Subsystem Device ID + uint32_t expansion_rom_addr; // 0x30: Expansion ROM Base Address + + // Capabilities and Interrupts + uint8_t cap_ptr; // 0x34: Capabilities Pointer + uint8_t reserved1[3]; // 0x35-0x37: Reserved + uint32_t reserved2; // 0x38-0x3B: Reserved + uint8_t interrupt_line; // 0x3C: Interrupt Line + uint8_t interrupt_pin; // 0x3D: Interrupt Pin + uint8_t min_gnt; // 0x3E: Min_Gnt + uint8_t max_lat; // 0x3F: Max_Lat + + // Capability list + uint8_t cap_data[192]; +}; diff --git a/drivers/network/virtio/common/ethernet.c b/drivers/network/virtio/common/ethernet.c index 4d0a68496..724fd74d8 100644 --- a/drivers/network/virtio/common/ethernet.c +++ b/drivers/network/virtio/common/ethernet.c @@ -83,6 +83,8 @@ uint32_t tx_descriptors[TX_COUNT]; int rx_last_desc_idx = 0; int tx_last_desc_idx = 0; +bool pci_ready = false; + static inline bool virtio_avail_full_rx(struct virtq *virtq) { return rx_last_desc_idx >= rx_virtq.num; @@ -410,6 +412,11 @@ static void eth_setup(void) void init(void) { + if (!pci_ready) { + sddf_dprintf("PCI driver has not set things up. Waiting for signaling\n"); + return; + } + assert(net_config_check_magic(&config)); assert(device_resources_check_magic(&device_resources)); @@ -456,6 +463,16 @@ void init(void) void notified(sddf_channel ch) { + if (ch == 10) { + pci_ready = true; + init(); + return; + } + if (!pci_ready) { + sddf_dprintf("PCI driver has not set things up. Waiting for signaling\n"); + return; + } + // @billn fix ridiculousness #if defined(CONFIG_ARCH_X86_64) if (ch == 16) { diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c new file mode 100644 index 000000000..417977053 --- /dev/null +++ b/drivers/pci/pci.c @@ -0,0 +1,444 @@ +/* + * Copyright 2026, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include "pci.h" + +#include +#include +#include +#include +#include + +uintptr_t pci_resources_vaddr = 0x60000000; +seL4_CPtr cnode_cptr_pci_resources; +seL4_CPtr vspace_cptr_ethernet_driver; +pci_resources_t *pci_resources; +cnode_specs_t *cnode_specs; +uint32_t kernel_objects_ut_idx = 2; + +seL4_CPtr cnode_cptr_ethernet_driver; + +bool acpi_ready = false; + +// regions[1..] are used for MSI-X BARs +uint8_t avail_region_idx = 1; + +__attribute__((__section__(".device_resources"))) device_resources_t device_resources; +__attribute__((__section__(".ecam_configs"))) pci_ecam_config_t pci_ecam_config; + +/** + * Look for the capability of a device by ID + * */ +static struct shared_pci_cap *find_pci_cap_by_id(struct pci_header_type0 *config_space, uint8_t cap_id) +{ + struct shared_pci_cap *cap = (struct shared_pci_cap *)((uintptr_t)config_space + config_space->cap_ptr); + while (cap != (struct shared_pci_cap *)config_space) { + if (cap->cap_id == cap_id) { + return cap; + } + cap = (struct shared_pci_cap *)((uintptr_t)config_space + cap->next_ptr); + } + return NULL; +} + +void configure_pci_bar(struct pci_header_type0 *pci_header, uint8_t bar_id, pci_bar_t pci_bar_cfg) +{ + sddf_dprintf("bar_id: %d, base_addr: 0x%lx\n", bar_id, pci_bar_cfg.base_addr); + if (pci_bar_cfg.base_addr) { + volatile uint32_t *mem_bar = (volatile uint32_t *)((uintptr_t)pci_header + 0x10 + (bar_id * 0x04)); + // TODO: check if the BAR type is matched + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id, *mem_bar); + *mem_bar = 0xFFFFFFFF; + // TODO: read the size of BAR and allocate from the resource window + // and map it to the device driver's PD + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id, *mem_bar); + // TODO: write the allocated physical address to the BAR register + *mem_bar = (uint32_t)pci_bar_cfg.base_addr; + // TODO: check if it has been updated + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id, *mem_bar); + } +} + +void map_pci_bar(struct pci_header_type0 *pci_header, uint8_t bar_id, uintptr_t target_vaddr) +{ + volatile uint32_t *mem_bar = (volatile uint32_t *)((uintptr_t)pci_header + 0x10 + (bar_id * 0x04)); + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id, *mem_bar); + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id + 1, mem_bar[1]); + bool memory_64bit = (*mem_bar) & 0x4; + uintptr_t dev_regs_paddr = *mem_bar; + if (memory_64bit) { + dev_regs_paddr = mem_bar[0] + ((uint64_t)mem_bar[1] << 32); + } + *mem_bar = 0xFFFFFFFF; + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id, *mem_bar); + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id + 1, mem_bar[1]); + uint32_t dev_regs_size = (~(*mem_bar) | 0xF) + 1; + sddf_dprintf("size: 0x%x\n", dev_regs_size); + // TODO: allocate memory region from the windows + *mem_bar = dev_regs_paddr & 0xFFFFFFFF; + if (memory_64bit) { + *(mem_bar + 1) = dev_regs_paddr >> 32; + } + sddf_dprintf("Memory BAR %d: 0x%x\n", bar_id, *mem_bar); + + + seL4_Error error; + uintptr_t cur_paddr = dev_regs_paddr; + uintptr_t end_paddr = dev_regs_paddr + dev_regs_size; + uintptr_t cur_vaddr = target_vaddr; + while (cur_paddr < end_paddr) { + error = retype_and_map_frame(cnode_specs, cur_paddr, cur_vaddr, vspace_cptr_ethernet_driver, seL4_X86_LargePageObject, seL4_ReadWrite); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to retype or map a frame.\n"); + return; + } + cur_paddr += (1 << seL4_LargePageBits); + cur_vaddr += (1 << seL4_LargePageBits); + } +} + +void configure_irqs(struct pci_header_type0 *pci_header, config_request_t config_request) +{ + bool ioapic_enabled = true; + for (int i = 0; i < config_request.num_irqs; i++) { + if (config_request.irqs[i].kind != irq_ioapic) { + ioapic_enabled = false; + } + + if (!ioapic_enabled && config_request.irqs[i].kind == irq_ioapic) { + sddf_dprintf("error: I/O APIC can not be enabled with MSI/MSI-X\n"); + return; + } + } + + // Enable/Disable I/O APIC interrupts + if (ioapic_enabled) { + pci_header->command &= (~BIT(10)); + return; + } else { + pci_header->command |= BIT(10); + } + + for (int i = 0; i < config_request.num_irqs; i++) { + switch (config_request.irqs[i].kind) { + case irq_ioapic: { + // TODO: figure out how to reconfigure interrupt vectors + break; + }; + case irq_msi: { + // TODO: configure MSI interrupts + break; + }; + case irq_msix: { + break; + }; + default: { + sddf_dprintf("error: device does not support MSI-X\n"); + }; + } + + } +} + +uint8_t get_pci_bridge_idx_by_bus(uint8_t pci_bus) +{ + for (int i = 0; i < pci_resources->num_bridges; i++) { + uint8_t num_res = pci_resources->bridges[i].num_dev_resources; + sddf_dprintf("num_res: %u\n", num_res); + for (int j = 0; j < num_res; j++) { + device_resource_t *dev_res = (device_resource_t *)&pci_resources->bridges[i].dev_resources[j]; + /* sddf_dprintf("resource type: %u, min_addr: 0x%lx, max_addr: 0x%lx\n", dev_res->type, dev_res->min_addr, dev_res->max_addr); */ + + if (dev_res->type == WORD_BUS) { + if (pci_bus >= dev_res->min_addr && pci_bus < dev_res->max_addr) { + sddf_dprintf("Found the bridge %u[0x%02lx-0x%02lx] containing bus 0x%02x\n", i, dev_res->min_addr, dev_res->max_addr, pci_bus); + return i; + } + + } + } + } + + // TODO: if it's not found + return 0; +} + +void configure_msi(struct pci_header_type0 *pci_header, uint8_t vector) +{ + struct msix_capability *msix_cap = (struct msix_capability *)find_pci_cap_by_id(pci_header, PCI_CAP_ID_MSIX); + + if (msix_cap) { + // Bits 2-0 refer to BAR ID + uint8_t bar_id = msix_cap->table_offset_bir & 0x5; + pci_bar_t msix_bar; + msix_bar.bar_id = bar_id; + /* msix_bar.base_addr = device_resources.regions[avail_region_idx].io_addr; */ + msix_bar.ioport = false; + + map_pci_bar(pci_header, bar_id, 0x4000000); + + // Enable MSI-X + struct msix_msg_ctrl *msg_ctrl = &msix_cap->msg_ctrl; + msg_ctrl->msix_enable = 1; + sddf_dprintf("Table Size: 0x%x\n", msg_ctrl->table_size + 1); + sddf_dprintf("Function Mask: 0x%x\n", msg_ctrl->func_mask); + sddf_dprintf("MSI-X Enable: 0x%x\n", msg_ctrl->msix_enable); + + struct msix_table *msix_table = (struct msix_table *)device_resources.regions[avail_region_idx].region.vaddr; + msix_table->msg_addr_low = 0xFEEu << 20; + msix_table->msg_data = 0x4030 + vector; + msix_table->vec_ctrl = 0x0; + sddf_dprintf("Vector 0 Message Addr Low: 0x%x\n", msix_table->msg_addr_low); + sddf_dprintf("Vector 0 Message Addr Hi: 0x%x\n", msix_table->msg_addr_hi); + sddf_dprintf("Vector 0 Message Data: 0x%x\n", msix_table->msg_data); + sddf_dprintf("Vector 0 Vector Control: 0x%x\n", msix_table->vec_ctrl); + + uint32_t *msix_pba = (uint32_t *)( + 0x800); + sddf_dprintf("PBA: 0x%x\n", msix_pba[0]); + + } +} + +pci_bridge_t *find_pci_bridge(uintptr_t header_addr, uintptr_t ecam_base) +{ + uintptr_t header_offset = header_addr - ecam_base; + uint32_t dev_slot = header_offset >> 15; + uint32_t func_slot = header_offset & 0xFFFF; + uintptr_t target_bridge_adr = dev_slot << 16 + func_slot; + + if (header_addr == 0x0) { + target_bridge_adr = 0x0; + } + sddf_dprintf("Target PCI bridge addr: 0x%lx\n", header_offset); + uint32_t num_bridges = pci_resources->num_bridges; + for (int i = 0; i < num_bridges; i++) { + pci_bridge_t *pci_bridge = &pci_resources->bridges[i]; + sddf_dprintf("pci_bridge addr: 0x%lx\n", pci_bridge->adr); + if (target_bridge_adr == pci_bridge->adr) { + return pci_bridge; + } + } + + return NULL; +} + +void bind_irq(pci_bridge_t *pci_bridge, struct pci_header_type0 *pci_header, uint8_t pci_bus, uint8_t pci_dev, uint8_t pci_func, uint8_t irq_num) +{ + uint8_t base_irq_cap = 138; + + uint8_t num_prt_entries = pci_bridge->num_prt_entries; + sddf_dprintf("num_prt_entries: %u\n", num_prt_entries); + uint8_t gsi_number = 0; + for (int j = 0; j < num_prt_entries; j++) { + pci_prt_t *pci_prt = (pci_prt_t *)&pci_bridge->prt_entries[j]; + sddf_dprintf("addr: 0x%X, pin: %u, gsi: %u\n", pci_prt->address, pci_prt->pin, pci_prt->gsi); + uint32_t dev_num = (pci_prt->address >> 16) & 0x1F; + uint32_t func_num = pci_prt->address & 0xFFFF; + if (func_num != 0xFFFF) { + sddf_dprintf("func numebr: 0x%X, pci_prt->address: 0x%X, &: 0x%X\n", func_num, pci_prt->address, pci_prt->address & 0xFFFF); + sddf_dprintf("Error: PRT rule (address: 0x%X, pin: %u, gsi: %u) does not apply to the current implementation\n", pci_prt->address, pci_prt->pin, pci_prt->gsi); + return; + } + + if (dev_num == pci_dev) { + gsi_number = pci_prt->gsi; + sddf_dprintf("Found the GSI numebr %u for the device\n", gsi_number); + break; + } + } + + if (gsi_number == 0) { + sddf_dprintf("Error: failed to find the PRT rule for PCI device at %02x:%02x.%x\n", pci_bus, pci_dev, pci_func); + return; + } + + sddf_dprintf("Try creating an IRQ handler capability: "); + seL4_Error error = seL4_IRQControl_GetIOAPIC(cnode_cptr_pci_resources + 1, cnode_cptr_ethernet_driver, base_irq_cap + irq_num, 58, 0, gsi_number, 1, 0, 1); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to create an IO/APIC IRQ handler - %d\n", error); + } else { + sddf_dprintf("Success!\n"); + } + + sddf_dprintf("Try minting a notification capability: "); + error = seL4_CNode_Mint(cnode_cptr_pci_resources, 250, 58, cnode_cptr_ethernet_driver, 1, 58, seL4_ReadWrite, 1 << irq_num); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to mint a notification - %d\n", error); + } else { + sddf_dprintf("Success!\n"); + } + + seL4_CPtr handler_cap = cnode_cptr_ethernet_driver + base_irq_cap + irq_num; + seL4_CPtr ntf_cap = cnode_cptr_pci_resources + 250; + + seL4_Word ret = seL4_DebugCapIdentify(handler_cap); + sddf_dprintf("ret: %lu\n", ret); + sddf_dprintf("Try bind the handler to notification: "); + error = seL4_IRQHandler_SetNotification(handler_cap, ntf_cap); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to bind to notification - %d\n", error); + } else { + sddf_dprintf("Success!\n"); + } + + sddf_deferred_notify(1); +} + +struct pci_header_type1 *find_parent_pci_bridge(uintptr_t bus_base, uint8_t bus_start, uint8_t bus_end, uint8_t child_bus) +{ + struct pci_header_type1 *parent_bridge = NULL; + + for (uint8_t pci_bus = bus_start; pci_bus < bus_end; pci_bus++) { + for (uint8_t pci_dev = 0; pci_dev < 32; pci_dev++) { + for (uint8_t pci_func = 0; pci_func < 8; pci_func++) { + struct pci_header_type1 *bridge_header = (struct pci_header_type1 *)(bus_base + (pci_bus << 20) + (pci_dev << 15) + (pci_func << 12)); + // Bits[6:0] - Header Layout specifying header type + if ((bridge_header->header_type & 0x3F) == 1) { + sddf_dprintf(" - primary bus num: 0x%x\n", bridge_header->primary_bus_num); + sddf_dprintf(" - secondary bus num: 0x%x\n", bridge_header->secondary_bus_num); + sddf_dprintf(" - subordinate bus num: 0x%x\n", bridge_header->subordinate_bus_num); + + if (parent_bridge == NULL) { + parent_bridge = bridge_header; + sddf_dprintf("update, header: 0x%x, ecam_base: 0x%lx\n", (uintptr_t)bridge_header, bus_base); + } else { + if (bridge_header->secondary_bus_num >= parent_bridge->secondary_bus_num && + bridge_header->subordinate_bus_num <= parent_bridge->subordinate_bus_num) { + sddf_dprintf("update\n"); + parent_bridge = bridge_header; + } + } + } + } + } + } + + return parent_bridge; +} + + +// TODO: pass bus start and end as arguments +void pci_ecam_scan(uintptr_t bus_base, uint8_t bus_start, uint8_t bus_end) +{ + for (uint8_t pci_bus = bus_start; pci_bus < bus_end; pci_bus++) { + for (uint8_t pci_dev = 0; pci_dev < 32; pci_dev++) { + for (uint8_t pci_func = 0; pci_func < 8; pci_func++) { + struct pci_header_type0 *pci_header = (struct pci_header_type0 *)(bus_base + (pci_bus << 20) + (pci_dev << 15) + (pci_func << 12)); + if (pci_header->vendor_id != 0xffff && pci_header->vendor_id != 0x0000) { + sddf_dprintf("bus: 0x%lx, dev: 0x%lx, func: 0x%lx, vendor_id: 0x%x, device_id: 0x%x, type: %u\n", + (((uintptr_t)pci_header >> 20) & 0xff), + (((uintptr_t)pci_header >> 15) & 0x1f), + (((uintptr_t)pci_header >> 12) & 0x7), + pci_header->vendor_id, + pci_header->device_id, + pci_header->header_type & 0x3F); + } + + + // TODO: convert it to general solution + /* if (pci_bus == 0 && pci_dev == 2 && pci_func == 0) { */ + /* struct pci_header_type1 *parent_bridge_header = find_parent_pci_bridge(bus_base, bus_start, bus_end, pci_bus); */ + /* sddf_dprintf("parent bridge: 0x%lx\n", (uintptr_t)parent_bridge_header); */ + /* pci_bridge_t *pci_bridge = find_pci_bridge((uintptr_t)parent_bridge_header, bus_base); */ + /* map_pci_bar(pci_header, 4, 0x60000000); */ + /* bind_irq(pci_bridge, pci_header, pci_bus, pci_dev, pci_func, 16); */ + /* } */ + + if (pci_bus == 1 && pci_dev == 0 && pci_func == 0) { + struct pci_header_type1 *parent_bridge_header = find_parent_pci_bridge(bus_base, bus_start, bus_end, pci_bus); + sddf_dprintf("parent bridge: 0x%lx\n", (uintptr_t)parent_bridge_header); + pci_bridge_t *pci_bridge = find_pci_bridge((uintptr_t)parent_bridge_header, bus_base); + map_pci_bar(pci_header, 0, 0x2000000); + /* bind_irq(pci_bridge, pci_header, pci_bus, pci_dev, pci_func, 16); */ + } + + } + } + } +} + +void print_cnode_caps() +{ + sddf_dprintf("========Descriptions of received capabilities========\n"); + sddf_dprintf("cnode_caps start: %u, end: %u\n", cnode_specs->start, cnode_specs->end); + sddf_dprintf("size of pci_resources_t: %lu\n", sizeof(pci_resources_t)); + sddf_dprintf("idx, base_addr, end_addr\n") + sddf_dprintf("%3u: (IRQControl capability)\n", 1); + for (int i = cnode_specs->start; i < cnode_specs->end; i++) { + sddf_dprintf("%3u: 0x%09lx, 0x%09lx\n", i, cnode_specs->caps[i].base_addr, cnode_specs->caps[i].end_addr); + } +} + +void get_ut_by_paddr(uintptr_t target_paddr) +{ + for (int i = cnode_specs->start; i < cnode_specs->end; i++) { + if (target_paddr >= cnode_specs->caps[i].base_addr && target_paddr < cnode_specs->caps[i].end_addr) { + sddf_dprintf("Found the untyped %u containing the target physical address: 0x%lx\n", i, target_paddr); + } + } +} + +void init(void) +{ + if (!acpi_ready) { + sddf_dprintf("ACPI driver has not set things up. Waiting for signaling\n"); + return; + } + + pci_resources = (pci_resources_t *)pci_resources_vaddr; + cnode_specs = (cnode_specs_t *)&pci_resources->cnode_specs; + sddf_dprintf("cptr_pci_resources: 0x%lx\n", (uintptr_t)cnode_cptr_pci_resources); + sddf_dprintf("cptr_ethernet_driver: 0x%lx\n", (uintptr_t)cnode_cptr_ethernet_driver); + cnode_specs->cptr = cnode_cptr_pci_resources; + + sddf_dprintf("=========PCI driver is running==========\n"); + + print_cnode_caps(); + + for (int i = 0; i < pci_resources->num_pci_groups; i++) { + sddf_dprintf("PCI segment group: %u, base addr: 0x%lx, bus_range: [%u-%u]\n", + pci_resources->pci_seg_groups[i].group_id, + pci_resources->pci_seg_groups[i].base_addr, + pci_resources->pci_seg_groups[i].bus_start, + pci_resources->pci_seg_groups[i].bus_end); + pci_seg_group_t *pci_seg_group = &pci_resources->pci_seg_groups[i]; + pci_ecam_scan(pci_seg_group->base_addr, + pci_seg_group->bus_start, + pci_seg_group->bus_end); + } + + /* sddf_dprintf("=========Descriptions of PCI resources==========\n"); */ + /* for (int i = 0; i < pci_resources->num_bridges; i++) { */ + /* uint8_t num_res = pci_resources->bridges[i].num_dev_resources; */ + /* sddf_dprintf("num_res: %u\n", num_res); */ + /* for (int j = 0; j < num_res; j++) { */ + /* device_resource_t *dev_res = (device_resource_t *)&pci_resources->bridges[i].dev_resources[j]; */ + /* sddf_dprintf("resource type: %u, min_addr: 0x%lx, max_addr: 0x%lx\n", dev_res->type, dev_res->min_addr, dev_res->max_addr); */ + + /* if (dev_res->type == DWORD_MEMORY || dev_res->type == WORD_MEMORY || dev_res->type == QWORD_MEMORY) { */ + /* get_ut_by_paddr(dev_res->min_addr); */ + /* } */ + /* } */ + + /* uint8_t num_prt_entries = pci_resources->bridges[i].num_prt_entries; */ + /* sddf_dprintf("num_prt_entries: %u\n", num_prt_entries); */ + /* for (int j = 0; j < num_prt_entries; j++) { */ + /* pci_prt_t *pci_prt = (pci_prt_t *)&pci_resources->bridges[i].prt_entries[j]; */ + /* sddf_dprintf("addr: 0x%X, pin: %u, gsi: %u\n", pci_prt->address, pci_prt->pin, pci_prt->gsi); */ + /* } */ + /* } */ + + +} + +void notified(microkit_channel ch) +{ + sddf_dprintf("\n[PCI driver] notified by ch %d\n", ch); + if (ch == 0 && !acpi_ready) { + acpi_ready = true; + init(); + } + +} diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h new file mode 100644 index 000000000..8854a49ba --- /dev/null +++ b/drivers/pci/pci.h @@ -0,0 +1,337 @@ +/* + * Copyright 2026, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +// PCI Capability IDs +#define PCI_CAP_ID_PM 0x01 // Power Management +#define PCI_CAP_ID_AGP 0x02 // AGP +#define PCI_CAP_ID_VPD 0x03 // Vital Product Data +#define PCI_CAP_ID_SLOTID 0x04 // Slot Identification +#define PCI_CAP_ID_MSI 0x05 // Message Signaled Interrupts +#define PCI_CAP_ID_CHSWP 0x06 // CompactPCI HotSwap +#define PCI_CAP_ID_PCIX 0x07 // PCI-X +#define PCI_CAP_ID_HT 0x08 // HyperTransport +#define PCI_CAP_ID_VNDR 0x09 // Vendor Specific +#define PCI_CAP_ID_DBG 0x0A // Debug port +#define PCI_CAP_ID_CCRC 0x0B // CompactPCI Central Resource Control +#define PCI_CAP_ID_SHPC 0x0C // PCI Standard Hot-Plug Controller +#define PCI_CAP_ID_SSVID 0x0D // Bridge subsystem vendor/device ID +#define PCI_CAP_ID_AGP3 0x0E // AGP Target PCI-PCI bridge +#define PCI_CAP_ID_SECDEV 0x0F // Secure Device +#define PCI_CAP_ID_EXP 0x10 // PCI Express +#define PCI_CAP_ID_MSIX 0x11 // MSI-X +#define PCI_CAP_ID_SATA 0x12 // SATA Data/Index Conf. +#define PCI_CAP_ID_AF 0x13 // PCI Advanced Features +#define PCI_CAP_ID_EA 0x14 // PCI Enhanced Allocation + + +typedef struct pcie_driver_config { + void *ecam_base; + uint64_t ecam_size; + uint8_t bus_range; +} pcie_driver_config_t; + +// Type 0 PCI configuration space header for endpoints +struct pci_header_type0 { + // Device Identification + uint16_t vendor_id; // 0x00: Vendor ID + uint16_t device_id; // 0x02: Device ID + uint16_t command; // 0x04: Command Register + uint16_t status; // 0x06: Status Register + uint8_t revision_id; // 0x08: Revision ID + uint8_t prog_if; // 0x09: Programming Interface + uint8_t subclass; // 0x0A: Sub Class Code + uint8_t class_code; // 0x0B: Base Class Code + uint8_t cache_line_size; // 0x0C: Cache Line Size + uint8_t latency_timer; // 0x0D: Latency Timer + uint8_t header_type; // 0x0E: Header Type + uint8_t bist; // 0x0F: Built-in Self Test + + // Base Address Registers (BARs) + uint32_t bar[6]; // 0x10-0x27: Base Address Registers + + // Subsystem Information + uint32_t cardbus_cis_ptr; // 0x28: CardBus CIS Pointer + uint16_t subsystem_vendor_id; // 0x2C: Subsystem Vendor ID + uint16_t subsystem_device_id; // 0x2E: Subsystem Device ID + uint32_t expansion_rom_addr; // 0x30: Expansion ROM Base Address + + // Capabilities and Interrupts + uint8_t cap_ptr; // 0x34: Capabilities Pointer + uint8_t reserved1[3]; // 0x35-0x37: Reserved + uint32_t reserved2; // 0x38-0x3B: Reserved + uint8_t interrupt_line; // 0x3C: Interrupt Line + uint8_t interrupt_pin; // 0x3D: Interrupt Pin + uint8_t min_gnt; // 0x3E: Min_Gnt + uint8_t max_lat; // 0x3F: Max_Lat + + // Capability list + uint8_t cap_data[192]; +}; + +// Type 1 PCI configuration space header for switches, bridges, etc. +struct pci_header_type1 { + // Device Identification + uint16_t vendor_id; // 0x00: Vendor ID + uint16_t device_id; // 0x02: Device ID + uint16_t command; // 0x04: Command Register + uint16_t status; // 0x06: Status Register + uint8_t revision_id; // 0x08: Revision ID + uint8_t prog_if; // 0x09: Programming Interface + uint8_t subclass; // 0x0A: Sub Class Code + uint8_t class_code; // 0x0B: Base Class Code + uint8_t cache_line_size; // 0x0C: Cache Line Size + uint8_t latency_timer; // 0x0D: Latency Timer + uint8_t header_type; // 0x0E: Header Type + uint8_t bist; // 0x0F: Built-in Self Test + + uint32_t bar0; // 0x10: Base Address Register 0 + uint32_t bar1; // 0x14: Base Address Register 1 + + uint8_t primary_bus_num; // 0x18: primary bus number + uint8_t secondary_bus_num; // 0x19: Secondary bus number + uint8_t subordinate_bus_num; // 0x20: Secondary bus number + uint8_t secondary_latency_timer; // 0x21: Secondary bus number + + uint8_t io_base; // 0x1C: I/O port base address + uint8_t io_limit; // 0x1D: I/O port address + uint16_t secondary_status; // 0x1E: Secondary Status + + uint16_t mem_base; // 0x20: Memory Base + uint16_t mem_limit; // 0x22: Memory Limit + uint16_t pre_mem_base; // 0x24: Prefetchable Memory Base + uint16_t pre_mem_limit; // 0x26: Prefetchable Memory Limit + uint32_t pre_mem_base_upper; // 0x28: Prefetchable Memory Base Upper Bits + uint32_t pre_mem_limit_upper; // 0x2C: Prefetchable Memory Limit Upper Bits + uint16_t io_base_upper; // 0x30: I/O Base Upper + uint16_t io_limit_upper; // 0x32: I/O Limit Upper + + uint8_t cap_ptr; // 0x34: Capability Pointer + uint8_t reserved[3]; + uint32_t exp_rom_base; // 0x38: Expand ROM Base Address + uint8_t interrupt_line; // 0x3C: Interrupt Line + uint8_t interrupt_pin; // 0x3D: Interrupt Pin + uint16_t min_gnt; // 0x3E: Bridge Control +}; + +// =========== ACPI ============ + +#define BIOS_AREA_START 0xE0000 +#define BIOS_AREA_END 0x100000 + +/** + * Root System Description Pointer (RSDP) + * https://wiki.osdev.org/RSDP + * */ +typedef struct acpi_rsdp { + char signature[8]; // "RSD PTR " + uint8_t checksum; // Checksum of first 20 bytes + char oem_id[6]; + uint8_t revision; // 0 for ACPI 1.0, 2 for ACPI 2.0+ + uint32_t rsdt_addr; // 32-bit RSDT address (ACPI 1.0) + uint32_t length; // Length (ACPI 2.0+) + uint64_t xsdt_addr; // 64-bit XSDT address (ACPI 2.0+) + uint8_t ext_checksum; // Checksum of all fields + uint8_t reserved[3]; +} acpi_rsdp_t; + +/** + * System Description Table Header + * + * All SDT share the same header but have different data part. + * See https://wiki.osdev.org/RSDT for more details. + * */ +typedef struct acpi_sdt_header { + char signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + char oem_id[6]; + char oem_table_id[8]; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +} acpi_sdt_header_t; + +/** + * + * MCFG Table is structured as: + * - ACPI SDT Header + * - 8-byte reserved area + * - A list of memory mapped configuration base address allocation structures. + * + * ECAM Base Address Allocation Structure + * + * see PCI Firmware Specification 3.3 Table 4-3 for more details. + * */ +typedef struct mcfg_ecam_alloc { + uint64_t base_addr; + uint16_t pci_seg_group; + uint8_t start_bus; + uint8_t end_bus; + uint32_t reserved; +} mcfg_ecam_alloc_t; + +// Shared Capability Structure +struct shared_pci_cap { + uint8_t cap_id; /* Generic PCI field: PCI_CAP_ID_VNDR */ + uint8_t next_ptr; /* Generic PCI field: next ptr. */ +}; + +// MSI Message Control Register +struct msi_msg_ctrl { + uint8_t msi_enable : 1; /* Enable MSI (RW)*/ + uint8_t mul_msg_cap: 3; /* Multiple Message Capable: table_size = 2 ** (mul_msg_cap) (RO) */ + uint8_t mul_msg_en: 3; /* Multiple Message Enable: table_size = 2 ** (mul_msg_en) (RW) */ + uint8_t addr_64 : 1; /* 64-bit Address Capable (RO) */ + uint8_t per_vec_masking : 1; /* Per-Vector Masking Capable (RO) */ + uint8_t ext_msg_data_cap : 1; /* Extended Message Data Capable (RO) */ + uint8_t ext_msg_data_en : 1; /* Extended Message Data Enable (RW) */ + uint8_t reserved : 5; +} __attribute__((packed)); + +// MSI-X Message Control Register +struct msix_msg_ctrl { + uint16_t table_size : 11; /* Real Table Size = table_size + 1 */ + uint8_t reserved : 3; + uint8_t func_mask : 1; /* Function Mask: disable all interrupts if set */ + uint8_t msix_enable : 1; /* MSI-X Enable */ +} __attribute__((packed)); + +// MSI Capability (ID: 0x05) +struct msi_capability { + uint8_t cap_id; /* Generic PCI field: PCI_CAP_ID_VNDR */ + uint8_t next_ptr; /* Generic PCI field: next ptr. */ + uint16_t msg_ctrl; /* Message Control register */ + uint32_t msg_addr; /* Message Address */ + uint32_t msg_addr_upper; /* Message Address - high 32 bits */ + uint16_t msg_data; /* Message Data */ + uint16_t reserved; +}; + +// MSI-X Capability (ID 0x11) +struct msix_capability { + uint8_t cap_id; /* Generic PCI field: PCI_CAP_ID_VNDR */ + uint8_t next_ptr; /* Generic PCI field: next ptr. */ + struct msix_msg_ctrl msg_ctrl; /* Message Control register */ + uint32_t table_offset_bir; /* Table offset and BAR indicator */ + uint32_t pba_offset_bir; /* Pending bit array offset and BAR */ +}; + +// MSI-X Table Structure +struct msix_table { + uint32_t msg_addr_low; + uint32_t msg_addr_hi; + uint32_t msg_data; + uint32_t vec_ctrl; +}; + +#define PCI_DEV_MAX_BARS 6 +#define PCI_DEV_MAX_IRQS 8 +#define ECAM_MAX_REQUESTS 64 + +typedef enum bar_locatable { + any_32b, less_1m, any_64b +} bar_locatable_t; + +typedef enum irq_kind : uint8_t { + irq_ioapic, irq_msi, irq_msix +} irq_kind_t; + +typedef struct pci_irq { + uint64_t pin; + uint64_t vector; + uint64_t kind; +} pci_irq_t; + +typedef struct pci_bar { + uint8_t bar_id; + uint64_t base_addr; + bool ioport; + bool mem_64b; +} pci_bar_t; + +typedef struct config_request { + uint8_t bus; + uint8_t dev; + uint8_t func; + uint16_t device_id; + uint16_t vendor_id; + pci_bar_t bars[PCI_DEV_MAX_BARS]; + uint8_t num_irqs; + pci_irq_t irqs[PCI_DEV_MAX_IRQS]; +} config_request_t; + +typedef struct pci_ecam_config { + char magic[5]; + uint8_t num_requests; + config_request_t requests[ECAM_MAX_REQUESTS]; +} pci_ecam_config_t; + +// ===================sort out this============= +#define MAX_NUM_PCI_SEG_GROUP 16 +#define MAX_NUM_AS_RESOURCES 10 +#define MAX_NUM_PRT_ENTRIES 256 + +typedef struct pci_seg_group { + uint64_t base_addr; + uint16_t group_id; + uint8_t bus_start; + uint8_t bus_end; + uint8_t reserved[4]; +} __attribute__((packed)) pci_seg_group_t; + +enum device_resource_type { + IO_PORT = 0, + DWORD_MEMORY, + DWORD_IO, + DWORD_BUS, + WORD_MEMORY, + WORD_IO, + WORD_BUS, + QWORD_MEMORY, + QWORD_IO, + QWORD_BUS, +}; + +typedef struct { + enum device_resource_type type; + uintptr_t min_addr; + uintptr_t max_addr; +} device_resource_t; + +typedef struct { + uint32_t address; + uint8_t pin; + uint8_t gsi; +} pci_prt_t; + +typedef struct { + /* char path_name[AML_MAX_PATH_STR]; */ + /* uint32_t path_len; */ + uint32_t bus_start; + uint32_t bus_end; + uintptr_t adr; + device_resource_t dev_resources[MAX_NUM_AS_RESOURCES]; + uint8_t num_dev_resources; + pci_prt_t prt_entries[MAX_NUM_PRT_ENTRIES]; + uint8_t num_prt_entries; + uint8_t segment_id; +} pci_bridge_t; + +typedef struct { + pci_seg_group_t pci_seg_groups[MAX_NUM_PCI_SEG_GROUP]; + uint32_t num_pci_groups; + pci_bridge_t bridges[30]; + uint32_t num_bridges; + cnode_specs_t cnode_specs; +} pci_resources_t; diff --git a/drivers/pci/pci_driver.mk b/drivers/pci/pci_driver.mk new file mode 100644 index 000000000..d8483950e --- /dev/null +++ b/drivers/pci/pci_driver.mk @@ -0,0 +1,27 @@ +# +# Copyright 2026, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Include this snippet in your project Makefile to build +# the PCI driver +# +# NOTES: +# Generates pci_driver.elf +# Expects libsddf_util_debug.a to be in ${LIBS} + +PCI_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +pci_driver.elf: pci/pci.o + $(LD) $(LDFLAGS) $< $(LIBS) -o $@ + +pci/pci.o: ${PCI_DIR}/pci.c ${CHECK_FLAGS_BOARD_MD5} |pci $(SDDF_LIBC_INCLUDE) + ${CC} ${CFLAGS} -o $@ -c $< + +pci: + mkdir -p pci + +clean:: + rm -rf pci +clobber:: + rm -f pci_driver.elf diff --git a/drivers/timer/hpet/timer.c b/drivers/timer/hpet/timer.c new file mode 100644 index 000000000..8d532c3ed --- /dev/null +++ b/drivers/timer/hpet/timer.c @@ -0,0 +1,186 @@ +/* + * Copyright 2025, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +__attribute__((__section__(".device_resources"), retain, used)) device_resources_t device_resources; + +/* hpet data structures / memory maps + * each timer has its own configuration registers: + * - Timer n Configuration and Capability Register + * - Timer n Comparator Value Register + * - Timer n FSB Interrupt Route Register + * see "IA-PC HPET (High Precision Event Timers) Specification" for more details */ +typedef struct __attribute__((packed)) hpet_timer { + uint64_t config; + uint64_t comparator; + uint64_t fsb_irr; + char padding[8]; +} hpet_timer_t; + +/* General HPET config bits */ +/* 1 if main counter is running and interrupts are enabled */ +#define ENABLE_CNF 0 +/* 1 if LegacyReplacementRoute is being used */ +#define LEG_RT_CNF 1 + +/* HPET timer config bits - these can't be changed, but allow us to + * find out details of the timer */ +/* 0 is reserved */ +/* 0 if edge triggered, 1 if level triggered. */ +#define TN_INT_TYPE_CNF 1 +/* Set to 1 to cause an interrupt when main timer hits comparator for this timer */ +#define TN_INT_ENB_CNF 2 +/* If this bit is 1 you can write a 1 to it for periodic interrupts, + or a 0 for non-periodic interrupts */ +#define TN_TYPE_CNF 3 +/* If this bit is 1, hardware supports periodic mode for this timer */ +#define TN_PER_INT_CAP 4 +/* 1 = timer is 64 bit, 0 = timer is 32 bit */ +#define TN_SIZE_CAP 5 +/* Writing 1 to this bit allows software to directly set a periodic timers accumulator */ +#define TN_VAL_SET_CNF 6 +/* 7 is reserved */ +/* Set this bit to force the timer to be a 32-bit timer (only works on a 64-bit timer) */ +#define TN_32MODE_CNF 8 +/* 5 bit wide field (9:13). Specifies routing for IO APIC if using */ +#define TN_INT_ROUTE_CNF 9 +/* Set this bit to force interrupt delivery to the front side bus, don't use the IO APIC */ +#define TN_FSB_EN_CNF 14 +/* If this bit is one, bit TN_FSB_EN_CNF can be set */ +#define TN_FSB_INT_DEL_CAP 15 +/* Bits 16:31 are reserved */ +/* Read-only 32-bit field that specifies which routes in the IO APIC this timer can be configured + to take */ +#define TN_INT_ROUTE_CAP 32 + +#define HPET_GENERAL_CAP_ID_REG 0x0 +#define HPET_GENERAL_CONFIG_REG 0x10 +#define HPET_GENERAL_ISR_REG 0x20 +#define HPET_MAIN_COUNTER_REG 0xF0 +#define HPET_TIMER1_OFFSET 0x120 + +#define LOCAL_APIC_ADDR 0x0FEE00000llu + +// @terryb: remove hard-coded IRQ channel +#define IRQ_CH 0 +#define IRQ_NUM 0x30 +uintptr_t HPET_REGION = 0x50000000; + +volatile hpet_timer_t *timer_0; +uint64_t tick_period_fs; // main counter tick period in femtoseconds + +#define MAX_TIMEOUTS SDDF_TIMER_MAX_CLIENTS + +uint64_t timeouts[MAX_TIMEOUTS]; +uint64_t next_timeout = UINT64_MAX; + +bool pci_ready = false; + +uint64_t ns_to_ticks(uint64_t ns) +{ + return ns * 1000000 / tick_period_fs; +} + +uint64_t ticks_to_ns(uint64_t ticks) +{ + return ticks * tick_period_fs / 1000000; +} + +uint64_t get_time(void) +{ + uint64_t time = *(uint64_t *)(HPET_REGION + HPET_MAIN_COUNTER_REG); + return ticks_to_ns(time); +} + +void set_timeout(uint64_t timeout) +{ + timer_0->comparator = ns_to_ticks(timeout); +} + +static void process_timeouts(uint64_t curr_time) +{ + uint64_t next_timeout = UINT64_MAX; + for (int i = 0; i < MAX_TIMEOUTS; i++) { + if (timeouts[i] <= curr_time) { + sddf_notify(i); + timeouts[i] = UINT64_MAX; + } else if (timeouts[i] < next_timeout) { + next_timeout = timeouts[i]; + } + } + + if (next_timeout != UINT64_MAX) { + set_timeout(next_timeout); + } +} + +void init(void) +{ + // Read COUNTER_CLK_PERIOD 32:63 from General Capabilities and ID Register + volatile uint64_t cap = *((uint64_t *)HPET_REGION + HPET_GENERAL_CAP_ID_REG); + tick_period_fs = cap >> 32; + + // Enable all timer interrupts + volatile uint64_t *general_config_reg = (void *)HPET_REGION + HPET_GENERAL_CONFIG_REG; + *general_config_reg |= BIT(ENABLE_CNF); + + timer_0 = (void *)HPET_REGION + HPET_TIMER1_OFFSET; + // Enable Timer 0 interrupts + timer_0->config |= BIT(TN_FSB_EN_CNF) | BIT(TN_INT_ENB_CNF); + // Direct timer interrupts to local APIC: write address and value (interrupt vector) + // interrupt vector = vector (in SDF) + irq_user_min(0x10) + IRQ_INT_OFFSET(0x20) + // @terryb: remove hard-coded IRQ number + timer_0->fsb_irr = (LOCAL_APIC_ADDR << 32llu) | IRQ_NUM; + + next_timeout = UINT64_MAX; + for (int i = 0; i < MAX_TIMEOUTS; i++) { + timeouts[i] = UINT64_MAX; + } + + microkit_deferred_irq_ack(IRQ_CH); +} + +seL4_MessageInfo_t protected(microkit_channel ch, microkit_msginfo msginfo) +{ + switch (microkit_msginfo_get_label(msginfo)) { + + case 0: { + uint64_t now = get_time(); + microkit_mr_set(0, now); + return microkit_msginfo_new(0, 1); + } + + case 1: { + uint64_t delta = microkit_mr_get(0); + uint64_t now = get_time(); + + timeouts[ch] = now + delta; + process_timeouts(now); + return microkit_msginfo_new(0, 0); + } + + default: + return microkit_msginfo_new(0, 0); + } +} + +void notified(microkit_channel ch) +{ + if (ch != IRQ_CH) { + return; + } + + microkit_deferred_irq_ack(IRQ_CH); + + uint64_t now = get_time(); + + process_timeouts(now); +} diff --git a/examples/echo_server/core_config/single_core.json b/examples/echo_server/core_config/single_core.json index afa381c0a..7df2a6989 100644 --- a/examples/echo_server/core_config/single_core.json +++ b/examples/echo_server/core_config/single_core.json @@ -1,4 +1,6 @@ { + "acpi_driver": 0, + "pci_driver": 0, "timer_driver": 0, "serial_driver": 0, "serial_virt_tx": 0, diff --git a/examples/echo_server/echo.mk b/examples/echo_server/echo.mk index eb9fa749d..494fc6b1a 100644 --- a/examples/echo_server/echo.mk +++ b/examples/echo_server/echo.mk @@ -53,7 +53,8 @@ vpath %.c ${SDDF} ${ECHO_SERVER} IMAGES := eth_driver.elf echo.elf benchmark.elf idle.elf \ network_virt_rx.elf network_virt_tx.elf network_copy.elf \ - timer_driver.elf serial_driver.elf serial_virt_tx.elf + timer_driver.elf serial_driver.elf serial_virt_tx.elf \ + pci_driver.elf acpi_driver.elf CFLAGS += \ @@ -85,21 +86,42 @@ all: loader.img echo.elf: $(ECHO_OBJS) libsddf_util.a lib_sddf_lwip_echo.a $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ +include ${SDDF}/drivers/acpi/acpi_driver.mk +include ${SDDF}/drivers/pci/pci_driver.mk +include ${SDDF}/util/util.mk +include ${SDDF}/network/components/network_components.mk +include ${SDDF}/network/lib_sddf_lwip/lib_sddf_lwip.mk +include ${ETHERNET_DRIVER}/eth_driver.mk +include ${BENCHMARK}/benchmark.mk +include ${TIMER_DRIVER}/timer_driver.mk +include ${UART_DRIVER}/serial_driver.mk +include ${SERIAL_COMPONENTS}/serial_components.mk + +ifdef NET_NEED_TIMER +export NET_NEED_TIMER +endif + # Need to build libsddf_util_debug.a because it's included in LIBS # for the unimplemented libc dependencies ${IMAGES}: libsddf_util_debug.a +test_%_table.dat: $(ECHO_SERVER)/acpi_tables/%_table.dat + cp $^ $@ + $(SYSTEM_FILE): $(METAPROGRAM) $(IMAGES) $(DTB) ifneq ($(strip $(DTS)),) $(PYTHON)\ $(METAPROGRAM) --sddf $(SDDF) --board $(MICROKIT_BOARD) \ --dtb $(DTB) --output . --sdf $(SYSTEM_FILE) --objcopy $(OBJCOPY) --smp $(SMP_CONFIG) \ - $(if $(BENCH_PMU_EVENTS), --bench_pmu_events $(BENCH_PMU_EVENTS)) + $${NET_NEED_TIMER:+--need_timer} else $(PYTHON)\ - $(METAPROGRAM) --sddf $(SDDF) --board $(MICROKIT_BOARD) \ + $(METAPROGRAM) --sddf $(SDDF) --board $(X86_BOARD) \ --output . --sdf $(SYSTEM_FILE) --objcopy $(OBJCOPY) --smp $(SMP_CONFIG) \ - $(if $(BENCH_PMU_EVENTS), --bench_pmu_events $(BENCH_PMU_EVENTS)) + $${NET_NEED_TIMER:+--need_timer} +endif +ifdef NET_NEED_TIMER + $(OBJCOPY) --update-section .timer_client_config=timer_client_ethernet_driver.data eth_driver.elf endif $(OBJCOPY) --update-section .device_resources=serial_driver_device_resources.data serial_driver.elf $(OBJCOPY) --update-section .serial_driver_config=serial_driver_config.data serial_driver.elf @@ -121,20 +143,14 @@ endif $(OBJCOPY) --update-section .lib_sddf_lwip_config=lib_sddf_lwip_config_client1.data echo1.elf touch $@ -${IMAGE_FILE} $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) +SPEC = capdl_spec.json +$(BUILD_DIR): + mkdir -p $@ + +${IMAGE_FILE} $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) $(BUILD_DIR) $(MICROKIT_TOOL) $(SYSTEM_FILE) --search-path $(BUILD_DIR) \ --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) \ - -o $(IMAGE_FILE) -r $(REPORT_FILE) - - -include ${SDDF}/util/util.mk -include ${SDDF}/network/components/network_components.mk -include ${SDDF}/network/lib_sddf_lwip/lib_sddf_lwip.mk -include ${ETHERNET_DRIVER}/eth_driver.mk -include ${BENCHMARK}/benchmark.mk -include ${TIMER_DRIVER}/timer_driver.mk -include ${UART_DRIVER}/serial_driver.mk -include ${SERIAL_COMPONENTS}/serial_components.mk + -o $(IMAGE_FILE) -r $(REPORT_FILE) --capdl-json ${SPEC} qemu: $(IMAGE_FILE) $(QEMU) $(QEMU_ARCH_ARGS) $(QEMU_NET_ARGS) \ diff --git a/examples/echo_server/meta.py b/examples/echo_server/meta.py index 0699dfa3b..880ebbb2c 100644 --- a/examples/echo_server/meta.py +++ b/examples/echo_server/meta.py @@ -16,8 +16,12 @@ ProtectionDomain = SystemDescription.ProtectionDomain MemoryRegion = SystemDescription.MemoryRegion +CNode = SystemDescription.CNode Map = SystemDescription.Map +CapMap = SystemDescription.CapMap +BootInfo = SystemDescription.BootInfo Channel = SystemDescription.Channel +IrqIoapic = SystemDescription.IrqIoapic """ @@ -151,6 +155,59 @@ def serialise(self) -> bytes: num_pmu_events, ) +class AcpiTablesConfig: + def __init__( + self, + max_total_size: int, + ): + self.max_total_size = max_total_size + self.patched_tables_end = 0 + self.alignment = 0x1000 + self.max_num_acpi_tables = 20 # This needs to be synced with MAX_NUM_ACPI_TABLES in acpi.h + self.num_tables = 0 + self.acpi_table_bytes = bytearray() + self.acpi_table_pointers = [0] * self.max_num_acpi_tables + + # TODO: add the checks + def add_acpi_table(self, acpi_file): + acpi_file = "/Users/terrybai/tmp/acpi_vb105/vb105_acpi/" + acpi_file + ".dat" + print(acpi_file) + assert os.path.isfile(acpi_file) + with open(acpi_file, "rb") as data_file: + byte_list = list(data_file.read()) + + if len(byte_list) + len(self.acpi_table_bytes) < self.max_total_size: + self.acpi_table_pointers[self.num_tables] = len(self.acpi_table_bytes) + self.acpi_table_bytes.extend(byte_list) + self.patched_tables_end = len(self.acpi_table_bytes) + self.num_tables += 1 + + trailing_len = len(self.acpi_table_bytes) % self.alignment + if trailing_len != 0: + padding_len = self.alignment - trailing_len + if padding_len + len(self.acpi_table_bytes) < self.max_total_size: + self.acpi_table_bytes.extend(b"\x00" * padding_len) + + def tables_serialise(self): + pack_str = "<" + "B" * len(self.acpi_table_bytes) + + return struct.pack( + pack_str, + *self.acpi_table_bytes + ) + + def summary_serialise(self): + pack_str = "<" + "Q" * self.max_num_acpi_tables + "QQII" + + return struct.pack( + pack_str, + *self.acpi_table_pointers, + self.patched_tables_end, + self.max_total_size, + self.alignment, + self.num_tables, + ) + # Adds ".elf" to elf strings def copy_elf(source_elf: str, new_elf: str, elf_number=None): @@ -190,6 +247,7 @@ def generate( dtb: Optional[DeviceTree], get_core: Callable[[str], int], pmu_event_ids: List[int], + net_need_timer: bool, ): uart_node = None ethernet_node = None @@ -202,14 +260,59 @@ def generate( timer_node = dtb.node(board.timer) assert timer_node is not None + acpi_driver = ProtectionDomain("acpi_driver", "acpi_driver.elf", priority=200, stack_size=0x5000) + pci_driver = ProtectionDomain("pci_driver", "pci_driver.elf", priority=199) + + acpi_tables_config = AcpiTablesConfig(0x500000) + # acpi_tables_config.add_acpi_table("mcfg") + # acpi_tables_config.add_acpi_table("dsdt") + # for i in range(1, 18): + # acpi_tables_config.add_acpi_table("ssdt" + str(i)) + + acpi_driver.add_boot_info(BootInfo("remaining_untypeds")) + acpi_driver.add_boot_info(BootInfo("rsdp")) + + cnode_remaining_untypeds = CNode("remaining_untypeds", True, 9) + sdf.add_cnode(cnode_remaining_untypeds) + acpi_driver.add_cap_map(CapMap(CapMap.CapType.Cnode, None, cnode_remaining_untypeds, 1)) + acpi_driver.add_cap_map(CapMap(CapMap.CapType.Vspace, pci_driver, None, 2)) + + cnode_pci_resources = CNode("pci_resources", False, 8) + sdf.add_cnode(cnode_pci_resources) + acpi_driver.add_cap_map(CapMap(CapMap.CapType.Cnode, None, cnode_pci_resources, 3)) + pci_driver.add_cap_map(CapMap(CapMap.CapType.Cnode, None, cnode_pci_resources, 1)) + + mr_aml_object_pool = MemoryRegion(sdf, "aml_object_pool", 0x100000) + sdf.add_mr(mr_aml_object_pool) + acpi_driver.add_map(Map(mr_aml_object_pool, 0x30000000, "rw")) + + mr_aml_state_stack = MemoryRegion(sdf, "aml_state_stack", 0x10000) + sdf.add_mr(mr_aml_state_stack) + acpi_driver.add_map(Map(mr_aml_state_stack, 0x50000000, "rw")) + + mr_acpi_tables_copy = MemoryRegion(sdf, "acpi_tables_copy", 0x50000) + sdf.add_mr(mr_acpi_tables_copy) + acpi_driver.add_map(Map(mr_acpi_tables_copy, 0x40000000, "rw")) + + mr_pci_resources = MemoryRegion(sdf, "pci_resources", 0x20000) + sdf.add_mr(mr_pci_resources) + acpi_driver.add_map(Map(mr_pci_resources, 0x60000000, "rw", cached=False)) + pci_driver.add_map(Map(mr_pci_resources, 0x60000000, "rw", cached=False)) + + sdf.add_channel(Channel(acpi_driver, pci_driver, a_id=0, b_id=0)) + timer_driver = ProtectionDomain( - "timer_driver", "timer_driver.elf", priority=102, cpu=get_core("timer_driver") + "timer_driver", "timer_driver.elf", priority=253, cpu=get_core("timer_driver") ) timer_system = Sddf.Timer(sdf, timer_node, timer_driver) if board.arch == SystemDescription.Arch.X86_64: add_x86_hpet(sdf, timer_driver) + uart_driver = ProtectionDomain("serial_driver", "serial_driver.elf", priority=100) + serial_virt_tx = ProtectionDomain( + "serial_virt_tx", "serial_virt_tx.elf", priority=99 + ) uart_driver = ProtectionDomain( "serial_driver", "serial_driver.elf", @@ -234,7 +337,8 @@ def generate( ) if board.arch == SystemDescription.Arch.X86_64: - serial_port = SystemDescription.IoPort(0x3F8, 8, 0) + print("board: ", board) + serial_port = SystemDescription.IoPort(board.serial, 8, 0) uart_driver.add_ioport(serial_port) ethernet_driver = ProtectionDomain( @@ -273,22 +377,19 @@ def generate( sdf.add_mr(mbox) ethernet_driver.add_map(Map(mbox, 0x3000000, perms="rw", cached=False)) - if board.arch == SystemDescription.Arch.X86_64: - hw_net_rings = SystemDescription.MemoryRegion( - sdf, "hw_net_rings", 65536, paddr=0x7A000000 - ) + if board.name == "qemu_virt_x86": + hw_net_rings = MemoryRegion(sdf, "hw_net_rings", 65536, paddr=0x7A000000) sdf.add_mr(hw_net_rings) - hw_net_rings_map = SystemDescription.Map(hw_net_rings, 0x7000_0000, "rw") - ethernet_driver.add_map(hw_net_rings_map) + ethernet_driver.add_map(Map(hw_net_rings, 0x7000_0000, "rw")) - virtio_net_regs = SystemDescription.MemoryRegion( - sdf, "virtio_net_regs", 0x4000, paddr=0xFE000000 - ) - sdf.add_mr(virtio_net_regs) - virtio_net_regs_map = SystemDescription.Map( - virtio_net_regs, 0x6000_0000, "rw", cached=False - ) - ethernet_driver.add_map(virtio_net_regs_map) + # virtio_net_regs = SystemDescription.MemoryRegion( + # sdf, "virtio_net_regs", 0x4000, paddr=0xFE000000 + # ) + # sdf.add_mr(virtio_net_regs) + # virtio_net_regs_map = SystemDescription.Map( + # virtio_net_regs, 0x6000_0000, "rw", cached=False + # ) + # ethernet_driver.add_map(virtio_net_regs_map) virtio_net_irq = SystemDescription.IrqIoapic( ioapic_id=0, pin=11, vector=1, id=16 @@ -301,6 +402,61 @@ def generate( pci_config_data_port = SystemDescription.IoPort(0xCFC, 4, 2) ethernet_driver.add_ioport(pci_config_data_port) + pci_driver.add_cap_map(CapMap(CapMap.CapType.Vspace, ethernet_driver, None, 2)) + pci_driver.add_cap_map(CapMap(CapMap.CapType.Cnode, ethernet_driver, None, 3)) + sdf.add_channel(Channel(pci_driver, ethernet_driver, a_id=1, b_id=10)) + + if board.name == "vb_105": + # ecam_mr = MemoryRegion(sdf, name="ecam", size=0x1000, paddr=0xE0100000) + # sdf.add_mr(ecam_mr) + # ethernet_driver.add_map(Map(ecam_mr, vaddr=0x3000000, perms="rw")) + + # eth_region_0 = MemoryRegion( + # sdf, name="eth_region_0", size=0x100000, paddr=board.ethernet + # ) + # sdf.add_mr(eth_region_0) + # ethernet_driver.add_map( + # Map(eth_region_0, vaddr=0x2000000, perms="rw", cached=False) + # ) + + hw_rx_ring_buffer = MemoryRegion( + sdf, name="hw_rx_ring_buffer", size=0x4000, paddr=0x10000000 + ) + sdf.add_mr(hw_rx_ring_buffer) + ethernet_driver.add_map(Map(hw_rx_ring_buffer, vaddr=0x2400000, perms="rw", cached=False)) + + hw_tx_ring_buffer = MemoryRegion( + sdf, name="hw_tx_ring_buffer", size=0x4000, paddr=0x10004000 + ) + sdf.add_mr(hw_tx_ring_buffer) + ethernet_driver.add_map(Map(hw_tx_ring_buffer, vaddr=0x2404000, perms="rw", cached=False)) + + # MSI + # eth_irq = SystemDescription.IrqMsi( + # pci_bus=65, pci_device=0, pci_func=0, vector=1, handle=0 + # ) + + # MSI-X + # eth_msix_table = MemoryRegion( + # sdf, name="eth_msix_table", size=0x8000, paddr=0x6000e04000 + # ) + # sdf.add_mr(eth_msix_table) + # ethernet_driver.add_map( + # Map(eth_msix_table, vaddr=0x4000000, perms="rw") + # ) + # eth_irq = SystemDescription.IrqMsi( + # pci_bus=65, pci_device=0, pci_func=0, vector=2, handle=0 + # ) + + # Legacy I/O APIC + eth_irq = IrqIoapic(ioapic_id=0, + pin=16, + vector=8, + trigger=IrqIoapic.Trigger.LEVEL, + polarity=IrqIoapic.Polarity.ACTIVELOW, + id=16) + ethernet_driver.add_irq(eth_irq) + net_virt_tx = ProtectionDomain( "net_virt_tx", "network_virt_tx.elf", @@ -343,6 +499,9 @@ def generate( serial_system.add_client(client1) timer_system.add_client(client0) timer_system.add_client(client1) + if net_need_timer: + print("need a timer") + timer_system.add_client(ethernet_driver) net_system.add_client_with_copier(client0, client0_net_copier) net_system.add_client_with_copier(client1, client1_net_copier) @@ -351,6 +510,8 @@ def generate( # Echo server protection domains child_pds = [ + acpi_driver, + pci_driver, uart_driver, serial_virt_tx, ethernet_driver, @@ -489,6 +650,14 @@ def generate( "eth_driver.elf", "timer_client_config", "timer_client_ethernet_driver" ) + with open(f"{output_dir}/acpi_tables_summary.data", "wb+") as f: + f.write(acpi_tables_config.summary_serialise()) + update_elf_section("acpi_driver.elf", "acpi_tables_summary", "acpi_tables_summary") + + with open(f"{output_dir}/acpi_tables.data", "wb+") as f: + f.write(acpi_tables_config.tables_serialise()) + update_elf_section("acpi_driver.elf", "acpi_tables", "acpi_tables") + with open(f"{output_dir}/benchmark_client_config.data", "wb+") as f: f.write(bench_client_config.serialise()) update_elf_section( @@ -562,6 +731,7 @@ def generate( parser.add_argument("--board", required=True, choices=[b.name for b in BOARDS]) parser.add_argument("--output", required=True) parser.add_argument("--sdf", required=True) + parser.add_argument("--need_timer", action="store_true", default=False) parser.add_argument("--objcopy", required=True) parser.add_argument("--smp", required=True) parser.add_argument("--bench_pmu_events", required=False) @@ -618,4 +788,4 @@ def generate( pmu_event_ids.append(bench_pmu_events[pmu_events[i]][0]) - generate(args.sdf, args.output, dtb, get_core, pmu_event_ids) + generate(args.sdf, args.output, dtb, get_core, pmu_event_ids, args.need_timer) diff --git a/examples/echo_server/run.sh b/examples/echo_server/run.sh new file mode 100755 index 000000000..2f39edb95 --- /dev/null +++ b/examples/echo_server/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env sh + +# cd /Users/terrybai/ts/sddf/examples/echo_server && \ +# rm -rf build && \ +# make -j$(nproc) BUILD_DIR=build MICROKIT_BOARD=x86_64_generic X86_BOARD=qemu_virt_x86 MICROKIT_CONFIG=debug MICROKIT_SDK=/Users/terrybai/ts/microkit/release/microkit-sdk-2.2.0-dev qemu + +cd /Users/terrybai/ts/sddf/examples/echo_server && \ +rm -rf build && \ +# make -j$(nproc) BUILD_DIR=build MICROKIT_BOARD=x86_64_generic X86_BOARD=qemu_virt_x86 MICROKIT_CONFIG=debug MICROKIT_SDK=/Users/terrybai/ts/microkit/release/microkit-sdk-2.2.0-dev qemu +make -j$(nproc) BUILD_DIR=build MICROKIT_BOARD=x86_64_generic X86_BOARD=vb_105 MICROKIT_CONFIG=debug MICROKIT_SDK=/Users/terrybai/ts/microkit/release/microkit-sdk-2.2.0-dev && \ +# mq.sh run -s vb_105 -f ./build/sel4.elf -f ./build/loader.img -c "fnjkeqhtreqfgkjadfg" +/Users/terrybai/tmp/machine_queue/mq.sh run -s viscous -f ./build/sel4.elf -f ./build/loader.img -c "fnjkeqhtreqfgkjadfg" diff --git a/examples/pci/Makefile b/examples/pci/Makefile new file mode 100644 index 000000000..706b8182e --- /dev/null +++ b/examples/pci/Makefile @@ -0,0 +1,32 @@ +# +# Copyright 2026, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(MICROKIT_BOARD)),) +$(error MICROKIT_BOARD must be specified) +endif +BUILD_DIR ?= build +override BUILD_DIR := $(abspath ${BUILD_DIR}) +export BUILD_DIR +export SDDF := $(abspath ../..) +override MICROKIT_SDK := $(abspath ${MICROKIT_SDK}) + +IMAGE_FILE := $(BUILD_DIR)/loader.img +REPORT_FILE := $(BUILD_DIR)/report.txt + +all: ${IMAGE_FILE} + +qemu ${IMAGE_FILE} ${REPORT_FILE} clean clobber: ${BUILD_DIR}/Makefile FORCE + ${MAKE} -C ${BUILD_DIR} MICROKIT_SDK=${MICROKIT_SDK} $(notdir $@) + +${BUILD_DIR}/Makefile: pci.mk + mkdir -p ${BUILD_DIR} + cp pci.mk ${BUILD_DIR}/Makefile + +FORCE: diff --git a/examples/pci/meta.py b/examples/pci/meta.py new file mode 100644 index 000000000..46935a147 --- /dev/null +++ b/examples/pci/meta.py @@ -0,0 +1,55 @@ +# Copyright 2026, UNSW +# SPDX-License-Identifier: BSD-2-Clause +import os +import sys +import argparse +from sdfgen import SystemDescription, Sddf, DeviceTree +from typing import List, Tuple, Callable, Optional +import importlib +from importlib.metadata import version + +sys.path.append( + os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../tools/meta") +) + +# Use importlib to dynamically load. Using `from` import below other code is bad style. +board_module = importlib.import_module("board") +BOARDS = board_module.BOARDS + +assert version("sdfgen").split(".")[1] == "28", "Unexpected sdfgen version" + +ProtectionDomain = SystemDescription.ProtectionDomain + + +def generate(sdf_file: str, output_dir: str, dtb: Optional[DeviceTree]): + acpi_driver = ProtectionDomain("acpi_driver", "acpi_driver.elf", priority=253) + + pds = [acpi_driver] + for pd in pds: + sdf.add_pd(pd) + + with open(f"{output_dir}/{sdf_file}", "w+") as f: + f.write(sdf.render()) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--dtb", required=False) + parser.add_argument("--sddf", required=True) + parser.add_argument("--board", required=True, choices=[b.name for b in BOARDS]) + parser.add_argument("--output", required=True) + parser.add_argument("--sdf", required=True) + + args = parser.parse_args() + + board = next(filter(lambda b: b.name == args.board, BOARDS)) + + sdf = SystemDescription(board.arch, board.paddr_top) + sddf = Sddf(args.sddf) + + dtb = None + if board.arch != SystemDescription.Arch.X86_64: + with open(args.dtb, "rb") as f: + dtb = DeviceTree(f.read()) + + generate(args.sdf, args.output, dtb) diff --git a/examples/pci/pci.mk b/examples/pci/pci.mk new file mode 100644 index 000000000..a9a468739 --- /dev/null +++ b/examples/pci/pci.mk @@ -0,0 +1,88 @@ +# +# Copyright 2026, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(SDDF)),) +$(error SDDF must be specified) +endif + +BUILD_DIR ?= build +# By default we make a debug build so that the client debug prints can be seen. +MICROKIT_CONFIG ?= debug +IMAGE_FILE := loader.img +REPORT_FILE := report.txt + +SUPPORTED_BOARDS := \ + x86_64_generic + +ifeq ($(strip $(TOOLCHAIN)),) + TOOLCHAIN := clang +endif + +include ${SDDF}/tools/make/board/common.mk + +TOP := ${SDDF}/examples/pci +METAPROGRAM := $(TOP)/meta.py +UTIL := $(SDDF)/util +ACPI_DRIVER := $(SDDF)/drivers/acpi/acpi.mk +ACPI_DRIVER := $(SDDF)/drivers/pci/pci.mk +SYSTEM_FILE := pci.system +SDDF_CUSTOM_LIBC := 1 + +IMAGES := acpi_driver.elf pci_driver.elf + +CFLAGS += \ + -Wall -Wno-unused-function -Werror -Wno-unused-command-line-argument \ + -I$(SDDF)/include \ + -I$(SDDF)/include/microkit + +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := --start-group -lmicrokit -Tmicrokit.ld libsddf_util_debug.a --end-group + + +all: $(IMAGE_FILE) + +include ${SDDF}/drivers/acpi/acpi_driver.mk +include ${SDDF}/drivers/pci/pci_driver.mk +include ${SDDF}/util/util.mk + +${IMAGES}: libsddf_util_debug.a + +client.o: ${TOP}/client.c + $(CC) -c $(CFLAGS) $< -o client.o +client.elf: client.o + $(LD) $(LDFLAGS) $< $(LIBS) -o $@ + +# $(SYSTEM_FILE): $(METAPROGRAM) $(IMAGES) $(DTB) +# ifneq ($(strip $(DTS)),) +# $(PYTHON) $(METAPROGRAM) --sddf $(SDDF) --board $(MICROKIT_BOARD) --dtb $(DTB) --output . --sdf $(SYSTEM_FILE) +# else +# $(PYTHON) $(METAPROGRAM) --sddf $(SDDF) --board $(X86_BOARD) --output . --sdf $(SYSTEM_FILE) +# endif +# touch $@ + +$(SYSTEM_FILE): $(TOP)/$(SYSTEM_FILE) $(IMAGES) + cp $< $@ + +SPEC = capdl_spec.json +$(BUILD_DIR): + mkdir -p $@ + +$(IMAGE_FILE) $(REPORT_FILE): $(SYSTEM_FILE) $(BUILD_DIR) + $(MICROKIT_TOOL) $(SYSTEM_FILE) --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) --capdl-json ${SPEC} + +qemu: $(IMAGE_FILE) + $(QEMU) $(QEMU_ARCH_ARGS) \ + -nographic \ + -d guest_errors + +clean:: + rm -f client.o +clobber:: clean + rm -f client.elf ${IMAGE_FILE} ${REPORT_FILE} diff --git a/examples/pci/pci.system b/examples/pci/pci.system new file mode 100644 index 000000000..0cb1c6ca2 --- /dev/null +++ b/examples/pci/pci.system @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/pci/run.sh b/examples/pci/run.sh new file mode 100755 index 000000000..bb1bd1f0c --- /dev/null +++ b/examples/pci/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +cd /Users/terrybai/ts/sddf/examples/pci && \ +rm -rf build && \ +make BUILD_DIR=build MICROKIT_BOARD=x86_64_generic X86_BOARD=qemu_virt_x86 MICROKIT_CONFIG=debug MICROKIT_SDK=/Users/terrybai/ts/microkit/release/microkit-sdk-2.2.0-dev qemu diff --git a/include/sddf/util/cspace.h b/include/sddf/util/cspace.h new file mode 100644 index 000000000..ddad7a9aa --- /dev/null +++ b/include/sddf/util/cspace.h @@ -0,0 +1,61 @@ +/* + * Copyright 2026, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +#define MAX_NUM_CAP_SLOTS 512 + +/* #define IDX_TO_CPTR(idx) (seL4_CPtr)(cnode_cptr_remaining_untypeds + idx) */ +#define IDX_TO_CPTR(cnode_specs, idx) (seL4_CPtr)(cnode_specs->cptr + idx) +#define GET_OBJECT_SIZE(object_type, size_bits) (1ULL << get_object_size_bits(object_type, size_bits)) + +typedef struct { + uintptr_t base_addr; + uintptr_t end_addr; + uint8_t is_device; + uint8_t object_type; + uint32_t parent; + uint32_t child; + uint32_t next; +} cap_desc_t; + +typedef struct { + cap_desc_t caps[MAX_NUM_CAP_SLOTS]; + uint32_t start; // start index + uint32_t end; // end index + uint32_t active_ut_idx; // Index of untyped to be allocated for kernel objects + seL4_CPtr cptr; // CNode capability address +} cnode_specs_t; + +uint8_t get_object_size_bits(seL4_Word object_type, seL4_Word size_bits); + +void update_active_ut_idx(cnode_specs_t *cnode_specs); + +seL4_Error cnode_untypeds_revoke(cnode_specs_t *cnode_specs); + +seL4_Error retype_at_paddr(cnode_specs_t *cnode_specs, + seL4_Word target_paddr, + seL4_Word object_type, + seL4_Word size_bits, + uint32_t *retyped_cptr_idx); + +seL4_Error untyped_retype(cnode_specs_t *cnode_specs, + uint32_t ut_idx, + seL4_Word object_type, + seL4_Word size_bits, + uint32_t *retyped_cap_idx); + +bool update_cnode_specs_after_revoke(cnode_specs_t *cnode_specs, + uint32_t ut_idx); + +seL4_Error pass_ut_with_range(cnode_specs_t *dst_cnode_specs, + cnode_specs_t *src_cnode_specs, + uintptr_t min_addr, + uintptr_t max_addr); diff --git a/include/sddf/util/vspace.h b/include/sddf/util/vspace.h new file mode 100644 index 000000000..df70f9c9e --- /dev/null +++ b/include/sddf/util/vspace.h @@ -0,0 +1,12 @@ + +#pragma once + +#include +#include +#include + +#define PAGE_OFFSET(vaddr) (vaddr & 0xFFF) +#define PAGE_SIZE GET_OBJECT_SIZE(seL4_X86_4K, 0) + +bool map_memory_region(cnode_specs_t *cnode_specs, uintptr_t paddr, uintptr_t size, uintptr_t vaddr); +seL4_Error retype_and_map_frame(cnode_specs_t *cnode_specs, uintptr_t paddr, uintptr_t vaddr, seL4_CPtr vspace, seL4_Word page_type, seL4_CapRights_t rights); diff --git a/tools/make/board/x86_64_generic.mk b/tools/make/board/x86_64_generic.mk index de67d78a0..7c457e07d 100644 --- a/tools/make/board/x86_64_generic.mk +++ b/tools/make/board/x86_64_generic.mk @@ -12,21 +12,44 @@ ETH_DRIV ?= eth_driver_virtio.elf TIMER_DRIV_DIR ?= tsc_hpet UART_DRIV_DIR ?= pc99 -CPU := generic - -SEL4_64B := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG)/elf/sel4.elf -SEL4_32B := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG)/elf/sel4_32.elf - -QEMU := qemu-system-x86_64 -QEMU_ARCH_ARGS := -machine q35 \ - -kernel $(SEL4_32B) \ - -m size=2G \ - -serial mon:stdio \ - -cpu qemu64,+fsgsbase,+pdpe1gb,+pcid,+invpcid,+xsave,+xsaves,+xsaveopt \ - -initrd $(IMAGE_FILE) - -# The PCI slot is hard-coded in the virtIO drivers for now, so we have to -# specify the slot with QEMU as well. -# See https://github.com/au-ts/sddf/issues/607 for details. -QEMU_NET_ARGS ?= -device virtio-net-pci,netdev=netdev0,addr=0x2.0 -QEMU_BLK_ARGS ?= -device virtio-blk-pci,drive=hd,addr=0x3.0 +ifeq (${X86_BOARD},) + X86_BOARD := qemu_virt_x86 +endif + +ifeq (${X86_BOARD},qemu_virt_x86) + BLK_DRIV_DIR ?= virtio/pci + NET_DRIV_DIR ?= virtio/pci + ETH_DRIV ?= eth_driver_virtio.elf + UART_DRIV_DIR ?= pc99 + + CPU := generic + + SEL4_64B := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG)/elf/sel4.elf + SEL4_32B := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG)/elf/sel4_32.elf + + QEMU := qemu-system-x86_64 + QEMU_ARCH_ARGS := -machine q35 \ + -kernel $(SEL4_32B) \ + -m size=2G \ + -serial mon:stdio \ + -cpu qemu64,+fsgsbase,+pdpe1gb,+pcid,+invpcid,+xsave,+xsaves,+xsaveopt \ + -initrd $(IMAGE_FILE) + + # The PCI slot is hard-coded in the virtIO drivers for now, so we have to + # specify the slot with QEMU as well. + # See https://github.com/au-ts/sddf/issues/607 for details. + QEMU_NET_ARGS ?= -device virtio-net-pci,netdev=netdev0,addr=0x2.0 + QEMU_BLK_ARGS ?= -device virtio-blk-pci,drive=hd,addr=0x3.0 + +else ifeq ($(X86_BOARD), $(filter ${X86_BOARD},makatea vb_105)) + NET_DRIV_DIR := ixgbe + ETH_DRIV := eth_driver_ixgbe.elf + UART_DRIV_DIR := pc99 + CPU := generic + + DTS := + SEL4_64B = $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG)/elf/sel4.elf + SEL4_32B := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG)/elf/sel4_32.elf +else +$(error Unsupported X86_BOARD given) +endif diff --git a/tools/make/toolchain/gcc.mk b/tools/make/toolchain/gcc.mk index 2baa5e43a..f157d5583 100644 --- a/tools/make/toolchain/gcc.mk +++ b/tools/make/toolchain/gcc.mk @@ -7,11 +7,14 @@ ifndef BOARD_DIR $(error BOARD_DIR not defined) endif -ARCH := $(shell sed -n 's/#define *CONFIG_SEL4_ARCH *\([^ ]*\).*$$/\1/p' $(BOARD_DIR)/include/kernel/gen_config.h) +# ARCH := $(shell sed -n 's/#define *CONFIG_SEL4_ARCH *\([^ ]*\).*$$/\1/p' $(BOARD_DIR)/include/kernel/gen_config.h) +ARCH := x86_64 ifeq ($(ARCH),aarch64) TRIPLE := aarch64-none-elf else ifeq ($(ARCH),riscv64) TRIPLE := riscv64-unknown-elf +else ifeq ($(ARCH),x86_64) + TRIPLE := x86_64-elf else $(error Unsupported ARCH given) endif @@ -36,7 +39,6 @@ OPTIMISATION ?= -g -O2 CFLAGS += \ -MD \ - -mstrict-align \ -ffreestanding \ ${OPTIMISATION} \ -Wall \ diff --git a/tools/meta/board.py b/tools/meta/board.py index fba22a67a..840a0fc71 100644 --- a/tools/meta/board.py +++ b/tools/meta/board.py @@ -40,9 +40,9 @@ class Board: name: str arch: SystemDescription.Arch paddr_top: int - serial: Optional[str] = None - ethernet: Optional[str] = None - timer: Optional[str] = None + serial: Optional[str | int] = None + ethernet: Optional[str | int] = None + timer: Optional[str | int] = None i2c: Optional[str] = None partition: int = 0 blk: Optional[str] = None @@ -195,11 +195,12 @@ class Board: ethernet="axi/ethernet@ff0e0000", ), Board( - name="x86_64_generic", + name="qemu_virt_x86", arch=SystemDescription.Arch.X86_64, paddr_top=0x70000000, - timer=None, - serial=None, + serial=0x3F8, + timer=0xFED00000, + ethernet=0xFE000000, ), Board( name="x86_64_generic_vtx", @@ -208,4 +209,20 @@ class Board: timer=None, serial=None, ), + Board( + name="makatea", + arch=SystemDescription.Arch.X86_64, + paddr_top=0x70000000, + serial=0x2F8, + timer=0xFED00000, + ethernet=0xE0800000, + ), + Board( + name="vb_105", + arch=SystemDescription.Arch.X86_64, + paddr_top=0x70000000, + serial=0x3F8, + timer=0xFED00000, + ethernet=0x6000C00000, + ), ] diff --git a/util/cspace.c b/util/cspace.c new file mode 100644 index 000000000..848338e6b --- /dev/null +++ b/util/cspace.c @@ -0,0 +1,321 @@ + +#pragma once +#include +#include + +seL4_Word max_size_bits(seL4_Word size) +{ + seL4_Word i = 63; + while (((1ULL << i) & size) == 0) { + i--; + } + return i; +} + +// TODO: check if this makes sense to go to libsel4 +// https://github.com/seL4/seL4_libs/blob/master/libsel4vka/arch_include/x86/vka/arch/object.h#L62 +uint8_t get_object_size_bits(seL4_Word object_type, seL4_Word size_bits) +{ + switch (object_type) { + /* Generic objects. */ + case seL4_UntypedObject: + return size_bits; + case seL4_TCBObject: + return seL4_TCBBits; + case seL4_EndpointObject: + return seL4_EndpointBits; + case seL4_NotificationObject: + return seL4_NotificationBits; + case seL4_CapTableObject: + return (seL4_SlotBits + size_bits); + case seL4_X86_4K: + return seL4_PageBits; + case seL4_X86_LargePageObject: + return seL4_LargePageBits; + case seL4_X86_PageTableObject: + return seL4_PageTableBits; + case seL4_X86_PageDirectoryObject: + return seL4_PageDirBits; + default: + // TODO: double-check this + return size_bits; + } +} + +seL4_Error get_untyped_at_paddr(cnode_specs_t *cnode_specs, + seL4_Word target_paddr, + uint32_t *target_ut_idx) +{ + uint32_t ut_idx = cnode_specs->end; + for (uint32_t i = cnode_specs->start; i < cnode_specs->end; i++) { + if (cnode_specs->caps[i].base_addr <= target_paddr && + target_paddr < cnode_specs->caps[i].end_addr && + cnode_specs->caps[i].object_type == seL4_UntypedObject) { + ut_idx = i; + break; + } + } + if (ut_idx == cnode_specs->end) { + sddf_dprintf("Error: Untyped containing physical address 0x%lx is not found\n", target_paddr); + return seL4_InvalidArgument; + } + /* sddf_dprintf("Found the untyped containing physical address: 0x%lx\n", target_paddr); */ + /* sddf_dprintf("ut idx: %u, base_addr: 0x%lx, end_addr: 0x%lx\n", ut_idx, cnode_specs->caps[ut_idx].base_addr, cnode_specs->caps[ut_idx].end_addr); */ + + seL4_Error error; + + // Divide untyped to smaller ones + // TODO: figure out what's the maxinum and minimum bits here + for (int bits = 63; bits >= 12; bits--) { + while (target_paddr - cnode_specs->caps[ut_idx].base_addr >= (1ULL << bits)) { + error = untyped_retype(cnode_specs, ut_idx, seL4_UntypedObject, bits, NULL); + if (error != seL4_NoError){ + sddf_dprintf("Error: failed to divide an untyped(%d)[0x%lx-0x%lx] to a smaller untyped with size_bits=%d\n", + ut_idx, + cnode_specs->caps[ut_idx].base_addr, + cnode_specs->caps[ut_idx].end_addr, + bits); + return error; + } + } + } + + *target_ut_idx = ut_idx; + return seL4_NoError; +} + +seL4_Error pass_ut_with_range(cnode_specs_t *dst_cnode_specs, + cnode_specs_t *src_cnode_specs, + uintptr_t min_addr, + uintptr_t max_addr) +{ + if (min_addr >= max_addr) { + return seL4_NoError; + } + + uint32_t target_ut_idx; + seL4_Error error = get_untyped_at_paddr(src_cnode_specs, min_addr, &target_ut_idx); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to found the untyped containing physical address: 0x%lx\n", min_addr); + return error; + } + + seL4_Word max_align_size_bits = 0; + while (max_align_size_bits < 64) { + uint8_t offset_bit = (src_cnode_specs->caps[target_ut_idx].base_addr >> max_align_size_bits) & 0x1; + if (offset_bit) break; + max_align_size_bits += 1; + } + seL4_Word max_align_size = (1ULL << max_align_size_bits); + + seL4_Word avai_mem_size = src_cnode_specs->caps[target_ut_idx].end_addr - min_addr; + seL4_Word avai_mem_size_bits = max_size_bits(avai_mem_size); + seL4_Word max_target_size_bits = max_size_bits(max_addr - min_addr); + seL4_Word new_ut_size_bits = MIN(MIN(avai_mem_size_bits, max_target_size_bits), max_align_size_bits); + seL4_Word new_ut_size = (1ULL << new_ut_size_bits); + + uint32_t retyped_cptr_idx; + /* sddf_dprintf("Try passing the ut min_addr: 0x%lx, max_addr: 0x%lx\n", min_addr, max_addr); */ + error = untyped_retype(src_cnode_specs, target_ut_idx, seL4_UntypedObject, new_ut_size_bits, &retyped_cptr_idx); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to retype an untyped [0x%lx-0x%lx] from an untyped(%d)[0x%lx-0x%lx]\n", + min_addr, + min_addr + new_ut_size, + target_ut_idx, + src_cnode_specs->caps[target_ut_idx].base_addr, + src_cnode_specs->caps[target_ut_idx].end_addr); + return error; + } + + // TODO: remove hardcoded value + // depth = guardSize + radixSize = 50 + 8 for CNode 'remaining_untypeds' + error = seL4_CNode_Copy(dst_cnode_specs->cptr, dst_cnode_specs->end, 58, src_cnode_specs->cptr, retyped_cptr_idx, 58, seL4_ReadWrite); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to copy a capability\n"); + return error; + } + /* sddf_dprintf("pass ut to slot %d in destination CNode from slot %d in src\n", dst_cnode_specs->end, target_ut_idx); */ + + dst_cnode_specs->caps[dst_cnode_specs->end].base_addr = min_addr; + dst_cnode_specs->caps[dst_cnode_specs->end].end_addr = min_addr + new_ut_size; + dst_cnode_specs->end++; + + if (min_addr + new_ut_size < max_addr) { + pass_ut_with_range(dst_cnode_specs, src_cnode_specs, min_addr + new_ut_size, max_addr); + } + return seL4_NoError; +} + +seL4_Error untyped_retype(cnode_specs_t *cnode_specs, + uint32_t ut_idx, + seL4_Word object_type, + seL4_Word size_bits, + uint32_t *retyped_cap_idx) +{ + // @terryb: need to update this if we remove self-ref cap at slot 0 + seL4_Error error = seL4_Untyped_Retype(cnode_specs->cptr + ut_idx, + object_type, + size_bits, + cnode_specs->cptr, 0, 0, + cnode_specs->end, 1); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to retype an object type %lu, cptr: 0x%lx, size_bits: %lu - error: %d\n", object_type, cnode_specs->cptr + ut_idx, size_bits, error); + return error; + } + + cnode_specs->caps[cnode_specs->end].base_addr = cnode_specs->caps[ut_idx].base_addr; + cnode_specs->caps[cnode_specs->end].end_addr = cnode_specs->caps[ut_idx].base_addr + GET_OBJECT_SIZE(object_type, size_bits); + cnode_specs->caps[cnode_specs->end].object_type = object_type; + cnode_specs->caps[cnode_specs->end].is_device = cnode_specs->caps[ut_idx].is_device; + cnode_specs->caps[cnode_specs->end].parent = ut_idx; + cnode_specs->caps[cnode_specs->end].child = 0; + cnode_specs->caps[cnode_specs->end].next = 0; + cnode_specs->caps[ut_idx].base_addr = cnode_specs->caps[cnode_specs->end].end_addr; + + if (cnode_specs->caps[ut_idx].child == 0) { + cnode_specs->caps[ut_idx].child = cnode_specs->end; + } else { + uint32_t child_ut_idx = cnode_specs->caps[ut_idx].child; + + while (cnode_specs->caps[child_ut_idx].next != 0) { + child_ut_idx = cnode_specs->caps[child_ut_idx].next; + } + cnode_specs->caps[child_ut_idx].next = cnode_specs->end; + } + + if (retyped_cap_idx != NULL) { + *retyped_cap_idx = cnode_specs->end; + } + cnode_specs->end++; + + return seL4_NoError; +} + +seL4_Error retype_at_paddr(cnode_specs_t *cnode_specs, + seL4_Word target_paddr, + seL4_Word object_type, + seL4_Word size_bits, + uint32_t *retyped_cptr_idx) +{ + uint32_t dest_ut_idx; + seL4_Error error = get_untyped_at_paddr(cnode_specs, target_paddr, &dest_ut_idx); + if (error != seL4_NoError) { + return error; + } + + seL4_Word avai_mem_size = cnode_specs->caps[dest_ut_idx].end_addr - target_paddr; + seL4_Word avai_mem_size_bits = max_size_bits(avai_mem_size); + + if (object_type != seL4_UntypedObject && avai_mem_size_bits < size_bits) { + sddf_dprintf("Error: Untyped(%d)[0x%lx-0x%lx] has insufficient memory for (paddr: 0x%lx, size_bits: %lu)\n", + dest_ut_idx, + cnode_specs->caps[dest_ut_idx].base_addr, + cnode_specs->caps[dest_ut_idx].end_addr, + target_paddr, + size_bits); + return 0; + } + + // Retype the target object + return untyped_retype(cnode_specs, dest_ut_idx, object_type, size_bits, retyped_cptr_idx); +} + +void clear_cnode_specs_entry(cnode_specs_t *cnode_specs, uint32_t ut_idx) +{ + cnode_specs->caps[ut_idx].base_addr = 0; + cnode_specs->caps[ut_idx].end_addr = 0; + cnode_specs->caps[ut_idx].is_device = 0; + cnode_specs->caps[ut_idx].object_type = 0; + cnode_specs->caps[ut_idx].parent = 0; + cnode_specs->caps[ut_idx].child = 0; +} + +bool update_cnode_specs_after_revoke(cnode_specs_t *cnode_specs, + uint32_t ut_idx) +{ + if (cnode_specs->caps[ut_idx].child) { + uint32_t child_ut_idx = cnode_specs->caps[ut_idx].child; + uintptr_t base_addr = 0; + uintptr_t end_addr = 0; + while (child_ut_idx != 0) { + bool success = update_cnode_specs_after_revoke(cnode_specs, child_ut_idx); + if (!success) { + return success; + } + if (base_addr == end_addr) { + base_addr = cnode_specs->caps[child_ut_idx].base_addr; + end_addr = cnode_specs->caps[child_ut_idx].end_addr; + clear_cnode_specs_entry(cnode_specs, child_ut_idx); + } else if (end_addr == cnode_specs->caps[child_ut_idx].base_addr) { + end_addr = cnode_specs->caps[child_ut_idx].end_addr; + clear_cnode_specs_entry(cnode_specs, child_ut_idx); + } else { + sddf_dprintf("Error: something wrong during re-collecting untypeds\n"); + return false; + } + + uint32_t child_cleared_idx = child_ut_idx; + child_ut_idx = cnode_specs->caps[child_ut_idx].next; + cnode_specs->caps[child_cleared_idx].next = 0; + } + + if (end_addr != cnode_specs->caps[ut_idx].base_addr) { + sddf_dprintf("Error: something wrong during re-collecting untypeds\n"); + return false; + } + cnode_specs->caps[ut_idx].base_addr = base_addr; + cnode_specs->caps[ut_idx].child = 0; + } + return true; +} + +void update_active_ut_idx(cnode_specs_t *cnode_specs) +{ + // TODO: find a proper untyped for PT objects, not the first one is used by capDL initialiser + uint32_t non_dev_mem_id = 0; + uint32_t i; + for (i = cnode_specs->start; i < cnode_specs->end; i++) { + if (cnode_specs->caps[i].is_device == false && cnode_specs->caps[i].object_type == seL4_UntypedObject) { + if (non_dev_mem_id == 5) { + cnode_specs->active_ut_idx = i; + break; + } + non_dev_mem_id++; + } + } + if (i < cnode_specs->end) { + sddf_dprintf("Found an untyped for kernel objects: ut idx: 0x%x, paddr: 0x%lx\n", cnode_specs->active_ut_idx, cnode_specs->caps[i].base_addr); + } else { + sddf_dprintf("[Error] failed to find an available untyped for kernel objects allocation\n"); + } +} + +seL4_Error cnode_untypeds_revoke(cnode_specs_t *cnode_specs) +{ + for (uint32_t i = cnode_specs->end - 1; i >= cnode_specs->start; i--) { + uint32_t parent_ut_idx = i; + while (cnode_specs->caps[parent_ut_idx].parent) { + parent_ut_idx = cnode_specs->caps[parent_ut_idx].parent; + } + + // Revoke if this cap has been divided into small ones + if (parent_ut_idx != i) { + // TODO: proper way to calculate `depth` + seL4_Error error = seL4_CNode_Revoke(cnode_specs->cptr, parent_ut_idx, 58); + if (error != seL4_NoError) { + return error; + } + + bool success = update_cnode_specs_after_revoke(cnode_specs, parent_ut_idx); + if (!success) { + return seL4_IllegalOperation; + } + } + + if (cnode_specs->caps[i].end_addr == 0) { + cnode_specs->end = i; + } + } + + return seL4_NoError; +} diff --git a/util/util.mk b/util/util.mk index 9d4503dea..2f18825c3 100644 --- a/util/util.mk +++ b/util/util.mk @@ -15,7 +15,7 @@ ifeq ($(strip $(ARCH)),) $(error ARCH must be specified) endif -OBJS_LIBUTIL := cache.o sddf_printf.o assert.o bitarray.o fsmalloc.o +OBJS_LIBUTIL := cache.o sddf_printf.o assert.o bitarray.o fsmalloc.o cspace.o vspace.o ifeq ($(strip $(SDDF_CUSTOM_LIBC)),1) CFLAGS += -I${SDDF}/include/sddf/util/custom_libc diff --git a/util/vspace.c b/util/vspace.c new file mode 100644 index 000000000..87e1cdd80 --- /dev/null +++ b/util/vspace.c @@ -0,0 +1,101 @@ +#pragma once +#include +#include +#include +#include +#include + + +seL4_Error map_frame(cnode_specs_t *ut_cnode_specs, seL4_CPtr frame_cap, seL4_CPtr vspace, uintptr_t vaddr, seL4_CapRights_t rights) +{ + seL4_Error err = seL4_X86_Page_Map(frame_cap, vspace, vaddr, rights, seL4_X86_Default_VMAttributes); + + for (int i = 0; i < 4 && err == seL4_FailedLookup; i++) { + seL4_Word failed = seL4_MappingFailedLookupLevel(); + uint32_t retyped_cptr_idx; + + switch (failed) { + case SEL4_MAPPING_LOOKUP_NO_PT: { + err = untyped_retype(ut_cnode_specs, ut_cnode_specs->active_ut_idx, seL4_X86_PageTableObject, 0, &retyped_cptr_idx); + if (err != seL4_NoError) { + return err; + } + err = seL4_X86_PageTable_Map(IDX_TO_CPTR(ut_cnode_specs, retyped_cptr_idx), vspace, vaddr, seL4_X86_Default_VMAttributes); + break; + } + case SEL4_MAPPING_LOOKUP_NO_PD: { + err = untyped_retype(ut_cnode_specs, ut_cnode_specs->active_ut_idx, seL4_X86_PageDirectoryObject, 0, &retyped_cptr_idx); + if (err != seL4_NoError) { + return err; + } + err = seL4_X86_PageDirectory_Map(IDX_TO_CPTR(ut_cnode_specs, retyped_cptr_idx), vspace, vaddr, seL4_X86_Default_VMAttributes); + break; + } + case SEL4_MAPPING_LOOKUP_NO_PDPT: { + err = untyped_retype(ut_cnode_specs, ut_cnode_specs->active_ut_idx, seL4_X86_PDPTObject, 0, &retyped_cptr_idx); + if (err != seL4_NoError) { + return err; + } + err = seL4_X86_PDPT_Map(IDX_TO_CPTR(ut_cnode_specs, retyped_cptr_idx), vspace, vaddr, seL4_X86_Default_VMAttributes); + break; + } + } + + if (err == seL4_NoError) { + err = seL4_X86_Page_Map(frame_cap, vspace, vaddr, rights, seL4_X86_Default_VMAttributes); + } + } + + return err; +} + + +seL4_Error retype_and_map_frame(cnode_specs_t *cnode_specs, uintptr_t paddr, uintptr_t vaddr, seL4_CPtr vspace, seL4_Word page_type, seL4_CapRights_t rights) +{ + uint32_t retyped_cptr_idx; + // TODO: round_down for large pages and check if it's a page type + if (page_type == seL4_X86_4K) { + paddr = ROUND_DOWN(paddr, 1UL << seL4_PageBits); + vaddr = ROUND_DOWN(vaddr, 1UL << seL4_PageBits); + } else if (page_type == seL4_X86_LargePageObject) { + paddr = ROUND_DOWN(paddr, 1UL << seL4_LargePageBits); + vaddr = ROUND_DOWN(vaddr, 1UL << seL4_LargePageBits); + } + seL4_Error error = retype_at_paddr(cnode_specs, paddr, page_type, 0, &retyped_cptr_idx); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to retype at paddr 0x%lx\n", paddr); + return error; + } + + /* sddf_dprintf("retyped and try mapping at vaddr: 0x%lx with ut idx: %u, 0x%lx\n", vaddr, retyped_cptr_idx, IDX_TO_CPTR(retyped_cptr_idx)); */ + error = map_frame(cnode_specs, IDX_TO_CPTR(cnode_specs, retyped_cptr_idx), vspace, vaddr, rights); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to map frame at vaddr: 0x%lx, err - %u\n", vaddr, error); + return error; + } + + return seL4_NoError; +} + + +// TODO: add permissions +bool map_memory_region(cnode_specs_t *cnode_specs, uintptr_t paddr, uintptr_t size, uintptr_t vaddr) +{ + assert(PAGE_OFFSET(paddr) == PAGE_OFFSET(vaddr)); + + sddf_dprintf("map 0x%lx-0x%lx to 0x%lx\n", paddr, paddr + size, vaddr); + uintptr_t mapped_size = 0; + uintptr_t paddr_start = ROUND_DOWN(paddr, PAGE_SIZE); + uintptr_t vaddr_start = ROUND_DOWN(vaddr, PAGE_SIZE); + uintptr_t end_paddr = paddr + size; + while (paddr_start + mapped_size < end_paddr) { + seL4_Error error = retype_and_map_frame(cnode_specs, paddr_start + mapped_size, vaddr_start + mapped_size, seL4_CapInitThreadVSpace, seL4_X86_4K, seL4_CanRead); + if (error != seL4_NoError) { + sddf_dprintf("Error: failed to retype or map a frame.\n"); + return false; + } + mapped_size += PAGE_SIZE; + } + + return true; +} diff --git a/virtio/transport/pci.c b/virtio/transport/pci.c index d7aebcef3..39a4349c0 100644 --- a/virtio/transport/pci.c +++ b/virtio/transport/pci.c @@ -97,6 +97,8 @@ void pci_debug_print_header(uint8_t bus, uint8_t dev, uint8_t func, pci_gen_dev_ LOG_VIRTIO_TRANSPORT("\tHeader type: a general device\n"); LOG_VIRTIO_TRANSPORT("\tLatency timer: 0x%x\n", pci_device_header->common_hdr.latency_timer); LOG_VIRTIO_TRANSPORT("\tCache line size: 0x%x\n", pci_device_header->common_hdr.cache_line_size); + LOG_VIRTIO_TRANSPORT("\tIRQ PIN: 0x%x\n", pci_device_header->irq_pin); + LOG_VIRTIO_TRANSPORT("\tIRQ LINE: 0x%x\n", pci_device_header->irq_line); for (int i = 0; i < PCI_GEN_DEV_NUM_BARS; i++) { LOG_VIRTIO_TRANSPORT("\tBAR%d: 0x%x\n", i, pci_device_header->base_address_registers[i]);