Skip to content

Commit 7d936e3

Browse files
xvancAndy-Python-Programmer
authored andcommitted
implement sysenter support for system calls
1 parent 62f7833 commit 7d936e3

File tree

4 files changed

+102
-4
lines changed

4 files changed

+102
-4
lines changed

src/aero_kernel/src/arch/x86_64/syscall.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use super::interrupts::InterruptErrorStack;
1010

1111
extern "C" {
1212
fn x86_64_syscall_handler();
13+
fn x86_64_sysenter_handler();
1314
}
1415

1516
const ARCH_SET_GS: usize = 0x1001;
@@ -54,7 +55,7 @@ fn arch_prctl(command: usize, address: usize) -> Result<usize, AeroSyscallError>
5455
}
5556

5657
#[no_mangle]
57-
extern "C" fn x86_64_do_syscall(stack: &mut InterruptErrorStack) {
58+
pub(super) extern "C" fn x86_64_do_syscall(stack: &mut InterruptErrorStack) {
5859
let stack = &mut stack.stack;
5960

6061
let syscall_number = stack.scratch.rax as usize; // syscall number
@@ -94,8 +95,10 @@ extern "C" fn x86_64_do_syscall(stack: &mut InterruptErrorStack) {
9495
/// Initializes support for the `syscall` and `sysret` instructions for the
9596
/// current CPU.
9697
pub(super) fn init() {
98+
let cpuid = CpuId::new();
99+
97100
// Check if syscall is supported as it is a required CPU feature for aero to run.
98-
let has_syscall = CpuId::new()
101+
let has_syscall = cpuid
99102
.get_extended_processor_and_feature_identifiers()
100103
.map_or(false, |i| i.has_syscall_sysret());
101104

@@ -107,7 +110,7 @@ pub(super) fn init() {
107110
* CPU supports them and the target pointer width is 64.
108111
*/
109112
let syscall_base = GdtEntryType::KERNEL_CODE << 3;
110-
let sysret_base = (GdtEntryType::USER_CODE32_UNUSED << 3) | 3;
113+
let sysret_base = (GdtEntryType::KERNEL_TLS << 3) | 3;
111114

112115
let star_hi = syscall_base as u32 | ((sysret_base as u32) << 16);
113116

@@ -121,4 +124,26 @@ pub(super) fn init() {
121124
let efer = io::rdmsr(io::IA32_EFER);
122125
io::wrmsr(io::IA32_EFER, efer | 1);
123126
}
127+
128+
/*
129+
* Enable support for the `sysenter` instruction for system calls.
130+
*
131+
* This instruction is only supported on AMD processors in Legacy mode,
132+
* not in Long mode (Compatibility or 64-bit modes), so still report support
133+
* for it via `cpuid`. In this case the #UD exception is caught to handle the
134+
* system call.
135+
*/
136+
let has_sysenter = cpuid
137+
.get_feature_info()
138+
.map_or(false, |i| i.has_sysenter_sysexit());
139+
140+
if has_sysenter {
141+
unsafe {
142+
io::wrmsr(
143+
io::IA32_SYSENTER_CS,
144+
(GdtEntryType::KERNEL_CODE << 3) as u64,
145+
);
146+
io::wrmsr(io::IA32_SYSENTER_EIP, x86_64_sysenter_handler as u64);
147+
}
148+
}
124149
}

src/aero_kernel/src/arch/x86_64/syscall_handler.asm

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ global x86_64_syscall_handler
2828
%define USERLAND_SS 0x23
2929
%define USERLAND_CS 0x2b
3030

31+
%define FMASK 0x300 ; TF | DF
32+
3133
; 64-bit SYSCALL instruction entry point. The instruction supports
3234
; to to 6 arguments in registers.
3335
;
@@ -92,3 +94,66 @@ x86_64_syscall_handler:
9294

9395
swapgs
9496
o64 sysret
97+
98+
; 64-bit SYSENTER entry point
99+
;
100+
; The SYSENTER mechanism performs a fast transition to the kernel.
101+
; The new CS is loaded from the IA32_SYSENTER_CS MSR, and the new instruction
102+
; and stack pointers are loaded from IA32_SYSENTER_EIP and IA32_SYSENTER_ESP,
103+
; respectively. RFLAGS.IF is cleared, but other flags are unchanged.
104+
;
105+
; As the instruction does not save *any* state, the user is required to provide
106+
; the return RIP and RSP in the RCX and R11 registers, respectively. These
107+
; addresses must be canonical.
108+
;
109+
; The instruction expects the call number and arguments in the same registers as
110+
; for SYSCALL.
111+
;
112+
section .text.x86_64_sysenter_handler
113+
global x86_64_sysenter_handler:function (x86_64_sysenter_handler.end - x86_64_sysenter_handler)
114+
align 16
115+
x86_64_sysenter_handler:
116+
swapgs
117+
118+
; Build the interrupt frame expected by the kernel.
119+
push USERLAND_SS
120+
push r11
121+
pushfq
122+
push USERLAND_CS
123+
push rcx
124+
125+
; Mask the same flags as for SYSCALL.
126+
; Note that up to this pont the code can be single-stepped if the user sets TF.
127+
pushfq
128+
and dword [rsp], 0x300
129+
popfq
130+
131+
push rax
132+
push_scratch
133+
push_preserved
134+
push 0
135+
136+
; Sore the stack pointer (interrupt frame pointer) in RBP for save keeping,
137+
; and align the stack as specified by the SysV calling convention.
138+
mov rbp, rsp
139+
and rsp, ~0xf
140+
141+
mov rdi, rbp
142+
call x86_64_do_syscall
143+
144+
; Reload the stack pointer, skipping the error code.
145+
lea rsp, [rbp + 8]
146+
pop_preserved
147+
pop_scratch
148+
149+
; Restore RFLAGS
150+
add rsp, 16
151+
popfq
152+
153+
; Move the return RIP and RSP into the registers expected by SYSEXIT.
154+
mov rdx, rcx
155+
mov rcx, r11
156+
157+
swapgs
158+
o64 sysexit
159+
.end:

src/aero_kernel/src/arch/x86_64/task.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,11 @@ pub fn arch_task_spinup(from: &mut ArchTask, to: &ArchTask) {
375375
}
376376

377377
unsafe {
378-
super::gdt::get_task_state_segement().rsp[0] = to.context_switch_rsp.as_u64();
378+
// Load the new thread's kernel stack pointer everywhere it's needed.
379+
let kstackp = to.context_switch_rsp.as_u64();
380+
super::gdt::get_task_state_segement().rsp[0] = kstackp;
381+
io::wrmsr(io::IA32_SYSENTER_ESP, kstackp);
382+
379383
task_spinup(&mut from.context, to.context.as_ref());
380384

381385
// make a restore point for the current FS base.

src/aero_kernel/src/utils/io.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ pub const IA32_LSTAR: u32 = 0xc0000082;
3939
/// System Call Flag Mask (R/W).
4040
pub const IA32_FMASK: u32 = 0xc0000084;
4141

42+
pub const IA32_SYSENTER_CS: u32 = 0x174;
43+
pub const IA32_SYSENTER_ESP: u32 = 0x175;
44+
pub const IA32_SYSENTER_EIP: u32 = 0x176;
45+
4246
/// APIC Location and Status (R/W).
4347
///
4448
/// ```text

0 commit comments

Comments
 (0)