Hooks to get the other CPUs into long mode
This commit is contained in:
parent
08900d56c8
commit
a8948fb246
|
@ -1,6 +1,8 @@
|
||||||
//! # ACPI
|
//! # ACPI
|
||||||
//! Code to parse the ACPI tables
|
//! Code to parse the ACPI tables
|
||||||
|
|
||||||
|
use core::intrinsics::{atomic_load, atomic_store};
|
||||||
|
|
||||||
use memory::{Frame, FrameAllocator};
|
use memory::{Frame, FrameAllocator};
|
||||||
use paging::{entry, ActivePageTable, Page, PhysicalAddress, VirtualAddress};
|
use paging::{entry, ActivePageTable, Page, PhysicalAddress, VirtualAddress};
|
||||||
|
|
||||||
|
@ -16,6 +18,9 @@ pub mod rsdt;
|
||||||
pub mod sdt;
|
pub mod sdt;
|
||||||
pub mod xsdt;
|
pub mod xsdt;
|
||||||
|
|
||||||
|
const TRAMPOLINE: usize = 0x7E00;
|
||||||
|
const AP_STARTUP: usize = 0x8000;
|
||||||
|
|
||||||
pub fn init_sdt(sdt: &'static Sdt) {
|
pub fn init_sdt(sdt: &'static Sdt) {
|
||||||
print!(" ");
|
print!(" ");
|
||||||
for &c in sdt.signature.iter() {
|
for &c in sdt.signature.iter() {
|
||||||
|
@ -37,16 +42,33 @@ pub fn init_sdt(sdt: &'static Sdt) {
|
||||||
println!(" This is my local APIC");
|
println!(" This is my local APIC");
|
||||||
} else {
|
} else {
|
||||||
if asp_local_apic.flags & 1 == 1 {
|
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;
|
let icr = 0x00004500 | (asp_local_apic.id as u64) << 32;
|
||||||
println!(" Sending IPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr));
|
println!(" Sending IPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr));
|
||||||
local_apic.set_icr(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));
|
println!(" Sending SIPI to {}: {:>016X} {:?}", asp_local_apic.id, icr, LocalApicIcr::from_bits(icr));
|
||||||
local_apic.set_icr(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 {
|
} else {
|
||||||
println!(" CPU Disabled");
|
println!(" CPU Disabled");
|
||||||
}
|
}
|
||||||
|
@ -63,15 +85,6 @@ pub fn init_sdt(sdt: &'static Sdt) {
|
||||||
pub unsafe fn init<A>(allocator: &mut A, active_table: &mut ActivePageTable) -> Option<Acpi>
|
pub unsafe fn init<A>(allocator: &mut A, active_table: &mut ActivePageTable) -> Option<Acpi>
|
||||||
where A: FrameAllocator
|
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 start_addr = 0xE0000;
|
||||||
let end_addr = 0xFFFFF;
|
let end_addr = 0xFFFFF;
|
||||||
|
|
||||||
|
@ -86,6 +99,13 @@ pub unsafe fn init<A>(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
|
// Search for RSDP
|
||||||
if let Some(rsdp) = RSDP::search(start_addr, end_addr) {
|
if let Some(rsdp) = RSDP::search(start_addr, end_addr) {
|
||||||
println!("{:?}", rsdp);
|
println!("{:?}", rsdp);
|
||||||
|
|
|
@ -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"
|
%include "startup-common.asm"
|
||||||
|
|
||||||
startup_arch:
|
startup_arch:
|
||||||
|
@ -74,106 +116,118 @@ long_mode:
|
||||||
;rust init
|
;rust init
|
||||||
xor rax, rax
|
xor rax, rax
|
||||||
mov eax, [kernel_base + 0x18]
|
mov eax, [kernel_base + 0x18]
|
||||||
mov rbx, gdtr
|
|
||||||
jmp rax
|
jmp rax
|
||||||
|
|
||||||
gdtr:
|
long_mode_ap:
|
||||||
dw gdt.end + 1 ; size
|
mov rax, gdt.kernel_data
|
||||||
dq gdt ; offset
|
mov ds, rax
|
||||||
|
mov es, rax
|
||||||
|
mov fs, rax
|
||||||
|
mov gs, rax
|
||||||
|
mov ss, rax
|
||||||
|
|
||||||
gdt:
|
mov qword [trampoline.ready], 1
|
||||||
.null equ $ - gdt
|
.lp:
|
||||||
dq 0
|
hlt
|
||||||
|
jmp .lp
|
||||||
|
|
||||||
.kernel_code equ $ - gdt
|
gdtr:
|
||||||
istruc GDTEntry
|
dw gdt.end + 1 ; size
|
||||||
at GDTEntry.limitl, dw 0
|
dq gdt ; offset
|
||||||
at GDTEntry.basel, dw 0
|
|
||||||
at GDTEntry.basem, db 0
|
gdt:
|
||||||
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code
|
.null equ $ - gdt
|
||||||
at GDTEntry.flags__limith, db flags.long_mode
|
dq 0
|
||||||
at GDTEntry.baseh, db 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
|
iend
|
||||||
|
.end:
|
||||||
.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:
|
|
||||||
|
|
Loading…
Reference in a new issue