diff options
-rw-r--r-- | i386/Makefrag.am | 2 | ||||
-rw-r--r-- | i386/i386at/acpi_parse_apic.c | 543 | ||||
-rw-r--r-- | i386/i386at/acpi_parse_apic.h | 163 |
3 files changed, 708 insertions, 0 deletions
diff --git a/i386/Makefrag.am b/i386/Makefrag.am index 035fd34d..0f788f30 100644 --- a/i386/Makefrag.am +++ b/i386/Makefrag.am @@ -32,6 +32,8 @@ libkernel_a_SOURCES += \ if PLATFORM_at libkernel_a_SOURCES += \ + i386/i386at/acpi_parse_apic.h \ + i386/i386at/acpi_parse_apic.c \ i386/i386at/boothdr.S \ i386/i386at/com.c \ i386/i386at/com.h \ diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c new file mode 100644 index 00000000..55b6a09b --- /dev/null +++ b/i386/i386at/acpi_parse_apic.c @@ -0,0 +1,543 @@ +/* acpi_parse_apic.h - ACPI-MADT table parser. Source file + Copyright (C) 2018 Juan Bosco Garcia + Copyright (C) 2019 2020 Almudena Garcia Jurado-Centurion + Written by Juan Bosco Garcia and Almudena Garcia Jurado-Centurion + + This file is part of Min_SMP. + + Min_SMP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + Min_SMP is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <string.h> /* memcmp, memcpy... */ + +#include <include/stdint.h> /* uint16_t, uint32_t... */ + +#include <mach/machine.h> /* machine_slot */ + +#include <kern/printf.h> /* printf */ +#include <kern/debug.h> +#include <i386/vm_param.h> /* phystokv */ +#include <i386/apic.h> /* lapic, ioapic... */ +#include <i386at/acpi_parse_apic.h> +#include <vm/vm_kern.h> + +static struct acpi_apic *apic_madt = NULL; + +/* + * acpi_print_info: shows by screen the ACPI's rsdp and rsdt virtual address + * and the number of entries stored in RSDT table. + * + * Receives as input the references of RSDP and RSDT tables, + * and the number of entries stored in RSDT. + */ +void +acpi_print_info(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, int acpi_rsdt_n) +{ + + printf("ACPI:\n"); + printf(" rsdp = %x; rsdp->rsdt_addr = %x\n", rsdp, rsdp->rsdt_addr); + printf(" rsdt = %x; rsdt->length = %x (n = %x)\n", rsdt, rsdt->header.length, + acpi_rsdt_n); +} + +/* + * acpi_checksum: calculates the checksum of an ACPI table. + * Receives as input the virtual address of the table. + * + * Returns 0 if success, other value if error. + */ +static uint8_t +acpi_checksum(void *addr, uint32_t length) +{ + uint8_t *bytes = addr; + uint8_t checksum = 0; + unsigned int i; + + /* Sum all bytes of addr */ + for (i = 0; i < length; i++) + checksum += bytes[i]; + + return checksum; +} + +/* + * acpi_check_signature: check if a signature match with the signature of its table. + * + * Receive as parameter both signatures: table signature, the signature which needs to check, + * and real signature, the genuine signature of the table. + * + * Return 0 if success, other if error. + */ + +static int +acpi_check_signature(uint8_t table_signature[], uint8_t real_signature[], uint8_t length) +{ + return memcmp(table_signature, real_signature, length); +} + + +/* + * acpi_check_rsdp: + * check if the RDSP "candidate" table is the real RSDP table. + * + * Compare the table signature with the ACPI signature for this table + * and check is the checksum is correct. + * + * Receives as input the reference of RSDT table. + * + * Preconditions: RSDP pointer must not be NULL. + * + * Returns 0 if correct. + */ +static int8_t +acpi_check_rsdp(struct acpi_rsdp *rsdp) +{ + uint32_t checksum; + int is_rsdp; + + /* Check if rsdp signature match with the ACPI RSDP signature. */ + is_rsdp = acpi_check_signature(rsdp->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 (checksum != 0) + return ACPI_BAD_CHECKSUM; + + return ACPI_SUCCESS; +} + +/* + * acpi_check_rsdp_align: check if the RSDP address is aligned. + * Preconditions: The address must not be NULL + * + * Returns ACPI_SUCCESS (0) if success, ACPI_BAD_ALIGN if error + */ + +static int8_t +acpi_check_rsdp_align(void *addr) +{ + /* check alignment. */ + if ((uint32_t)addr & (ACPI_RSDP_ALIGN-1)) + return ACPI_BAD_ALIGN; + + return ACPI_SUCCESS; +} + +/* + * acpi_search_rsdp: search the rsdp table in a memory range. + * + * Receives as input the initial virtual address, and the lenght + * of memory range. + * + * Preconditions: The start address (addr) must be aligned. + * + * Returns the reference to rsdp structure if success, NULL if failure. + */ +static struct acpi_rsdp* +acpi_search_rsdp(void *addr, uint32_t length) +{ + void *end; + + /* 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; + } + } + + return NULL; +} + +/* + * 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. + */ +struct acpi_rsdp* +acpi_get_rsdp(void) +{ + struct acpi_rsdp *rsdp = NULL; + uint16_t *start = 0x0; + uint32_t base = 0x0; + + /* EDBA start address. */ + start = (uint16_t*) phystokv(0x040e); + base = *start; + + if (base != 0) { /* Memory check. */ + + base <<= 4; /* base = base * 16 */ + + /* check alignment. */ + if (acpi_check_rsdp_align(base) == ACPI_BAD_ALIGN) + return NULL; + + /* Search the RSDP in first 1024 bytes from EDBA. */ + rsdp = acpi_search_rsdp((void*)base,1024); + } + + if (rsdp == NULL) { + /* If RSDP isn't in EDBA, search in the BIOS read-only memory space between 0E0000h and 0FFFFFh */ + rsdp = acpi_search_rsdp((void*) 0x0e0000, 0x100000 - 0x0e0000); + } + + 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 + * and a reference to store the number of entries of 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) +{ + 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); + + /* Check if the RSDT mapping is fine. */ + if (rsdt == NULL) + return NULL; + + /* Check is rsdt signature is equals to ACPI RSDT signature. */ + signature_check = acpi_check_signature(rsdt->header.signature, ACPI_RSDT_SIG, + 4*sizeof(uint8_t)); + + 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]); + + return rsdt; +} + +/* + * acpi_get_apic: get MADT/APIC table from RSDT entries. + * + * Receives as input the RSDT initial address, + * and the number of entries of RSDT table. + * + * Returns a reference to APIC/MADT table if success, NULL if failure. + */ +static struct acpi_apic* +acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n) +{ + struct acpi_dhdr *descr_header; + int check_signature; + + /* Search APIC entries in rsdt table. */ + for (int i = 0; i < acpi_rsdt_n; i++) { + descr_header = (struct acpi_dhdr*) kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_dhdr), + VM_PROT_READ | VM_PROT_WRITE); + + /* 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. + * + * Receives as input the Local APIC entry in MADT/APIC table. + */ +static void +acpi_apic_add_lapic(struct acpi_apic_lapic *lapic_entry) +{ + /* If cpu flag is correct */ + if (lapic_entry->flags & 0x1) { + /* Add cpu to processors' list. */ + apic_add_cpu(lapic_entry->apic_id); + } + +} + +/* + * apic_add_ioapic: add a new IOAPIC to IOAPICS array + * and increase the number of IOAPIC. + * + * Receives as input the IOAPIC entry in MADT/APIC table. + */ + +static void +acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry) +{ + int ret_value = 0; + IoApicData io_apic; + + /* Fill IOAPIC structure with its main fields */ + io_apic.apic_id = ioapic_entry->apic_id; + io_apic.addr = ioapic_entry->addr; + io_apic.base = ioapic_entry->base; + + /* Insert IOAPIC in the list. */ + apic_add_ioapic(io_apic); +} + + +/* + * apic_add_ioapic: add a new IOAPIC to IOAPICS list + * and increase the number of IOAPIC. + * + * Receives as input the IOAPIC entry in MADT/APIC table. + */ + +static void +acpi_apic_add_irq_override(struct acpi_apic_irq_override* irq_override) +{ + IrqOverrideData irq_over; + + /* Fills IRQ override structure with its fields */ + irq_over.bus = irq_override->bus; + irq_over.irq = irq_override->irq; + irq_over.gsi = irq_override->gsi; + irq_over.flags = irq_override->flags; + + /* Insert IRQ override in the list */ + apic_add_irq_override(irq_over); +} + + +/* + * apic_parse_table: parse the MADT/APIC table. + * + * Read the APIC/MADT table entry to entry, + * registering the APIC structures (Local APIC, IOAPIC or IRQ override) entries in their lists. + */ + +static int +acpi_apic_parse_table(struct acpi_apic *apic) +{ + int ret_value = 0; + struct acpi_apic_dhdr *apic_entry = NULL; + uint32_t end = 0; + uint8_t numcpus = 1; + + /* Get the address of first APIC entry */ + apic_entry = (struct acpi_apic_dhdr*) apic->entry; + + /* Get the end address of APIC table */ + end = (uint32_t) apic + apic->header.length; + + /* Initialize number of cpus */ + numcpus = apic_get_numcpus(); + + /* Search in APIC entry. */ + while ((uint32_t)apic_entry < end) { + struct acpi_apic_lapic *lapic_entry; + struct acpi_apic_ioapic *ioapic_entry; + struct acpi_apic_irq_override *irq_override_entry; + + /* Check entry type. */ + switch(apic_entry->type) { + + /* If APIC entry is a CPU's Local APIC. */ + case ACPI_APIC_ENTRY_LAPIC: + if(numcpus < NCPUS) { + /* Store Local APIC data. */ + lapic_entry = (struct acpi_apic_lapic*) apic_entry; + acpi_apic_add_lapic(lapic_entry); + } + break; + + /* If APIC entry is an IOAPIC. */ + case ACPI_APIC_ENTRY_IOAPIC: + + /* Store IOAPIC data. */ + ioapic_entry = (struct acpi_apic_ioapic*) apic_entry; + acpi_apic_add_ioapic(ioapic_entry); + + break; + + /* If APIC entry is a IRQ Override. */ + case ACPI_APIC_ENTRY_IRQ_OVERRIDE: + + /* Store IRQ Override data. */ + irq_override_entry = (struct acpi_apic_irq_override*) apic_entry; + acpi_apic_add_irq_override(irq_override_entry); + break; + + } + + /* Get next APIC entry. */ + apic_entry = (struct acpi_apic_dhdr*)((uint32_t) apic_entry + + apic_entry->length); + + /* Update number of cpus. */ + numcpus = apic_get_numcpus(); + } + + return ACPI_SUCCESS; +} + + +/* + * acpi_apic_setup: parses the APIC/MADT table, to find the Local APIC and IOAPIC structures + * and the common address for Local APIC. + * + * Receives as input a reference for APIC/MADT table. + * Returns 0 if success. + * + * Fills the cpu_to_lapic and ioapics array, indexed by Kernel ID + * with a relationship between Kernel ID and APIC ID, + * and map the Local APIC common address, to fill the lapic reference. + * + * Precondition: The APIC pointer must not be NULL + */ + +static int +acpi_apic_setup(struct acpi_apic *apic) +{ + int apic_checksum; + ApicLocalUnit* lapic; + 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 = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), VM_PROT_READ); + + if (lapic == NULL) + return ACPI_NO_LAPIC; + + apic_lapic_init(lapic); + acpi_apic_parse_table(apic); + + ncpus = apic_get_numcpus(); + nioapics = apic_get_num_ioapics(); + + if (ncpus == 0 || nioapics == 0 || ncpus > NCPUS) + return ACPI_APIC_FAILURE; + + /* Refit the apic-cpu array. */ + if(ncpus < NCPUS) { + int refit = apic_refit_cpulist(); + if (refit != -0) + return ACPI_FIT_FAILURE; + } + + return ACPI_SUCCESS; +} + +/* + * acpi_apic_init: find the MADT/APIC table in ACPI tables + * and parses It to find Local APIC and IOAPIC structures. + * Each Local APIC stores the info and control structores for a cpu. + * The IOAPIC controls the communication of the processors with the I/O devices. + * + * Returns 0 if success, -1 if error. + */ +int +acpi_apic_init(void) +{ + struct acpi_rsdp *rsdp = 0; + struct acpi_rsdt *rsdt = 0; + int acpi_rsdt_n; + int ret_acpi_setup; + int apic_init_success = 0; + + /* Try to get the RSDP pointer. */ + rsdp = acpi_get_rsdp(); + if (rsdp == NULL) + 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); + + apic_init_success = apic_data_init(); + if (apic_init_success != ACPI_SUCCESS) + return ACPI_APIC_FAILURE; + + /* + * Starts the parsing of APIC table, to find the APIC structures. + * and enumerate them. This function also find the common Local APIC address. + */ + ret_acpi_setup = acpi_apic_setup(apic_madt); + if (ret_acpi_setup != ACPI_SUCCESS) + return ret_acpi_setup; + + /* Prints a table with the list of each cpu and each IOAPIC with its APIC ID. */ + apic_print_info(); + + return ACPI_SUCCESS; +} diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h new file mode 100644 index 00000000..d071da4f --- /dev/null +++ b/i386/i386at/acpi_parse_apic.h @@ -0,0 +1,163 @@ +/* acpi_parse_apic.h - ACPI-MADT table parser. Header file + Copyright (C) 2018 Juan Bosco Garcia + Copyright (C) 2019 2020 Almudena Garcia Jurado-Centurion + Written by Juan Bosco Garcia and Almudena Garcia Jurado-Centurion + + This file is part of Min_SMP. + + Min_SMP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + Min_SMP is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +#include <include/stdint.h> + +enum ACPI_RETURN { + ACPI_BAD_CHECKSUM = -1, + ACPI_BAD_ALIGN = -2, + ACPI_NO_RSDP = -3, + ACPI_NO_RSDT = -4, + ACPI_BAD_SIGNATURE = -5, + ACPI_NO_APIC = -6, + ACPI_NO_LAPIC = -7, + ACPI_APIC_FAILURE = -8, + ACPI_FIT_FAILURE = -9, + ACPI_SUCCESS = 0, +}; + +#define ACPI_RSDP_ALIGN 16 +#define ACPI_RSDP_SIG "RSD PTR " + +struct acpi_rsdp { + uint8_t signature[8]; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t revision[1]; + uint32_t rsdt_addr; +} __attribute__((__packed__)); + + +/* + * RSDT Entry Header + * + * Header which stores the descriptors of tables pointed from RDSP's Entry Field + * Includes the signature of the table, to identify each table. + * + * In MADT, the signature is 'APIC'. + */ +struct acpi_dhdr { + uint8_t signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t oem_table_id[8]; + uint32_t oem_revision; + uint8_t creator_id[4]; + uint32_t creator_revision; +} __attribute__((__packed__)); + + +#define ACPI_RSDT_SIG "RSDT" + +struct acpi_rsdt { + struct acpi_dhdr header; + uint32_t entry[0]; +} __attribute__((__packed__)); + +/* APIC table signature. */ +#define ACPI_APIC_SIG "APIC" + +/* Types value for MADT entries: Local APIC, IOAPIC and IRQ Override. */ +enum ACPI_APIC_ENTRY_TYPE { + ACPI_APIC_ENTRY_LAPIC = 0, + ACPI_APIC_ENTRY_IOAPIC = 1, + ACPI_APIC_ENTRY_IRQ_OVERRIDE = 2, + ACPI_APIC_ENTRY_NONMASK_IRQ = 4 +}; + +/* + * APIC descriptor header + * Define the type of the structure (Local APIC, I/O APIC or others). + * Type: Local APIC (0), I/O APIC (1). + */ +struct acpi_apic_dhdr { + uint8_t type; + uint8_t length; +} __attribute__((__packed__)); + + +/* + * Multiple APIC Description Table (MADT) + * + * Describes the APIC structures which exist in the machine. + * Includes the common address where Local APIC is mapped in main memory. + * + * Entry field stores the descriptors of APIC structures. + */ +struct acpi_apic { + struct acpi_dhdr header; /* Header, which stores the descriptor for RDST's Entry field. */ + uint32_t lapic_addr; /* Local Interrupt Controller Address. */ + uint32_t flags; + struct acpi_apic_dhdr entry[0]; /* Interrupt Controller Structure */ +} __attribute__((__packed__)); + +/* + * Processor Local APIC Structure + * + * Stores information about APIC ID, flags and ACPI Processor UID + */ + +struct acpi_apic_lapic { + struct acpi_apic_dhdr header; + uint8_t processor_id; /* ACPI Processor UID */ + uint8_t apic_id; + uint32_t flags; +} __attribute__((__packed__)); + + +/* + * I/O APIC Structure + * + * Stores information about APIC ID, and I/O APIC tables + */ + +struct acpi_apic_ioapic { + struct acpi_apic_dhdr header; + uint8_t apic_id; + uint8_t reserved; + uint32_t addr; + uint32_t base; +} __attribute__((__packed__)); + +/* + * IRQ Override structure + * + * Stores information about IRQ override, with busses and IRQ. + */ + +struct acpi_apic_irq_override { + struct acpi_apic_dhdr header; + uint8_t bus; + uint8_t irq; + uint32_t gsi; + uint16_t flags; +} __attribute__((__packed__)); + +int acpi_apic_init(void); +void acpi_print_info(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, int acpi_rsdt_n); + + +#endif /* __ACPI_H__ */ |