Begin using sysretq in the system call handler.
This commit is contained in:
@@ -14,11 +14,12 @@ pub const GDT_NULL: usize = 0;
|
||||
pub const GDT_KERNEL_CODE: usize = 1;
|
||||
pub const GDT_KERNEL_DATA: usize = 2;
|
||||
pub const GDT_KERNEL_TLS: usize = 3;
|
||||
pub const GDT_USER_CODE: usize = 4;
|
||||
pub const GDT_USER_CODE32_UNUSED: usize = 4;
|
||||
pub const GDT_USER_DATA: usize = 5;
|
||||
pub const GDT_USER_TLS: usize = 6;
|
||||
pub const GDT_TSS: usize = 7;
|
||||
pub const GDT_TSS_HIGH: usize = 8;
|
||||
pub const GDT_USER_CODE: usize = 6;
|
||||
pub const GDT_USER_TLS: usize = 7;
|
||||
pub const GDT_TSS: usize = 8;
|
||||
pub const GDT_TSS_HIGH: usize = 9;
|
||||
|
||||
pub const GDT_A_PRESENT: u8 = 1 << 7;
|
||||
pub const GDT_A_RING_0: u8 = 0 << 5;
|
||||
@@ -61,7 +62,7 @@ pub static mut GDTR: DescriptorTablePointer<SegmentDescriptor> = DescriptorTable
|
||||
};
|
||||
|
||||
#[thread_local]
|
||||
pub static mut GDT: [GdtEntry; 9] = [
|
||||
pub static mut GDT: [GdtEntry; 10] = [
|
||||
// Null
|
||||
GdtEntry::new(0, 0, 0, 0),
|
||||
// Kernel code
|
||||
@@ -70,10 +71,12 @@ pub static mut GDT: [GdtEntry; 9] = [
|
||||
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
|
||||
// Kernel TLS
|
||||
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
|
||||
// User code
|
||||
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
|
||||
// Dummy 32-bit user code - apparently necessary for SYSEXIT. We restrict it to ring 0 anyway.
|
||||
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_PROTECTED_MODE),
|
||||
// User data
|
||||
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
|
||||
// User (64-bit) code
|
||||
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
|
||||
// User TLS
|
||||
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
|
||||
// TSS
|
||||
|
||||
@@ -285,6 +285,6 @@ impl IdtEntry {
|
||||
// A function to set the offset more easily
|
||||
pub fn set_func(&mut self, func: unsafe extern fn()) {
|
||||
self.set_flags(IdtFlags::PRESENT | IdtFlags::RING_0 | IdtFlags::INTERRUPT);
|
||||
self.set_offset(8, func as usize);
|
||||
self.set_offset((crate::gdt::GDT_KERNEL_CODE as u16) << 3, func as usize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@ impl IretRegisters {
|
||||
println!("RFLAG: {:>016X}", { self.rflags });
|
||||
println!("CS: {:>016X}", { self.cs });
|
||||
println!("RIP: {:>016X}", { self.rip });
|
||||
|
||||
if self.cs & 0b11 != 0b00 {
|
||||
println!("RSP: {:>016X}", { self.rsp });
|
||||
println!("SS: {:>016X}", { self.ss });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,18 @@ use crate::{
|
||||
use x86::msr;
|
||||
|
||||
pub unsafe fn init() {
|
||||
msr::wrmsr(msr::IA32_STAR, ((gdt::GDT_KERNEL_CODE as u64) << 3) << 32);
|
||||
// IA32_STAR[31:0] are reserved.
|
||||
|
||||
// The base selector of the two consecutive segments for kernel code and the immediately
|
||||
// suceeding stack (data).
|
||||
let syscall_cs_ss_base = (gdt::GDT_KERNEL_CODE as u16) << 3;
|
||||
// The base selector of the three consecutive segments (of which two are used) for user code
|
||||
// and user data. It points to a 32-bit code segment, which must be followed by a data segment
|
||||
// (stack), and a 64-bit code segment.
|
||||
let sysret_cs_ss_base = ((gdt::GDT_USER_CODE32_UNUSED as u16) << 3) | u16::from(gdt::GDT_A_RING_3);
|
||||
let star_high = u32::from(syscall_cs_ss_base) | (u32::from(sysret_cs_ss_base) << 16);
|
||||
|
||||
msr::wrmsr(msr::IA32_STAR, u64::from(star_high) << 32);
|
||||
msr::wrmsr(msr::IA32_LSTAR, syscall_instruction as u64);
|
||||
msr::wrmsr(msr::IA32_FMASK, 0x0300); // Clear trap flag and interrupt enable
|
||||
msr::wrmsr(msr::IA32_KERNEL_GSBASE, &gdt::TSS as *const _ as u64);
|
||||
@@ -52,15 +63,13 @@ function!(syscall_instruction => {
|
||||
// Yes, this is magic. No, you don't need to understand
|
||||
"
|
||||
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
|
||||
mov gs:[28], rsp // Save userspace stack pointer
|
||||
mov rsp, gs:[4] // Load kernel stack pointer
|
||||
push WORD PTR 5 * 8 + 3 // Push fake userspace SS (resembling iret frame)
|
||||
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 WORD PTR 6 * 8 + 3 // Push fake userspace CS (resembling iret frame)
|
||||
push rcx // Push userspace return pointer
|
||||
swapgs // Restore gs
|
||||
",
|
||||
|
||||
// Push context registers
|
||||
@@ -85,7 +94,15 @@ function!(syscall_instruction => {
|
||||
pop_scratch!(),
|
||||
|
||||
// Return
|
||||
"iretq\n",
|
||||
"
|
||||
pop rcx // Pop userspace return pointer
|
||||
add rsp, 2
|
||||
pop r11 // Pop rflags
|
||||
add rsp, 10 // Pop SS and rsp
|
||||
mov rsp, gs:[28] // Restore userspace stack pointer
|
||||
swapgs // Restore gs from TSS to kernel data
|
||||
sysretq // Return into userspace; RCX=>RIP,R11=>RFLAGS
|
||||
",
|
||||
});
|
||||
|
||||
interrupt_stack!(syscall, |stack| {
|
||||
|
||||
@@ -58,7 +58,7 @@ pub unsafe fn map() {
|
||||
|
||||
#[cfg(feature = "pti")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn unmap() {
|
||||
pub unsafe extern "C" fn unmap() {
|
||||
// Switch to per-CPU stack
|
||||
switch_stack(PTI_CONTEXT_STACK, PTI_CPU_STACK.as_ptr() as usize + PTI_CPU_STACK.len());
|
||||
|
||||
@@ -83,4 +83,4 @@ pub unsafe fn map() {}
|
||||
|
||||
#[cfg(not(feature = "pti"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn unmap() {}
|
||||
pub unsafe extern "C" fn unmap() {}
|
||||
|
||||
Reference in New Issue
Block a user