Merge branch 'master' into auxv
This commit is contained in:
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
22
build.rs
22
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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
435
src/arch/x86_64/interrupt/handler.rs
Normal file
435
src/arch/x86_64/interrupt/handler.rs
Normal file
@@ -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",
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
(|$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)));
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None);
|
||||
}}
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern fn syscall_instruction() {
|
||||
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, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack)
|
||||
});
|
||||
}
|
||||
|
||||
function!(syscall_instruction => {
|
||||
// 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");
|
||||
"
|
||||
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 scratch registers
|
||||
interrupt_push!();
|
||||
// Push context registers
|
||||
"push rax\n",
|
||||
push_scratch!(),
|
||||
push_preserved!(),
|
||||
push_fs!(),
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
|
||||
// TODO: Map PTI
|
||||
// $crate::arch::x86_64::pti::map();
|
||||
|
||||
// Map kernel
|
||||
pti::map();
|
||||
// Call inner funtion
|
||||
"mov rdi, rsp\n",
|
||||
"call __inner_syscall_instruction\n",
|
||||
|
||||
inner(rsp as *mut InterruptStack);
|
||||
// TODO: Unmap PTI
|
||||
// $crate::arch::x86_64::pti::unmap();
|
||||
|
||||
// Unmap kernel
|
||||
pti::unmap();
|
||||
// Pop context registers
|
||||
pop_fs!(),
|
||||
pop_preserved!(),
|
||||
pop_scratch!(),
|
||||
|
||||
// Interrupt return
|
||||
interrupt_pop!();
|
||||
iret!()
|
||||
}
|
||||
// Return
|
||||
"iretq\n",
|
||||
});
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern fn syscall() {
|
||||
with_interrupt_stack! {
|
||||
unsafe fn inner(stack) -> usize {
|
||||
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)
|
||||
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!();
|
||||
// Set a restore point for clone
|
||||
let rbp;
|
||||
asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
|
||||
let scratch = &stack.scratch;
|
||||
syscall::syscall(scratch.rax, stack.preserved.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack)
|
||||
})
|
||||
});
|
||||
|
||||
// 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");
|
||||
}
|
||||
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",
|
||||
});
|
||||
|
||||
@@ -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 ) => {
|
||||
|
||||
@@ -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>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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] }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
174
src/asm/x86_64/trampoline.asm
Normal file
174
src/asm/x86_64/trampoline.asm
Normal 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
|
||||
@@ -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};
|
||||
|
||||
11
src/lib.rs
11
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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
macros::InterruptStack,
|
||||
interrupt::InterruptStack,
|
||||
paging::{
|
||||
entry::EntryFlags,
|
||||
mapper::MapperFlushAll,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user