summaryrefslogtreecommitdiff
path: root/i386
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2024-01-31 02:12:26 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2024-02-01 02:22:41 +0100
commit88cbab7aaefdcd2b630d3f970a0c65a50ad419da (patch)
treef8bd78edbd05d7bb7f7cfcef4530b639b8cdb032 /i386
parentab91fba7200bea7596a95c654c1c467df79b656a (diff)
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.c258
-rw-r--r--i386/i386at/acpi_parse_apic.h18
-rw-r--r--i386/i386at/model_dep.c8
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();