mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Implement cpu_local with GS and ensure GS points to TSS
This commit is contained in:
parent
52bde1721e
commit
c2f7a10b84
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -852,7 +852,7 @@ dependencies = [
|
||||
"uart_16550",
|
||||
"uefi",
|
||||
"uefi-services",
|
||||
"x86_64 0.14.11",
|
||||
"x86_64",
|
||||
"xmas-elf 0.8.0",
|
||||
]
|
||||
|
||||
@ -1090,11 +1090,10 @@ dependencies = [
|
||||
"spin 0.9.8",
|
||||
"static_assertions",
|
||||
"tdx-guest",
|
||||
"trapframe",
|
||||
"unwinding",
|
||||
"volatile",
|
||||
"x86",
|
||||
"x86_64 0.14.11",
|
||||
"x86_64",
|
||||
"xarray",
|
||||
]
|
||||
|
||||
@ -1267,15 +1266,6 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "11.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rle-decode-fast"
|
||||
version = "1.0.3"
|
||||
@ -1437,8 +1427,8 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"iced-x86",
|
||||
"lazy_static",
|
||||
"raw-cpuid 10.7.0",
|
||||
"x86_64 0.14.11",
|
||||
"raw-cpuid",
|
||||
"x86_64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1512,16 +1502,6 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trapframe"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "105000258ba41c463b63403c9341c55a298f35f6137b1cca08c10f0409ef8d3a"
|
||||
dependencies = [
|
||||
"raw-cpuid 11.0.2",
|
||||
"x86_64 0.15.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typeflags"
|
||||
version = "0.1.0"
|
||||
@ -1714,7 +1694,7 @@ checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"bitflags 1.3.2",
|
||||
"raw-cpuid 10.7.0",
|
||||
"raw-cpuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1729,18 +1709,6 @@ dependencies = [
|
||||
"volatile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x86_64"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bc79523af8abf92fb1a970c3e086c5a343f6bcc1a0eb890f575cbb3b45743df"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"bitflags 2.6.0",
|
||||
"rustversion",
|
||||
"volatile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xarray"
|
||||
version = "0.1.0"
|
||||
|
@ -3,7 +3,7 @@
|
||||
use ostd::cpu::UserContext;
|
||||
|
||||
use super::SyscallReturn;
|
||||
use crate::{cpu::LinuxAbi, prelude::*};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(u64)]
|
||||
@ -18,7 +18,7 @@ pub enum ArchPrctlCode {
|
||||
pub fn sys_arch_prctl(
|
||||
code: u64,
|
||||
addr: u64,
|
||||
_ctx: &Context,
|
||||
ctx: &Context,
|
||||
user_ctx: &mut UserContext,
|
||||
) -> Result<SyscallReturn> {
|
||||
let arch_prctl_code = ArchPrctlCode::try_from(code)?;
|
||||
@ -26,17 +26,24 @@ pub fn sys_arch_prctl(
|
||||
"arch_prctl_code: {:?}, addr = 0x{:x}",
|
||||
arch_prctl_code, addr
|
||||
);
|
||||
let res = do_arch_prctl(arch_prctl_code, addr, user_ctx).unwrap();
|
||||
let res = do_arch_prctl(arch_prctl_code, addr, ctx, user_ctx).unwrap();
|
||||
Ok(SyscallReturn::Return(res as _))
|
||||
}
|
||||
|
||||
pub fn do_arch_prctl(code: ArchPrctlCode, addr: u64, ctx: &mut UserContext) -> Result<u64> {
|
||||
pub fn do_arch_prctl(
|
||||
code: ArchPrctlCode,
|
||||
addr: u64,
|
||||
ctx: &Context,
|
||||
user_ctx: &mut UserContext,
|
||||
) -> Result<u64> {
|
||||
match code {
|
||||
ArchPrctlCode::ARCH_SET_FS => {
|
||||
ctx.set_tls_pointer(addr as usize);
|
||||
ctx.task.set_tls_pointer(addr as usize);
|
||||
user_ctx.set_tls_pointer(addr as usize);
|
||||
user_ctx.activate_tls_pointer();
|
||||
Ok(0)
|
||||
}
|
||||
ArchPrctlCode::ARCH_GET_FS => Ok(ctx.tls_pointer() as u64),
|
||||
ArchPrctlCode::ARCH_GET_FS => Ok(user_ctx.tls_pointer() as u64),
|
||||
ArchPrctlCode::ARCH_GET_GS | ArchPrctlCode::ARCH_SET_GS => {
|
||||
return_errno_with_message!(Errno::EINVAL, "GS cannot be accessed from the user space")
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ use ostd::{cpu::UserContext, user::UserContextApi};
|
||||
|
||||
use super::{constants::*, SyscallReturn};
|
||||
use crate::{
|
||||
cpu::LinuxAbi,
|
||||
fs::{
|
||||
file_table::FileDesc,
|
||||
fs_resolver::{FsPath, AT_FDCWD},
|
||||
|
@ -117,11 +117,17 @@ SECTIONS
|
||||
# processor, while it would be copied to other dynamically allocated memory
|
||||
# areas for the application processors.
|
||||
. = ALIGN(4096);
|
||||
.cpu_local : AT(ADDR(.cpu_local) - KERNEL_VMA) {
|
||||
__cpu_local_start = .;
|
||||
KEEP(*(SORT(.cpu_local)))
|
||||
__cpu_local_end = .;
|
||||
__cpu_local_start = .;
|
||||
# Make sure that cpu_local_tss is right at the beginning of CPU local area,
|
||||
# which stores the task state segment in x86_64 architecture, so that
|
||||
# when trap from ring3 to ring0, CPU can switch stack correctly.
|
||||
.cpu_local_tss : AT(ADDR(.cpu_local_tss) - KERNEL_VMA) {
|
||||
*(.cpu_local_tss)
|
||||
}
|
||||
.cpu_local : AT(ADDR(.cpu_local) - KERNEL_VMA) {
|
||||
KEEP(*(SORT(.cpu_local)))
|
||||
}
|
||||
__cpu_local_end = .;
|
||||
|
||||
.bss : AT(ADDR(.bss) - KERNEL_VMA) {
|
||||
__bss = .;
|
||||
|
@ -39,7 +39,6 @@ owo-colors = { version = "3", optional = true }
|
||||
ostd-pod = { git = "https://github.com/asterinas/ostd-pod", rev = "c4644be", version = "0.1.1" }
|
||||
spin = "0.9.4"
|
||||
static_assertions = "1.1.0"
|
||||
trapframe = "0.10.0"
|
||||
unwinding = { version = "0.2.2", default-features = false, features = ["fde-gnu-eh-frame-hdr", "hide-trace", "panic", "personality", "unwinder"] }
|
||||
volatile = { version = "0.4.5", features = ["unstable"] }
|
||||
xarray = { git = "https://github.com/asterinas/xarray", version = "0.1.0" }
|
||||
|
@ -2,25 +2,25 @@
|
||||
|
||||
//! Architecture dependent CPU-local information utilities.
|
||||
|
||||
use x86_64::registers::segmentation::{Segment64, FS};
|
||||
use x86_64::registers::segmentation::{Segment64, GS};
|
||||
|
||||
/// Sets the base address for the CPU local storage by writing to the FS base model-specific register.
|
||||
/// Sets the base address for the CPU local storage by writing to the GS base model-specific register.
|
||||
/// This operation is marked as `unsafe` because it directly interfaces with low-level CPU registers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - This function is safe to call provided that the FS register is dedicated entirely for CPU local storage
|
||||
/// - This function is safe to call provided that the GS register is dedicated entirely for CPU local storage
|
||||
/// and is not concurrently accessed for other purposes.
|
||||
/// - The caller must ensure that `addr` is a valid address and properly aligned, as required by the CPU.
|
||||
/// - This function should only be called in contexts where the CPU is in a state to accept such changes,
|
||||
/// such as during processor initialization.
|
||||
pub(crate) unsafe fn set_base(addr: u64) {
|
||||
FS::write_base(x86_64::addr::VirtAddr::new(addr));
|
||||
GS::write_base(x86_64::addr::VirtAddr::new(addr));
|
||||
}
|
||||
|
||||
/// Gets the base address for the CPU local storage by reading the FS base model-specific register.
|
||||
/// Gets the base address for the CPU local storage by reading the GS base model-specific register.
|
||||
pub(crate) fn get_base() -> u64 {
|
||||
FS::read_base().as_u64()
|
||||
GS::read_base().as_u64()
|
||||
}
|
||||
|
||||
use crate::cpu::local::single_instr::{
|
||||
@ -29,7 +29,7 @@ use crate::cpu::local::single_instr::{
|
||||
SingleInstructionSubAssign,
|
||||
};
|
||||
|
||||
/// The GDT ensures that the FS segment is initialized to zero on boot.
|
||||
/// The GDT ensures that the GS segment is initialized to zero on boot.
|
||||
/// This assertion checks that the base address has been set.
|
||||
macro_rules! debug_assert_initialized {
|
||||
() => {
|
||||
@ -49,7 +49,7 @@ macro_rules! impl_numeric_single_instruction_for {
|
||||
debug_assert_initialized!();
|
||||
|
||||
core::arch::asm!(
|
||||
concat!("add fs:[{0}], {1", $register_format, "}"),
|
||||
concat!("add gs:[{0}], {1", $register_format, "}"),
|
||||
in(reg) offset,
|
||||
in($inout_type) val,
|
||||
options(nostack),
|
||||
@ -62,7 +62,7 @@ macro_rules! impl_numeric_single_instruction_for {
|
||||
debug_assert_initialized!();
|
||||
|
||||
core::arch::asm!(
|
||||
concat!("sub fs:[{0}], {1", $register_format, "}"),
|
||||
concat!("sub gs:[{0}], {1", $register_format, "}"),
|
||||
in(reg) offset,
|
||||
in($inout_type) val,
|
||||
options(nostack),
|
||||
@ -75,7 +75,7 @@ macro_rules! impl_numeric_single_instruction_for {
|
||||
debug_assert_initialized!();
|
||||
|
||||
core::arch::asm!(
|
||||
concat!("and fs:[{0}], {1", $register_format, "}"),
|
||||
concat!("and gs:[{0}], {1", $register_format, "}"),
|
||||
in(reg) offset,
|
||||
in($inout_type) val,
|
||||
options(nostack),
|
||||
@ -88,7 +88,7 @@ macro_rules! impl_numeric_single_instruction_for {
|
||||
debug_assert_initialized!();
|
||||
|
||||
core::arch::asm!(
|
||||
concat!("or fs:[{0}], {1", $register_format, "}"),
|
||||
concat!("or gs:[{0}], {1", $register_format, "}"),
|
||||
in(reg) offset,
|
||||
in($inout_type) val,
|
||||
options(nostack),
|
||||
@ -101,7 +101,7 @@ macro_rules! impl_numeric_single_instruction_for {
|
||||
debug_assert_initialized!();
|
||||
|
||||
core::arch::asm!(
|
||||
concat!("xor fs:[{0}], {1", $register_format, "}"),
|
||||
concat!("xor gs:[{0}], {1", $register_format, "}"),
|
||||
in(reg) offset,
|
||||
in($inout_type) val,
|
||||
options(nostack),
|
||||
@ -115,7 +115,7 @@ macro_rules! impl_numeric_single_instruction_for {
|
||||
|
||||
let val: Self;
|
||||
core::arch::asm!(
|
||||
concat!("mov {0", $register_format, "}, fs:[{1}]"),
|
||||
concat!("mov {0", $register_format, "}, gs:[{1}]"),
|
||||
out($inout_type) val,
|
||||
in(reg) offset,
|
||||
options(nostack, readonly),
|
||||
@ -129,7 +129,7 @@ macro_rules! impl_numeric_single_instruction_for {
|
||||
debug_assert_initialized!();
|
||||
|
||||
core::arch::asm!(
|
||||
concat!("mov fs:[{0}], {1", $register_format, "}"),
|
||||
concat!("mov gs:[{0}], {1", $register_format, "}"),
|
||||
in(reg) offset,
|
||||
in($inout_type) val,
|
||||
options(nostack),
|
||||
@ -162,7 +162,7 @@ macro_rules! impl_generic_single_instruction_for {
|
||||
|
||||
let val: Self;
|
||||
core::arch::asm!(
|
||||
concat!("mov {0}, fs:[{1}]"),
|
||||
concat!("mov {0}, gs:[{1}]"),
|
||||
out(reg) val,
|
||||
in(reg) offset,
|
||||
options(nostack, readonly),
|
||||
@ -176,7 +176,7 @@ macro_rules! impl_generic_single_instruction_for {
|
||||
debug_assert_initialized!();
|
||||
|
||||
core::arch::asm!(
|
||||
concat!("mov fs:[{0}], {1}"),
|
||||
concat!("mov gs:[{0}], {1}"),
|
||||
in(reg) offset,
|
||||
in(reg) val,
|
||||
options(nostack),
|
||||
@ -202,7 +202,7 @@ impl SingleInstructionLoad for bool {
|
||||
|
||||
let val: u8;
|
||||
core::arch::asm!(
|
||||
"mov {0}, fs:[{1}]",
|
||||
"mov {0}, gs:[{1}]",
|
||||
out(reg_byte) val,
|
||||
in(reg) offset,
|
||||
options(nostack, readonly),
|
||||
@ -218,7 +218,7 @@ impl SingleInstructionStore for bool {
|
||||
|
||||
let val: u8 = if val { 1 } else { 0 };
|
||||
core::arch::asm!(
|
||||
"mov fs:[{0}], {1}",
|
||||
"mov gs:[{0}], {1}",
|
||||
in(reg) offset,
|
||||
in(reg_byte) val,
|
||||
options(nostack),
|
||||
|
@ -12,10 +12,11 @@ use core::{
|
||||
use bitflags::bitflags;
|
||||
use cfg_if::cfg_if;
|
||||
use log::debug;
|
||||
pub use trapframe::GeneralRegs as RawGeneralRegs;
|
||||
use trapframe::UserContext as RawUserContext;
|
||||
use x86::bits64::segmentation::wrfsbase;
|
||||
use x86_64::registers::rflags::RFlags;
|
||||
|
||||
pub use super::trap::GeneralRegs as RawGeneralRegs;
|
||||
use super::trap::{TrapFrame, UserContext as RawUserContext};
|
||||
use crate::{
|
||||
task::scheduler,
|
||||
trap::call_irq_callback_functions,
|
||||
@ -76,6 +77,27 @@ impl UserContext {
|
||||
pub fn fp_regs_mut(&mut self) -> &mut FpRegs {
|
||||
&mut self.fp_regs
|
||||
}
|
||||
|
||||
/// Sets thread-local storage pointer.
|
||||
pub fn set_tls_pointer(&mut self, tls: usize) {
|
||||
self.set_fsbase(tls)
|
||||
}
|
||||
|
||||
/// Gets thread-local storage pointer.
|
||||
pub fn tls_pointer(&self) -> usize {
|
||||
self.fsbase()
|
||||
}
|
||||
|
||||
/// Activates thread-local storage pointer on the current CPU.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The method by itself is safe because the value of the TLS register won't affect kernel code.
|
||||
/// But if the user relies on the TLS pointer, make sure that the pointer is correctly set when
|
||||
/// entering the user space.
|
||||
pub fn activate_tls_pointer(&self) {
|
||||
unsafe { wrfsbase(self.fsbase() as u64) }
|
||||
}
|
||||
}
|
||||
|
||||
impl UserContextApiInternal for UserContext {
|
||||
@ -135,8 +157,8 @@ impl UserContextApiInternal for UserContext {
|
||||
return_reason
|
||||
}
|
||||
|
||||
fn as_trap_frame(&self) -> trapframe::TrapFrame {
|
||||
trapframe::TrapFrame {
|
||||
fn as_trap_frame(&self) -> TrapFrame {
|
||||
TrapFrame {
|
||||
rax: self.user_context.general.rax,
|
||||
rbx: self.user_context.general.rbx,
|
||||
rcx: self.user_context.general.rcx,
|
||||
|
@ -9,11 +9,13 @@ use core::fmt::Debug;
|
||||
use bitflags::bitflags;
|
||||
use log::info;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
use volatile::{access::ReadWrite, Volatile};
|
||||
|
||||
use super::registers::Capability;
|
||||
use crate::{mm::Vaddr, trap::IrqLine};
|
||||
use crate::{
|
||||
mm::Vaddr,
|
||||
trap::{IrqLine, TrapFrame},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FaultEventRegisters {
|
||||
|
@ -8,10 +8,12 @@ use alloc::{boxed::Box, fmt::Debug, sync::Arc, vec::Vec};
|
||||
|
||||
use id_alloc::IdAlloc;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
use x86_64::registers::rflags::{self, RFlags};
|
||||
|
||||
use crate::sync::{Mutex, PreemptDisabled, SpinLock, SpinLockGuard};
|
||||
use crate::{
|
||||
sync::{Mutex, PreemptDisabled, SpinLock, SpinLockGuard},
|
||||
trap::TrapFrame,
|
||||
};
|
||||
|
||||
/// The global allocator for software defined IRQ lines.
|
||||
pub(crate) static IRQ_ALLOCATOR: Once<SpinLock<IdAlloc>> = Once::new();
|
||||
|
@ -8,7 +8,6 @@ use core::{
|
||||
};
|
||||
|
||||
use log::info;
|
||||
use trapframe::TrapFrame;
|
||||
use x86::cpuid::cpuid;
|
||||
|
||||
use crate::{
|
||||
@ -16,7 +15,7 @@ use crate::{
|
||||
pit::{self, OperatingMode},
|
||||
TIMER_FREQ,
|
||||
},
|
||||
trap::IrqLine,
|
||||
trap::{IrqLine, TrapFrame},
|
||||
};
|
||||
|
||||
/// The frequency of TSC(Hz)
|
||||
|
@ -60,6 +60,10 @@ pub(crate) fn check_tdx_init() {
|
||||
}
|
||||
|
||||
pub(crate) fn init_on_bsp() {
|
||||
// SAFETY: this function is only called once on BSP.
|
||||
unsafe {
|
||||
crate::arch::trap::init(true);
|
||||
}
|
||||
irq::init();
|
||||
kernel::acpi::init();
|
||||
|
||||
|
@ -10,10 +10,12 @@ use core::fmt::Write;
|
||||
|
||||
use log::debug;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use super::{device::serial::SerialPort, kernel::IO_APIC};
|
||||
use crate::{sync::SpinLock, trap::IrqLine};
|
||||
use crate::{
|
||||
sync::SpinLock,
|
||||
trap::{IrqLine, TrapFrame},
|
||||
};
|
||||
|
||||
/// Prints the formatted arguments to the standard output using the serial port.
|
||||
#[inline]
|
||||
|
@ -11,6 +11,7 @@ core::arch::global_asm!(include_str!("switch.S"));
|
||||
pub(crate) struct TaskContext {
|
||||
pub regs: CalleeRegs,
|
||||
pub rip: usize,
|
||||
pub fsbase: usize,
|
||||
}
|
||||
|
||||
impl TaskContext {
|
||||
@ -18,8 +19,19 @@ impl TaskContext {
|
||||
Self {
|
||||
regs: CalleeRegs::new(),
|
||||
rip: 0,
|
||||
fsbase: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets thread-local storage pointer.
|
||||
pub fn set_tls_pointer(&mut self, tls: usize) {
|
||||
self.fsbase = tls;
|
||||
}
|
||||
|
||||
/// Gets thread-local storage pointer.
|
||||
pub fn tls_pointer(&self) -> usize {
|
||||
self.fsbase
|
||||
}
|
||||
}
|
||||
|
||||
/// Callee-saved registers.
|
||||
|
@ -14,7 +14,11 @@ context_switch: # (cur: *mut TaskContext, nxt: *TaskContext)
|
||||
mov [rdi + 32], r13
|
||||
mov [rdi + 40], r14
|
||||
mov [rdi + 48], r15
|
||||
rdfsbase r15
|
||||
mov [rdi + 64], r15
|
||||
# Restore nxt's registers
|
||||
mov r15, [rsi + 64]
|
||||
wrfsbase r15
|
||||
mov rsp, [rsi + 0]
|
||||
mov rbx, [rsi + 8]
|
||||
mov rbp, [rsi + 16]
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use log::warn;
|
||||
use tdx_guest::{tdcall::accept_page, tdvmcall::map_gpa, TdxTrapFrame};
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use crate::{
|
||||
mm::{
|
||||
@ -13,6 +12,7 @@ use crate::{
|
||||
PAGE_SIZE,
|
||||
},
|
||||
prelude::Paddr,
|
||||
trap::TrapFrame,
|
||||
};
|
||||
|
||||
const SHARED_BIT: u8 = 51;
|
||||
|
@ -10,7 +10,6 @@ use core::{
|
||||
|
||||
use log::info;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
use x86::{
|
||||
cpuid::cpuid,
|
||||
msr::{wrmsr, IA32_TSC_DEADLINE},
|
||||
@ -26,7 +25,7 @@ use crate::{
|
||||
tsc::TSC_FREQ,
|
||||
},
|
||||
},
|
||||
trap::IrqLine,
|
||||
trap::{IrqLine, TrapFrame},
|
||||
};
|
||||
|
||||
/// Initializes APIC with tsc deadline mode or periodic mode.
|
||||
|
@ -12,13 +12,12 @@ use core::{cell::RefCell, sync::atomic::Ordering};
|
||||
|
||||
pub use jiffies::Jiffies;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use self::apic::APIC_TIMER_CALLBACK;
|
||||
use crate::{
|
||||
arch::x86::kernel,
|
||||
cpu_local,
|
||||
trap::{self, IrqLine},
|
||||
trap::{self, IrqLine, TrapFrame},
|
||||
};
|
||||
|
||||
/// The timer frequency (Hz). Here we choose 1000Hz since 1000Hz is easier for unit conversion and
|
||||
|
120
ostd/src/arch/x86/trap/gdt.rs
Normal file
120
ostd/src/arch/x86/trap/gdt.rs
Normal file
@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: MPL-2.0 OR MIT
|
||||
//
|
||||
// The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs),
|
||||
// which is released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2020 - 2024 Runji Wang
|
||||
//
|
||||
// We make the following new changes:
|
||||
// * Link TaskStateSegment to .cpu_local area.
|
||||
// * Init TaskStateSegment on bsp/ap respectively.
|
||||
//
|
||||
// These changes are released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Configure Global Descriptor Table (GDT).
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::cell::SyncUnsafeCell;
|
||||
|
||||
use x86_64::{
|
||||
instructions::tables::{lgdt, load_tss, sgdt},
|
||||
registers::{
|
||||
model_specific::Star,
|
||||
segmentation::{Segment64, GS},
|
||||
},
|
||||
structures::{
|
||||
gdt::{Descriptor, SegmentSelector},
|
||||
tss::TaskStateSegment,
|
||||
DescriptorTablePointer,
|
||||
},
|
||||
PrivilegeLevel, VirtAddr,
|
||||
};
|
||||
|
||||
/// Init TSS & GDT.
|
||||
pub unsafe fn init(on_bsp: bool) {
|
||||
// Allocate stack for trap from user, set the stack top to TSS,
|
||||
// so that when trap from ring3 to ring0, CPU can switch stack correctly.
|
||||
let tss = if on_bsp {
|
||||
init_local_tss_on_bsp()
|
||||
} else {
|
||||
init_local_tss_on_ap()
|
||||
};
|
||||
|
||||
let (tss0, tss1) = match Descriptor::tss_segment(tss) {
|
||||
Descriptor::SystemSegment(tss0, tss1) => (tss0, tss1),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// FIXME: the segment limit assumed by x86_64 does not include the I/O port bitmap.
|
||||
|
||||
// Get current GDT.
|
||||
let gdtp = sgdt();
|
||||
let entry_count = (gdtp.limit + 1) as usize / size_of::<u64>();
|
||||
let old_gdt = core::slice::from_raw_parts(gdtp.base.as_ptr::<u64>(), entry_count);
|
||||
|
||||
// Allocate new GDT with 7 more entries.
|
||||
//
|
||||
// NOTICE: for fast syscall:
|
||||
// STAR[47:32] = K_CS = K_SS - 8
|
||||
// STAR[63:48] = U_CS32 = U_SS32 - 8 = U_CS - 16
|
||||
let mut gdt = Vec::from(old_gdt);
|
||||
gdt.extend([tss0, tss1, KCODE64, KDATA64, UCODE32, UDATA32, UCODE64].iter());
|
||||
let gdt = Vec::leak(gdt);
|
||||
|
||||
// Load new GDT and TSS.
|
||||
lgdt(&DescriptorTablePointer {
|
||||
limit: gdt.len() as u16 * 8 - 1,
|
||||
base: VirtAddr::new(gdt.as_ptr() as _),
|
||||
});
|
||||
load_tss(SegmentSelector::new(
|
||||
entry_count as u16,
|
||||
PrivilegeLevel::Ring0,
|
||||
));
|
||||
|
||||
let sysret = SegmentSelector::new(entry_count as u16 + 4, PrivilegeLevel::Ring3).0;
|
||||
let syscall = SegmentSelector::new(entry_count as u16 + 2, PrivilegeLevel::Ring0).0;
|
||||
Star::write_raw(sysret, syscall);
|
||||
|
||||
USER_SS = sysret + 8;
|
||||
USER_CS = sysret + 16;
|
||||
}
|
||||
|
||||
// The linker script ensure that cpu_local_tss section is right
|
||||
// at the beginning of cpu_local area, so that gsbase (offset zero)
|
||||
// points to LOCAL_TSS.
|
||||
#[allow(dead_code)]
|
||||
#[link_section = ".cpu_local_tss"]
|
||||
static LOCAL_TSS: SyncUnsafeCell<TaskStateSegment> = SyncUnsafeCell::new(TaskStateSegment::new());
|
||||
|
||||
unsafe fn init_local_tss_on_bsp() -> &'static TaskStateSegment {
|
||||
let tss_ptr = LOCAL_TSS.get();
|
||||
|
||||
let trap_stack_top = Box::leak(Box::new([0u8; 0x1000])).as_ptr() as u64 + 0x1000;
|
||||
(*tss_ptr).privilege_stack_table[0] = VirtAddr::new(trap_stack_top);
|
||||
&*tss_ptr
|
||||
}
|
||||
|
||||
unsafe fn init_local_tss_on_ap() -> &'static TaskStateSegment {
|
||||
let gs_base = GS::read_base().as_u64();
|
||||
let tss_ptr = gs_base as *mut TaskStateSegment;
|
||||
|
||||
let trap_stack_top = Box::leak(Box::new([0u8; 0x1000])).as_ptr() as u64 + 0x1000;
|
||||
(*tss_ptr).privilege_stack_table[0] = VirtAddr::new(trap_stack_top);
|
||||
&*tss_ptr
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
static mut USER_SS: u16 = 0;
|
||||
#[no_mangle]
|
||||
static mut USER_CS: u16 = 0;
|
||||
|
||||
const KCODE64: u64 = 0x00209800_00000000; // EXECUTABLE | USER_SEGMENT | PRESENT | LONG_MODE
|
||||
const UCODE64: u64 = 0x0020F800_00000000; // EXECUTABLE | USER_SEGMENT | USER_MODE | PRESENT | LONG_MODE
|
||||
const KDATA64: u64 = 0x00009200_00000000; // DATA_WRITABLE | USER_SEGMENT | PRESENT
|
||||
#[allow(dead_code)]
|
||||
const UDATA64: u64 = 0x0000F200_00000000; // DATA_WRITABLE | USER_SEGMENT | USER_MODE | PRESENT
|
||||
const UCODE32: u64 = 0x00cffa00_0000ffff; // EXECUTABLE | USER_SEGMENT | USER_MODE | PRESENT
|
||||
const UDATA32: u64 = 0x00cff200_0000ffff; // EXECUTABLE | USER_SEGMENT | USER_MODE | PRESENT
|
47
ostd/src/arch/x86/trap/idt.rs
Normal file
47
ostd/src/arch/x86/trap/idt.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: MPL-2.0 OR MIT
|
||||
//
|
||||
// The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs),
|
||||
// which is released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2020 - 2024 Runji Wang
|
||||
//
|
||||
// We make the following new changes:
|
||||
// * Include `trap.S` in this file and remove unused function `sidt`.
|
||||
// * Link `VECTORS` to `trap_handler_table` defined in `trap.S`.
|
||||
//
|
||||
// These changes are released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Configure Interrupt Descriptor Table (GDT).
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use core::arch::global_asm;
|
||||
|
||||
use x86_64::{
|
||||
structures::idt::{Entry, HandlerFunc, InterruptDescriptorTable},
|
||||
PrivilegeLevel,
|
||||
};
|
||||
|
||||
global_asm!(include_str!("trap.S"));
|
||||
|
||||
pub fn init() {
|
||||
extern "C" {
|
||||
#[link_name = "trap_handler_table"]
|
||||
static VECTORS: [HandlerFunc; 256];
|
||||
}
|
||||
|
||||
let idt = Box::leak(Box::new(InterruptDescriptorTable::new()));
|
||||
let entries: &'static mut [Entry<HandlerFunc>; 256] =
|
||||
unsafe { core::mem::transmute_copy(&idt) };
|
||||
for i in 0..256 {
|
||||
let opt = unsafe { entries[i].set_handler_fn(VECTORS[i]) };
|
||||
// Enable user space `int3` and `into`.
|
||||
if i == 3 || i == 4 {
|
||||
opt.set_privilege_level(PrivilegeLevel::Ring3);
|
||||
}
|
||||
}
|
||||
idt.load();
|
||||
}
|
@ -1,11 +1,28 @@
|
||||
// SPDX-License-Identifier: MPL-2.0 OR MIT
|
||||
//
|
||||
// The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs),
|
||||
// which is released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2020 - 2024 Runji Wang
|
||||
//
|
||||
// We make the following new changes:
|
||||
// * Implement the `trap_handler` of Asterinas.
|
||||
//
|
||||
// These changes are released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Handles trap.
|
||||
|
||||
mod gdt;
|
||||
mod idt;
|
||||
mod syscall;
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use cfg_if::cfg_if;
|
||||
use log::debug;
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use super::ex_table::ExTable;
|
||||
use crate::{
|
||||
@ -31,6 +48,168 @@ cpu_local_cell! {
|
||||
static IS_KERNEL_INTERRUPTED: bool = false;
|
||||
}
|
||||
|
||||
/// Trap frame of kernel interrupt
|
||||
///
|
||||
/// # Trap handler
|
||||
///
|
||||
/// You need to define a handler function like this:
|
||||
///
|
||||
/// ```
|
||||
/// #[no_mangle]
|
||||
/// extern "sysv64" fn trap_handler(tf: &mut TrapFrame) {
|
||||
/// match tf.trap_num {
|
||||
/// 3 => {
|
||||
/// println!("TRAP: BreakPoint");
|
||||
/// tf.rip += 1;
|
||||
/// }
|
||||
/// _ => panic!("TRAP: {:#x?}", tf),
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct TrapFrame {
|
||||
// Pushed by 'trap.S'
|
||||
pub rax: usize,
|
||||
pub rbx: usize,
|
||||
pub rcx: usize,
|
||||
pub rdx: usize,
|
||||
pub rsi: usize,
|
||||
pub rdi: usize,
|
||||
pub rbp: usize,
|
||||
pub rsp: usize,
|
||||
pub r8: usize,
|
||||
pub r9: usize,
|
||||
pub r10: usize,
|
||||
pub r11: usize,
|
||||
pub r12: usize,
|
||||
pub r13: usize,
|
||||
pub r14: usize,
|
||||
pub r15: usize,
|
||||
pub _pad: usize,
|
||||
|
||||
pub trap_num: usize,
|
||||
pub error_code: usize,
|
||||
|
||||
// Pushed by CPU
|
||||
pub rip: usize,
|
||||
pub cs: usize,
|
||||
pub rflags: usize,
|
||||
}
|
||||
|
||||
/// Initialize interrupt handling on x86_64.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function will:
|
||||
///
|
||||
/// - Disable interrupt.
|
||||
/// - Switch to a new [GDT], extend 7 more entries from the current one.
|
||||
/// - Switch to a new [TSS], `GSBASE` pointer to its base address.
|
||||
/// - Switch to a new [IDT], override the current one.
|
||||
/// - Enable [`syscall`] instruction.
|
||||
/// - set `EFER::SYSTEM_CALL_EXTENSIONS`
|
||||
///
|
||||
/// [GDT]: https://wiki.osdev.org/GDT
|
||||
/// [IDT]: https://wiki.osdev.org/IDT
|
||||
/// [TSS]: https://wiki.osdev.org/Task_State_Segment
|
||||
/// [`syscall`]: https://www.felixcloutier.com/x86/syscall
|
||||
///
|
||||
#[cfg(any(target_os = "none", target_os = "uefi"))]
|
||||
pub unsafe fn init(on_bsp: bool) {
|
||||
x86_64::instructions::interrupts::disable();
|
||||
gdt::init(on_bsp);
|
||||
idt::init();
|
||||
syscall::init();
|
||||
}
|
||||
|
||||
/// User space context.
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct UserContext {
|
||||
pub general: GeneralRegs,
|
||||
pub trap_num: usize,
|
||||
pub error_code: usize,
|
||||
}
|
||||
|
||||
/// General registers.
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct GeneralRegs {
|
||||
pub rax: usize,
|
||||
pub rbx: usize,
|
||||
pub rcx: usize,
|
||||
pub rdx: usize,
|
||||
pub rsi: usize,
|
||||
pub rdi: usize,
|
||||
pub rbp: usize,
|
||||
pub rsp: usize,
|
||||
pub r8: usize,
|
||||
pub r9: usize,
|
||||
pub r10: usize,
|
||||
pub r11: usize,
|
||||
pub r12: usize,
|
||||
pub r13: usize,
|
||||
pub r14: usize,
|
||||
pub r15: usize,
|
||||
pub rip: usize,
|
||||
pub rflags: usize,
|
||||
pub fsbase: usize,
|
||||
pub gsbase: usize,
|
||||
}
|
||||
|
||||
impl UserContext {
|
||||
/// Get number of syscall.
|
||||
pub fn get_syscall_num(&self) -> usize {
|
||||
self.general.rax
|
||||
}
|
||||
|
||||
/// Get return value of syscall.
|
||||
pub fn get_syscall_ret(&self) -> usize {
|
||||
self.general.rax
|
||||
}
|
||||
|
||||
/// Set return value of syscall.
|
||||
pub fn set_syscall_ret(&mut self, ret: usize) {
|
||||
self.general.rax = ret;
|
||||
}
|
||||
|
||||
/// Get syscall args.
|
||||
pub fn get_syscall_args(&self) -> [usize; 6] {
|
||||
[
|
||||
self.general.rdi,
|
||||
self.general.rsi,
|
||||
self.general.rdx,
|
||||
self.general.r10,
|
||||
self.general.r8,
|
||||
self.general.r9,
|
||||
]
|
||||
}
|
||||
|
||||
/// Set instruction pointer.
|
||||
pub fn set_ip(&mut self, ip: usize) {
|
||||
self.general.rip = ip;
|
||||
}
|
||||
|
||||
/// Set stack pointer.
|
||||
pub fn set_sp(&mut self, sp: usize) {
|
||||
self.general.rsp = sp;
|
||||
}
|
||||
|
||||
/// Get stack pointer.
|
||||
pub fn get_sp(&self) -> usize {
|
||||
self.general.rsp
|
||||
}
|
||||
|
||||
/// Set thread-local storage pointer.
|
||||
pub fn set_tls(&mut self, tls: usize) {
|
||||
self.general.fsbase = tls;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this function is called within the context of an IRQ handler
|
||||
/// and the IRQ occurs while the CPU is executing in the kernel mode.
|
||||
/// Otherwise, it returns false.
|
||||
@ -38,7 +217,7 @@ pub fn is_kernel_interrupted() -> bool {
|
||||
IS_KERNEL_INTERRUPTED.load()
|
||||
}
|
||||
|
||||
/// Only from kernel
|
||||
/// Handle traps (only from kernel).
|
||||
#[no_mangle]
|
||||
extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
||||
if CpuException::is_cpu_exception(f.trap_num as u16) {
|
140
ostd/src/arch/x86/trap/syscall.S
Normal file
140
ostd/src/arch/x86/trap/syscall.S
Normal file
@ -0,0 +1,140 @@
|
||||
/* SPDX-License-Identifier: MPL-2.0 OR MIT
|
||||
*
|
||||
* The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs),
|
||||
* which is released under the following license:
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (c) 2020 - 2024 Runji Wang
|
||||
*
|
||||
* We make the following new changes:
|
||||
* * Skip saving/restoring the fsgsbase registers.
|
||||
*
|
||||
* These changes are released under the following license:
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
.text
|
||||
# extern "sysv64" fn syscall_return(&mut GeneralRegs)
|
||||
.global syscall_return
|
||||
syscall_return:
|
||||
# disable interrupt
|
||||
cli
|
||||
|
||||
# save callee-saved registers
|
||||
push r15
|
||||
push r14
|
||||
push r13
|
||||
push r12
|
||||
push rbp
|
||||
push rbx
|
||||
|
||||
push rdi # keep rsp 16 bytes align
|
||||
mov gs:4, rsp # store kernel rsp -> TSS.sp0
|
||||
mov rsp, rdi # set rsp -> GeneralRegs
|
||||
|
||||
# restore user gsbase
|
||||
swapgs
|
||||
|
||||
pop rax
|
||||
pop rbx
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rbp
|
||||
pop r8 # skip rsp
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
pop r12
|
||||
pop r13
|
||||
pop r14
|
||||
pop r15
|
||||
# rip
|
||||
# rflags
|
||||
# fsbase
|
||||
# gsbase
|
||||
# trap_num
|
||||
# error_code
|
||||
|
||||
# determain sysret or iret
|
||||
cmp dword ptr [rsp + 4*8], 0x100 # syscall?
|
||||
je sysret
|
||||
iret:
|
||||
# construct trap frame
|
||||
push [USER_SS] # push ss
|
||||
push [rsp - 8*8] # push rsp
|
||||
push [rsp + 3*8] # push rflags
|
||||
push [USER_CS] # push cs
|
||||
push [rsp + 4*8] # push rip
|
||||
|
||||
iretq
|
||||
|
||||
sysret:
|
||||
pop rcx # rcx = rip
|
||||
pop r11 # r11 = rflags
|
||||
mov rsp, [rsp - 11*8] # load rsp
|
||||
|
||||
sysretq
|
||||
|
||||
# sysretq instruction do:
|
||||
# - load cs, ss
|
||||
# - load rflags <- r11
|
||||
# - load rip <- rcx
|
||||
|
||||
.global syscall_entry
|
||||
syscall_entry:
|
||||
# syscall instruction do:
|
||||
# - load cs
|
||||
# - store rflags -> r11
|
||||
# - mask rflags
|
||||
# - store rip -> rcx
|
||||
# - load rip
|
||||
|
||||
swapgs # swap in kernel gs
|
||||
mov gs:12, rsp # store user rsp -> scratch at TSS.sp1
|
||||
mov rsp, gs:4 # load kernel rsp <- TSS.sp0
|
||||
pop rsp # load rsp -> GeneralRegs
|
||||
add rsp, 21*8 # rsp -> error code of GeneralRegs
|
||||
|
||||
push 0x100 # push trap_num
|
||||
sub rsp, 16 # skip fsbase, gsbase
|
||||
# push general registers
|
||||
push r11 # push rflags
|
||||
push rcx # push rip
|
||||
|
||||
.global trap_syscall_entry
|
||||
trap_syscall_entry:
|
||||
push r15
|
||||
push r14
|
||||
push r13
|
||||
push r12
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push gs:12 # push rsp
|
||||
push rbp
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push rbx
|
||||
push rax
|
||||
|
||||
# restore callee-saved registers
|
||||
mov rsp, gs:4 # load kernel rsp <- TSS.sp0
|
||||
pop rbx
|
||||
|
||||
pop rbx
|
||||
pop rbp
|
||||
pop r12
|
||||
pop r13
|
||||
pop r14
|
||||
pop r15
|
||||
|
||||
# go back to Rust
|
||||
ret
|
101
ostd/src/arch/x86/trap/syscall.rs
Normal file
101
ostd/src/arch/x86/trap/syscall.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: MPL-2.0 OR MIT
|
||||
//
|
||||
// The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs),
|
||||
// which is released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright (c) 2020 - 2024 Runji Wang
|
||||
//
|
||||
// We make the following new changes:
|
||||
// * Revise some comments.
|
||||
//
|
||||
// These changes are released under the following license:
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Configure fast syscall.
|
||||
|
||||
use core::arch::global_asm;
|
||||
|
||||
use x86::cpuid::CpuId;
|
||||
use x86_64::{
|
||||
registers::{
|
||||
control::{Cr4, Cr4Flags},
|
||||
model_specific::{Efer, EferFlags, LStar, SFMask},
|
||||
rflags::RFlags,
|
||||
},
|
||||
VirtAddr,
|
||||
};
|
||||
|
||||
use super::UserContext;
|
||||
|
||||
global_asm!(include_str!("syscall.S"));
|
||||
|
||||
pub fn init() {
|
||||
let cpuid = CpuId::new();
|
||||
unsafe {
|
||||
// Enable `syscall` instruction.
|
||||
assert!(cpuid
|
||||
.get_extended_processor_and_feature_identifiers()
|
||||
.unwrap()
|
||||
.has_syscall_sysret());
|
||||
Efer::update(|efer| {
|
||||
efer.insert(EferFlags::SYSTEM_CALL_EXTENSIONS);
|
||||
});
|
||||
|
||||
// Enable `FSGSBASE` instructions.
|
||||
assert!(cpuid.get_extended_feature_info().unwrap().has_fsgsbase());
|
||||
Cr4::update(|cr4| {
|
||||
cr4.insert(Cr4Flags::FSGSBASE);
|
||||
});
|
||||
|
||||
// Flags to clear on syscall.
|
||||
// Copy from Linux 5.0, TF|DF|IF|IOPL|AC|NT
|
||||
const RFLAGS_MASK: u64 = 0x47700;
|
||||
|
||||
LStar::write(VirtAddr::new(syscall_entry as usize as u64));
|
||||
SFMask::write(RFlags::from_bits(RFLAGS_MASK).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
extern "sysv64" {
|
||||
fn syscall_entry();
|
||||
fn syscall_return(regs: &mut UserContext);
|
||||
}
|
||||
|
||||
impl UserContext {
|
||||
/// Go to user space with the context, and come back when a trap occurs.
|
||||
///
|
||||
/// On return, the context will be reset to the status before the trap.
|
||||
/// Trap reason and error code will be placed at `trap_num` and `error_code`.
|
||||
///
|
||||
/// If the trap was triggered by `syscall` instruction, the `trap_num` will be set to `0x100`.
|
||||
///
|
||||
/// If `trap_num` is `0x100`, it will go user by `sysret` (`rcx` and `r11` are dropped),
|
||||
/// otherwise it will use `iret`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// use trapframe::{UserContext, GeneralRegs};
|
||||
///
|
||||
/// // init user space context
|
||||
/// let mut context = UserContext {
|
||||
/// general: GeneralRegs {
|
||||
/// rip: 0x1000,
|
||||
/// rsp: 0x10000,
|
||||
/// ..Default::default()
|
||||
/// },
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// // go to user
|
||||
/// context.run();
|
||||
/// // back from user
|
||||
/// println!("back from user: {:#x?}", context);
|
||||
/// ```
|
||||
pub fn run(&mut self) {
|
||||
unsafe {
|
||||
syscall_return(self);
|
||||
}
|
||||
}
|
||||
}
|
149
ostd/src/arch/x86/trap/trap.S
Normal file
149
ostd/src/arch/x86/trap/trap.S
Normal file
@ -0,0 +1,149 @@
|
||||
/* SPDX-License-Identifier: MPL-2.0 OR MIT
|
||||
*
|
||||
* The original source code is from [trapframe-rs](https://github.com/rcore-os/trapframe-rs),
|
||||
* which is released under the following license:
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (c) 2020 - 2024 Runji Wang
|
||||
*
|
||||
* We make the following new changes:
|
||||
* * Add the `trap_handler_table`.
|
||||
*
|
||||
* These changes are released under the following license:
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
.equ NUM_INT, 256
|
||||
|
||||
.altmacro
|
||||
.macro DEF_HANDLER, i
|
||||
.Ltrap_handler_\i:
|
||||
.if \i == 8 || (\i >= 10 && \i <= 14) || \i == 17
|
||||
# error code pushed by CPU
|
||||
push \i # interrupt vector
|
||||
jmp trap_common
|
||||
.else
|
||||
push 0 # fill in error code in TrapFrame
|
||||
push \i # interrupt vector
|
||||
jmp trap_common
|
||||
.endif
|
||||
.endm
|
||||
|
||||
.section .text
|
||||
_trap_handlers:
|
||||
.set i, 0
|
||||
.rept NUM_INT
|
||||
DEF_HANDLER %i
|
||||
.set i, i + 1
|
||||
.endr
|
||||
|
||||
.macro DEF_TABLE_ENTRY, i
|
||||
.quad .Ltrap_handler_\i
|
||||
.endm
|
||||
|
||||
.section .rodata
|
||||
.global trap_handler_table
|
||||
trap_handler_table:
|
||||
.set i, 0
|
||||
.rept NUM_INT
|
||||
DEF_TABLE_ENTRY %i
|
||||
.set i, i + 1
|
||||
.endr
|
||||
|
||||
.section .text
|
||||
.global trap_common
|
||||
trap_common:
|
||||
push rax
|
||||
mov ax, [rsp + 4*8] # load cs
|
||||
and ax, 0x3 # test
|
||||
jz __from_kernel # continue trap
|
||||
|
||||
__from_user:
|
||||
/*
|
||||
kernel stack:
|
||||
- ptr to GeneralRegs
|
||||
- ss
|
||||
- rsp
|
||||
- rflags
|
||||
- cs
|
||||
- rip
|
||||
- error code
|
||||
- trap num
|
||||
- rax
|
||||
*/
|
||||
swapgs # swap in kernel gs
|
||||
mov rax, [rsp + 6*8] # rax = user rsp
|
||||
mov gs:12, rax # store user rsp -> scratch at TSS.sp1
|
||||
|
||||
mov rsp, [rsp + 8*8] # load rsp -> GeneralRegs
|
||||
add rsp, 22*8 # rsp -> top of GeneralRegs
|
||||
mov rax, gs:4 # rax = kernel stack
|
||||
|
||||
# push trap_num, error_code
|
||||
push [rax - 6*8] # push error_code
|
||||
push [rax - 7*8] # push trap_num
|
||||
sub rsp, 16 # skip fsbase, gsbase
|
||||
# push general registers
|
||||
push [rax - 3*8] # push rflags
|
||||
push [rax - 5*8] # push rip
|
||||
mov rax, [rax - 8*8] # pop rax
|
||||
jmp trap_syscall_entry
|
||||
|
||||
__from_kernel:
|
||||
/*
|
||||
kernel stack:
|
||||
- rflags
|
||||
- cs
|
||||
- rip
|
||||
- error code
|
||||
- trap num
|
||||
- rax
|
||||
*/
|
||||
pop rax
|
||||
push 0
|
||||
push r15
|
||||
push r14
|
||||
push r13
|
||||
push r12
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
lea r8, [rsp + 13*8]
|
||||
push r8 # push rsp
|
||||
push rbp
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push rbx
|
||||
push rax
|
||||
|
||||
mov rdi, rsp
|
||||
call trap_handler
|
||||
|
||||
.global trap_return
|
||||
trap_return:
|
||||
pop rax
|
||||
pop rbx
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rbp
|
||||
pop r8 # skip rsp
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
pop r12
|
||||
pop r13
|
||||
pop r14
|
||||
pop r15
|
||||
|
||||
# skip padding, trap_num, error_code
|
||||
add rsp, 24
|
||||
|
||||
iretq
|
@ -121,7 +121,7 @@ fn ap_early_entry(local_apic_id: u32) -> ! {
|
||||
|
||||
// SAFETY: this function is only called once on this AP.
|
||||
unsafe {
|
||||
trapframe::init();
|
||||
crate::arch::trap::init(false);
|
||||
}
|
||||
|
||||
// SAFETY: this function is only called once on this AP, after the BSP has
|
||||
|
@ -18,6 +18,7 @@
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(ptr_sub_ptr)]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(sync_unsafe_cell)]
|
||||
// The `generic_const_exprs` feature is incomplete however required for the page table
|
||||
// const generic implementation. We are using this feature in a conservative manner.
|
||||
#![allow(incomplete_features)]
|
||||
@ -87,7 +88,6 @@ pub unsafe fn init() {
|
||||
mm::kspace::init_kernel_page_table(mm::init_page_meta());
|
||||
mm::misc_init();
|
||||
|
||||
trapframe::init();
|
||||
// SAFETY: This function is called only once in the entire system.
|
||||
unsafe { trap::softirq::init() };
|
||||
arch::init_on_bsp();
|
||||
|
@ -32,6 +32,7 @@ pub struct Task {
|
||||
user_space: Option<Arc<UserSpace>>,
|
||||
ctx: UnsafeCell<TaskContext>,
|
||||
/// kernel stack, note that the top is SyscallFrame/TrapFrame
|
||||
#[allow(dead_code)]
|
||||
kstack: KernelStack,
|
||||
|
||||
schedule_info: TaskScheduleInfo,
|
||||
@ -53,6 +54,22 @@ impl Task {
|
||||
&self.ctx
|
||||
}
|
||||
|
||||
/// Sets thread-local storage pointer.
|
||||
pub fn set_tls_pointer(&self, tls: usize) {
|
||||
let ctx_ptr = self.ctx.get();
|
||||
|
||||
// SAFETY: it's safe to set user tls pointer in kernel context.
|
||||
unsafe { (*ctx_ptr).set_tls_pointer(tls) }
|
||||
}
|
||||
|
||||
/// Gets thread-local storage pointer.
|
||||
pub fn tls_pointer(&self) -> usize {
|
||||
let ctx_ptr = self.ctx.get();
|
||||
|
||||
// SAFETY: it's safe to get user tls pointer in kernel context.
|
||||
unsafe { (*ctx_ptr).tls_pointer() }
|
||||
}
|
||||
|
||||
/// Yields execution so that another task may be scheduled.
|
||||
///
|
||||
/// Note that this method cannot be simply named "yield" as the name is
|
||||
@ -176,21 +193,14 @@ impl TaskOptions {
|
||||
current_task.exit();
|
||||
}
|
||||
|
||||
let mut new_task = Task {
|
||||
func: self.func.unwrap(),
|
||||
data: self.data.unwrap(),
|
||||
user_space: self.user_space,
|
||||
ctx: UnsafeCell::new(TaskContext::default()),
|
||||
kstack: KernelStack::new_with_guard_page()?,
|
||||
schedule_info: TaskScheduleInfo {
|
||||
cpu: AtomicCpuId::default(),
|
||||
priority: self.priority,
|
||||
cpu_affinity: self.cpu_affinity,
|
||||
},
|
||||
};
|
||||
let kstack = KernelStack::new_with_guard_page()?;
|
||||
|
||||
let ctx = new_task.ctx.get_mut();
|
||||
ctx.set_instruction_pointer(kernel_task_entry as usize);
|
||||
let mut ctx = UnsafeCell::new(TaskContext::default());
|
||||
if let Some(user_space) = self.user_space.as_ref() {
|
||||
ctx.get_mut().set_tls_pointer(user_space.tls_pointer());
|
||||
};
|
||||
ctx.get_mut()
|
||||
.set_instruction_pointer(kernel_task_entry as usize);
|
||||
// We should reserve space for the return address in the stack, otherwise
|
||||
// we will write across the page boundary due to the implementation of
|
||||
// the context switch.
|
||||
@ -199,7 +209,21 @@ impl TaskOptions {
|
||||
// to at least 16 bytes. And a larger alignment is needed if larger arguments
|
||||
// are passed to the function. The `kernel_task_entry` function does not
|
||||
// have any arguments, so we only need to align the stack pointer to 16 bytes.
|
||||
ctx.set_stack_pointer(crate::mm::paddr_to_vaddr(new_task.kstack.end_paddr() - 16));
|
||||
ctx.get_mut()
|
||||
.set_stack_pointer(crate::mm::paddr_to_vaddr(kstack.end_paddr() - 16));
|
||||
|
||||
let new_task = Task {
|
||||
func: self.func.unwrap(),
|
||||
data: self.data.unwrap(),
|
||||
user_space: self.user_space,
|
||||
ctx,
|
||||
kstack,
|
||||
schedule_info: TaskScheduleInfo {
|
||||
cpu: AtomicCpuId::default(),
|
||||
priority: self.priority,
|
||||
cpu_affinity: self.cpu_affinity,
|
||||
},
|
||||
};
|
||||
|
||||
Ok(new_task)
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use crate::{arch::irq::IRQ_LIST, cpu_local_cell};
|
||||
use crate::{arch::irq::IRQ_LIST, cpu_local_cell, trap::TrapFrame};
|
||||
|
||||
pub(crate) fn call_irq_callback_functions(trap_frame: &TrapFrame, irq_number: usize) {
|
||||
// For x86 CPUs, interrupts are not re-entrant. Local interrupts will be disabled when
|
||||
|
@ -4,11 +4,10 @@
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use crate::{
|
||||
arch::irq::{self, IrqCallbackHandle, IRQ_ALLOCATOR},
|
||||
prelude::*,
|
||||
trap::TrapFrame,
|
||||
Error,
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@ pub mod softirq;
|
||||
|
||||
pub use handler::in_interrupt_context;
|
||||
pub use softirq::SoftIrqLine;
|
||||
pub use trapframe::TrapFrame;
|
||||
|
||||
pub(crate) use self::handler::call_irq_callback_functions;
|
||||
pub use self::irq::{disable_local, DisabledLocalIrqGuard, IrqCallbackFunction, IrqLine};
|
||||
pub use crate::arch::trap::TrapFrame;
|
||||
|
@ -4,9 +4,7 @@
|
||||
|
||||
//! User space.
|
||||
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use crate::{cpu::UserContext, mm::VmSpace, prelude::*, task::Task};
|
||||
use crate::{cpu::UserContext, mm::VmSpace, prelude::*, task::Task, trap::TrapFrame};
|
||||
|
||||
/// A user space.
|
||||
///
|
||||
@ -47,6 +45,16 @@ impl UserSpace {
|
||||
pub fn user_mode(&self) -> UserMode<'_> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets thread-local storage pointer.
|
||||
pub fn set_tls_pointer(&mut self, tls: usize) {
|
||||
self.init_ctx.set_tls_pointer(tls)
|
||||
}
|
||||
|
||||
/// Gets thread-local storage pointer.
|
||||
pub fn tls_pointer(&self) -> usize {
|
||||
self.init_ctx.tls_pointer()
|
||||
}
|
||||
}
|
||||
|
||||
/// Specific architectures need to implement this trait. This should only used in [`UserMode`]
|
||||
|
Loading…
x
Reference in New Issue
Block a user