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 new file mode 100644 index 00000000..8780e733 --- /dev/null +++ b/include/kernel/infrastructure/i686/firmware/asm/mp.h @@ -0,0 +1,125 @@ +/* + * 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. + */ + +#ifndef JINUE_KERNEL_INFRASTRUCTURE_I686_FIRMWARE_ASM_MP_H +#define JINUE_KERNEL_INFRASTRUCTURE_I686_FIRMWARE_ASM_MP_H + +/* Multiprocessor Specification 1.4 section 4.1 MP Floating Pointer + * Structure */ + +#define MP_FLOATING_PTR_SIGNATURE "_MP_" + + +/* Table 4-1. MP Floating Pointer Structure Fields, flags in the MP Feature + * Information byte 2 field. Bit 6 is defined in Appendix E Errata. */ + +#define MP_FEATURE2_IMCRP (1 << 7) + +#define MP_FEATURE2_MULTIPLE_CLK_SOURCES (1 << 6) + +/* Multiprocessor Specification 1.4 section 4.2 MP Configuration Table + * Header */ + +#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 */ + +#define MP_ENTRY_TYPE_PROCESSOR 0 + +#define MP_ENTRY_TYPE_BUS 1 + +#define MP_ENTRY_TYPE_IO_APIC 2 + +#define MP_ENTRY_TYPE_IO_INTR 3 + +#define MP_ENTRY_TYPE_LOCAL_INTR 4 + +/* Multiprocessor Specification 1.4 Table 4-8 Bus Type String Values */ + +#define MP_BUS_TYPE_EISA "EISA" + +#define MP_BUS_TYPE_ISA "ISA" + +#define MP_BUS_TYPE_PCI "PCI" + +/* Multiprocessor Specification 1.4 section 4.3.3 I/O APIC Entries */ + +#define MP_IO_API_FLAG_EN (1 << 0) + +/* Multiprocessor Specification 1.4 Table 4-10 I/O Interrupt Entry Fields */ + +#define MP_PO_BUS_DEFAULT 0 + +#define MP_PO_ACTIVE_HIGH 1 + +#define MP_PO_ACTIVE_LOW 3 + + +#define MP_EL_BUS_DEFAULT (0 << 2) + +#define MP_EL_EDGE (1 << 2) + +#define MP_EL_LEVEL (2 << 2) + +/* Multiprocessor Specification 1.4 Table 4-11 Interrupt Type Values */ + +#define MP_INTR_TYPE_INT 0 + +#define MP_INTR_TYPE_NMI 1 + +#define MP_INTR_TYPE_SMI 2 + +#define MP_INTR_TYPE_EXTINT 3 + +/* Multiprocessor Specification 1.4 Table 5-1 Default Configurations */ + +#define MP_NO_DEFAULT 0 + +#define MP_DEFAULT_ISA 1 + +#define MP_DEFAULT_EISA_NO_IRQ0 2 + +#define MP_DEFAULT_EISA 3 + +#define MP_DEFAULT_MCA 4 + +#define MP_DEFAULT_ISA_PCI_APIC 5 + +#define MP_DEFAULT_EISA_PIC_APIC 6 + +#define MP_DEFAULT_MCA_PCI_APIC 7 + +#endif diff --git a/include/kernel/infrastructure/i686/firmware/bios.h b/include/kernel/infrastructure/i686/firmware/bios.h new file mode 100644 index 00000000..a9d02b69 --- /dev/null +++ b/include/kernel/infrastructure/i686/firmware/bios.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef JINUE_KERNEL_INFRASTRUCTURE_I686_FIRMWARE_BIOS_H +#define JINUE_KERNEL_INFRASTRUCTURE_I686_FIRMWARE_BIOS_H + +#include +#include +#include + +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 new file mode 100644 index 00000000..e7cb1e9d --- /dev/null +++ b/include/kernel/infrastructure/i686/firmware/mp.h @@ -0,0 +1,120 @@ +/* + * 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. + */ + +#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 + * Structure */ + +typedef struct { + char signature[4]; + uint32_t addr; + uint8_t length; + uint8_t revision; + uint8_t checksum; + uint8_t feature1; + uint8_t feature2; + uint8_t feature_reserved[3]; +} mp_ptr_struct_t; + +/* Multiprocessor Specification 1.4 section 4.2 MP Configuration Table + * Header */ + +typedef struct { + char signature[4]; + uint16_t base_length; + uint8_t revision; + uint8_t checksum; + char oem_id[8]; + char product_id[12]; + uint32_t oem_table_addr; + uint16_t oem_table_size; + uint16_t entry_count; + uint32_t lapic_addr; + uint16_t ext_table_length; + uint8_t ext_table_checksum; + uint8_t reserved; + const char entries[]; +} mp_conf_table_t; + +/* Multiprocessor Specification 1.4 section 4.3.1 Processor Entries */ + +typedef struct { + uint8_t entry_type; + uint8_t apic_id; + uint8_t apic_version; + uint8_t cpu_flags; + uint32_t cpu_signature; + uint32_t feature_flags; + uint32_t reserved1; + uint32_t reserved2; +} mp_entry_processor_t; + +/* Multiprocessor Specification 1.4 section 4.3.2 Bus Entries */ + +typedef struct { + uint8_t entry_type; + uint8_t bus_id; + char bus_type[6]; +} mp_entry_bus_t; + +/* Multiprocessor Specification 1.4 section 4.3.3 I/O APIC Entries */ + +typedef struct { + uint8_t entry_type; + uint8_t apic_id; + uint8_t apic_version; + uint8_t flag; + uint32_t addr; +} mp_entry_ioapic_t; + +/* Multiprocessor Specification 1.4 section 4.3.4 I/O Interrupt Assignment + * Entries and 4.3.5 Local Interrupt Assignment Entries */ + +typedef struct { + uint8_t entry_type; + uint8_t intr_type; + uint16_t io_intr_flag; + uint8_t source_bus_id; + uint8_t source_bus_irq; + uint8_t dest_apic_id; + 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 a341cf3e..5632ad33 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -107,6 +107,8 @@ sources.kernel.c = \ infrastructure/i686/drivers/uart16550a.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 a9c98d88..c7f63ab5 100644 --- a/kernel/infrastructure/i686/firmware/acpi.c +++ b/kernel/infrastructure/i686/firmware/acpi.c @@ -32,9 +32,10 @@ #include #include #include -#include #include +#include #include +#include #include #include #include @@ -78,15 +79,14 @@ static const acpi_rsdp_t *find_rsdp(void) { } } - const char *bottom = (const char *)0x10000; - const char *top = (const char *)(0xa0000 - 1024); - const char *ebda = (const char *)(16 * (*(uint16_t *)BIOS_BDA_EBDA_SEGMENT)); + const char *top = (const char *)(0xa0000 - KB); + const char *ebda = (const char *)get_bios_ebda_addr(); - if(ebda < bottom || ebda > top) { + 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 new file mode 100644 index 00000000..5d6554b9 --- /dev/null +++ b/kernel/infrastructure/i686/firmware/bios.c @@ -0,0 +1,74 @@ +/* + * 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 + +/** + * Get address of the Extended BIOS Data Area (EBDA) + * + * The returned address is guaranteed to be aligned on a 16-byte boundary. This + * information is read from the Bios Data Area (BDA). This function must be + * called early in the boot process while conventional memory is still mapped + * 1:1 in virtual memory. + * + * @return address of EBDA, 0 for none or if it could not be determined + */ +uint32_t get_bios_ebda_addr(void) { + uintptr_t ebda = 16 * (*(uint16_t *)BIOS_BDA_EBDA_SEGMENT); + + if(ebda < 0x80000 || ebda >= 0xa0000) { + return NULL; + } + + return ebda; +} + +/** + * Get the base (aka. conventional) memory size from the BIOS + * + * The returned size is guaranteed to be a multiple of 1 kB. This information + * is read from the Bios Data Area (BDA). This function must be called early in + * the boot process while conventional memory is still mapped 1:1 in virtual + * memory. + * + * @return base memory size, 0 if it could not be determined + */ +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..1f906896 --- /dev/null +++ b/kernel/infrastructure/i686/firmware/mp.c @@ -0,0 +1,254 @@ +/* + * 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; + } + } + + 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 report_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); + report_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 * diff --git a/tests/Makefile b/tests/Makefile index c3ae361f..3de29d9b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -40,6 +40,7 @@ tests = \ test_detect_qemu \ test_boot_nopae \ test_boot_pae \ + test_mp \ test_ipc logs := $(foreach t,$(tests),$(t).log) diff --git a/tests/test_mp.sh b/tests/test_mp.sh new file mode 100755 index 00000000..93b7285d --- /dev/null +++ b/tests/test_mp.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# 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. + +run + +check_kernel_start + +# If check_no_panic, check_no_error would also fail, but check_no_panic provides +# more relevant context in the log. +check_no_panic + +check_no_error + +echo "* Check floating pointer structure was found" +grep -E "Floating pointer structure found at address 0x[1-9a-f][0-9a-f]{4}" $LOG || fail + +echo "* Check configuration table was found and mapped" +grep -E "Configuration table version 1.[14] at address 0x[1-9a-f][0-9a-f]{4}" $LOG || fail + +check_reboot