diff --git a/repos/base-hw/src/bootstrap/platform.h b/repos/base-hw/src/bootstrap/platform.h index 8fb1b5743f..0807840766 100644 --- a/repos/base-hw/src/bootstrap/platform.h +++ b/repos/base-hw/src/bootstrap/platform.h @@ -16,6 +16,7 @@ #include #include +#include #include #include diff --git a/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc b/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc index 8ec2e444ac..8e3db77147 100644 --- a/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc +++ b/repos/base-hw/src/bootstrap/spec/x86_64/platform.cc @@ -170,6 +170,176 @@ static void amd_enable_serializing_lfence() } +static inline void memory_add_region(addr_t base, addr_t size, + Hw::Memory_region_array &early, + Hw::Memory_region_array &late) +{ + using namespace Hw; + + static constexpr size_t initial_map_max = 1024 * 1024 * 1024; + + /* + * Exclude first physical page, so that it will become part of the + * MMIO allocator. The framebuffer requests this page as MMIO. + */ + if (base == 0 && size >= get_page_size()) { + base = get_page_size(); + size -= get_page_size(); + } + + /* exclude AP boot code page from normal RAM allocator */ + if (base <= AP_BOOT_CODE_PAGE && AP_BOOT_CODE_PAGE < base + size) { + if (AP_BOOT_CODE_PAGE - base) + early.add(Memory_region { base, AP_BOOT_CODE_PAGE - base }); + + size -= AP_BOOT_CODE_PAGE - base; + size -= (get_page_size() > size) ? size : get_page_size(); + base = AP_BOOT_CODE_PAGE + get_page_size(); + } + + /* skip partial 4k pages (seen with Qemu with ahci model enabled) */ + if (!aligned(base, 12)) { + auto new_base = align_addr(base, 12); + size -= (new_base - base > size) ? size : new_base - base; + base = new_base; + } + + /* remove partial 4k pages */ + if (!aligned(size, 12)) + size -= size & 0xffful; + + if (!size) + return; + + if (base >= initial_map_max) { + late.add(Memory_region { base, size }); + return; + } + + if (base + size <= initial_map_max) { + early.add(Memory_region { base, size }); + return; + } + + size_t low_size = initial_map_max - base; + early.add(Memory_region { base, low_size }); + late.add(Memory_region { initial_map_max, size - low_size }); +} + + +static inline void parse_multi_boot_1(::Board::Boot_info &info, + Hw::Memory_region_array &early, + Hw::Memory_region_array &late) +{ + for (unsigned i = 0; true; i++) { + using Mmap = Multiboot_info::Mmap; + + Mmap v(Multiboot_info(__initial_bx).phys_ram_mmap_base(i)); + if (!v.base()) break; + + Mmap::Addr::access_t base = v.read(); + Mmap::Length::access_t size = v.read(); + + memory_add_region(base, size, early, late); + } + + /* search ACPI RSDP pointer at known places */ + + /* BIOS range to scan for */ + enum { BIOS_BASE = 0xe0000, BIOS_SIZE = 0x20000 }; + info.acpi_rsdp = search_rsdp(BIOS_BASE, BIOS_SIZE); + + if (!info.acpi_rsdp.valid()) { + /* page 0 is remapped to 2M - 4k - see crt_translation table */ + addr_t const bios_addr = 2 * 1024 * 1024 - 4096; + + /* search EBDA (BIOS addr + 0x40e) */ + addr_t ebda_phys = (*reinterpret_cast(bios_addr + 0x40e)) << 4; + if (ebda_phys < 0x1000) + ebda_phys = bios_addr; + + info.acpi_rsdp = search_rsdp(ebda_phys, 0x1000 /* EBDA size */); + } +} + + +static inline void parse_multi_boot_2(::Board::Boot_info &info, + Hw::Memory_region_array &early, + Hw::Memory_region_array &late) +{ + Multiboot2_info mbi2(__initial_bx); + + mbi2.for_each_tag([&] (Multiboot2_info::Memory const & m) { + uint32_t const type = m.read(); + + if (type != Multiboot2_info::Memory::Type::MEMORY) + return; + + uint64_t const base = m.read(); + uint64_t const size = m.read(); + + memory_add_region(base, size, early, late); + }, + [&] (Hw::Acpi_rsdp const &rsdp_v1) { + /* only use ACPI RSDP v1 if nothing available/valid by now */ + if (!info.acpi_rsdp.valid()) + info.acpi_rsdp = rsdp_v1; + }, + [&] (Hw::Acpi_rsdp const &rsdp_v2) { + /* prefer v2 ever, override stored previous rsdp v1 potentially */ + info.acpi_rsdp = rsdp_v2; + }, + [&] (Hw::Framebuffer const &fb) { + info.framebuffer = fb; + }, + [&] (uint64_t const efi_sys_tab) { + info.efi_system_table = efi_sys_tab; + }); +} + + +static inline void acpi_system_description_table(Hw::Acpi_rsdp & acpi_rsdp, + auto const &per_table) +{ + if (!acpi_rsdp.valid()) + return; + + uint64_t const table_addr = acpi_rsdp.xsdt ? acpi_rsdp.xsdt : acpi_rsdp.rsdt; + if (!table_addr) + return; + + auto lambda = [&] (auto paddr_table) { + addr_t const table_virt_addr = paddr_table; + per_table(*reinterpret_cast(table_virt_addr), + paddr_table); + }; + + Hw::Acpi_generic &table = *reinterpret_cast(table_addr); + if (!memcmp(table.signature, "RSDT", 4)) { + Hw::for_each_rsdt_entry(table, lambda); + } else if (!memcmp(table.signature, "XSDT", 4)) { + Hw::for_each_xsdt_entry(table, lambda); + } +} + + +static inline void for_each_cpu(Hw::Acpi_rsdp & acpi_rsdp, + auto const &per_cpu_fn) +{ + auto lambda = [&] (Hw::Acpi_generic &table, uint64_t) { + Hw::for_each_apic_struct(table,[&](Hw::Apic_madt const *e) { + if (e->type != Hw::Apic_madt::LAPIC) + return; + + /* check if APIC is enabled in hardware */ + Hw::Apic_madt::Lapic lapic(e); + if (lapic.valid()) per_cpu_fn(lapic.id()); + }); + }; + acpi_system_description_table(acpi_rsdp, lambda); +} + + Bootstrap::Platform::Board::Board() : core_mmio(Memory_region { 0, 0x1000 }, @@ -179,179 +349,51 @@ Bootstrap::Platform::Board::Board() Memory_region { __initial_bx & ~0xFFFUL, get_page_size() }) { - Hw::Acpi_rsdp & acpi_rsdp = info.acpi_rsdp; - static constexpr size_t initial_map_max = 1024 * 1024 * 1024; - - auto lambda = [&] (addr_t base, addr_t size) { - - /* - * Exclude first physical page, so that it will become part of the - * MMIO allocator. The framebuffer requests this page as MMIO. - */ - if (base == 0 && size >= get_page_size()) { - base = get_page_size(); - size -= get_page_size(); - } - - /* exclude AP boot code page from normal RAM allocator */ - if (base <= AP_BOOT_CODE_PAGE && AP_BOOT_CODE_PAGE < base + size) { - if (AP_BOOT_CODE_PAGE - base) - early_ram_regions.add(Memory_region { base, - AP_BOOT_CODE_PAGE - base }); - - size -= AP_BOOT_CODE_PAGE - base; - size -= (get_page_size() > size) ? size : get_page_size(); - base = AP_BOOT_CODE_PAGE + get_page_size(); - } - - /* skip partial 4k pages (seen with Qemu with ahci model enabled) */ - if (!aligned(base, 12)) { - auto new_base = align_addr(base, 12); - size -= (new_base - base > size) ? size : new_base - base; - base = new_base; - } - - /* remove partial 4k pages */ - if (!aligned(size, 12)) - size -= size & 0xffful; - - if (!size) - return; - - if (base >= initial_map_max) { - late_ram_regions.add(Memory_region { base, size }); - return; - } - - if (base + size <= initial_map_max) { - early_ram_regions.add(Memory_region { base, size }); - return; - } - - size_t low_size = initial_map_max - base; - early_ram_regions.add(Memory_region { base, low_size }); - late_ram_regions.add(Memory_region { initial_map_max, size - low_size }); + switch (__initial_ax) { + case Multiboot_info::MAGIC: + parse_multi_boot_1(info, early_ram_regions, late_ram_regions); + break; + case Multiboot2_info::MAGIC: + parse_multi_boot_2(info, early_ram_regions, late_ram_regions); + break; + default: + error("invalid multiboot magic value: ", Hex(__initial_ax)); }; - if (__initial_ax == Multiboot2_info::MAGIC) { - Multiboot2_info mbi2(__initial_bx); + /* scan ACPI tables for FADT */ + auto lambda = [&] (Hw::Acpi_generic &table, uint64_t paddr) { - mbi2.for_each_tag([&] (Multiboot2_info::Memory const & m) { - uint32_t const type = m.read(); + /* its signature is called FACP */ + if (memcmp(table.signature, "FACP", 4)) + return; - if (type != Multiboot2_info::Memory::Type::MEMORY) - return; + info.acpi_fadt = addr_t(&table); - uint64_t const base = m.read(); - uint64_t const size = m.read(); + /* show firmware that we're an ACPI-aware OS */ + Hw::Acpi_fadt fadt(&table); + fadt.takeover_acpi(); - lambda(base, size); - }, - [&] (Hw::Acpi_rsdp const &rsdp_v1) { - /* only use ACPI RSDP v1 if nothing available/valid by now */ - if (!acpi_rsdp.valid()) - acpi_rsdp = rsdp_v1; - }, - [&] (Hw::Acpi_rsdp const &rsdp_v2) { - /* prefer v2 ever, override stored previous rsdp v1 potentially */ - acpi_rsdp = rsdp_v2; - }, - [&] (Hw::Framebuffer const &fb) { - info.framebuffer = fb; - }, - [&] (uint64_t const efi_sys_tab) { - info.efi_system_table = efi_sys_tab; - }); - } else if (__initial_ax == Multiboot_info::MAGIC) { - for (unsigned i = 0; true; i++) { - using Mmap = Multiboot_info::Mmap; + Hw::Acpi_facs facs(fadt.facs()); + facs.wakeup_vector(AP_BOOT_CODE_PAGE); - Mmap v(Multiboot_info(__initial_bx).phys_ram_mmap_base(i)); - if (!v.base()) break; + auto mem_aligned = paddr & _align_mask(12); + auto mem_size = align_addr(paddr + table.size, 12) - mem_aligned; + core_mmio.add({ mem_aligned, mem_size }); + }; + acpi_system_description_table(info.acpi_rsdp, lambda); - Mmap::Addr::access_t base = v.read(); - Mmap::Length::access_t size = v.read(); - - lambda(base, size); - } - - /* search ACPI RSDP pointer at known places */ - - /* BIOS range to scan for */ - enum { BIOS_BASE = 0xe0000, BIOS_SIZE = 0x20000 }; - acpi_rsdp = search_rsdp(BIOS_BASE, BIOS_SIZE); - - if (!acpi_rsdp.valid()) { - /* page 0 is remapped to 2M - 4k - see crt_translation table */ - addr_t const bios_addr = 2 * 1024 * 1024 - 4096; - - /* search EBDA (BIOS addr + 0x40e) */ - addr_t ebda_phys = (*reinterpret_cast(bios_addr + 0x40e)) << 4; - if (ebda_phys < 0x1000) - ebda_phys = bios_addr; - - acpi_rsdp = search_rsdp(ebda_phys, 0x1000 /* EBDA size */); - } - } else { - error("invalid multiboot magic value: ", Hex(__initial_ax)); - } - - /* remember max supported CPUs and use ACPI to get the actual number */ - unsigned const max_cpus = - Hw::Mm::CPU_LOCAL_MEMORY_AREA_SIZE / Hw::Mm::CPU_LOCAL_MEMORY_SLOT_SIZE; + /* get the actual number of cpus */ + static constexpr unsigned MAX_CPUS = + Hw::Mm::CPU_LOCAL_MEMORY_AREA_SIZE / + Hw::Mm::CPU_LOCAL_MEMORY_SLOT_SIZE; cpus = 0; + for_each_cpu(info.acpi_rsdp, [this] (unsigned) { cpus++; }); - /* scan ACPI tables to find out number of CPUs in this machine */ - if (acpi_rsdp.valid()) { - uint64_t const table_addr = acpi_rsdp.xsdt ? acpi_rsdp.xsdt : acpi_rsdp.rsdt; - - if (table_addr) { - auto rsdt_xsdt_lambda = [&](auto paddr_table) { - addr_t const table_virt_addr = paddr_table; - Hw::Acpi_generic * table = reinterpret_cast(table_virt_addr); - - if (!memcmp(table->signature, "FACP", 4)) { - info.acpi_fadt = addr_t(table); - - Hw::Acpi_fadt fadt(table); - fadt.takeover_acpi(); - - Hw::Acpi_facs facs(fadt.facs()); - facs.wakeup_vector(AP_BOOT_CODE_PAGE); - - auto mem_aligned = paddr_table & _align_mask(12); - auto mem_size = align_addr(paddr_table + table->size, 12) - mem_aligned; - core_mmio.add({ mem_aligned, mem_size }); - } - - if (memcmp(table->signature, "APIC", 4)) - return; - - Hw::for_each_apic_struct(*table,[&](Hw::Apic_madt const *e){ - if (e->type == Hw::Apic_madt::LAPIC) { - Hw::Apic_madt::Lapic lapic(e); - - /* check if APIC is enabled in hardware */ - if (lapic.valid()) - cpus ++; - } - }); - }; - - auto table = reinterpret_cast(table_addr); - if (!memcmp(table->signature, "RSDT", 4)) { - Hw::for_each_rsdt_entry(*table, rsdt_xsdt_lambda); - } else if (!memcmp(table->signature, "XSDT", 4)) { - Hw::for_each_xsdt_entry(*table, rsdt_xsdt_lambda); - } - } - } - - if (!cpus || cpus > max_cpus) { - Genode::warning("CPU count is unsupported ", cpus, "/", max_cpus, - acpi_rsdp.valid() ? " - invalid or missing RSDT/XSDT" - : " - invalid RSDP"); - cpus = !cpus ? 1 : max_cpus; + if (!cpus || cpus > MAX_CPUS) { + Genode::warning("CPU count is unsupported ", cpus, "/", MAX_CPUS, + info.acpi_rsdp.valid() ? " - invalid or missing RSDT/XSDT" + : " - invalid RSDP"); + cpus = !cpus ? 1 : MAX_CPUS; } /* @@ -473,9 +515,7 @@ Board::Serial::Serial(addr_t, size_t, unsigned baudrate) unsigned Bootstrap::Platform::_prepare_cpu_memory_area() { - using namespace Genode; - - for (size_t id = 0; id < board.cpus; id++) - _prepare_cpu_memory_area(id); + for_each_cpu(board.info.acpi_rsdp, [this] (unsigned id) { + _prepare_cpu_memory_area(id); }); return board.cpus; }