From 8e4c6923b6712b3174edde5e1bc3ef961cce76d0 Mon Sep 17 00:00:00 2001 From: Michael Neumann Date: Sun, 3 Oct 2010 12:29:32 +0200 Subject: [PATCH] Implement ACPI MADT parsing This will be used to enumerate CPUs if BIOS does not provide MP table. Apply commit 85fd9f87e82e63599bd0293dc0112f8e26f8c3d6 for x86_x64. --- sys/platform/pc64/conf/files | 1 + sys/platform/pc64/include/smp.h | 3 + sys/platform/pc64/x86_64/mp_machdep.c | 4 + sys/platform/pc64/x86_64/mp_madt.c | 414 ++++++++++++++++++++++++++ 4 files changed, 422 insertions(+) create mode 100644 sys/platform/pc64/x86_64/mp_madt.c diff --git a/sys/platform/pc64/conf/files b/sys/platform/pc64/conf/files index ea505b494f..2247ca69e8 100644 --- a/sys/platform/pc64/conf/files +++ b/sys/platform/pc64/conf/files @@ -176,6 +176,7 @@ platform/pc64/x86_64/console.c standard platform/pc64/x86_64/ipl_funcs.c standard kern/syscalls.c standard platform/pc64/x86_64/mp_machdep.c optional smp +platform/pc64/x86_64/mp_madt.c optional smp dev/misc/atkbd/atkbd_isa.c optional atkbd dev/misc/atkbdc_layer/atkbdc_isa.c optional atkbdc dev/misc/ppc/ppc.c optional ppc diff --git a/sys/platform/pc64/include/smp.h b/sys/platform/pc64/include/smp.h index a25e1b2b36..e943bef83a 100644 --- a/sys/platform/pc64/include/smp.h +++ b/sys/platform/pc64/include/smp.h @@ -130,6 +130,9 @@ void io_apic_set_id (int, int); int io_apic_get_id (int); int ext_int_setup (int, int); +/* functions in mp_madt.c */ +int madt_probe(void); + #if defined(READY) void clr_io_apic_mask24 (int, u_int32_t); void set_io_apic_mask24 (int, u_int32_t); diff --git a/sys/platform/pc64/x86_64/mp_machdep.c b/sys/platform/pc64/x86_64/mp_machdep.c index 68b6c7dbd6..968afd1b5e 100644 --- a/sys/platform/pc64/x86_64/mp_machdep.c +++ b/sys/platform/pc64/x86_64/mp_machdep.c @@ -541,6 +541,10 @@ mp_enable(u_int boot_addr) POSTCODE(MP_ENABLE_POST); +#if 0 + madt_probe(); +#endif + mpfps_paddr = mptable_probe(); if (mpfps_paddr == 0) panic("mp_enable: mptable_probe failed\n"); diff --git a/sys/platform/pc64/x86_64/mp_madt.c b/sys/platform/pc64/x86_64/mp_madt.c new file mode 100644 index 0000000000..c164872c16 --- /dev/null +++ b/sys/platform/pc64/x86_64/mp_madt.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2009 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#define ACPI_RSDP_EBDA_MAPSZ 1024 +#define ACPI_RSDP_BIOS_MAPSZ 0x20000 +#define ACPI_RSDP_BIOS_MAPADDR 0xe0000 + +#define ACPI_RSDP_ALIGN 16 + +#define ACPI_RSDP_SIGLEN 8 +#define ACPI_RSDP_SIG "RSD PTR " + +#define ACPI_SDTH_SIGLEN 4 +#define ACPI_RSDT_SIG "RSDT" +#define ACPI_XSDT_SIG "XSDT" +#define ACPI_MADT_SIG "APIC" + +/* Root System Description Pointer */ +struct acpi_rsdp { + uint8_t rsdp_sig[ACPI_RSDP_SIGLEN]; + uint8_t rsdp_cksum; + uint8_t rsdp_oem_id[6]; + uint8_t rsdp_rev; + uint32_t rsdp_rsdt; + uint32_t rsdp_len; + uint64_t rsdp_xsdt; + uint8_t rsdp_ext_cksum; + uint8_t rsdp_rsvd[3]; +} __packed; + +/* System Description Table Header */ +struct acpi_sdth { + uint8_t sdth_sig[ACPI_SDTH_SIGLEN]; + uint32_t sdth_len; + uint8_t sdth_rev; + uint8_t sdth_cksum; + uint8_t sdth_oem_id[6]; + uint8_t sdth_oem_tbid[8]; + uint32_t sdth_oem_rev; + uint32_t sdth_crt_id; + uint32_t sdth_crt_rev; +} __packed; + +/* Extended System Description Table */ +struct acpi_xsdt { + struct acpi_sdth xsdt_hdr; + uint64_t xsdt_ents[1]; +} __packed; + +/* Root System Description Table */ +struct acpi_rsdt { + struct acpi_sdth rsdt_hdr; + uint32_t rsdt_ents[1]; +} __packed; + +/* Multiple APIC Description Table */ +struct acpi_madt { + struct acpi_sdth madt_hdr; + uint32_t madt_lapic_addr; + uint32_t madt_flags; + uint8_t madt_ents[1]; +} __packed; + +/* Common parts of MADT APIC structure */ +struct acpi_madt_ent { + uint8_t me_type; /* MADT_ENT_ */ + uint8_t me_len; +} __packed; + +#define MADT_ENT_LAPIC 0 + +/* MADT Processor Local APIC */ +struct acpi_madt_lapic { + struct acpi_madt_ent ml_hdr; + uint8_t ml_cpu_id; + uint8_t ml_apic_id; + uint32_t ml_flags; /* MADT_LAPIC_ */ +} __packed; + +#define MADT_LAPIC_ENABLED 0x1 + +typedef vm_paddr_t (*madt_search_t)(vm_paddr_t); + +static const struct acpi_rsdp *madt_rsdp_search(const uint8_t *, int); +static void *madt_sdth_map(vm_paddr_t); +static void madt_sdth_unmap(struct acpi_sdth *); +static vm_paddr_t madt_search_xsdt(vm_paddr_t); +static vm_paddr_t madt_search_rsdt(vm_paddr_t); +static int madt_parse(vm_paddr_t); + +extern u_long ebda_addr; + +int +madt_probe(void) +{ + const struct acpi_rsdp *rsdp; + madt_search_t search; + vm_paddr_t search_paddr, madt_paddr; + vm_size_t mapsz; + uint8_t *ptr; + + if (ebda_addr != 0) { + mapsz = ACPI_RSDP_EBDA_MAPSZ; + ptr = pmap_mapdev(ebda_addr, mapsz); + + rsdp = madt_rsdp_search(ptr, mapsz); + if (rsdp == NULL) { + kprintf("madt: RSDP not in EBDA\n"); + pmap_unmapdev((vm_offset_t)ptr, mapsz); + + ptr = NULL; + mapsz = 0; + } else { + kprintf("madt: RSDP in EBDA\n"); + goto found_rsdp; + } + } + + mapsz = ACPI_RSDP_BIOS_MAPSZ; + ptr = pmap_mapdev(ACPI_RSDP_BIOS_MAPADDR, mapsz); + + rsdp = madt_rsdp_search(ptr, mapsz); + if (rsdp == NULL) { + kprintf("madt_probe: no RSDP\n"); + pmap_unmapdev((vm_offset_t)ptr, mapsz); + return ENOENT; + } else { + kprintf("madt: RSDP in BIOS mem\n"); + } + +found_rsdp: + if (rsdp->rsdp_rev != 2) { + search_paddr = rsdp->rsdp_rsdt; + search = madt_search_rsdt; + } else { + search_paddr = rsdp->rsdp_xsdt; + search = madt_search_xsdt; + } + pmap_unmapdev((vm_offset_t)ptr, mapsz); + + madt_paddr = search(search_paddr); + if (madt_paddr == 0) { + kprintf("madt_probe: can't locate MADT\n"); + return ENOENT; + } + return madt_parse(madt_paddr); +} + +static const struct acpi_rsdp * +madt_rsdp_search(const uint8_t *target, int size) +{ + const struct acpi_rsdp *rsdp; + int i; + + KKASSERT(size > sizeof(*rsdp)); + + for (i = 0; i < size - sizeof(*rsdp); i += ACPI_RSDP_ALIGN) { + rsdp = (const struct acpi_rsdp *)&target[i]; + if (memcmp(rsdp->rsdp_sig, ACPI_RSDP_SIG, + ACPI_RSDP_SIGLEN) == 0) + return rsdp; + } + return NULL; +} + +static void * +madt_sdth_map(vm_paddr_t paddr) +{ + struct acpi_sdth *sdth; + vm_size_t mapsz; + + sdth = pmap_mapdev(paddr, sizeof(*sdth)); + mapsz = sdth->sdth_len; + pmap_unmapdev((vm_offset_t)sdth, sizeof(*sdth)); + + if (mapsz < sizeof(*sdth)) + return NULL; + + return pmap_mapdev(paddr, mapsz); +} + +static void +madt_sdth_unmap(struct acpi_sdth *sdth) +{ + pmap_unmapdev((vm_offset_t)sdth, sdth->sdth_len); +} + +static vm_paddr_t +madt_search_xsdt(vm_paddr_t xsdt_paddr) +{ + struct acpi_xsdt *xsdt; + vm_paddr_t madt_paddr = 0; + int i, nent; + + if (xsdt_paddr == 0) { + kprintf("madt_search_xsdt: XSDT paddr == 0\n"); + return 0; + } + + xsdt = madt_sdth_map(xsdt_paddr); + if (xsdt == NULL) { + kprintf("madt_search_xsdt: can't map XSDT\n"); + return 0; + } + + if (memcmp(xsdt->xsdt_hdr.sdth_sig, ACPI_XSDT_SIG, + ACPI_SDTH_SIGLEN) != 0) { + kprintf("madt_search_xsdt: not XSDT\n"); + goto back; + } + + if (xsdt->xsdt_hdr.sdth_rev != 1) { + kprintf("madt_search_xsdt: unsupported XSDT revision %d\n", + xsdt->xsdt_hdr.sdth_rev); + goto back; + } + + nent = (xsdt->xsdt_hdr.sdth_len - sizeof(xsdt->xsdt_hdr)) / + sizeof(xsdt->xsdt_ents[0]); + for (i = 0; i < nent; ++i) { + struct acpi_sdth *sdth; + + if (xsdt->xsdt_ents[i] == 0) + continue; + + sdth = madt_sdth_map(xsdt->xsdt_ents[i]); + if (sdth != NULL) { + int ret; + + ret = memcmp(sdth->sdth_sig, ACPI_MADT_SIG, + ACPI_SDTH_SIGLEN); + madt_sdth_unmap(sdth); + + if (ret == 0) { + kprintf("madt: MADT in XSDT\n"); + madt_paddr = xsdt->xsdt_ents[i]; + break; + } + } + } +back: + madt_sdth_unmap(&xsdt->xsdt_hdr); + return madt_paddr; +} + +static vm_paddr_t +madt_search_rsdt(vm_paddr_t rsdt_paddr) +{ + struct acpi_rsdt *rsdt; + vm_paddr_t madt_paddr = 0; + int i, nent; + + if (rsdt_paddr == 0) { + kprintf("madt_search_rsdt: RSDT paddr == 0\n"); + return 0; + } + + rsdt = madt_sdth_map(rsdt_paddr); + if (rsdt == NULL) { + kprintf("madt_search_rsdt: can't map RSDT\n"); + return 0; + } + + if (memcmp(rsdt->rsdt_hdr.sdth_sig, ACPI_RSDT_SIG, + ACPI_SDTH_SIGLEN) != 0) { + kprintf("madt_search_rsdt: not RSDT\n"); + goto back; + } + + if (rsdt->rsdt_hdr.sdth_rev != 1) { + kprintf("madt_search_rsdt: unsupported RSDT revision %d\n", + rsdt->rsdt_hdr.sdth_rev); + goto back; + } + + nent = (rsdt->rsdt_hdr.sdth_len - sizeof(rsdt->rsdt_hdr)) / + sizeof(rsdt->rsdt_ents[0]); + for (i = 0; i < nent; ++i) { + struct acpi_sdth *sdth; + + if (rsdt->rsdt_ents[i] == 0) + continue; + + sdth = madt_sdth_map(rsdt->rsdt_ents[i]); + if (sdth != NULL) { + int ret; + + ret = memcmp(sdth->sdth_sig, ACPI_MADT_SIG, + ACPI_SDTH_SIGLEN); + madt_sdth_unmap(sdth); + + if (ret == 0) { + kprintf("madt: MADT in RSDT\n"); + madt_paddr = rsdt->rsdt_ents[i]; + break; + } + } + } +back: + madt_sdth_unmap(&rsdt->rsdt_hdr); + return madt_paddr; +} + +static int +madt_parse(vm_paddr_t madt_paddr) +{ + struct acpi_madt *madt; + int size, cur, error; + + KKASSERT(madt_paddr != 0); + + madt = madt_sdth_map(madt_paddr); + KKASSERT(madt != NULL); + + if (madt->madt_hdr.sdth_rev != 1 && madt->madt_hdr.sdth_rev != 2) { + kprintf("madt_parse: unsupported MADT revision %d\n", + madt->madt_hdr.sdth_rev); + error = EOPNOTSUPP; + goto back; + } + + if (madt->madt_hdr.sdth_len < + sizeof(*madt) - sizeof(madt->madt_ents)) { + kprintf("madt_parse: invalid MADT length %u\n", + madt->madt_hdr.sdth_len); + error = EINVAL; + goto back; + } + + kprintf("madt: LAPIC address 0x%08x, flags %#x\n", + madt->madt_lapic_addr, madt->madt_flags); + + size = madt->madt_hdr.sdth_len - + (sizeof(*madt) - sizeof(madt->madt_ents)); + cur = 0; + error = 0; + + while (size - cur > sizeof(struct acpi_madt_ent)) { + const struct acpi_madt_ent *ent; + + ent = (const struct acpi_madt_ent *)&madt->madt_ents[cur]; + if (ent->me_len < sizeof(*ent)) { + kprintf("madt_parse: invalid MADT entry len %d\n", + ent->me_len); + error = EINVAL; + break; + } + if (ent->me_len > (size - cur)) { + kprintf("madt_parse: invalid MADT entry len %d, " + "> table length\n", ent->me_len); + error = EINVAL; + break; + } + + cur += ent->me_len; + + if (ent->me_type == MADT_ENT_LAPIC) { + const struct acpi_madt_lapic *lapic_ent; + + if (ent->me_len < sizeof(*lapic_ent)) { + kprintf("madt_parse: invalid MADT lapic entry " + "len %d\n", ent->me_len); + error = EINVAL; + break; + } + lapic_ent = (const struct acpi_madt_lapic *)ent; + if (lapic_ent->ml_flags & MADT_LAPIC_ENABLED) { + kprintf("madt: cpu_id %d, apic_id %d\n", + lapic_ent->ml_cpu_id, + lapic_ent->ml_apic_id); + } + } + } +back: + madt_sdth_unmap(&madt->madt_hdr); + return error; +} -- 2.41.0