1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
use core::{arch::asm, fmt};
use memory_addr::VirtAddr;
use super::GdtStruct;
/// Saved registers when a trap (interrupt or exception) occurs.
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Default, Clone, Copy)]
pub struct TrapFrame {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rbx: u64,
pub rbp: u64,
pub rsi: u64,
pub rdi: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
// Pushed by `trap.S`
pub vector: u64,
pub error_code: u64,
// Pushed by CPU
pub rip: u64,
pub cs: u64,
pub rflags: u64,
pub rsp: u64,
pub ss: u64,
}
impl TrapFrame {
/// Whether the trap is from userspace.
pub const fn is_user(&self) -> bool {
self.cs & 0b11 == 3
}
/// To set the stack pointer
pub fn set_user_sp(&mut self, user_sp: usize) {
self.rsp = user_sp as _;
}
/// 用于第一次进入应用程序时的初始化
pub fn app_init_context(app_entry: usize, user_sp: usize) -> Self {
// 当前版本的riscv不支持使用set_spp函数,需要手动修改
// 修改当前的sstatus为User,即是第8位置0
let mut trap_frame = TrapFrame::default();
trap_frame.set_user_sp(user_sp);
trap_frame.cs = GdtStruct::UCODE64_SELECTOR.0 as _;
trap_frame.ss = GdtStruct::UDATA_SELECTOR.0 as _;
trap_frame.rip = app_entry as _;
trap_frame
}
/// set the return code
pub fn set_ret_code(&mut self, ret_value: usize) {
self.rax = ret_value as _;
}
/// 设置TLS
pub fn set_tls(&mut self, _tls_value: usize) {
// panic!("set tls: {:#x}", tls_value);
// unsafe {
// write_thread_pointer(tls_value);
// }
todo!("set tls");
}
/// 获取 sp
pub fn get_sp(&self) -> usize {
self.rsp as _
}
/// 设置 pc
pub fn set_pc(&mut self, pc: usize) {
self.rip = pc as _;
}
/// 设置 arg0
pub fn set_arg0(&mut self, arg: usize) {
self.rdi = arg as _;
}
/// 设置 arg1
pub fn set_arg1(&mut self, arg: usize) {
self.rsi = arg as _;
}
/// 设置 arg2
pub fn set_arg2(&mut self, arg: usize) {
self.rdx = arg as _;
}
/// 获取 pc
pub fn get_pc(&self) -> usize {
self.rip as _
}
/// 获取 ret
pub fn get_ret_code(&self) -> usize {
self.rax as _
}
/// 设置返回地址
pub fn set_ra(&mut self, _ra: usize) {
todo!()
}
/// 获取所有 syscall 参数
pub fn get_syscall_args(&self) -> [usize; 6] {
[self.rdi, self.rsi, self.rdx, self.r10, self.r8, self.r9].map(|n| n as _)
}
/// 获取 syscall id
pub fn get_syscall_num(&self) -> usize {
self.rax as _
}
}
#[repr(C)]
#[derive(Debug, Default)]
struct ContextSwitchFrame {
r15: u64,
r14: u64,
r13: u64,
r12: u64,
rbx: u64,
rbp: u64,
rip: u64,
}
/// A 512-byte memory region for the FXSAVE/FXRSTOR instruction to save and
/// restore the x87 FPU, MMX, XMM, and MXCSR registers.
///
/// See <https://www.felixcloutier.com/x86/fxsave> for more details.
#[allow(missing_docs)]
#[repr(C, align(16))]
#[derive(Debug)]
pub struct FxsaveArea {
pub fcw: u16,
pub fsw: u16,
pub ftw: u16,
pub fop: u16,
pub fip: u64,
pub fdp: u64,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub st: [u64; 16],
pub xmm: [u64; 32],
_padding: [u64; 12],
}
static_assertions::const_assert_eq!(core::mem::size_of::<FxsaveArea>(), 512);
/// Extended state of a task, such as FP/SIMD states.
pub struct ExtendedState {
/// Memory region for the FXSAVE/FXRSTOR instruction.
pub fxsave_area: FxsaveArea,
}
#[cfg(feature = "fp_simd")]
impl ExtendedState {
#[inline]
fn save(&mut self) {
unsafe { core::arch::x86_64::_fxsave64(&mut self.fxsave_area as *mut _ as *mut u8) }
}
#[inline]
fn restore(&self) {
unsafe { core::arch::x86_64::_fxrstor64(&self.fxsave_area as *const _ as *const u8) }
}
const fn default() -> Self {
let mut area: FxsaveArea = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
area.fcw = 0x37f;
area.ftw = 0xffff;
area.mxcsr = 0x1f80;
Self { fxsave_area: area }
}
}
impl fmt::Debug for ExtendedState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ExtendedState")
.field("fxsave_area", &self.fxsave_area)
.finish()
}
}
/// Saved hardware states of a task.
///
/// The context usually includes:
///
/// - Callee-saved registers
/// - Stack pointer register
/// - Thread pointer register (for thread-local storage, currently unsupported)
/// - FP/SIMD registers
///
/// On context switch, current task saves its context from CPU to memory,
/// and the next task restores its context from memory to CPU.
///
/// On x86_64, callee-saved registers are saved to the kernel stack by the
/// `PUSH` instruction. So that [`rsp`] is the `RSP` after callee-saved
/// registers are pushed, and [`kstack_top`] is the top of the kernel stack
/// (`RSP` before any push).
///
/// [`rsp`]: TaskContext::rsp
/// [`kstack_top`]: TaskContext::kstack_top
#[derive(Debug)]
pub struct TaskContext {
/// The kernel stack top of the task.
pub kstack_top: VirtAddr,
/// `RSP` after all callee-saved registers are pushed.
pub rsp: u64,
/// Thread Local Storage (TLS).
pub fs_base: usize,
/// Extended states, i.e., FP/SIMD states.
#[cfg(feature = "fp_simd")]
pub ext_state: ExtendedState,
}
impl TaskContext {
/// Creates a new default context for a new task.
pub const fn new() -> Self {
Self {
kstack_top: VirtAddr::from(0),
rsp: 0,
fs_base: 0,
#[cfg(feature = "fp_simd")]
ext_state: ExtendedState::default(),
}
}
/// Initializes the context for a new task, with the given entry point and
/// kernel stack.
pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) {
unsafe {
// x86_64 calling convention: the stack must be 16-byte aligned before
// calling a function. That means when entering a new task (`ret` in `context_switch`
// is executed), (stack pointer + 8) should be 16-byte aligned.
let frame_ptr = (kstack_top.as_mut_ptr() as *mut u64).sub(1);
let frame_ptr = (frame_ptr as *mut ContextSwitchFrame).sub(1);
core::ptr::write(
frame_ptr,
ContextSwitchFrame {
rip: entry as _,
..Default::default()
},
);
self.rsp = frame_ptr as u64;
}
self.kstack_top = kstack_top;
self.fs_base = tls_area.as_usize();
}
/// Switches to another task.
///
/// It first saves the current task's context from CPU to this place, and then
/// restores the next task's context from `next_ctx` to CPU.
pub fn switch_to(&mut self, next_ctx: &Self) {
#[cfg(feature = "fp_simd")]
{
self.ext_state.save();
next_ctx.ext_state.restore();
}
#[cfg(any(feature = "tls", feature = "monolithic"))]
{
self.fs_base = super::read_thread_pointer();
unsafe { super::write_thread_pointer(next_ctx.fs_base) };
}
#[cfg(feature = "monolithic")]
unsafe {
// change gs data
asm!("mov gs:[offset __PERCPU_KERNEL_RSP_OFFSET], {kernel_sp}",
kernel_sp = in(reg) next_ctx.kstack_top.as_usize() + core::mem::size_of::<TrapFrame>());
}
crate::set_tss_stack_top(next_ctx.kstack_top + core::mem::size_of::<TrapFrame>());
unsafe { context_switch(&mut self.rsp, &next_ctx.rsp) }
}
}
#[naked]
unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) {
asm!(
"
push rbp
push rbx
push r12
push r13
push r14
push r15
mov [rdi], rsp
mov rsp, [rsi]
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret",
options(noreturn),
)
}