diff options
author | Damien Zammit <damien@zamaudio.com> | 2024-01-31 02:12:26 +0000 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2024-02-01 02:22:41 +0100 |
commit | 88cbab7aaefdcd2b630d3f970a0c65a50ad419da (patch) | |
tree | f8bd78edbd05d7bb7f7cfcef4530b639b8cdb032 /i386 | |
parent | ab91fba7200bea7596a95c654c1c467df79b656a (diff) | |
download | gnumach-88cbab7aaefdcd2b630d3f970a0c65a50ad419da.tar.gz gnumach-88cbab7aaefdcd2b630d3f970a0c65a50ad419da.tar.bz2 gnumach-88cbab7aaefdcd2b630d3f970a0c65a50ad419da.zip |
ACPI: Support XSDT (ACPI >= v2.0)
This enables gnumach to additionally parse the XSDT table
if the revision of ACPI is 2.
TESTED: Still works on qemu (ACPI v1.0)
TESTED: Works on a x86 board with XSDT (ACPI v2.0)
Message-ID: <20240131021218.1335821-1-damien@zamaudio.com>
Diffstat (limited to 'i386')
-rw-r--r-- | i386/i386at/acpi_parse_apic.c | 258 | ||||
-rw-r--r-- | i386/i386at/acpi_parse_apic.h | 18 | ||||
-rw-r--r-- | i386/i386at/model_dep.c | 8 |
3 files changed, 195 insertions, 89 deletions
diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c index 1aef53ed..dcd5da89 100644 --- a/i386/i386at/acpi_parse_apic.c +++ b/i386/i386at/acpi_parse_apic.c @@ -43,13 +43,12 @@ unsigned lapic_addr; * and the number of entries stored in RSDT. */ void -acpi_print_info(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, int acpi_rsdt_n) +acpi_print_info(phys_addr_t rsdp, void *rsdt, int acpi_rsdt_n) { printf("ACPI:\n"); - printf(" rsdp = %p; rsdp->rsdt_addr = %x\n", rsdp, rsdp->rsdt_addr); - printf(" rsdt = %p; rsdt->length = %x (n = %x)\n", rsdt, rsdt->header.length, - acpi_rsdt_n); + printf(" rsdp = 0x%lx\n", rsdp); + printf(" rsdt/xsdt = 0x%p (n = %d)\n", rsdt, acpi_rsdt_n); } /* @@ -99,27 +98,45 @@ acpi_check_signature(const uint8_t table_signature[], const char *real_signature * * Preconditions: RSDP pointer must not be NULL. * - * Returns 0 if correct. + * Returns 1 if ACPI 1.0 and sets sdt_base + * Returns 2 if ACPI >= 2.0 and sets sdt_base */ static int8_t -acpi_check_rsdp(struct acpi_rsdp *rsdp) +acpi_check_rsdp(struct acpi_rsdp2 *rsdp, phys_addr_t *sdt_base) { - uint32_t checksum; int is_rsdp; + uint8_t cksum; /* Check if rsdp signature match with the ACPI RSDP signature. */ - is_rsdp = acpi_check_signature(rsdp->signature, ACPI_RSDP_SIG, 8*sizeof(uint8_t)); + is_rsdp = acpi_check_signature(rsdp->v1.signature, ACPI_RSDP_SIG, 8*sizeof(uint8_t)); if (is_rsdp != ACPI_SUCCESS) return ACPI_BAD_SIGNATURE; - /* If match, calculates rdsp checksum and check It. */ - checksum = acpi_checksum(rsdp, sizeof(struct acpi_rsdp)); + if (rsdp->v1.revision == 0) { + // ACPI 1.0 + *sdt_base = rsdp->v1.rsdt_addr; + printf("ACPI v1.0\n"); + cksum = acpi_checksum((void *)(&rsdp->v1), sizeof(struct acpi_rsdp)); - if (checksum != 0) - return ACPI_BAD_CHECKSUM; + if (cksum != 0) + return ACPI_BAD_CHECKSUM; - return ACPI_SUCCESS; + return 1; + + } else if (rsdp->v1.revision == 2) { + // ACPI >= 2.0 + *sdt_base = rsdp->xsdt_addr; + printf("ACPI >= v2.0\n"); + cksum = acpi_checksum((void *)rsdp, sizeof(struct acpi_rsdp2)); + + if (cksum != 0) + return ACPI_BAD_CHECKSUM; + + return 2; + } + + return ACPI_NO_RSDP; } /* @@ -147,38 +164,41 @@ acpi_check_rsdp_align(void *addr) * * Preconditions: The start address (addr) must be aligned. * - * Returns the reference to rsdp structure if success, NULL if failure. + * Returns the physical address of rsdp structure if success, 0 if failure. */ -static struct acpi_rsdp* -acpi_search_rsdp(void *addr, uint32_t length) +static phys_addr_t +acpi_search_rsdp(void *addr, uint32_t length, int *is_64bit) { void *end; + int version = 0; + phys_addr_t sdt_base = 0; /* Search RDSP in memory space between addr and addr+lenght. */ for (end = addr+length; addr < end; addr += ACPI_RSDP_ALIGN) { /* Check if the current memory block stores the RDSP. */ - if ((addr != NULL) && (acpi_check_rsdp(addr) == ACPI_SUCCESS)) { - /* If yes, return RSDP address */ - return (struct acpi_rsdp*) addr; + if ((addr != NULL) && ((version = acpi_check_rsdp(addr, &sdt_base)) > 0)) { + /* If yes, return RSDT/XSDT address */ + *is_64bit = (version == 2); + return sdt_base; } } - return NULL; + return 0; } /* * acpi_get_rsdp: tries to find the RSDP table, * searching It in many memory ranges, as It's written in ACPI Specification. * - * Returns the reference to RDSP structure if success, NULL if failure. + * Returns the reference to RDSP structure if success, 0 if failure. */ -static struct acpi_rsdp* -acpi_get_rsdp(void) +static phys_addr_t +acpi_get_rsdp(int *is_64bit) { uint16_t *start = 0; phys_addr_t base = 0; - struct acpi_rsdp *rsdp = NULL; + phys_addr_t rsdp = 0; /* EDBA start address. */ start = (uint16_t*) phystokv(0x040e); @@ -186,41 +206,18 @@ acpi_get_rsdp(void) /* check alignment. */ if (acpi_check_rsdp_align((void *)base) == ACPI_BAD_ALIGN) - return NULL; - rsdp = acpi_search_rsdp((void *)base, 1024); + return 0; + rsdp = acpi_search_rsdp((void *)base, 1024, is_64bit); - if (rsdp == NULL) { + if (rsdp == 0) { /* If RSDP isn't in EDBA, search in the BIOS read-only memory space between 0E0000h and 0FFFFFh */ - rsdp = acpi_search_rsdp((void *)phystokv(0xe0000), 0x100000 - 0x0e0000); + rsdp = acpi_search_rsdp((void *)phystokv(0xe0000), 0x100000 - 0x0e0000, is_64bit); } return rsdp; } /* - * acpi_check_rsdt: check if the RSDT initial address is correct - * checking its checksum. - * - * Receives as input a reference for the RSDT "candidate" table. - * Returns 0 if success. - * - * Preconditions: rsdp must not be NULL. - * - */ -static int -acpi_check_rsdt(struct acpi_rsdt *rsdt) -{ - uint8_t checksum; - - checksum = acpi_checksum(rsdt, rsdt->header.length); - - if (checksum != 0) - return ACPI_BAD_CHECKSUM; - - return ACPI_SUCCESS; -} - -/* * acpi_get_rsdt: Get RSDT table reference from RSDP entries. * * Receives as input a reference for RSDP table @@ -229,16 +226,12 @@ acpi_check_rsdt(struct acpi_rsdt *rsdt) * Returns the reference to RSDT table if success, NULL if error. */ static struct acpi_rsdt* -acpi_get_rsdt(struct acpi_rsdp *rsdp, int* acpi_rsdt_n) +acpi_get_rsdt(phys_addr_t rsdp_phys, int* acpi_rsdt_n) { - phys_addr_t rsdt_phys; struct acpi_rsdt *rsdt = NULL; - int acpi_check; int signature_check; - /* Get rsdt address from rsdp table. */ - rsdt_phys = rsdp->rsdt_addr; - rsdt = (struct acpi_rsdt*) kmem_map_aligned_table(rsdt_phys, sizeof(struct acpi_rsdt), VM_PROT_READ); + rsdt = (struct acpi_rsdt*) kmem_map_aligned_table(rsdp_phys, sizeof(struct acpi_rsdt), VM_PROT_READ); /* Check if the RSDT mapping is fine. */ if (rsdt == NULL) @@ -251,12 +244,6 @@ acpi_get_rsdt(struct acpi_rsdp *rsdp, int* acpi_rsdt_n) if (signature_check != ACPI_SUCCESS) return NULL; - /* Check if rsdt is correct. */ - acpi_check = acpi_check_rsdt(rsdt); - - if (acpi_check != ACPI_SUCCESS) - return NULL; - /* Calculated number of elements stored in rsdt. */ *acpi_rsdt_n = (rsdt->header.length - sizeof(rsdt->header)) / sizeof(rsdt->entry[0]); @@ -265,6 +252,40 @@ acpi_get_rsdt(struct acpi_rsdp *rsdp, int* acpi_rsdt_n) } /* + * acpi_get_xsdt: Get XSDT table reference from RSDPv2 entries. + * + * Receives as input a reference for RSDPv2 table + * and a reference to store the number of entries of XSDT. + * + * Returns the reference to XSDT table if success, NULL if error. + */ +static struct acpi_xsdt* +acpi_get_xsdt(phys_addr_t rsdp_phys, int* acpi_xsdt_n) +{ + struct acpi_xsdt *xsdt = NULL; + int signature_check; + + xsdt = (struct acpi_xsdt*) kmem_map_aligned_table(rsdp_phys, sizeof(struct acpi_xsdt), VM_PROT_READ); + + /* Check if the RSDT mapping is fine. */ + if (xsdt == NULL) + return NULL; + + /* Check is rsdt signature is equals to ACPI RSDT signature. */ + signature_check = acpi_check_signature(xsdt->header.signature, ACPI_XSDT_SIG, + 4*sizeof(uint8_t)); + + if (signature_check != ACPI_SUCCESS) + return NULL; + + /* Calculated number of elements stored in rsdt. */ + *acpi_xsdt_n = (xsdt->header.length - sizeof(xsdt->header)) + / sizeof(xsdt->entry[0]); + + return xsdt; +} + +/* * acpi_get_apic: get MADT/APIC table from RSDT entries. * * Receives as input the RSDT initial address, @@ -296,6 +317,37 @@ acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n) } /* + * acpi_get_apic2: get MADT/APIC table from XSDT entries. + * + * Receives as input the XSDT initial address, + * and the number of entries of XSDT table. + * + * Returns a reference to APIC/MADT table if success, NULL if failure. + */ +static struct acpi_apic* +acpi_get_apic2(struct acpi_xsdt *xsdt, int acpi_xsdt_n) +{ + struct acpi_dhdr *descr_header; + int check_signature; + + /* Search APIC entries in rsdt table. */ + for (int i = 0; i < acpi_xsdt_n; i++) { + descr_header = (struct acpi_dhdr*) kmem_map_aligned_table(xsdt->entry[i], sizeof(struct acpi_dhdr), + VM_PROT_READ); + + /* Check if the entry contains an APIC. */ + check_signature = acpi_check_signature(descr_header->signature, ACPI_APIC_SIG, 4*sizeof(uint8_t)); + + if (check_signature == ACPI_SUCCESS) { + /* If yes, return the APIC. */ + return (struct acpi_apic*) descr_header; + } + } + + return NULL; +} + +/* * acpi_add_lapic: add a new Local APIC to cpu_to_lapic array * and increase the number of cpus. * @@ -382,6 +434,8 @@ acpi_apic_parse_table(struct acpi_apic *apic) /* Get the end address of APIC table */ end = (vm_offset_t) apic + apic->header.length; + printf("APIC entry=0x%p end=0x%x\n", apic_entry, end); + /* Initialize number of cpus */ numcpus = apic_get_numcpus(); @@ -391,6 +445,7 @@ acpi_apic_parse_table(struct acpi_apic *apic) struct acpi_apic_ioapic *ioapic_entry; struct acpi_apic_irq_override *irq_override_entry; + printf("APIC entry=0x%p end=0x%x\n", apic_entry, end); /* Check entry type. */ switch(apic_entry->type) { @@ -421,6 +476,9 @@ acpi_apic_parse_table(struct acpi_apic *apic) break; /* FIXME: There is another unhandled case */ + default: + printf("Unhandled APIC entry type 0x%x\n", apic_entry->type); + break; } /* Get next APIC entry. */ @@ -452,16 +510,9 @@ acpi_apic_parse_table(struct acpi_apic *apic) static int acpi_apic_setup(struct acpi_apic *apic) { - int apic_checksum; ApicLocalUnit* lapic_unit; uint8_t ncpus, nioapics; - /* Check the checksum of the APIC */ - apic_checksum = acpi_checksum(apic, apic->header.length); - - if(apic_checksum != 0) - return ACPI_BAD_CHECKSUM; - /* map common lapic address */ lapic_addr = apic->lapic_addr; lapic_unit = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), @@ -502,29 +553,64 @@ acpi_apic_setup(struct acpi_apic *apic) int acpi_apic_init(void) { - struct acpi_rsdp *rsdp = 0; + phys_addr_t rsdp = 0; struct acpi_rsdt *rsdt = 0; + struct acpi_xsdt *xsdt = 0; int acpi_rsdt_n; int ret_acpi_setup; int apic_init_success = 0; + int is_64bit = 0; + uint8_t checksum; - /* Try to get the RSDP pointer. */ - rsdp = acpi_get_rsdp(); - if (rsdp == NULL) + /* Try to get the RSDP physical address. */ + rsdp = acpi_get_rsdp(&is_64bit); + if (rsdp == 0) return ACPI_NO_RSDP; - /* Try to get the RSDT pointer. */ - rsdt = acpi_get_rsdt(rsdp, &acpi_rsdt_n); - if (rsdt == NULL) - return ACPI_NO_RSDT; - - /* Try to get the APIC table pointer. */ - apic_madt = acpi_get_apic(rsdt, acpi_rsdt_n); - if (apic_madt == NULL) - return ACPI_NO_APIC; - - /* Print the ACPI tables addresses. */ - acpi_print_info(rsdp, rsdt, acpi_rsdt_n); + if (!is_64bit) { + /* Try to get the RSDT pointer. */ + rsdt = acpi_get_rsdt(rsdp, &acpi_rsdt_n); + if (rsdt == NULL) + return ACPI_NO_RSDT; + + checksum = acpi_checksum((void *)rsdt, rsdt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Try to get the APIC table pointer. */ + apic_madt = acpi_get_apic(rsdt, acpi_rsdt_n); + if (apic_madt == NULL) + return ACPI_NO_APIC; + + checksum = acpi_checksum((void *)apic_madt, apic_madt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Print the ACPI tables addresses. */ + acpi_print_info(rsdp, rsdt, acpi_rsdt_n); + + } else { + /* Try to get the XSDT pointer. */ + xsdt = acpi_get_xsdt(rsdp, &acpi_rsdt_n); + if (xsdt == NULL) + return ACPI_NO_RSDT; + + checksum = acpi_checksum((void *)xsdt, xsdt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Try to get the APIC table pointer. */ + apic_madt = acpi_get_apic2(xsdt, acpi_rsdt_n); + if (apic_madt == NULL) + return ACPI_NO_APIC; + + checksum = acpi_checksum((void *)apic_madt, apic_madt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Print the ACPI tables addresses. */ + acpi_print_info(rsdp, xsdt, acpi_rsdt_n); + } apic_init_success = apic_data_init(); if (apic_init_success != ACPI_SUCCESS) diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h index bad10054..df36bb31 100644 --- a/i386/i386at/acpi_parse_apic.h +++ b/i386/i386at/acpi_parse_apic.h @@ -44,10 +44,17 @@ struct acpi_rsdp { uint8_t signature[8]; uint8_t checksum; uint8_t oem_id[6]; - uint8_t revision[1]; + uint8_t revision; uint32_t rsdt_addr; } __attribute__((__packed__)); +struct acpi_rsdp2 { + struct acpi_rsdp v1; + uint32_t length; + uint64_t xsdt_addr; + uint8_t checksum; + uint8_t reserved[3]; +} __attribute__((__packed__)); /* * RSDT Entry Header @@ -77,6 +84,13 @@ struct acpi_rsdt { uint32_t entry[0]; } __attribute__((__packed__)); +#define ACPI_XSDT_SIG "XSDT" + +struct acpi_xsdt { + struct acpi_dhdr header; + uint64_t entry[0]; +} __attribute__((__packed__)); + /* APIC table signature. */ #define ACPI_APIC_SIG "APIC" @@ -157,7 +171,7 @@ struct acpi_apic_irq_override { } __attribute__((__packed__)); int acpi_apic_init(void); -void acpi_print_info(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, int acpi_rsdt_n); +void acpi_print_info(phys_addr_t rsdp, void *rsdt, int acpi_rsdt_n); extern unsigned lapic_addr; diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c index 7c575ce2..9dbe7e01 100644 --- a/i386/i386at/model_dep.c +++ b/i386/i386at/model_dep.c @@ -154,7 +154,13 @@ void machine_init(void) hyp_init(); #else /* MACH_HYP */ #if defined(APIC) - acpi_apic_init(); + int err; + + err = acpi_apic_init(); + if (err) { + printf("acpi_apic_init failed with %d\n", err); + for (;;); + } #endif #if (NCPUS > 1) smp_init(); |