Include trampoline in kernel to fix multi_core on EFI

This commit is contained in:
Jeremy Soller
2020-07-15 21:46:15 -06:00
parent 1e44f157d0
commit c78b69969f
4 changed files with 210 additions and 8 deletions

View File

@@ -91,6 +91,26 @@ fn fill_from_location(f: &mut fs::File, loc: &Path) -> Result<(), Error> {
Ok(())
}
#[cfg(not(target_arch = "x86_64"))]
fn asm(_out_dir: &str) {}
#[cfg(target_arch = "x86_64")]
fn asm(out_dir: &str) {
use std::process::Command;
println!("cargo:rerun-if-changed=src/asm/x86_64/trampoline.asm");
let status = Command::new("nasm")
.arg("-f").arg("bin")
.arg("-o").arg(format!("{}/trampoline", out_dir))
.arg("src/asm/x86_64/trampoline.asm")
.status()
.expect("failed to run nasm");
if ! status.success() {
panic!("nasm failed with exit status {}", status);
}
}
fn main() {
println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
println!("cargo:rerun-if-env-changed=INITFS_FOLDER");
@@ -100,6 +120,8 @@ fn main() {
let mut f = fs::File::create(&dest_path).unwrap();
let src = env::var("INITFS_FOLDER");
asm(&out_dir);
// Write header
f.write_all(
b"

View File

@@ -5,7 +5,7 @@ use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
use crate::paging::entry::EntryFlags;
use super::sdt::Sdt;
use super::{AP_STARTUP, TRAMPOLINE, find_sdt, load_table, get_sdt_signature};
use super::{find_sdt, load_table, get_sdt_signature};
use core::intrinsics::{atomic_load, atomic_store};
use core::sync::atomic::Ordering;
@@ -22,6 +22,9 @@ pub struct Madt {
pub flags: u32
}
const TRAMPOLINE: usize = 0x8000;
static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
pub static mut MADT: Option<Madt> = None;
pub const FLAG_PCAT: u32 = 1;
@@ -52,13 +55,19 @@ impl Madt {
}
if cfg!(feature = "multi_core") {
// Map trampoline
let trampoline_frame = Frame::containing_address(PhysicalAddress::new(TRAMPOLINE));
let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
// Map trampoline
let result = active_table.map_to(trampoline_page, trampoline_frame, EntryFlags::PRESENT | EntryFlags::WRITABLE);
result.flush(active_table);
// Write trampoline, make sure TRAMPOLINE page is free for use
for i in 0..TRAMPOLINE_DATA.len() {
unsafe {
atomic_store((TRAMPOLINE as *mut u8).add(i), TRAMPOLINE_DATA[i]);
}
}
for madt_entry in madt.iter() {
println!(" {:?}", madt_entry);
match madt_entry {
@@ -73,7 +82,7 @@ impl Madt {
let stack_start = allocate_frames(64).expect("no more frames in acpi stack_start").start_address().get() + crate::KERNEL_OFFSET;
let stack_end = stack_start + 64 * 4096;
let ap_ready = TRAMPOLINE as *mut u64;
let ap_ready = (TRAMPOLINE + 8) as *mut u64;
let ap_cpu_id = unsafe { ap_ready.offset(1) };
let ap_page_table = unsafe { ap_ready.offset(2) };
let ap_stack_start = unsafe { ap_ready.offset(3) };
@@ -106,7 +115,7 @@ impl Madt {
// Send START IPI
{
//Start at 0x0800:0000 => 0x8000. Hopefully the bootloader code is still there
let ap_segment = (AP_STARTUP >> 12) & 0xFF;
let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
let mut icr = 0x4600 | ap_segment as u64;
if local_apic.x2 {

View File

@@ -39,9 +39,6 @@ pub mod aml;
mod rxsdt;
mod rsdp;
const TRAMPOLINE: usize = 0x7E00;
const AP_STARTUP: usize = TRAMPOLINE + 512;
pub fn get_sdt(sdt_address: usize, active_table: &mut ActivePageTable) -> &'static Sdt {
{
let page = Page::containing_address(VirtualAddress::new(sdt_address));

View File

@@ -0,0 +1,174 @@
; trampoline for bringing up APs
; compiled with nasm by build.rs, and included in src/acpi/madt.rs
ORG 0x8000
SECTION .text
USE16
trampoline:
jmp short startup_ap
times 8 - ($ - trampoline) nop
.ready: dq 0
.cpu_id: dq 0
.page_table: dq 0
.stack_start: dq 0
.stack_end: dq 0
.code: dq 0
startup_ap:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
; initialize stack to invalid value
mov sp, 0
;cr3 holds pointer to PML4
mov edi, 0x70000
mov cr3, edi
; Enable FPU
mov eax, cr0
and al, 11110011b ; Clear task switched (3) and emulation (2)
or al, 00100010b ; Set numeric error (5) monitor co-processor (1)
mov cr0, eax
; 18: Enable OSXSAVE
; 10: Unmasked SSE exceptions
; 9: FXSAVE/FXRSTOR
; 7: Page Global
; 5: Page Address Extension
; 4: Page Size Extension
mov eax, cr4
or eax, 1 << 18 | 1 << 10 | 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
mov cr4, eax
; initialize floating point registers
fninit
; 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
; 31: Paging
; 16: write protect kernel
; 0: Protected Mode
or ebx, 1 << 31 | 1 << 16 | 1
mov cr0, ebx
; far jump to enable Long Mode and load CS with 64 bit segment
jmp gdt.kernel_code:long_mode_ap
USE64
long_mode_ap:
mov rax, gdt.kernel_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
mov ss, rax
mov rcx, [trampoline.stack_end]
lea rsp, [rcx - 256]
mov rdi, trampoline.cpu_id
mov rax, [trampoline.code]
mov qword [trampoline.ready], 1
jmp rax
struc GDTEntry
.limitl resw 1
.basel resw 1
.basem resb 1
.attribute resb 1
.flags__limith resb 1
.baseh resb 1
endstruc
attrib:
.present equ 1 << 7
.ring1 equ 1 << 5
.ring2 equ 1 << 6
.ring3 equ 1 << 5 | 1 << 6
.user equ 1 << 4
;user
.code equ 1 << 3
; code
.conforming equ 1 << 2
.readable equ 1 << 1
; data
.expand_down equ 1 << 2
.writable equ 1 << 1
.accessed equ 1 << 0
;system
; legacy
.tssAvailabe16 equ 0x1
.ldt equ 0x2
.tssBusy16 equ 0x3
.call16 equ 0x4
.task equ 0x5
.interrupt16 equ 0x6
.trap16 equ 0x7
.tssAvailabe32 equ 0x9
.tssBusy32 equ 0xB
.call32 equ 0xC
.interrupt32 equ 0xE
.trap32 equ 0xF
; long mode
.ldt32 equ 0x2
.tssAvailabe64 equ 0x9
.tssBusy64 equ 0xB
.call64 equ 0xC
.interrupt64 equ 0xE
.trap64 equ 0xF
flags:
.granularity equ 1 << 7
.available equ 1 << 4
;user
.default_operand_size equ 1 << 6
; code
.long_mode equ 1 << 5
; data
.reserved equ 1 << 5
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
.end equ $ - gdt