Skip to content

Commit

Permalink
monolithic: enter user space
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed May 31, 2024
1 parent ba8f1fc commit f55ce42
Show file tree
Hide file tree
Showing 22 changed files with 361 additions and 68 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ members = [
"apps/task/yield",
"apps/task/priority",
"apps/task/tls",

"variants/monolithic",
]

[profile.release]
Expand Down
2 changes: 2 additions & 0 deletions modules/axhal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ fp_simd = []
paging = ["axalloc", "page_table"]
irq = []
tls = ["alloc"]
uspace = ["paging"]
default = []

[dependencies]
log = "0.4"
cfg-if = "1.0"
bitflags = "2.2"
spin = "0.9"
static_assertions = "1.1.0"
axlog = { path = "../axlog" }
axconfig = { path = "../axconfig" }
Expand Down
2 changes: 1 addition & 1 deletion modules/axhal/src/arch/riscv/trap.S
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
trap_vector_base:
// sscratch == 0: trap from S mode
// sscratch != 0: trap from U mode
csrrw sp, sscratch, sp // switch sscratch and sp
csrrw sp, sscratch, sp // swap sscratch and sp
bnez sp, .Ltrap_entry_u

csrr sp, sscratch // put supervisor sp back
Expand Down
73 changes: 72 additions & 1 deletion modules/axhal/src/arch/x86_64/context.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use core::{arch::asm, fmt};
use memory_addr::VirtAddr;

#[cfg(feature = "irq")]
use x86_64::registers::rflags::RFlags;

use super::gdt::GdtStruct;

/// Saved registers when a trap (interrupt or exception) occurs.
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Default, Clone)]
#[derive(Debug, Default, Clone, Copy)]
pub struct TrapFrame {
pub rax: u64,
pub rcx: u64,
Expand Down Expand Up @@ -41,6 +46,72 @@ impl TrapFrame {
}
}

#[cfg(feature = "uspace")]
pub struct UspaceContext(TrapFrame);

#[cfg(feature = "uspace")]
impl UspaceContext {
pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
Self(TrapFrame {
rdi: arg0 as _,
rip: entry as _,
cs: GdtStruct::UCODE64_SELECTOR.0 as _,
#[cfg(feature = "irq")]
rflags: RFlags::INTERRUPT_FLAG.bits(), // IOPL = 0, IF = 1
rsp: ustack_top.as_usize() as _,
ss: GdtStruct::UDATA_SELECTOR.0 as _,
..Default::default()
})
}

pub const fn new_clone(tf: &TrapFrame, ustack_top: VirtAddr) -> Self {
let mut tf = *tf;
// cs, ss are not pushed into TrapFrame in syscall_entry
tf.cs = GdtStruct::UCODE64_SELECTOR.0 as _;
tf.ss = GdtStruct::UDATA_SELECTOR.0 as _;
tf.rsp = ustack_top.as_usize() as _;
tf.rax = 0; // for child thread, clone returns 0
Self(tf)
}

pub const fn new_fork(tf: &TrapFrame) -> Self {
let mut tf = *tf;
// cs, ss are not pushed into TrapFrame in syscall_entry
tf.cs = GdtStruct::UCODE64_SELECTOR.0 as _;
tf.ss = GdtStruct::UDATA_SELECTOR.0 as _;
tf.rax = 0; // for child process, fork returns 0
Self(tf)
}

pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! {
super::disable_irqs();
super::tss_set_rsp0(kstack_top);
asm!("
mov rsp, {tf}
pop rax
pop rcx
pop rdx
pop rbx
pop rbp
pop rsi
pop rdi
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
add rsp, 16 // skip vector, error_code
swapgs
iretq",
tf = in(reg) &self.0,
options(noreturn),
)
}
}

#[repr(C)]
#[derive(Debug, Default)]
struct ContextSwitchFrame {
Expand Down
31 changes: 31 additions & 0 deletions modules/axhal/src/arch/x86_64/gdt.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
use core::fmt;
use spin::Once;

use x86_64::instructions::tables::{lgdt, load_tss};
use x86_64::registers::segmentation::{Segment, SegmentSelector, CS};
use x86_64::structures::gdt::{Descriptor, DescriptorFlags};
use x86_64::structures::{tss::TaskStateSegment, DescriptorTablePointer};
use x86_64::{addr::VirtAddr, PrivilegeLevel};

#[no_mangle]
#[percpu::def_percpu]
static TSS: Once<TaskStateSegment> = Once::new();

#[percpu::def_percpu]
static GDT: Once<GdtStruct> = Once::new();

/// A wrapper of the Global Descriptor Table (GDT) with maximum 16 entries.
#[repr(align(16))]
pub struct GdtStruct {
Expand Down Expand Up @@ -86,3 +94,26 @@ impl fmt::Debug for GdtStruct {
.finish()
}
}

/// Initializes the per-CPU TSS and GDT structures and loads them into the
/// current CPU.
pub fn init_gdt() {
unsafe {
let tss = TSS.current_ref_raw();
let gdt = GDT.current_ref_raw();
let tss = tss.call_once(|| TaskStateSegment::new());
let gdt = gdt.call_once(|| GdtStruct::new(tss));
gdt.load();
gdt.load_tss();
}
}

/// Sets the stack pointer for privilege level 0 (RSP0) of the current TSS.
///
/// # Safety
///
/// Must be called after initialization and preemption is disabled.
pub unsafe fn tss_set_rsp0(rsp0: memory_addr::VirtAddr) {
let tss = unsafe { TSS.current_ref_mut_raw().get_mut_unchecked() };
tss.privilege_stack_table[0] = x86_64::VirtAddr::new(rsp0.as_usize() as u64);
}
15 changes: 14 additions & 1 deletion modules/axhal/src/arch/x86_64/idt.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use core::fmt;
use spin::Once;

use x86_64::addr::VirtAddr;
use x86_64::structures::idt::{Entry, HandlerFunc, InterruptDescriptorTable};
use x86_64::structures::DescriptorTablePointer;

const NUM_INT: usize = 256;

static IDT: Once<IdtStruct> = Once::new();

/// A wrapper of the Interrupt Descriptor Table (IDT).
#[repr(transparent)]
pub struct IdtStruct {
Expand Down Expand Up @@ -33,7 +36,11 @@ impl IdtStruct {
};
for i in 0..NUM_INT {
#[allow(clippy::missing_transmute_annotations)]
entries[i].set_handler_fn(unsafe { core::mem::transmute(ENTRIES[i]) });
let opt = entries[i].set_handler_fn(unsafe { core::mem::transmute(ENTRIES[i]) });
if i == 0x80 {
// enable legacy int 0x80 syscall
opt.set_privilege_level(x86_64::PrivilegeLevel::Ring3);
}
}
idt
}
Expand Down Expand Up @@ -66,3 +73,9 @@ impl fmt::Debug for IdtStruct {
.finish()
}
}

