From 1dbdb2729a6b8e48ed1c11c0f7a65b3f92699496 Mon Sep 17 00:00:00 2001 From: Almudena Garcia Date: Mon, 27 Jul 2020 19:57:14 +0200 Subject: [PATCH 4/7] smp: Add APIC finder and parser To find the processors, It's necessary to find the APIC (MADT) table This table is found inside ACPI tables. This set of functions find the MADT table, and parse it to find the APIC structures and register it in the kernel. *acpi_parse_apic.h: ACPI structures and function prototypes. *acpi_parse_apic.c: ACPI/APIC function definitions. --- i386/i386at/acpi_parse_apic.c | 567 ++++++++++++++++++++++++++++++++++ i386/i386at/acpi_parse_apic.h | 144 +++++++++ 2 files changed, 711 insertions(+) create mode 100644 i386/i386at/acpi_parse_apic.c create mode 100644 i386/i386at/acpi_parse_apic.h diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c new file mode 100644 index 00000000..4b579c5d --- /dev/null +++ b/i386/i386at/acpi_parse_apic.c @@ -0,0 +1,567 @@ +/* + * Copyright 2018 Juan Bosco Garcia + * Copyright 2018 2019 2020 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 of the License, 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 Min_SMP. If not, see . + */ + +#include +#include //memcmp, memcpy... + +#include //lapic, ioapic... +#include //printf +#include //uint16_t, uint32_t... +#include //machine_slot +#include //phystokv +#include +#include + +#define BAD_CHECKSUM -1 +#define NO_RSDP -2 +#define NO_RSDT -2 +#define BAD_SIGNATURE -3 +#define NO_APIC -4 + +struct acpi_apic *apic_madt = NULL; + +static struct acpi_rsdp* acpi_get_rsdp(void); +static int acpi_check_rsdt(struct acpi_rsdt *); +static struct acpi_rsdt* acpi_get_rsdt(struct acpi_rsdp *rsdp, int* acpi_rsdt_n); +static struct acpi_apic* acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n); +static int acpi_apic_setup(struct acpi_apic *apic); +static int acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry); +static int acpi_apic_add_lapic(struct acpi_apic_lapic *lapic_entry); +static int acpi_apic_parse_table(struct acpi_apic *apic); + + +/* + * 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; + + /* Try to get the RSDP pointer. */ + rsdp = acpi_get_rsdp(); + if (rsdp == NULL) + return NO_RSDP; + + /* Try to get the RSDT pointer. */ + rsdt = acpi_get_rsdt(rsdp, &acpi_rsdt_n); + if (rsdt == NULL) + return NO_RSDT; + + /* Try to get the APIC table pointer. */ + apic_madt = acpi_get_apic(rsdt, acpi_rsdt_n); + if (apic_madt == NULL) + return NO_APIC; + + /* Print the ACPI tables addresses. */ + acpi_print_info(rsdp, rsdt, acpi_rsdt_n); + + /* + * 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 != 0) + return ret_acpi_setup; + + /* Prints a table with the list of each cpu and each IOAPIC with its APIC ID. */ + apic_print_info(); + + return 0; +} + +/* + * 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 int +acpi_checksum(void *addr, uint32_t length) +{ + char *bytes = addr; + int checksum = 0; + unsigned int i; + + /* Sum all bytes of addr */ + for (i = 0; i < length; i++) + checksum += bytes[i]; + + return checksum & 0xff; +} + +/* + * acpi_check_rsdp: + * check if the RDST "candidate" table is the real RSDT 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. + * + * Returns 0 if correct. + */ +static int +acpi_check_rsdp(struct acpi_rsdp *rsdp) +{ + uint32_t checksum; + int is_rsdp; + int ret_value = 0; + + /* Check the integrity of RSDP. */ + if (rsdp == NULL) + ret_value = NO_RSDP; + else { + /* Check if rsdp signature match with the ACPI RSDP signature. */ + is_rsdp = memcmp(rsdp->signature, ACPI_RSDP_SIG, sizeof(rsdp->signature)); + + if (is_rsdp == 0) { + /* If match, calculates rdsp checksum and check It. */ + checksum = acpi_checksum(rsdp, sizeof(struct acpi_rsdp)); + + if (checksum != 0) + ret_value = BAD_CHECKSUM; + } + else + ret_value = BAD_SIGNATURE; + } + + return ret_value; +} + +/* + * acpi_search_rsdp: search the rsdp table in a memory range. + * + * Receives as input the initial virtual address, and the lenght + * of memory range. + * + * Returns the reference to rsdp structure if success, NULL if failure. + */ +static struct acpi_rsdp* +acpi_search_rsdp(void *addr, uint32_t length) +{ + struct acpi_rsdp *rsdp = NULL; + void *end; + + /* check alignment. */ + if ( (uint32_t)addr & (ACPI_RSDP_ALIGN-1) ) + return NULL; + + /* 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 (acpi_check_rsdp(addr) == 0) { + /* If yes, store RSDP address */ + rsdp = (struct acpi_rsdp*) addr; + break; + } + } + + return rsdp; +} + +/* + * 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 */ + + /* 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. + */ +static int +acpi_check_rsdt(struct acpi_rsdt *rsdt) +{ + uint32_t checksum; + int ret_value = 0; + + if (rsdt == NULL) + ret_value = NO_RSDT; + else { + checksum = acpi_checksum(rsdt, rsdt->header.length); + + if (checksum != 0) + ret_value = BAD_CHECKSUM; + } + + return ret_value; +} + +/* + * 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; + + if (rsdp != NULL) { + /* 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 is rsdt signature is equals to ACPI RSDT signature. */ + signature_check = memcmp(rsdt->header.signature, ACPI_RSDT_SIG, + sizeof(rsdt->header.signature)); + + if (signature_check == 0) { + /* Check if rsdt is correct. */ + acpi_check = acpi_check_rsdt(rsdt); + + if (acpi_check == 0) { + /* Calculated number of elements stored in rsdt. */ + *acpi_rsdt_n = (rsdt->header.length - sizeof(rsdt->header)) + / sizeof(rsdt->entry[0]); + + } + } + + if (signature_check != 0 || acpi_check != 0) + rsdt = NULL; + } + + 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_apic *apic = NULL; + struct acpi_dhdr *descr_header; + int check_signature; + + int i; + + if (rsdt != NULL) { + /* Search APIC entries in rsdt table. */ + for (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); + + /* Check if the entry contains an APIC. */ + check_signature = memcmp(descr_header->signature, ACPI_APIC_SIG, sizeof(descr_header->signature)); + + if (check_signature == 0) { + /* If yes, store the entry in apic. */ + apic = (struct acpi_apic*) kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_apic), VM_PROT_READ | VM_PROT_WRITE); + + printf("found apic in address %x\n", apic); + break; + } + } + } + + return apic; +} + +/* + * 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 int +acpi_apic_add_lapic(struct acpi_apic_lapic *lapic_entry) +{ + int ret_value = 0; + int lapic_id; + + if (lapic_entry == NULL) + ret_value = -1; + else { + /* If cpu flag is correct */ + if (lapic_entry->flags & 0x1) { + /* Get the CPU's APIC ID. */ + lapic_id = lapic_entry->apic_id; + + /* Add cpu to processors' list. */ + apic_add_cpu(lapic_id); + + printf("new cpu found with apic id %x\n", lapic_entry->apic_id);; + } + } + + return ret_value; +} + +/* + * 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 int +acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry) +{ + int ret_value = 0; + IoApicData io_apic; + + if (ioapic_entry == NULL) + ret_value = -1; + else { + /* 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); + + printf("new ioapic found with apic id %x\n", io_apic.apic_id); + } + + return ret_value; +} + + +/* + * 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 int +acpi_apic_add_irq_override(struct acpi_apic_irq_override* irq_override) +{ + int ret_value = 0; + IrqOverrideData irq_over; + + if (irq_override == NULL) + ret_value = -1; + else { + /* 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); + } + + return ret_value; +} + + +/* + * 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 +apic_parse_table(struct acpi_apic *apic) +{ + int ret_value = 0; + struct acpi_apic_dhdr *apic_entry = NULL; + uint32_t end = 0; + + if (apic != NULL) { + /* Get the address of first APIC entry */ + apic_entry = apic->entry; + + /* Get the end address of APIC table */ + end = (uint32_t) apic + apic->header.length; + + /* 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: + + /* 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_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); + } + } + else /* apic == NULL. */ + ret_value = -1; + + return ret_value; +} + + +/* + * 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. + */ + +static int +acpi_apic_setup(struct acpi_apic *apic) +{ + int apic_checksum; + int ret_value = 0; + ApicLocalUnit* lapic; + int ncpus, nioapics; + int init_success = 0; + + + if (apic != NULL) { + + /* Check the checksum of the APIC */ + apic_checksum = acpi_checksum(apic, apic->header.length); + + if(apic_checksum != 0) + ret_value = BAD_CHECKSUM; + else { + init_success = apic_data_init(); + + if (init_success == 0) { + printf("lapic found in address %x\n", apic->lapic_addr); + + /* map common lapic address */ + lapic = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), VM_PROT_READ); + + if (lapic != NULL) { + printf("lapic mapped in address %x\n", lapic); + apic_lapic_init(lapic); + } + + apic_parse_table(apic); + + ncpus = apic_get_numcpus(); + nioapics = apic_get_num_ioapics(); + + if(ncpus == 0 || nioapics == 0) + ret_value = -1; + else { + apic_refit_cpulist(); + printf("%d cpus found. %d ioapics found\n", ncpus, nioapics); + } + } + else + ret_value = -1; + } + } + else /* apic == NULL */ + ret_value = NO_APIC; + + return ret_value; +} + diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h new file mode 100644 index 00000000..486ad7b2 --- /dev/null +++ b/i386/i386at/acpi_parse_apic.h @@ -0,0 +1,144 @@ +/* + * Copyright 2018 Juan Bosco Garcia + * 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 of the License, 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 Min_SMP. If not, see . + */ + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +#include + +#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. */ +#define ACPI_APIC_ENTRY_LAPIC 0 +#define ACPI_APIC_ENTRY_IOAPIC 1 +#define ACPI_APIC_IRQ_OVERRIDE 2 +#define 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__ */ + -- 2.27.0