Merge branch 'master' into auxv

This commit is contained in:
jD91mZM2
2020-07-18 12:19:11 +02:00
27 changed files with 878 additions and 603 deletions

26
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

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

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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)) };
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View 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",
});
}
};
}

View File

@@ -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 {

View File

@@ -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);
});
}

View File

@@ -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};

View File

@@ -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",
});

View File

@@ -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 ) => {

View File

@@ -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>());
}
}

View File

@@ -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] }
}
}

View File

@@ -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]

View File

@@ -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;

View File

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

View File

@@ -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};

View File

@@ -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);
});
}

View File

@@ -4,7 +4,7 @@
use crate::{
arch::{
macros::InterruptStack,
interrupt::InterruptStack,
paging::{
entry::EntryFlags,
mapper::MapperFlushAll,

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}