From 73bd3adb6ebe27ccbfcae930698fba18c62f6aac Mon Sep 17 00:00:00 2001 From: Philippe Aubertin Date: Thu, 23 Jan 2025 23:51:36 -0500 Subject: [PATCH] Map structures --- include/kernel/infrastructure/acpi/acpi.h | 2 + include/kernel/infrastructure/acpi/tables.h | 2 +- .../infrastructure/i686/firmware/asm/bios.h | 2 + .../infrastructure/i686/firmware/asm/mp.h | 4 + .../infrastructure/i686/firmware/bios.h | 5 +- .../kernel/infrastructure/i686/firmware/mp.h | 9 +- kernel/Makefile | 1 + kernel/infrastructure/acpi/acpi.c | 16 +- kernel/infrastructure/i686/firmware/acpi.c | 5 +- kernel/infrastructure/i686/firmware/bios.c | 17 +- kernel/infrastructure/i686/firmware/mp.c | 255 ++++++++++++++++++ kernel/infrastructure/i686/init.c | 5 +- 12 files changed, 308 insertions(+), 15 deletions(-) create mode 100644 kernel/infrastructure/i686/firmware/mp.c diff --git a/include/kernel/infrastructure/acpi/acpi.h b/include/kernel/infrastructure/acpi/acpi.h index 3a836af9..666a493e 100644 --- a/include/kernel/infrastructure/acpi/acpi.h +++ b/include/kernel/infrastructure/acpi/acpi.h @@ -38,6 +38,8 @@ #include #include +bool verify_acpi_checksum(const void *buffer, size_t buflen); + bool verify_acpi_rsdp(const acpi_rsdp_t *rsdp); void map_acpi_tables(kern_paddr_t rsdp_paddr, const acpi_table_def_t *table_defs); diff --git a/include/kernel/infrastructure/acpi/tables.h b/include/kernel/infrastructure/acpi/tables.h index f614b33b..9361571b 100644 --- a/include/kernel/infrastructure/acpi/tables.h +++ b/include/kernel/infrastructure/acpi/tables.h @@ -150,7 +150,7 @@ typedef PACKED_STRUCT { acpi_table_header_t header; uint32_t local_intr_controller_addr; uint32_t flags; - char entries[]; + const char entries[]; } acpi_madt_t; typedef PACKED_STRUCT { diff --git a/include/kernel/infrastructure/i686/firmware/asm/bios.h b/include/kernel/infrastructure/i686/firmware/asm/bios.h index 4a78fbd4..4f363d69 100644 --- a/include/kernel/infrastructure/i686/firmware/asm/bios.h +++ b/include/kernel/infrastructure/i686/firmware/asm/bios.h @@ -34,4 +34,6 @@ #define BIOS_BDA_EBDA_SEGMENT 0x40e +#define BIOS_BDA_MEMORY_SIZE 0x413 + #endif diff --git a/include/kernel/infrastructure/i686/firmware/asm/mp.h b/include/kernel/infrastructure/i686/firmware/asm/mp.h index 402976a3..8780e733 100644 --- a/include/kernel/infrastructure/i686/firmware/asm/mp.h +++ b/include/kernel/infrastructure/i686/firmware/asm/mp.h @@ -50,6 +50,10 @@ #define MP_CONF_TABLE_SIGNATURE "PCMP" +#define MP_REVISION_V1_1 1 + +#define MP_REVISION_V1_4 4 + /* Multiprocessor Specification 1.4 Table 4-3 Base MP Configuration Table Entry * Types */ diff --git a/include/kernel/infrastructure/i686/firmware/bios.h b/include/kernel/infrastructure/i686/firmware/bios.h index ab257641..a9d02b69 100644 --- a/include/kernel/infrastructure/i686/firmware/bios.h +++ b/include/kernel/infrastructure/i686/firmware/bios.h @@ -33,8 +33,11 @@ #define JINUE_KERNEL_INFRASTRUCTURE_I686_FIRMWARE_BIOS_H #include +#include #include -uintptr_t get_bios_ebda_addr(void); +uint32_t get_bios_ebda_addr(void); + +size_t get_bios_base_memory_size(void); #endif diff --git a/include/kernel/infrastructure/i686/firmware/mp.h b/include/kernel/infrastructure/i686/firmware/mp.h index dd73bc49..b5cea82f 100644 --- a/include/kernel/infrastructure/i686/firmware/mp.h +++ b/include/kernel/infrastructure/i686/firmware/mp.h @@ -32,6 +32,7 @@ #ifndef JINUE_KERNEL_INFRASTRUCTURE_I686_FIRMWARE_MP_H #define JINUE_KERNEL_INFRASTRUCTURE_I686_FIRMWARE_MP_H +#include #include /* Multiprocessor Specification 1.4 section 4.1 MP Floating Pointer @@ -46,7 +47,7 @@ typedef struct { uint8_t feature1; uint8_t feature2; uint8_t feature_reserved[3]; -} mp_floating_ptr_t; +} mp_ptr_struct_t; /* Multiprocessor Specification 1.4 section 4.2 MP Configuration Table * Header */ @@ -65,7 +66,7 @@ typedef struct { uint16_t ext_table_length; uint8_t ext_table_checksum; uint8_t reserved; - char entries[]; + const char entries[]; } mp_conf_table_t; /* Multiprocessor Specification 1.4 section 4.3.1 Processor Entries */ @@ -112,4 +113,8 @@ typedef struct { uint8_t dest_apic_intn; } mp_entry_intr_t; +void find_mp(void); + +void init_mp(void); + #endif diff --git a/kernel/Makefile b/kernel/Makefile index 272924b2..5632ad33 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -108,6 +108,7 @@ sources.kernel.c = \ infrastructure/i686/drivers/vga.c \ infrastructure/i686/firmware/acpi.c \ infrastructure/i686/firmware/bios.c \ + infrastructure/i686/firmware/mp.c \ infrastructure/i686/pmap/nopae.c \ infrastructure/i686/pmap/pmap.c \ infrastructure/i686/pmap/pae.c \ diff --git a/kernel/infrastructure/acpi/acpi.c b/kernel/infrastructure/acpi/acpi.c index 756ee8e4..59a99af6 100644 --- a/kernel/infrastructure/acpi/acpi.c +++ b/kernel/infrastructure/acpi/acpi.c @@ -37,13 +37,17 @@ #include /** - * Verify the checksum of an ACPI data structure + * Verify the checksum of an ACPI data structure (RSDP, RSDT, ACPI table) + * + * On x86, this function is also used to verify the checksum of the floating + * pointer structure and MP configuration table header from Intel's + * Multiprocessor Specification since the checksum algorithm is the same. * - * @param buffer pointer to ACPI data structure + * @param buffer pointer to ACPI (or MP) data structure * @param buflen size of ACPI data structure * @return true for correct checksum, false for checksum mismatch */ -static bool verify_checksum(const void *buffer, size_t buflen) { +bool verify_acpi_checksum(const void *buffer, size_t buflen) { uint8_t sum = 0; for(int idx = 0; idx < buflen; ++idx) { @@ -64,7 +68,7 @@ bool verify_acpi_rsdp(const acpi_rsdp_t *rsdp) { return false; } - if(!verify_checksum(rsdp, ACPI_V1_RSDP_SIZE)) { + if(!verify_acpi_checksum(rsdp, ACPI_V1_RSDP_SIZE)) { return false; } @@ -76,7 +80,7 @@ bool verify_acpi_rsdp(const acpi_rsdp_t *rsdp) { return false; } - return verify_checksum(rsdp, sizeof(acpi_rsdp_t)); + return verify_acpi_checksum(rsdp, sizeof(acpi_rsdp_t)); } /** @@ -123,7 +127,7 @@ static const void *map_table(const acpi_table_header_t *header) { resize_map_in_kernel(header->length); - if(! verify_checksum(header, header->length)) { + if(! verify_acpi_checksum(header, header->length)) { return NULL; } diff --git a/kernel/infrastructure/i686/firmware/acpi.c b/kernel/infrastructure/i686/firmware/acpi.c index 44a1cca7..7bcd420b 100644 --- a/kernel/infrastructure/i686/firmware/acpi.c +++ b/kernel/infrastructure/i686/firmware/acpi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -79,14 +80,14 @@ static const acpi_rsdp_t *find_rsdp(void) { } /* TODO define some PC address map somewhere, use in VGA driver as well */ - const char *top = (const char *)(0xa0000 - 1024); + const char *top = (const char *)(0xa0000 - KB); const char *ebda = (const char *)get_bios_ebda_addr(); if(ebda == NULL || ebda > top) { return NULL; } - for(const char *addr = ebda; addr < ebda + 1024; addr += 16) { + for(const char *addr = ebda; addr < ebda + KB; addr += 16) { const acpi_rsdp_t *rsdp = (const acpi_rsdp_t *)addr; if(verify_acpi_rsdp(rsdp)) { diff --git a/kernel/infrastructure/i686/firmware/bios.c b/kernel/infrastructure/i686/firmware/bios.c index db47a787..b09d3205 100644 --- a/kernel/infrastructure/i686/firmware/bios.c +++ b/kernel/infrastructure/i686/firmware/bios.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024-2025 Philippe Aubertin. + * Copyright (C) 2025 Philippe Aubertin. * All rights reserved. * Redistribution and use in source and binary forms, with or without @@ -30,9 +30,11 @@ */ #include +#include #include -uintptr_t get_bios_ebda_addr(void) { +/* The return value must be aligned on 16 bytes. */ +uint32_t get_bios_ebda_addr(void) { uintptr_t ebda = 16 * (*(uint16_t *)BIOS_BDA_EBDA_SEGMENT); /* TODO define some PC address map somewhere, use in VGA driver as well */ @@ -42,3 +44,14 @@ uintptr_t get_bios_ebda_addr(void) { return ebda; } + +/* The return value must be aligned on 16 bytes */ +size_t get_bios_base_memory_size(void) { + size_t size_kb = *(uint16_t *)BIOS_BDA_MEMORY_SIZE; + + if(size_kb < 512 || size_kb > 640) { + return 0; + } + + return size_kb * KB; +} diff --git a/kernel/infrastructure/i686/firmware/mp.c b/kernel/infrastructure/i686/firmware/mp.c new file mode 100644 index 00000000..52c91275 --- /dev/null +++ b/kernel/infrastructure/i686/firmware/mp.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2025 Philippe Aubertin. + * All rights reserved. + + * 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 author nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include +#include + +#define MAXIMUM_TABLE_SIZE (16 * KB) + +#define PADDR_NULL 0 + +static struct { + const mp_ptr_struct_t *ptrst; + const mp_conf_table_t *table; + uint32_t ptrst_paddr; +} mp; + +/** + * Validate the floating pointer structure + * + * @param ptrst pointer to floating pointer structure candidate + * @return true if valid, false otherwise + */ +static bool validate_pointer_structure(const mp_ptr_struct_t *ptrst) { + if(strncmp(ptrst->signature, MP_FLOATING_PTR_SIGNATURE, sizeof(ptrst->signature)) != 0) { + return false; + } + + if(16 * ptrst->length != sizeof(*ptrst)) { + return false; + } + + /* The specification uses the same checksum algorithm as ACPI. */ + return verify_acpi_checksum(ptrst, sizeof(*ptrst)); +} + +/** + * Validate the header of the configuration table + * + * When this function is called, the configuration table is partially mapped, + * so only the header fields can be validated. Specifically, the checksum + * cannot be checked, so this needs to be done separately once the table is + * fully mapped. One validation check performed by this function is that the + * value in the base table size field is reasonable and we don't want to rely + * on this value to map the full table before this is confirmed. + * + * @param table pointer to configuration table candidate + * @return true if valid, false otherwise + */ +static bool validate_configuration_table_header(const mp_conf_table_t *table) { + if(strncmp(table->signature, MP_CONF_TABLE_SIGNATURE, sizeof(table->signature)) != 0) { + return false; + } + + if(table->base_length < sizeof(*table) || table->base_length > MAXIMUM_TABLE_SIZE) { + return false; + } + + return table->revision == MP_REVISION_V1_1 || table->revision == MP_REVISION_V1_4; +} + +/** + * Scan a range of physical memory to find the floating pointer structure + * + * The start and end addresses must both be aligned on a 16-byte boundary. + * + * @param from start address of scan + * @param to end address of scan + * @return physical address of structure, PADDR_NULL if not found + */ +static uint32_t scan_address_range(uint32_t from, uint32_t to) { + for(uint32_t addr = from; addr < to; addr += 16) { + /* At the stage of the boot process where this function is called, the + * memory where the floating pointer structure can be located is mapped + * 1:1 so a pointer to the structure has the same value as its physical + * address.*/ + const mp_ptr_struct_t *ptrst = (const mp_ptr_struct_t *)addr; + + if(validate_pointer_structure(ptrst)) { + return addr; + } + } + + return PADDR_NULL; +} + +/** + * Scan memory for the floating pointer structure + * + * The ranges where the structures can be located are defined in section 4 of + * the Multiprocessor Specification: + * + * " a. In the first kilobyte of Extended BIOS Data Area (EBDA), or + * b. Within the last kilobyte of system base memory (e.g., 639K-640K for + * systems with 640 KB of base memory or 511K-512K for systems with 512 KB + * of base memory) if the EBDA segment is undefined, or + * c. In the BIOS ROM address space between 0F0000h and 0FFFFFh. " + * + * @return physical address of structure, PADDR_NULL if not found + */ +static uint32_t scan_for_pointer_structure(void) { + uint32_t ebda = get_bios_ebda_addr(); + + if(ebda != 0) { + uint32_t ptrst = scan_address_range(ebda, ebda + KB); + + if(ptrst != PADDR_NULL) { + return ptrst; + } + } else { + uint32_t ptrst = PADDR_NULL; + uint32_t memtop = get_bios_base_memory_size(); + + if(memtop != 0) { + ptrst = scan_address_range(memtop - KB, memtop); + } + + if(ptrst != PADDR_NULL) { + return ptrst; + } + } + + /* TODO define some PC address map somewhere, use in VGA driver as well */ + return scan_address_range(0xf0000, 0x100000); +} + +/** + * Find the floating pointer structure in memory + * + * At the stage of the boot process where this function is called, the memory + * where the floating pointer structure can be located has to be mapped 1:1 so + * a pointer to the floating pointer structure has the same value as its + * physical address. + * + * @param paddr physical address of the structure, PADDR_NULL if not found + */ +void find_mp(void) { + mp.ptrst_paddr = scan_for_pointer_structure(); +} + +/** + * Map the floating pointer structure + * + * @param paddr physical address of the structure, PADDR_NULL if not found + */ +static const mp_ptr_struct_t *map_pointer_structure(uint32_t paddr) { + if(paddr == PADDR_NULL) { + return NULL; + } + + return map_in_kernel( + paddr, + sizeof(mp_ptr_struct_t), + JINUE_PROT_READ + ); +} + +/** + * Map the configuration table + * + * @param ptrst pointer to mapped floating pointer structure, NULL if not found + */ +static const mp_conf_table_t *map_configuration_table(const mp_ptr_struct_t *ptrst) { + if(ptrst == NULL || ptrst->addr == 0) { + return NULL; + } + + const mp_conf_table_t *table = map_in_kernel( + ptrst->addr, + sizeof(mp_conf_table_t), + JINUE_PROT_READ + ); + + if(! validate_configuration_table_header(table)) { + undo_map_in_kernel(); + return NULL; + } + + resize_map_in_kernel(table->base_length); + + if(! verify_acpi_checksum(table, table->base_length)) { + undo_map_in_kernel(); + return NULL; + } + + return table; +} + +/** + * Log information regarding Multiprocessor Specification (MP) data structures + */ +static void log_mp_info(void) { + info("Multiprocessor Specification (MP):"); + + const char *float_ptr = "Floating pointer structure"; + + if(mp.ptrst_paddr == PADDR_NULL) { + info(" %s not found", float_ptr); + } else { + info(" %s found at address %#" PRIx32, float_ptr, mp.ptrst_paddr); + } + + if(mp.table != NULL) { + info( + " Configuration table version 1.%d at address %#" PRIx32, + mp.table->revision == MP_REVISION_V1_1 ? 1 : 4, + mp.ptrst->addr + ); + } +} + +/** + * Map Intel Multiprocessor Specification (MP) data structures + */ +void init_mp(void) { + mp.ptrst = map_pointer_structure(mp.ptrst_paddr); + mp.table = map_configuration_table(mp.ptrst); + log_mp_info(); +} diff --git a/kernel/infrastructure/i686/init.c b/kernel/infrastructure/i686/init.c index 3f2e209b..092dfc74 100644 --- a/kernel/infrastructure/i686/init.c +++ b/kernel/infrastructure/i686/init.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -331,8 +332,9 @@ void machine_init(const config_t *config) { load_selectors(cpu_data); /* We must look for the ACPI RDSP while the relevant memory is still - * identity mapped before we swith to the initial address space. */ + * identity mapped before we switch to the initial address space. */ find_acpi_rsdp(); + find_mp(); /* This must be done before we switch to the new address space because only * the boot allocator can allocate multiple consecutive pages. */ @@ -359,6 +361,7 @@ void machine_init(const config_t *config) { initialize_page_allocator(&boot_alloc); init_acpi(); + init_mp(); /* create slab cache to allocate PDPTs *