diff --git a/arch/x86_64/src/acpi/mod.rs b/arch/x86_64/src/acpi/mod.rs index f35f351..eec67cf 100644 --- a/arch/x86_64/src/acpi/mod.rs +++ b/arch/x86_64/src/acpi/mod.rs @@ -1,6 +1,8 @@ //! # ACPI //! Code to parse the ACPI tables +use core::intrinsics::{atomic_load, atomic_store}; + use memory::{Frame, FrameAllocator}; use paging::{entry, ActivePageTable, Page, PhysicalAddress, VirtualAddress}; @@ -16,6 +18,9 @@ pub mod rsdt; pub mod sdt; pub mod xsdt; +const TRAMPOLINE: usize = 0x7E00; +const AP_STARTUP: usize = 0x8000; + pub fn init_sdt(sdt: &'static Sdt) { print!(" "); for &c in sdt.signature.iter() { @@ -37,16 +42,33 @@ pub fn init_sdt(sdt: &'static Sdt) { println!(" This is my local APIC"); } else { if asp_local_apic.flags & 1 == 1 { + let ap_ready = TRAMPOLINE as *mut u64; + let ap_stack = unsafe { ap_ready.offset(1) }; + + // Set the ap_ready to 0, volatile + unsafe { atomic_store(ap_ready, 0) }; + + // Send INIT IPI { let icr = 0x00004500 | (asp_local_apic.id as u64) << 32; println!(" Sending IPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr)); local_apic.set_icr(icr); } + + // Send START IPI { - let icr = 0x00004600 | (asp_local_apic.id as u64) << 32; + let ap_segment = (AP_STARTUP >> 12) & 0xFF; + let icr = 0x00004600 | ((asp_local_apic.id as u64) << 32) | ap_segment as u64; //Start at 0x0800:0000 => 0x8000. Hopefully the bootloader code is still there println!(" Sending SIPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr)); local_apic.set_icr(icr); } + + // Wait for trampoline ready + println!(" Waiting for AP {}", asp_local_apic.id); + while unsafe { atomic_load(ap_ready) } == 0 { + unsafe { asm!("pause" : : : : "intel", "volatile") }; + } + println!(" AP {} is ready!", asp_local_apic.id); } else { println!(" CPU Disabled"); } @@ -63,15 +85,6 @@ pub fn init_sdt(sdt: &'static Sdt) { pub unsafe fn init(allocator: &mut A, active_table: &mut ActivePageTable) -> Option where A: FrameAllocator { - // Stupidity of enormous proportion. Write the halt opcode to the 0'th physical address - // so that START IPI's can halt the processor - { - if active_table.translate_page(Page::containing_address(VirtualAddress::new(0))).is_none() { - active_table.identity_map(Frame::containing_address(PhysicalAddress::new(0)), entry::PRESENT | entry::WRITABLE, allocator); - } - unsafe { *(0 as *mut u8) = 0xF4 }; - } - let start_addr = 0xE0000; let end_addr = 0xFFFFF; @@ -86,6 +99,13 @@ pub unsafe fn init(allocator: &mut A, active_table: &mut ActivePageTable) -> } } + // Map trampoline + { + if active_table.translate_page(Page::containing_address(VirtualAddress::new(TRAMPOLINE))).is_none() { + active_table.identity_map(Frame::containing_address(PhysicalAddress::new(TRAMPOLINE)), entry::PRESENT | entry::WRITABLE, allocator); + } + } + // Search for RSDP if let Some(rsdp) = RSDP::search(start_addr, end_addr) { println!("{:?}", rsdp); diff --git a/bootloader/x86/startup-x86_64.asm b/bootloader/x86/startup-x86_64.asm index eec7782..8ac8d89 100644 --- a/bootloader/x86/startup-x86_64.asm +++ b/bootloader/x86/startup-x86_64.asm @@ -1,3 +1,45 @@ +trampoline: + .ready: dq 0 + .stack: dq 0 + + times 512 - ($ - trampoline) db 0 + +startup_ap: + cli + + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + + ; initialize stack + mov sp, 0x7C00 + + ;cr3 holds pointer to PML4 + mov edi, 0x70000 + mov cr3, edi + + ;enable Page Address Extension and Page Size Extension + mov eax, cr4 + or eax, 1 << 5 | 1 << 4 + mov cr4, eax + + ; load protected mode GDT + lgdt [gdtr] + + mov ecx, 0xC0000080 ; Read from the EFER MSR. + rdmsr + or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit. + wrmsr + + ;enabling paging and protection simultaneously + mov ebx, cr0 + or ebx, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode + mov cr0, ebx + + ; far jump to enable Long Mode and load CS with 64 bit segment + jmp gdt.kernel_code:long_mode_ap + %include "startup-common.asm" startup_arch: @@ -74,106 +116,118 @@ long_mode: ;rust init xor rax, rax mov eax, [kernel_base + 0x18] - mov rbx, gdtr jmp rax - gdtr: - dw gdt.end + 1 ; size - dq gdt ; offset +long_mode_ap: + mov rax, gdt.kernel_data + mov ds, rax + mov es, rax + mov fs, rax + mov gs, rax + mov ss, rax - gdt: - .null equ $ - gdt - dq 0 + mov qword [trampoline.ready], 1 +.lp: + hlt + jmp .lp - .kernel_code equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0 - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code - at GDTEntry.flags__limith, db flags.long_mode - at GDTEntry.baseh, db 0 +gdtr: + dw gdt.end + 1 ; size + dq gdt ; offset + +gdt: +.null equ $ - gdt + dq 0 + +.kernel_code equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw 0 + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 + at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code + at GDTEntry.flags__limith, db flags.long_mode + at GDTEntry.baseh, db 0 +iend + +.kernel_data equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw 0 + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 +; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it + at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable + at GDTEntry.flags__limith, db 0 + at GDTEntry.baseh, db 0 +iend + +.user_code equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw 0 + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 + at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.code + at GDTEntry.flags__limith, db flags.long_mode + at GDTEntry.baseh, db 0 +iend + +.user_data equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw 0 + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 +; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it + at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable + at GDTEntry.flags__limith, db 0 + at GDTEntry.baseh, db 0 +iend + +.user_tls equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw 0 + at GDTEntry.basel, dw 0 + at GDTEntry.basem, db 0 +; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it + at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable + at GDTEntry.flags__limith, db 0 + at GDTEntry.baseh, db 0 +iend + +.tss equ $ - gdt +istruc GDTEntry + at GDTEntry.limitl, dw (tss.end - tss) & 0xFFFF + at GDTEntry.basel, dw (tss-$$+0x7C00) & 0xFFFF + at GDTEntry.basem, db ((tss-$$+0x7C00) >> 16) & 0xFF + at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.tssAvailabe64 + at GDTEntry.flags__limith, db ((tss.end - tss) >> 16) & 0xF + at GDTEntry.baseh, db ((tss-$$+0x7C00) >> 24) & 0xFF +iend +dq 0 ;tss descriptors are extended to 16 Bytes + +.end equ $ - gdt + +struc TSS + .reserved1 resd 1 ;The previous TSS - if we used hardware task switching this would form a linked list. + .rsp0 resq 1 ;The stack pointer to load when we change to kernel mode. + .rsp1 resq 1 ;everything below here is unused now.. + .rsp2 resq 1 + .reserved2 resd 1 + .reserved3 resd 1 + .ist1 resq 1 + .ist2 resq 1 + .ist3 resq 1 + .ist4 resq 1 + .ist5 resq 1 + .ist6 resq 1 + .ist7 resq 1 + .reserved4 resd 1 + .reserved5 resd 1 + .reserved6 resw 1 + .iomap_base resw 1 +endstruc + +tss: + istruc TSS + at TSS.rsp0, dd 0x800000 - 128 + at TSS.iomap_base, dw 0xFFFF iend - - .kernel_data equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0 - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - ; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it - at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0 - at GDTEntry.baseh, db 0 - iend - - .user_code equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0 - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.code - at GDTEntry.flags__limith, db flags.long_mode - at GDTEntry.baseh, db 0 - iend - - .user_data equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0 - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - ; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0 - at GDTEntry.baseh, db 0 - iend - - .user_tls equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0 - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - ; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0 - at GDTEntry.baseh, db 0 - iend - - .tss equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw (tss.end - tss) & 0xFFFF - at GDTEntry.basel, dw (tss-$$+0x7C00) & 0xFFFF - at GDTEntry.basem, db ((tss-$$+0x7C00) >> 16) & 0xFF - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.tssAvailabe64 - at GDTEntry.flags__limith, db ((tss.end - tss) >> 16) & 0xF - at GDTEntry.baseh, db ((tss-$$+0x7C00) >> 24) & 0xFF - iend - dq 0 ;tss descriptors are extended to 16 Bytes - - .end equ $ - gdt - - struc TSS - .reserved1 resd 1 ;The previous TSS - if we used hardware task switching this would form a linked list. - .rsp0 resq 1 ;The stack pointer to load when we change to kernel mode. - .rsp1 resq 1 ;everything below here is unused now.. - .rsp2 resq 1 - .reserved2 resd 1 - .reserved3 resd 1 - .ist1 resq 1 - .ist2 resq 1 - .ist3 resq 1 - .ist4 resq 1 - .ist5 resq 1 - .ist6 resq 1 - .ist7 resq 1 - .reserved4 resd 1 - .reserved5 resd 1 - .reserved6 resw 1 - .iomap_base resw 1 - endstruc - - tss: - istruc TSS - at TSS.rsp0, dd 0x800000 - 128 - at TSS.iomap_base, dw 0xFFFF - iend - .end: +.end: