diff --git a/Cargo.lock b/Cargo.lock index b34799febbac44c91f7f4473862a9aaac6dfd9ff..b9f2271d78669c97e00c5927747add9527d2bbaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,7 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "raw-cpuid 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -63,11 +64,33 @@ dependencies = [ "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro-hack" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "raw-cpuid" version = "7.0.3" @@ -175,7 +198,10 @@ dependencies = [ "checksum linked_list_allocator 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "47de1a43fad0250ee197e9e124e5b5deab3d7b39d4428ae8a6d741ceb340c362" "checksum linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d" "checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +"checksum paste 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +"checksum paste-impl 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" "checksum raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" "checksum raw-cpuid 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e9c0f2091b865a94bc3c9d34896cc4bbda04453453c391f7eb224491be9ae1d" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" diff --git a/Cargo.toml b/Cargo.toml index 09bc6e36bcdd976570a90c07d32d0779e24bb9d9..d38edb708f8cc922918f2a334b861b2125174921 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ raw-cpuid = "8.0.0" redox_syscall = { path = "syscall" } slab_allocator = { path = "slab_allocator", optional = true } spin = "0.5.2" +paste = "0.1.18" [dependencies.goblin] version = "0.2.1" diff --git a/build.rs b/build.rs index 53c7b15d00df6de31278953c3196c5404067540f..a0f1d4d5b9ee1042bfee77d5d42da447b69d007d 100644 --- a/build.rs +++ b/build.rs @@ -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" diff --git a/src/acpi/madt.rs b/src/acpi/madt.rs index 61396b7bc07c29b0fa287c733ae045f1fd47b45a..cae2189e9aedfa406fa921afe80c093c735a5838 100644 --- a/src/acpi/madt.rs +++ b/src/acpi/madt.rs @@ -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 { diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index 4764aa43791474a5b0060b8cc849e50da0bf5f17..25e64eb87002bb64eaf659a990b7043a94e12953 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -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)); diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs index f5c950cd9f965a8389e4659bc59a2b15dda0e12d..c1713143ae094f93c16da977872f7f21b11ddfe8 100644 --- a/src/arch/x86_64/gdt.rs +++ b/src/arch/x86_64/gdt.rs @@ -99,7 +99,7 @@ pub unsafe fn set_tcb(pid: usize) { #[cfg(feature = "pti")] pub unsafe fn set_tss_stack(stack: usize) { - use arch::x86_64::pti::{PTI_CPU_STACK, PTI_CONTEXT_STACK}; + use super::pti::{PTI_CPU_STACK, PTI_CONTEXT_STACK}; TSS.rsp[0] = (PTI_CPU_STACK.as_ptr() as usize + PTI_CPU_STACK.len()) as u64; PTI_CONTEXT_STACK = stack; } diff --git a/src/arch/x86_64/graphical_debug/display.rs b/src/arch/x86_64/graphical_debug/display.rs index a88248be6bc7f24d62de6dda659997f87475a531..4f5ecbf8efe2ea707f862d9eb8d4507d2177891f 100644 --- a/src/arch/x86_64/graphical_debug/display.rs +++ b/src/arch/x86_64/graphical_debug/display.rs @@ -15,7 +15,7 @@ pub struct Display { impl Display { pub fn new(width: usize, height: usize, onscreen: usize) -> Display { let size = width * height; - let offscreen = unsafe { ::ALLOCATOR.alloc(Layout::from_size_align_unchecked(size * 4, 4096)) }; + let offscreen = unsafe { crate::ALLOCATOR.alloc(Layout::from_size_align_unchecked(size * 4, 4096)) }; unsafe { fast_set64(offscreen as *mut u64, 0, size/2) }; Display { width: width, @@ -144,6 +144,6 @@ impl Display { impl Drop for Display { fn drop(&mut self) { - unsafe { ::ALLOCATOR.dealloc(self.offscreen.as_mut_ptr() as *mut u8, Layout::from_size_align_unchecked(self.offscreen.len() * 4, 4096)) }; + unsafe { crate::ALLOCATOR.dealloc(self.offscreen.as_mut_ptr() as *mut u8, Layout::from_size_align_unchecked(self.offscreen.len() * 4, 4096)) }; } } diff --git a/src/arch/x86_64/graphical_debug/mod.rs b/src/arch/x86_64/graphical_debug/mod.rs index 081f567865c41849357fe0b9920261c6f00adc91..6ea97ed541ef276690e06eed9b6e31a0983cacf3 100644 --- a/src/arch/x86_64/graphical_debug/mod.rs +++ b/src/arch/x86_64/graphical_debug/mod.rs @@ -1,9 +1,9 @@ use spin::Mutex; -use memory::Frame; -use paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress}; -use paging::entry::EntryFlags; -use paging::mapper::MapperFlushAll; +use crate::memory::Frame; +use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress}; +use crate::paging::entry::EntryFlags; +use crate::paging::mapper::MapperFlushAll; pub use self::debug::DebugDisplay; use self::display::Display; @@ -54,13 +54,13 @@ pub fn init(active_table: &mut ActivePageTable) { { let size = width * height; - let onscreen = physbaseptr + ::KERNEL_OFFSET; + let onscreen = physbaseptr + crate::KERNEL_OFFSET; { let mut flush_all = MapperFlushAll::new(); let start_page = Page::containing_address(VirtualAddress::new(onscreen)); let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4)); for page in Page::range_inclusive(start_page, end_page) { - let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - ::KERNEL_OFFSET)); + let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - crate::KERNEL_OFFSET)); let flags = EntryFlags::PRESENT | EntryFlags::NO_EXECUTE | EntryFlags::WRITABLE | EntryFlags::HUGE_PAGE; let result = active_table.map_to(page, frame, flags); flush_all.consume(result); diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs index e1c0095cf466b0a39c2f53e44ce1a5e5d1d9e6cc..bb0eb2909daec8bc62959b33d3e85923fd2852e2 100644 --- a/src/arch/x86_64/idt.rs +++ b/src/arch/x86_64/idt.rs @@ -172,7 +172,7 @@ pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) { current_idt[13].set_func(exception::protection); current_idt[14].set_func(exception::page); // 15 reserved - current_idt[16].set_func(exception::fpu); + current_idt[16].set_func(exception::fpu_fault); current_idt[17].set_func(exception::alignment_check); current_idt[18].set_func(exception::machine_check); current_idt[19].set_func(exception::simd); @@ -186,7 +186,7 @@ pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) { if is_bsp { // Set up IRQs - current_idt[32].set_func(irq::pit); + current_idt[32].set_func(irq::pit_stack); current_idt[33].set_func(irq::keyboard); current_idt[34].set_func(irq::cascade); current_idt[35].set_func(irq::com2); diff --git a/src/arch/x86_64/interrupt/exception.rs b/src/arch/x86_64/interrupt/exception.rs index b55a13e5d4aabaa233fee488be18c73f6a4e4551..77b78f4fcc1de4cd1f84f6de60a0d97028b9926d 100644 --- a/src/arch/x86_64/interrupt/exception.rs +++ b/src/arch/x86_64/interrupt/exception.rs @@ -1,25 +1,26 @@ use crate::{ interrupt::stack_trace, ptrace, - syscall::flag::* + syscall::flag::*, + + interrupt_stack, + interrupt_error, }; extern { fn ksignal(signal: usize); } -interrupt_stack!(divide_by_zero, stack, { +interrupt_stack!(divide_by_zero, |stack| { println!("Divide by zero"); stack.dump(); stack_trace(); ksignal(SIGFPE); }); -interrupt_stack!(debug, stack, { +interrupt_stack!(debug, |stack| { let mut handled = false; - let guard = ptrace::set_process_regs(stack); - // Disable singlestep before there is a breakpoint, since the breakpoint // handler might end up setting it again but unless it does we want the // default to be false. @@ -33,8 +34,6 @@ interrupt_stack!(debug, stack, { stack.set_singlestep(had_singlestep); } - drop(guard); - if !handled { println!("Debug trap"); stack.dump(); @@ -42,12 +41,12 @@ interrupt_stack!(debug, stack, { } }); -interrupt_stack!(non_maskable, stack, { +interrupt_stack!(non_maskable, |stack| { println!("Non-maskable interrupt"); stack.dump(); }); -interrupt_stack!(breakpoint, stack, { +interrupt_stack!(breakpoint, |stack| { // The processor lets RIP point to the instruction *after* int3, so // unhandled breakpoint interrupt don't go in an infinite loop. But we // throw SIGTRAP anyway, so that's not a problem. @@ -61,81 +60,77 @@ interrupt_stack!(breakpoint, stack, { // int3 instruction. After all, it's the sanest thing to do. stack.iret.rip -= 1; - let guard = ptrace::set_process_regs(stack); - if ptrace::breakpoint_callback(PTRACE_STOP_BREAKPOINT, None).is_none() { - drop(guard); - println!("Breakpoint trap"); stack.dump(); ksignal(SIGTRAP); } }); -interrupt_stack!(overflow, stack, { +interrupt_stack!(overflow, |stack| { println!("Overflow trap"); stack.dump(); stack_trace(); ksignal(SIGFPE); }); -interrupt_stack!(bound_range, stack, { +interrupt_stack!(bound_range, |stack| { println!("Bound range exceeded fault"); stack.dump(); stack_trace(); ksignal(SIGSEGV); }); -interrupt_stack!(invalid_opcode, stack, { +interrupt_stack!(invalid_opcode, |stack| { println!("Invalid opcode fault"); stack.dump(); stack_trace(); ksignal(SIGILL); }); -interrupt_stack!(device_not_available, stack, { +interrupt_stack!(device_not_available, |stack| { println!("Device not available fault"); stack.dump(); stack_trace(); ksignal(SIGILL); }); -interrupt_error!(double_fault, stack, { +interrupt_error!(double_fault, |stack| { println!("Double fault"); stack.dump(); stack_trace(); ksignal(SIGSEGV); }); -interrupt_error!(invalid_tss, stack, { +interrupt_error!(invalid_tss, |stack| { println!("Invalid TSS fault"); stack.dump(); stack_trace(); ksignal(SIGSEGV); }); -interrupt_error!(segment_not_present, stack, { +interrupt_error!(segment_not_present, |stack| { println!("Segment not present fault"); stack.dump(); stack_trace(); ksignal(SIGSEGV); }); -interrupt_error!(stack_segment, stack, { +interrupt_error!(stack_segment, |stack| { println!("Stack segment fault"); stack.dump(); stack_trace(); ksignal(SIGSEGV); }); -interrupt_error!(protection, stack, { +interrupt_error!(protection, |stack| { println!("Protection fault"); stack.dump(); stack_trace(); ksignal(SIGSEGV); }); -interrupt_error!(page, stack, { +interrupt_error!(page, |stack| { let cr2: usize; asm!("mov rax, cr2" : "={rax}"(cr2) : : : "intel", "volatile"); println!("Page fault: {:>016X}", cr2); @@ -144,42 +139,42 @@ interrupt_error!(page, stack, { ksignal(SIGSEGV); }); -interrupt_stack!(fpu, stack, { +interrupt_stack!(fpu_fault, |stack| { println!("FPU floating point fault"); stack.dump(); stack_trace(); ksignal(SIGFPE); }); -interrupt_error!(alignment_check, stack, { +interrupt_error!(alignment_check, |stack| { println!("Alignment check fault"); stack.dump(); stack_trace(); ksignal(SIGBUS); }); -interrupt_stack!(machine_check, stack, { +interrupt_stack!(machine_check, |stack| { println!("Machine check fault"); stack.dump(); stack_trace(); ksignal(SIGBUS); }); -interrupt_stack!(simd, stack, { +interrupt_stack!(simd, |stack| { println!("SIMD floating point fault"); stack.dump(); stack_trace(); ksignal(SIGFPE); }); -interrupt_stack!(virtualization, stack, { +interrupt_stack!(virtualization, |stack| { println!("Virtualization fault"); stack.dump(); stack_trace(); ksignal(SIGBUS); }); -interrupt_error!(security, stack, { +interrupt_error!(security, |stack| { println!("Security exception"); stack.dump(); stack_trace(); diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs new file mode 100644 index 0000000000000000000000000000000000000000..06d060b176ae1a11b3184a6ee32db90c050052e6 --- /dev/null +++ b/src/arch/x86_64/interrupt/handler.rs @@ -0,0 +1,435 @@ +use core::mem; +use syscall::IntRegisters; + +const FLAG_SINGLESTEP: usize = 1 << 8; + +#[derive(Default)] +#[repr(packed)] +pub struct ScratchRegisters { + pub r11: usize, + pub r10: usize, + pub r9: usize, + pub r8: usize, + pub rsi: usize, + pub rdi: usize, + pub rdx: usize, + pub rcx: usize, + pub rax: usize, +} + +impl ScratchRegisters { + pub fn dump(&self) { + println!("RAX: {:>016X}", { self.rax }); + println!("RCX: {:>016X}", { self.rcx }); + println!("RDX: {:>016X}", { self.rdx }); + println!("RDI: {:>016X}", { self.rdi }); + println!("RSI: {:>016X}", { self.rsi }); + println!("R8: {:>016X}", { self.r8 }); + println!("R9: {:>016X}", { self.r9 }); + println!("R10: {:>016X}", { self.r10 }); + println!("R11: {:>016X}", { self.r11 }); + } +} + +#[derive(Default)] +#[repr(packed)] +pub struct PreservedRegisters { + pub r15: usize, + pub r14: usize, + pub r13: usize, + pub r12: usize, + pub rbp: usize, + pub rbx: usize, +} + +impl PreservedRegisters { + pub fn dump(&self) { + println!("RBX: {:>016X}", { self.rbx }); + println!("RBP: {:>016X}", { self.rbp }); + println!("R12: {:>016X}", { self.r12 }); + println!("R13: {:>016X}", { self.r13 }); + println!("R14: {:>016X}", { self.r14 }); + println!("R15: {:>016X}", { self.r15 }); + } +} + +#[derive(Default)] +#[repr(packed)] +pub struct IretRegisters { + pub rip: usize, + pub cs: usize, + pub rflags: usize, + + // ---- + // The following will only be present if interrupt is raised from another + // privilege ring. Otherwise, they are undefined values. + // ---- + + pub rsp: usize, + pub ss: usize +} + +impl IretRegisters { + pub fn dump(&self) { + println!("RFLAG: {:>016X}", { self.rflags }); + println!("CS: {:>016X}", { self.cs }); + println!("RIP: {:>016X}", { self.rip }); + } +} + +#[derive(Default)] +#[repr(packed)] +pub struct InterruptStack { + pub fs: usize, + pub preserved: PreservedRegisters, + pub scratch: ScratchRegisters, + pub iret: IretRegisters, +} + +impl InterruptStack { + pub fn dump(&self) { + self.iret.dump(); + self.scratch.dump(); + self.preserved.dump(); + println!("FS: {:>016X}", { self.fs }); + } + /// Saves all registers to a struct used by the proc: + /// scheme to read/write registers. + pub fn save(&self, all: &mut IntRegisters) { + all.fs = self.fs; + + all.r15 = self.preserved.r15; + all.r14 = self.preserved.r14; + all.r13 = self.preserved.r13; + all.r12 = self.preserved.r12; + all.rbp = self.preserved.rbp; + all.rbx = self.preserved.rbx; + all.r11 = self.scratch.r11; + all.r10 = self.scratch.r10; + all.r9 = self.scratch.r9; + all.r8 = self.scratch.r8; + all.rsi = self.scratch.rsi; + all.rdi = self.scratch.rdi; + all.rdx = self.scratch.rdx; + all.rcx = self.scratch.rcx; + all.rax = self.scratch.rax; + all.rip = self.iret.rip; + all.cs = self.iret.cs; + all.rflags = self.iret.rflags; + + // Set rsp and ss: + + const CPL_MASK: usize = 0b11; + + let cs: usize; + unsafe { + asm!("mov $0, cs" : "=r"(cs) ::: "intel"); + } + + if self.iret.cs & CPL_MASK == cs & CPL_MASK { + // Privilege ring didn't change, so neither did the stack + all.rsp = self as *const Self as usize // rsp after Self was pushed to the stack + + mem::size_of::<Self>() // disregard Self + - mem::size_of::<usize>() * 2; // well, almost: rsp and ss need to be excluded as they aren't present + unsafe { + asm!("mov $0, ss" : "=r"(all.ss) ::: "intel"); + } + } else { + all.rsp = self.iret.rsp; + all.ss = self.iret.ss; + } + } + /// Loads all registers from a struct used by the proc: + /// scheme to read/write registers. + pub fn load(&mut self, all: &IntRegisters) { + // TODO: Which of these should be allowed to change? + + // self.fs = all.fs; + self.preserved.r15 = all.r15; + self.preserved.r14 = all.r14; + self.preserved.r13 = all.r13; + self.preserved.r12 = all.r12; + self.preserved.rbp = all.rbp; + self.preserved.rbx = all.rbx; + self.scratch.r11 = all.r11; + self.scratch.r10 = all.r10; + self.scratch.r9 = all.r9; + self.scratch.r8 = all.r8; + self.scratch.rsi = all.rsi; + self.scratch.rdi = all.rdi; + self.scratch.rdx = all.rdx; + self.scratch.rcx = all.rcx; + self.scratch.rax = all.rax; + self.iret.rip = all.rip; + + // These should probably be restricted + // self.iret.cs = all.cs; + // self.iret.rflags = all.eflags; + } + /// Enables the "Trap Flag" in the FLAGS register, causing the CPU + /// to send a Debug exception after the next instruction. This is + /// used for singlestep in the proc: scheme. + pub fn set_singlestep(&mut self, enabled: bool) { + if enabled { + self.iret.rflags |= FLAG_SINGLESTEP; + } else { + self.iret.rflags &= !FLAG_SINGLESTEP; + } + } + /// Checks if the trap flag is enabled, see `set_singlestep` + pub fn is_singlestep(&self) -> bool { + self.iret.rflags & FLAG_SINGLESTEP == FLAG_SINGLESTEP + } +} + +#[derive(Default)] +#[repr(packed)] +pub struct InterruptErrorStack { + pub code: usize, + pub inner: InterruptStack, +} + +impl InterruptErrorStack { + pub fn dump(&self) { + println!("CODE: {:>016X}", { self.code }); + self.inner.dump(); + } +} + +#[macro_export] +macro_rules! intel_asm { + ($($strings:expr,)+) => { + global_asm!(concat!( + ".intel_syntax noprefix\n", + $($strings),+, + ".att_syntax prefix\n", + )); + }; +} +#[macro_export] +macro_rules! function { + ($name:ident => { $($body:expr,)+ }) => { + intel_asm!( + ".global ", stringify!($name), "\n", + ".type ", stringify!($name), ", @function\n", + ".section .text.", stringify!($name), ", \"ax\", @progbits\n", + stringify!($name), ":\n", + $($body),+, + ".size ", stringify!($name), ", . - ", stringify!($name), "\n", + ".text\n", + ); + extern "C" { + pub fn $name(); + } + }; +} + +#[macro_export] +macro_rules! push_scratch { + () => { " + // Push scratch registers + push rcx + push rdx + push rdi + push rsi + push r8 + push r9 + push r10 + push r11 + " }; +} +#[macro_export] +macro_rules! pop_scratch { + () => { " + // Pop scratch registers + pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdi + pop rdx + pop rcx + pop rax + " }; +} + +#[macro_export] +macro_rules! push_preserved { + () => { " + // Push preserved registers + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 + " }; +} +#[macro_export] +macro_rules! pop_preserved { + () => { " + // Pop preserved registers + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + " }; +} + +#[macro_export] +macro_rules! push_fs { + () => { " + // Push fs + push fs + + // Load kernel tls + // + // NOTE: We can't load the value directly into `fs`. So we need to use a + // scratch register (as preserved registers aren't backed up by the + // interrupt! macro) to store it. We also can't use `rax` as the temporary + // value, as during errors that's already used for the error code. + mov rcx, 0x18 + mov fs, cx + " }; +} +#[macro_export] +macro_rules! pop_fs { + () => { " + // Pop fs + pop fs + " }; +} + +#[macro_export] +macro_rules! interrupt_stack { + ($name:ident, |$stack:ident| $code:block) => { + paste::item! { + #[no_mangle] + unsafe extern "C" fn [<__interrupt_ $name>](stack: *mut $crate::arch::x86_64::interrupt::InterruptStack) { + // This inner function is needed because macros are buggy: + // https://github.com/dtolnay/paste/issues/7 + #[inline(always)] + unsafe fn inner($stack: &mut $crate::arch::x86_64::interrupt::InterruptStack) { + $code + } + let _guard = $crate::ptrace::set_process_regs(stack); + inner(&mut *stack); + } + + function!($name => { + // Backup all userspace registers to stack + "push rax\n", + push_scratch!(), + push_preserved!(), + push_fs!(), + + // TODO: Map PTI + // $crate::arch::x86_64::pti::map(); + + // Call inner function with pointer to stack + "mov rdi, rsp\n", + "call __interrupt_", stringify!($name), "\n", + + // TODO: Unmap PTI + // $crate::arch::x86_64::pti::unmap(); + + // Restore all userspace registers + pop_fs!(), + pop_preserved!(), + pop_scratch!(), + + "iretq\n", + }); + } + }; +} + +#[macro_export] +macro_rules! interrupt { + ($name:ident, || $code:block) => { + paste::item! { + #[no_mangle] + unsafe extern "C" fn [<__interrupt_ $name>]() { + $code + } + + function!($name => { + // Backup all userspace registers to stack + "push rax\n", + push_scratch!(), + push_fs!(), + + // TODO: Map PTI + // $crate::arch::x86_64::pti::map(); + + // Call inner function with pointer to stack + "call __interrupt_", stringify!($name), "\n", + + // TODO: Unmap PTI + // $crate::arch::x86_64::pti::unmap(); + + // Restore all userspace registers + pop_fs!(), + pop_scratch!(), + + "iretq\n", + }); + } + }; +} + +#[macro_export] +macro_rules! interrupt_error { + ($name:ident, |$stack:ident| $code:block) => { + paste::item! { + #[no_mangle] + unsafe extern "C" fn [<__interrupt_ $name>](stack: *mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) { + // This inner function is needed because macros are buggy: + // https://github.com/dtolnay/paste/issues/7 + #[inline(always)] + unsafe fn inner($stack: &mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) { + $code + } + let _guard = $crate::ptrace::set_process_regs(&mut (*stack).inner); + inner(&mut *stack); + } + + function!($name => { + // Move rax into code's place, put code in last instead (to be + // compatible with InterruptStack) + "xchg [rsp], rax\n", + + // Push all userspace registers + push_scratch!(), + push_preserved!(), + push_fs!(), + + // Put code in, it's now in rax + "push rax\n", + + // TODO: Map PTI + // $crate::arch::x86_64::pti::map(); + + // Call inner function with pointer to stack + "mov rdi, rsp\n", + "call __interrupt_", stringify!($name), "\n", + + // TODO: Unmap PTI + // $crate::arch::x86_64::pti::unmap(); + + // Pop code + "add rsp, 8\n", + + // Restore all userspace registers + pop_fs!(), + pop_preserved!(), + pop_scratch!(), + + "iretq\n", + }); + } + }; +} diff --git a/src/arch/x86_64/interrupt/ipi.rs b/src/arch/x86_64/interrupt/ipi.rs index eb747e647423ebab7d0d980075a06f5784312bf2..52aad2abe4c1c21a9609cd2aa0978392b73e2684 100644 --- a/src/arch/x86_64/interrupt/ipi.rs +++ b/src/arch/x86_64/interrupt/ipi.rs @@ -5,23 +5,23 @@ use crate::context; use crate::device::local_apic::LOCAL_APIC; use super::irq::PIT_TICKS; -interrupt!(wakeup, { +interrupt!(wakeup, || { LOCAL_APIC.eoi(); }); -interrupt!(tlb, { +interrupt!(tlb, || { LOCAL_APIC.eoi(); tlb::flush_all(); }); -interrupt!(switch, { +interrupt!(switch, || { LOCAL_APIC.eoi(); let _ = context::switch(); }); -interrupt!(pit, { +interrupt!(pit, || { LOCAL_APIC.eoi(); if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 { diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs index a01196f80ffb8e5ac219c04958a48724bd16b0be..a1e2f20ad77c73e2758d2a46af5c93c807d46f0e 100644 --- a/src/arch/x86_64/interrupt/irq.rs +++ b/src/arch/x86_64/interrupt/irq.rs @@ -2,12 +2,13 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use alloc::vec::Vec; +use crate::{interrupt, interrupt_stack}; use crate::context::timeout; use crate::device::{local_apic, ioapic, pic}; use crate::device::serial::{COM1, COM2}; use crate::ipi::{ipi, IpiKind, IpiTarget}; use crate::scheme::debug::debug_input; -use crate::{context, ptrace, time}; +use crate::{context, time}; //resets to 0 in context::switch() #[thread_local] @@ -31,10 +32,10 @@ unsafe fn ps2_interrupt(_index: usize) { mov ah, al in al, 0x60 " - : "={al}"(data), "={ah}"(status) - : - : "memory" - : "intel", "volatile" + : "={al}"(data), "={ah}"(status) + : + : "memory" + : "intel", "volatile" ); if status & 1 != 0 { @@ -168,7 +169,7 @@ unsafe fn ioapic_unmask(irq: usize) { ioapic::unmask(irq as u8); } -interrupt_stack!(pit, stack, { +interrupt_stack!(pit_stack, |_stack| { // Saves CPU time by not sending IRQ event irq_trigger(0); const PIT_RATE: u64 = 2_250_286; @@ -189,46 +190,45 @@ interrupt_stack!(pit, stack, { timeout::trigger(); if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 { - let _guard = ptrace::set_process_regs(stack); let _ = context::switch(); } }); -interrupt!(keyboard, { +interrupt!(keyboard, || { ps2_interrupt(0); eoi(1); }); -interrupt!(cascade, { +interrupt!(cascade, || { // No need to do any operations on cascade eoi(2); }); -interrupt!(com2, { +interrupt!(com2, || { while let Some(c) = COM2.lock().receive() { debug_input(c); } eoi(3); }); -interrupt!(com1, { +interrupt!(com1, || { while let Some(c) = COM1.lock().receive() { debug_input(c); } eoi(4); }); -interrupt!(lpt2, { +interrupt!(lpt2, || { trigger(5); eoi(5); }); -interrupt!(floppy, { +interrupt!(floppy, || { trigger(6); eoi(6); }); -interrupt!(lpt1, { +interrupt!(lpt1, || { if irq_method() == IrqMethod::Pic && pic::MASTER.isr() & (1 << 7) == 0 { // the IRQ was spurious, ignore it but increment a counter. SPURIOUS_COUNT_IRQ7.fetch_add(1, Ordering::Relaxed); @@ -238,42 +238,42 @@ interrupt!(lpt1, { eoi(7); }); -interrupt!(rtc, { +interrupt!(rtc, || { trigger(8); eoi(8); }); -interrupt!(pci1, { +interrupt!(pci1, || { trigger(9); eoi(9); }); -interrupt!(pci2, { +interrupt!(pci2, || { trigger(10); eoi(10); }); -interrupt!(pci3, { +interrupt!(pci3, || { trigger(11); eoi(11); }); -interrupt!(mouse, { +interrupt!(mouse, || { ps2_interrupt(1); eoi(12); }); -interrupt!(fpu, { +interrupt!(fpu, || { trigger(13); eoi(13); }); -interrupt!(ata1, { +interrupt!(ata1, || { trigger(14); eoi(14); }); -interrupt!(ata2, { +interrupt!(ata2, || { if irq_method() == IrqMethod::Pic && pic::SLAVE.isr() & (1 << 7) == 0 { SPURIOUS_COUNT_IRQ15.fetch_add(1, Ordering::Relaxed); pic::MASTER.ack(); @@ -283,17 +283,17 @@ interrupt!(ata2, { eoi(15); }); -interrupt!(lapic_timer, { +interrupt!(lapic_timer, || { println!("Local apic timer interrupt"); lapic_eoi(); }); -interrupt!(lapic_error, { +interrupt!(lapic_error, || { println!("Local apic internal error: ESR={:#0x}", local_apic::LOCAL_APIC.esr()); lapic_eoi(); }); -interrupt!(calib_pit, { +interrupt!(calib_pit, || { const PIT_RATE: u64 = 2_250_286; { @@ -309,7 +309,7 @@ interrupt!(calib_pit, { macro_rules! allocatable_irq( ( $idt:expr, $number:literal, $name:ident ) => { - interrupt!($name, { + interrupt!($name, || { allocatable_irq_generic($number); }); } diff --git a/src/arch/x86_64/interrupt/mod.rs b/src/arch/x86_64/interrupt/mod.rs index fcf75d12033ff2227f492206e60a9a1c150c836a..ef2588b1f5ab9d3a7e923d2908ff2141c6819766 100644 --- a/src/arch/x86_64/interrupt/mod.rs +++ b/src/arch/x86_64/interrupt/mod.rs @@ -1,11 +1,15 @@ //! Interrupt instructions +#[macro_use] +pub mod handler; + pub mod exception; pub mod ipi; pub mod irq; pub mod syscall; pub mod trace; +pub use self::handler::InterruptStack; pub use self::trace::stack_trace; pub use super::idt::{available_irqs_iter, is_reserved, set_reserved}; diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs index 9f5ed74b253032f23342867e5e5bb6126220c6f1..d7f64a837c60315de84674c43ff0bfc0417fe7d3 100644 --- a/src/arch/x86_64/interrupt/syscall.rs +++ b/src/arch/x86_64/interrupt/syscall.rs @@ -1,7 +1,10 @@ -use crate::arch::macros::InterruptStack; -use crate::arch::{gdt, pti}; -use crate::syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL}; -use crate::{ptrace, syscall}; +use crate::{ + arch::{gdt, interrupt::InterruptStack}, + context, + ptrace, + syscall, + syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL}, +}; use x86::msr; pub unsafe fn init() { @@ -14,121 +17,113 @@ pub unsafe fn init() { msr::wrmsr(msr::IA32_EFER, efer | 1); } -// Not a function pointer because it somehow messes up the returning -// from clone() (via clone_ret()). Not sure what the problem is. macro_rules! with_interrupt_stack { - (unsafe fn $wrapped:ident($stack:ident) -> usize $code:block) => { - #[inline(never)] - unsafe fn $wrapped(stack: *mut InterruptStack) { - let _guard = ptrace::set_process_regs(stack); - - let allowed = ptrace::breakpoint_callback(PTRACE_STOP_PRE_SYSCALL, None) - .and_then(|_| ptrace::next_breakpoint().map(|f| !f.contains(PTRACE_FLAG_IGNORE))); - - if allowed.unwrap_or(true) { - // If syscall not ignored - let $stack = &mut *stack; - $stack.scratch.rax = $code; - } - - ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None); + (|$stack:ident| $code:block) => {{ + let allowed = ptrace::breakpoint_callback(PTRACE_STOP_PRE_SYSCALL, None) + .and_then(|_| ptrace::next_breakpoint().map(|f| !f.contains(PTRACE_FLAG_IGNORE))); + + if allowed.unwrap_or(true) { + // If the syscall is `clone`, the clone won't return here. Instead, + // it'll return early and leave any undropped values. This is + // actually GOOD, because any references are at that point UB + // anyway, because they are based on the wrong stack. + let $stack = &mut *$stack; + (*$stack).scratch.rax = $code; } - } -} -#[naked] -pub unsafe extern fn syscall_instruction() { - with_interrupt_stack! { - unsafe fn inner(stack) -> usize { - let rbp; - asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); - - let scratch = &stack.scratch; - syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack) - } - } - - // Yes, this is magic. No, you don't need to understand - asm!(" - swapgs // Set gs segment to TSS - mov gs:[28], rsp // Save userspace rsp - mov rsp, gs:[4] // Load kernel rsp - push 5 * 8 + 3 // Push userspace data segment - push qword ptr gs:[28] // Push userspace rsp - mov qword ptr gs:[28], 0 // Clear userspace rsp - push r11 // Push rflags - push 4 * 8 + 3 // Push userspace code segment - push rcx // Push userspace return pointer - swapgs // Restore gs - " - : - : - : - : "intel", "volatile"); - - // Push scratch registers - interrupt_push!(); - - // Get reference to stack variables - let rsp: usize; - asm!("" : "={rsp}"(rsp) : : : "intel", "volatile"); - - // Map kernel - pti::map(); - - inner(rsp as *mut InterruptStack); - - // Unmap kernel - pti::unmap(); - - // Interrupt return - interrupt_pop!(); - iret!() + ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None); + }} } -#[naked] -pub unsafe extern fn syscall() { - with_interrupt_stack! { - unsafe fn inner(stack) -> usize { - let rbp; - asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); +#[no_mangle] +pub unsafe extern "C" fn __inner_syscall_instruction(stack: *mut InterruptStack) { + let _guard = ptrace::set_process_regs(stack); + with_interrupt_stack!(|stack| { + // Set a restore point for clone + let rbp; + asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); + + let scratch = &stack.scratch; + syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack) + }); +} - let scratch = &stack.scratch; - syscall::syscall(scratch.rax, stack.preserved.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack) +function!(syscall_instruction => { + // Yes, this is magic. No, you don't need to understand + " + swapgs // Set gs segment to TSS + mov gs:[28], rsp // Save userspace rsp + mov rsp, gs:[4] // Load kernel rsp + push 5 * 8 + 3 // Push userspace data segment + push QWORD PTR gs:[28] // Push userspace rsp + mov QWORD PTR gs:[28], 0 // Clear userspace rsp + push r11 // Push rflags + push 4 * 8 + 3 // Push userspace code segment + push rcx // Push userspace return pointer + swapgs // Restore gs + ", + + // Push context registers + "push rax\n", + push_scratch!(), + push_preserved!(), + push_fs!(), + + // TODO: Map PTI + // $crate::arch::x86_64::pti::map(); + + // Call inner funtion + "mov rdi, rsp\n", + "call __inner_syscall_instruction\n", + + // TODO: Unmap PTI + // $crate::arch::x86_64::pti::unmap(); + + // Pop context registers + pop_fs!(), + pop_preserved!(), + pop_scratch!(), + + // Return + "iretq\n", +}); + +interrupt_stack!(syscall, |stack| { + with_interrupt_stack!(|stack| { + { + let contexts = context::contexts(); + let context = contexts.current(); + if let Some(current) = context { + let current = current.read(); + let name = current.name.lock(); + println!("Warning: Context {} used deprecated `int 0x80` construct", core::str::from_utf8(&name).unwrap_or("(invalid utf8)")); + } else { + println!("Warning: Unknown context used deprecated `int 0x80` construct"); + } } - } - - // Push scratch registers - interrupt_push!(); - - // Get reference to stack variables - let rsp: usize; - asm!("" : "={rsp}"(rsp) : : : "intel", "volatile"); - - // Map kernel - pti::map(); - - inner(rsp as *mut InterruptStack); - // Unmap kernel - pti::unmap(); - - // Interrupt return - interrupt_pop!(); - iret!(); -} - -#[naked] -pub unsafe extern "C" fn clone_ret() { - // The C x86_64 ABI specifies that rbp is pushed to save the old - // call frame. Popping rbp means we're using the parent's call - // frame and thus will not only return from this function but also - // from the function above this one. - // When this is called, the stack should have been - // interrupt->inner->syscall->clone - // then changed to - // interrupt->inner->clone_ret->clone - // so this will return from "inner". - - asm!("pop rbp" : : : : "intel", "volatile"); -} + // Set a restore point for clone + let rbp; + asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); + + let scratch = &stack.scratch; + syscall::syscall(scratch.rax, stack.preserved.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack) + }) +}); + +function!(clone_ret => { + // The address of this instruction is injected by `clone` in process.rs, on + // top of the stack syscall->inner in this file, which is done using the rbp + // register we save there. + // + // The top of our stack here is the address pointed to by rbp, which is: + // + // - the previous rbp + // - the return location + // + // Our goal is to return from the parent function, inner, so we restore + // rbp... + "pop rbp\n", + // ...and we return to the address at the top of the stack + "ret\n", +}); diff --git a/src/arch/x86_64/macros.rs b/src/arch/x86_64/macros.rs index 6271229b9ff277aa0c4ff1c84f480024e43a4e49..5f888cb082967930fceb5b36606e5336d990c64e 100644 --- a/src/arch/x86_64/macros.rs +++ b/src/arch/x86_64/macros.rs @@ -1,6 +1,3 @@ -use core::mem; -use syscall::data::IntRegisters; - /// Print to console #[macro_export] macro_rules! print { @@ -18,400 +15,6 @@ macro_rules! println { ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); } -#[allow(dead_code)] -#[derive(Default)] -#[repr(packed)] -pub struct ScratchRegisters { - pub r11: usize, - pub r10: usize, - pub r9: usize, - pub r8: usize, - pub rsi: usize, - pub rdi: usize, - pub rdx: usize, - pub rcx: usize, - pub rax: usize, -} - -impl ScratchRegisters { - pub fn dump(&self) { - println!("RAX: {:>016X}", { self.rax }); - println!("RCX: {:>016X}", { self.rcx }); - println!("RDX: {:>016X}", { self.rdx }); - println!("RDI: {:>016X}", { self.rdi }); - println!("RSI: {:>016X}", { self.rsi }); - println!("R8: {:>016X}", { self.r8 }); - println!("R9: {:>016X}", { self.r9 }); - println!("R10: {:>016X}", { self.r10 }); - println!("R11: {:>016X}", { self.r11 }); - } -} - -macro_rules! scratch_push { - () => (asm!( - "push rax - push rcx - push rdx - push rdi - push rsi - push r8 - push r9 - push r10 - push r11" - : : : : "intel", "volatile" - )); -} - -macro_rules! scratch_pop { - () => (asm!( - "pop r11 - pop r10 - pop r9 - pop r8 - pop rsi - pop rdi - pop rdx - pop rcx - pop rax" - : : : : "intel", "volatile" - )); -} - -#[allow(dead_code)] -#[derive(Default)] -#[repr(packed)] -pub struct PreservedRegisters { - pub r15: usize, - pub r14: usize, - pub r13: usize, - pub r12: usize, - pub rbp: usize, - pub rbx: usize, -} - -impl PreservedRegisters { - pub fn dump(&self) { - println!("RBX: {:>016X}", { self.rbx }); - println!("RBP: {:>016X}", { self.rbp }); - println!("R12: {:>016X}", { self.r12 }); - println!("R13: {:>016X}", { self.r13 }); - println!("R14: {:>016X}", { self.r14 }); - println!("R15: {:>016X}", { self.r15 }); - } -} - -macro_rules! preserved_push { - () => (asm!( - "push rbx - push rbp - push r12 - push r13 - push r14 - push r15" - : : : : "intel", "volatile" - )); -} - -macro_rules! preserved_pop { - () => (asm!( - "pop r15 - pop r14 - pop r13 - pop r12 - pop rbp - pop rbx" - : : : : "intel", "volatile" - )); -} - -macro_rules! fs_push { - () => (asm!( - " - push fs - - // Load kernel tls - mov rax, 0x18 - mov fs, ax // can't load value directly into `fs` - " - : : : : "intel", "volatile" - )); -} - -macro_rules! fs_pop { - () => (asm!( - "pop fs" - : : : : "intel", "volatile" - )); -} - -#[allow(dead_code)] -#[derive(Default)] -#[repr(packed)] -pub struct IretRegisters { - pub rip: usize, - pub cs: usize, - pub rflags: usize, - // Will only be present if interrupt is raised from another - // privilege ring - pub rsp: usize, - pub ss: usize -} - -impl IretRegisters { - pub fn dump(&self) { - println!("RFLAG: {:>016X}", { self.rflags }); - println!("CS: {:>016X}", { self.cs }); - println!("RIP: {:>016X}", { self.rip }); - } -} - -macro_rules! iret { - () => (asm!( - "iretq" - : : : : "intel", "volatile" - )); -} - -/// Create an interrupt function that can safely run rust code -#[macro_export] -macro_rules! interrupt { - ($name:ident, $func:block) => { - #[naked] - pub unsafe extern fn $name () { - #[inline(never)] - unsafe fn inner() { - $func - } - - // Push scratch registers - scratch_push!(); - fs_push!(); - - // Map kernel - $crate::arch::x86_64::pti::map(); - - // Call inner rust function - inner(); - - // Unmap kernel - $crate::arch::x86_64::pti::unmap(); - - // Pop scratch registers and return - fs_pop!(); - scratch_pop!(); - iret!(); - } - }; -} - -#[allow(dead_code)] -#[derive(Default)] -#[repr(packed)] -pub struct InterruptStack { - pub fs: usize, - pub preserved: PreservedRegisters, - pub scratch: ScratchRegisters, - pub iret: IretRegisters, -} - -impl InterruptStack { - pub fn dump(&self) { - self.iret.dump(); - self.scratch.dump(); - self.preserved.dump(); - println!("FS: {:>016X}", { self.fs }); - } - /// Saves all registers to a struct used by the proc: - /// scheme to read/write registers. - pub fn save(&self, all: &mut IntRegisters) { - all.fs = self.fs; - - all.r15 = self.preserved.r15; - all.r14 = self.preserved.r14; - all.r13 = self.preserved.r13; - all.r12 = self.preserved.r12; - all.rbp = self.preserved.rbp; - all.rbx = self.preserved.rbx; - all.r11 = self.scratch.r11; - all.r10 = self.scratch.r10; - all.r9 = self.scratch.r9; - all.r8 = self.scratch.r8; - all.rsi = self.scratch.rsi; - all.rdi = self.scratch.rdi; - all.rdx = self.scratch.rdx; - all.rcx = self.scratch.rcx; - all.rax = self.scratch.rax; - all.rip = self.iret.rip; - all.cs = self.iret.cs; - all.rflags = self.iret.rflags; - - // Set rsp and ss: - - const CPL_MASK: usize = 0b11; - - let cs: usize; - unsafe { - asm!("mov $0, cs" : "=r"(cs) ::: "intel"); - } - - if self.iret.cs & CPL_MASK == cs & CPL_MASK { - // Privilege ring didn't change, so neither did the stack - all.rsp = self as *const Self as usize // rsp after Self was pushed to the stack - + mem::size_of::<Self>() // disregard Self - - mem::size_of::<usize>() * 2; // well, almost: rsp and ss need to be excluded as they aren't present - unsafe { - asm!("mov $0, ss" : "=r"(all.ss) ::: "intel"); - } - } else { - all.rsp = self.iret.rsp; - all.ss = self.iret.ss; - } - } - /// Loads all registers from a struct used by the proc: - /// scheme to read/write registers. - pub fn load(&mut self, all: &IntRegisters) { - // TODO: Which of these should be allowed to change? - - // self.fs = all.fs; - self.preserved.r15 = all.r15; - self.preserved.r14 = all.r14; - self.preserved.r13 = all.r13; - self.preserved.r12 = all.r12; - self.preserved.rbp = all.rbp; - self.preserved.rbx = all.rbx; - self.scratch.r11 = all.r11; - self.scratch.r10 = all.r10; - self.scratch.r9 = all.r9; - self.scratch.r8 = all.r8; - self.scratch.rsi = all.rsi; - self.scratch.rdi = all.rdi; - self.scratch.rdx = all.rdx; - self.scratch.rcx = all.rcx; - self.scratch.rax = all.rax; - self.iret.rip = all.rip; - - // These should probably be restricted - // self.iret.cs = all.cs; - // self.iret.rflags = all.eflags; - } - /// Enables the "Trap Flag" in the FLAGS register, causing the CPU - /// to send a Debug exception after the next instruction. This is - /// used for singlestep in the proc: scheme. - pub fn set_singlestep(&mut self, enabled: bool) { - if enabled { - self.iret.rflags |= 1 << 8; - } else { - self.iret.rflags &= !(1 << 8); - } - } - /// Checks if the trap flag is enabled, see `set_singlestep` - pub fn is_singlestep(&self) -> bool { - self.iret.rflags & 1 << 8 == 1 << 8 - } -} - -macro_rules! interrupt_push { - () => { - scratch_push!(); - preserved_push!(); - fs_push!(); - }; -} -macro_rules! interrupt_pop { - () => { - fs_pop!(); - preserved_pop!(); - scratch_pop!(); - }; -} - -#[macro_export] -macro_rules! interrupt_stack { - ($name:ident, $stack:ident, $func:block) => { - #[naked] - pub unsafe extern fn $name () { - #[inline(never)] - unsafe fn inner($stack: &mut $crate::arch::x86_64::macros::InterruptStack) { - $func - } - - // Push scratch registers - interrupt_push!(); - - // Get reference to stack variables - let rsp: usize; - asm!("" : "={rsp}"(rsp) : : : "intel", "volatile"); - - // Map kernel - $crate::arch::x86_64::pti::map(); - - // Call inner rust function - inner(&mut *(rsp as *mut $crate::arch::x86_64::macros::InterruptStack)); - - // Unmap kernel - $crate::arch::x86_64::pti::unmap(); - - // Pop scratch registers and return - interrupt_pop!(); - iret!(); - } - }; -} - -#[allow(dead_code)] -#[derive(Default)] -#[repr(packed)] -pub struct InterruptErrorStack { - pub fs: usize, - pub preserved: PreservedRegisters, - pub scratch: ScratchRegisters, - pub code: usize, - pub iret: IretRegisters, -} - -impl InterruptErrorStack { - pub fn dump(&self) { - self.iret.dump(); - println!("CODE: {:>016X}", { self.code }); - self.scratch.dump(); - self.preserved.dump(); - println!("FS: {:>016X}", { self.fs }); - } -} - -#[macro_export] -macro_rules! interrupt_error { - ($name:ident, $stack:ident, $func:block) => { - #[naked] - pub unsafe extern fn $name () { - #[inline(never)] - unsafe fn inner($stack: &$crate::arch::x86_64::macros::InterruptErrorStack) { - $func - } - - // Push scratch registers - interrupt_push!(); - - // Get reference to stack variables - let rsp: usize; - asm!("" : "={rsp}"(rsp) : : : "intel", "volatile"); - - // Map kernel - $crate::arch::x86_64::pti::map(); - - // Call inner rust function - inner(&*(rsp as *const $crate::arch::x86_64::macros::InterruptErrorStack)); - - // Unmap kernel - $crate::arch::x86_64::pti::unmap(); - - // Pop scratch registers, error code, and return - interrupt_pop!(); - asm!("add rsp, 8" : : : : "intel", "volatile"); // pop error code - iret!(); - } - }; -} #[macro_export] macro_rules! irqs( ( [ $( ($idt:expr, $number:literal, $name:ident) ,)* ], $submac:ident ) => { diff --git a/src/arch/x86_64/paging/entry.rs b/src/arch/x86_64/paging/entry.rs index 56f9755bc3f2eafe5335e459bde62a219cf58814..ba6f941d3f88dfc6c51d410aa11ef0feb9c2c84a 100644 --- a/src/arch/x86_64/paging/entry.rs +++ b/src/arch/x86_64/paging/entry.rs @@ -77,3 +77,12 @@ impl Entry { self.0 = (self.0 & !COUNTER_MASK) | (count << 52); } } + +#[cfg(test)] +mod tests { + #[test] + fn entry_has_required_arch_alignment() { + use super::Entry; + assert!(core::mem::align_of::<Entry>() >= core::mem::align_of::<u64>(), "alignment of Entry is less than the required alignment of u64 ({} < {})", core::mem::align_of::<Entry>(), core::mem::align_of::<u64>()); + } +} diff --git a/src/arch/x86_64/paging/table.rs b/src/arch/x86_64/paging/table.rs index 9ebfcef6efcb25319652c4b7c3834da00bff6695..a6f45c52c32115fa8d4eca1fa27504d699d92620 100644 --- a/src/arch/x86_64/paging/table.rs +++ b/src/arch/x86_64/paging/table.rs @@ -55,7 +55,7 @@ impl<L> Table<L> where L: TableLevel { } pub fn zero(&mut self) { - for entry in self.entries.iter_mut() { + for entry in unsafe { &mut self.entries }.iter_mut() { entry.set_zero(); } } @@ -63,12 +63,12 @@ impl<L> Table<L> where L: TableLevel { /// Set number of entries in first table entry fn set_entry_count(&mut self, count: u64) { debug_assert!(count <= ENTRY_COUNT as u64, "count can't be greater than ENTRY_COUNT"); - self.entries[0].set_counter_bits(count); + unsafe { &mut self.entries[0] }.set_counter_bits(count) } /// Get number of entries in first table entry fn entry_count(&self) -> u64 { - self.entries[0].counter_bits() + unsafe { &self.entries[0] }.counter_bits() } pub fn increment_entry_count(&mut self) { @@ -118,12 +118,12 @@ impl<L> Index<usize> for Table<L> where L: TableLevel { type Output = Entry; fn index(&self, index: usize) -> &Entry { - &self.entries[index] + unsafe { &self.entries[index] } } } impl<L> IndexMut<usize> for Table<L> where L: TableLevel { fn index_mut(&mut self, index: usize) -> &mut Entry { - &mut self.entries[index] + unsafe { &mut self.entries[index] } } } diff --git a/src/arch/x86_64/pti.rs b/src/arch/x86_64/pti.rs index 336371557377f46aec3c93203abd0818817d214e..8312521b706af63afa2f3bd8f3049c5ac343e351 100644 --- a/src/arch/x86_64/pti.rs +++ b/src/arch/x86_64/pti.rs @@ -2,11 +2,11 @@ use core::ptr; #[cfg(feature = "pti")] -use memory::Frame; +use crate::memory::Frame; #[cfg(feature = "pti")] -use paging::ActivePageTable; +use crate::paging::ActivePageTable; #[cfg(feature = "pti")] -use paging::entry::EntryFlags; +use crate::paging::entry::EntryFlags; #[cfg(feature = "pti")] #[thread_local] diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index 30425f0e2033b2adce9fe9b7ceefa3cc6c94841f..7cdec3d5de362568954768bba55feec4b14bebb9 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -10,7 +10,7 @@ use crate::allocator; #[cfg(feature = "acpi")] use crate::acpi; #[cfg(feature = "graphical_debug")] -use arch::x86_64::graphical_debug; +use crate::arch::x86_64::graphical_debug; use crate::arch::x86_64::pti; use crate::device; use crate::gdt; diff --git a/src/asm/x86_64/trampoline.asm b/src/asm/x86_64/trampoline.asm new file mode 100644 index 0000000000000000000000000000000000000000..39d89bcc083b0a594709cd1b6dff388981d1274b --- /dev/null +++ b/src/asm/x86_64/trampoline.asm @@ -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 diff --git a/src/context/context.rs b/src/context/context.rs index 1627b6efdb311c1a003c551eaed3f336c37171b2..0814ee67d7d806bf91f358a41c81f1d3e8893c0d 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -7,7 +7,7 @@ use core::cmp::Ordering; use core::mem; use spin::Mutex; -use crate::arch::{macros::InterruptStack, paging::PAGE_SIZE}; +use crate::arch::{interrupt::InterruptStack, paging::PAGE_SIZE}; use crate::common::unique::Unique; use crate::context::arch; use crate::context::file::{FileDescriptor, FileDescription}; diff --git a/src/lib.rs b/src/lib.rs index 2966c1b2259b51a802636c40adb2755eaffa4c80..c86197f78ee9c6bbfb6008820bdeeb45f31a82fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,10 +45,11 @@ #![feature(concat_idents)] #![feature(const_fn)] #![feature(core_intrinsics)] +#![feature(global_asm)] #![feature(integer_atomics)] #![feature(lang_items)] -#![feature(naked_functions)] #![feature(matches_macro)] // stable in current Rust +#![feature(naked_functions)] #![feature(ptr_internals)] #![feature(thread_local)] #![no_std] @@ -274,5 +275,11 @@ pub extern fn ksignal(signal: usize) { println!("NAME {}", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) }); } } - syscall::exit(signal & 0x7F); + + // Try running kill(getpid(), signal), but fallback to exiting + syscall::getpid() + .and_then(|pid| syscall::kill(pid, signal).map(|_| ())) + .unwrap_or_else(|_| { + syscall::exit(signal & 0x7F); + }); } diff --git a/src/ptrace.rs b/src/ptrace.rs index d098d9e2b22fc54a346d3c9bdd3cf1228118eb19..6158df1cffad556ef87aba5f3ad005b42086b70b 100644 --- a/src/ptrace.rs +++ b/src/ptrace.rs @@ -4,7 +4,7 @@ use crate::{ arch::{ - macros::InterruptStack, + interrupt::InterruptStack, paging::{ entry::EntryFlags, mapper::MapperFlushAll, diff --git a/src/syscall/driver.rs b/src/syscall/driver.rs index 7cf168db4f5a896566e36799527946912b68539a..63184269e47c2ea6286bd3a5d34c100c72c8817e 100644 --- a/src/syscall/driver.rs +++ b/src/syscall/driver.rs @@ -1,4 +1,4 @@ -use crate::macros::InterruptStack; +use crate::interrupt::InterruptStack; use crate::memory::{allocate_frames_complex, deallocate_frames, Frame}; use crate::paging::{ActivePageTable, PhysicalAddress, VirtualAddress}; use crate::paging::entry::EntryFlags; diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 886865fabff43db8ba144d63bda2fb391b79aab5..bee91ab038ec04b8c0c8e7d653d59a4d624e9c21 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -20,7 +20,7 @@ use self::flag::{CloneFlags, MapFlags, PhysmapFlags, WaitFlags}; use self::number::*; use crate::context::ContextId; -use crate::macros::InterruptStack; +use crate::interrupt::InterruptStack; use crate::scheme::{FileHandle, SchemeNamespace}; /// Debug diff --git a/src/syscall/process.rs b/src/syscall/process.rs index c9b041a06e56fe9f937e8924fc395657a4dff209..1dd447c74cfd01f11ea2ddd40bf37bb0668eacbc 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -131,25 +131,23 @@ pub fn clone(flags: CloneFlags, stack_base: usize) -> Result<ContextId> { } if let Some(ref stack) = context.kstack { - // Get the relative offset to the return address of this function + // Get the relative offset to the return address of the function + // obtaining `stack_base`. + // // (base pointer - start of stack) - one offset = stack_base - stack.as_ptr() as usize - mem::size_of::<usize>(); // Add clone ret let mut new_stack = stack.clone(); unsafe { + // Set clone's return value to zero. This is done because + // the clone won't return like normal, which means the value + // would otherwise never get set. if let Some(regs) = ptrace::rebase_regs_ptr_mut(context.regs, Some(&mut new_stack)) { - // We'll need to tell the clone that it should - // return 0, but that's it. We don't actually - // clone the registers, because it will then - // become None and be exempt from all kinds of - // ptracing until the current syscall has - // completed. (*regs).scratch.rax = 0; } - // Change the return address of the child - // (previously syscall) to the arch-specific - // clone_ret callback + // Change the return address of the child (previously + // syscall) to the arch-specific clone_ret callback let func_ptr = new_stack.as_mut_ptr().add(offset); *(func_ptr as *mut usize) = interrupt::syscall::clone_ret as usize; }