/// Initializes the global IDT and loads it into the current CPU.
pub fn init_idt() {
let idt = IDT.call_once(|| IdtStruct::new());
unsafe { idt.load() };
}
13 changes: 9 additions & 4 deletions modules/axhal/src/arch/x86_64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ mod context;
mod gdt;
mod idt;

#[cfg(feature = "uspace")]
mod syscall;

#[cfg(target_os = "none")]
mod trap;

Expand All @@ -11,10 +14,12 @@ use memory_addr::{PhysAddr, VirtAddr};
use x86::{controlregs, msr, tlb};
use x86_64::instructions::interrupts;

pub use self::context::{ExtendedState, FxsaveArea, TaskContext, TrapFrame};
pub use self::gdt::GdtStruct;
pub use self::idt::IdtStruct;
pub use x86_64::structures::tss::TaskStateSegment;
pub use self::context::{ExtendedState, FxsaveArea, TaskContext, TrapFrame, UspaceContext};
pub use self::gdt::{init_gdt, tss_set_rsp0, GdtStruct};
pub use self::idt::{init_idt, IdtStruct};

#[cfg(feature = "uspace")]
pub use self::syscall::init_syscall;

/// Allows the current CPU to respond to interrupts.
#[inline]
Expand Down
54 changes: 54 additions & 0 deletions modules/axhal/src/arch/x86_64/syscall.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.section .text
syscall_entry:
swapgs // switch to kernel gs
mov gs:[offset __PERCPU_USER_RSP_OFFSET], rsp // save user rsp
mov rsp, gs:[offset __PERCPU_TSS + {tss_rsp0_offset}] // switch to kernel stack

sub rsp, 8 // skip user ss
push gs:[offset __PERCPU_USER_RSP_OFFSET] // user rsp
push r11 // rflags
mov [rsp - 2 * 8], rcx // rip
sub rsp, 4 * 8 // skip until general registers

push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push rdi
push rsi
push rbp
push rbx
push rdx
push rcx
push rax

mov rdi, rsp
call x86_syscall_handler

pop rax
pop rcx
pop rdx
pop rbx
pop rbp
pop rsi
pop rdi
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15

add rsp, 7 * 8
mov rcx, [rsp - 5 * 8] // rip
mov r11, [rsp - 3 * 8] // rflags
mov rsp, [rsp - 2 * 8] // user rsp

swapgs
sysretq
51 changes: 51 additions & 0 deletions modules/axhal/src/arch/x86_64/syscall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use x86_64::addr::VirtAddr;
use x86_64::registers::model_specific::{Efer, EferFlags, KernelGsBase, LStar, SFMask, Star};
use x86_64::registers::rflags::RFlags;
use x86_64::structures::tss::TaskStateSegment;

use super::{GdtStruct, TrapFrame};

#[no_mangle]
#[percpu::def_percpu]
static USER_RSP_OFFSET: usize = 0;

core::arch::global_asm!(
include_str!("syscall.S"),
tss_rsp0_offset = const core::mem::offset_of!(TaskStateSegment, privilege_stack_table),
);

#[no_mangle]
pub(super) fn x86_syscall_handler(tf: &mut TrapFrame) {
info!(
"syscall {} [{}, {}, {}, {}]",
tf.rax, tf.rdi, tf.rsi, tf.rdx, tf.rdx
);
}

/// Initializes syscall support and setups the syscall handler.
pub fn init_syscall() {
extern "C" {
fn syscall_entry();
}
unsafe {
LStar::write(VirtAddr::new(syscall_entry as usize as _));
Star::write(
GdtStruct::UCODE64_SELECTOR,
GdtStruct::UDATA_SELECTOR,
GdtStruct::KCODE64_SELECTOR,
GdtStruct::KDATA_SELECTOR,
)
.unwrap();
SFMask::write(
RFlags::TRAP_FLAG
| RFlags::INTERRUPT_FLAG
| RFlags::DIRECTION_FLAG
| RFlags::IOPL_LOW
| RFlags::IOPL_HIGH
| RFlags::NESTED_TASK
| RFlags::ALIGNMENT_CHECK,
); // TF | IF | DF | IOPL | AC | NT (0x47700)
Efer::update(|efer| *efer |= EferFlags::SYSTEM_CALL_EXTENSIONS);
KernelGsBase::write(VirtAddr::new(0));
}
}
Loading

0 comments on commit f55ce42

Please sign in to comment.