diff --git a/Kernel/Interrupts/APIC.cpp b/Kernel/Interrupts/APIC.cpp index ca1a4dcd72..4efc9d007b 100644 --- a/Kernel/Interrupts/APIC.cpp +++ b/Kernel/Interrupts/APIC.cpp @@ -322,8 +322,9 @@ UNMAP_AFTER_INIT static NonnullOwnPtr create_identity_mapped_reg return region_or_error.release_value(); } -UNMAP_AFTER_INIT void APIC::do_boot_aps() +UNMAP_AFTER_INIT void APIC::setup_ap_boot_environment() { + VERIFY(!m_ap_boot_environment); VERIFY(m_processor_enabled_cnt > 1); u32 aps_to_enable = m_processor_enabled_cnt - 1; @@ -331,11 +332,12 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps() // Also account for the data appended to: // * aps_to_enable u32 values for ap_cpu_init_stacks // * aps_to_enable u32 values for ap_cpu_init_processor_info_array - auto apic_startup_region = create_identity_mapped_region(PhysicalAddress(0x8000), Memory::page_round_up(apic_ap_start_size + (2 * aps_to_enable * sizeof(u32)))); + constexpr u64 apic_startup_region_base = 0x8000; + auto apic_startup_region = create_identity_mapped_region(PhysicalAddress(apic_startup_region_base), Memory::page_round_up(apic_ap_start_size + (2 * aps_to_enable * sizeof(u32)))); memcpy(apic_startup_region->vaddr().as_ptr(), reinterpret_cast(apic_ap_start), apic_ap_start_size); // Allocate enough stacks for all APs - Vector> apic_ap_stacks; + m_ap_temporary_boot_stacks.ensure_capacity(aps_to_enable); for (u32 i = 0; i < aps_to_enable; i++) { auto stack_region_or_error = MM.allocate_kernel_region(Thread::default_kernel_stack_size, {}, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow); if (stack_region_or_error.is_error()) { @@ -344,14 +346,14 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps() } auto stack_region = stack_region_or_error.release_value(); stack_region->set_stack(true); - apic_ap_stacks.append(move(stack_region)); + m_ap_temporary_boot_stacks.unchecked_append(move(stack_region)); } // Store pointers to all stacks for the APs to use - auto ap_stack_array = APIC_INIT_VAR_PTR(u32, apic_startup_region->vaddr().as_ptr(), ap_cpu_init_stacks); - VERIFY(aps_to_enable == apic_ap_stacks.size()); + auto* ap_stack_array = APIC_INIT_VAR_PTR(u32, apic_startup_region->vaddr().as_ptr(), ap_cpu_init_stacks); + VERIFY(aps_to_enable == m_ap_temporary_boot_stacks.size()); for (size_t i = 0; i < aps_to_enable; i++) { - ap_stack_array[i] = apic_ap_stacks[i]->vaddr().get() + Thread::default_kernel_stack_size; + ap_stack_array[i] = m_ap_temporary_boot_stacks[i]->vaddr().get() + Thread::default_kernel_stack_size; dbgln_if(APIC_DEBUG, "APIC: CPU[{}] stack at {}", i + 1, VirtualAddress { ap_stack_array[i] }); } @@ -359,7 +361,7 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps() m_ap_processor_info.resize(aps_to_enable); for (size_t i = 0; i < aps_to_enable; i++) m_ap_processor_info[i] = make(); - auto ap_processor_info_array = &ap_stack_array[aps_to_enable]; + auto* ap_processor_info_array = &ap_stack_array[aps_to_enable]; for (size_t i = 0; i < aps_to_enable; i++) { ap_processor_info_array[i] = FlatPtr(m_ap_processor_info[i].ptr()); dbgln_if(APIC_DEBUG, "APIC: CPU[{}] processor at {}", i + 1, VirtualAddress { ap_processor_info_array[i] }); @@ -379,6 +381,15 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps() *APIC_INIT_VAR_PTR(u32, apic_startup_region->vaddr().as_ptr(), ap_cpu_init_cr0) = read_cr0(); *APIC_INIT_VAR_PTR(u32, apic_startup_region->vaddr().as_ptr(), ap_cpu_init_cr4) = read_cr4(); + m_ap_boot_environment = move(apic_startup_region); +} + +UNMAP_AFTER_INIT void APIC::do_boot_aps() +{ + VERIFY(m_ap_boot_environment); + VERIFY(m_processor_enabled_cnt > 1); + u32 aps_to_enable = m_processor_enabled_cnt - 1; + // Create an idle thread for each processor. We have to do this here // because we won't be able to send FlushTLB messages, so we have to // have all memory set up for the threads so that when the APs are @@ -414,7 +425,11 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps() // NOTE: Since this region is identity-mapped, we have to unmap it manually to prevent the virtual // address range from leaking into the general virtual range allocator. - apic_startup_region->unmap(Memory::Region::ShouldDeallocateVirtualRange::No); + m_ap_boot_environment->unmap(Memory::Region::ShouldDeallocateVirtualRange::No); + m_ap_boot_environment = nullptr; + // When the APs signal that they finished their initialization they have already switched over to their + // idle thread's stack, so the temporary boot stack can be deallocated + m_ap_temporary_boot_stacks.clear(); } UNMAP_AFTER_INIT void APIC::boot_aps() diff --git a/Kernel/Interrupts/APIC.h b/Kernel/Interrupts/APIC.h index 60f56333c0..adcc64f6b8 100644 --- a/Kernel/Interrupts/APIC.h +++ b/Kernel/Interrupts/APIC.h @@ -26,6 +26,7 @@ public: bool init_bsp(); void eoi(); + void setup_ap_boot_environment(); void boot_aps(); void enable(u32 cpu); void init_finished(u32 cpu); @@ -90,7 +91,9 @@ private: OwnPtr m_apic_base; Vector> m_ap_processor_info; + Vector> m_ap_temporary_boot_stacks; Vector m_ap_idle_threads; + OwnPtr m_ap_boot_environment; Atomic m_apic_ap_count { 0 }; Atomic m_apic_ap_continue { 0 }; u32 m_processor_cnt { 0 }; diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 700e166752..fee2ec1af9 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -216,6 +216,13 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init(BootInfo const& boot_info) Scheduler::initialize(); + if (APIC::initialized() && APIC::the().enabled_processor_count() > 1) { + // We must set up the AP boot environment before switching to a kernel process, + // as pages below address USER_RANGE_BASE are only accesible through the kernel + // page directory. + APIC::the().setup_ap_boot_environment(); + } + dmesgln("Starting SerenityOS..."); {