x86_64 System Calls
Relevant source files
This document covers the x86_64 system call implementation in the axcpu library, including the low-level assembly entry point, MSR configuration, and user-kernel transition mechanisms. The system call interface enables user space programs to request kernel services through the syscall instruction.
For general trap and exception handling beyond system calls, see x86_64 Trap and Exception Handling. For user space context management across architectures, see User Space Support.
System Call Architecture Overview
The x86_64 system call implementation consists of three main components: MSR configuration during initialization, the assembly entry point that handles the low-level transition, and the Rust handler that processes the actual system call.
flowchart TD
subgraph subGraph2["Context Management"]
SWAPGS["swapgs"]
STACK_SWITCH["Stack Switch"]
TRAPFRAME["TrapFrame Construction"]
RESTORE["Context Restore"]
end
subgraph subGraph0["System Call Flow"]
USER["User Space Process"]
SYSCALL_INSTR["syscall instruction"]
ENTRY["syscall_entry (Assembly)"]
HANDLER["x86_syscall_handler (Rust)"]
GENERIC["handle_syscall (Generic)"]
SYSRET["sysretq instruction"]
end
subgraph Configuration["Configuration"]
INIT["init_syscall()"]
LSTAR["LSTAR MSR"]
STAR["STAR MSR"]
SFMASK["SFMASK MSR"]
EFER["EFER MSR"]
end
ENTRY --> HANDLER
ENTRY --> STACK_SWITCH
ENTRY --> SWAPGS
ENTRY --> TRAPFRAME
GENERIC --> HANDLER
HANDLER --> GENERIC
HANDLER --> SYSRET
INIT --> EFER
INIT --> LSTAR
INIT --> SFMASK
INIT --> STAR
SYSCALL_INSTR --> ENTRY
SYSRET --> RESTORE
SYSRET --> USER
USER --> SYSCALL_INSTR
Sources: src/x86_64/syscall.rs(L1 - L49) src/x86_64/syscall.S(L1 - L56)
System Call Initialization
The init_syscall function configures the x86_64 Fast System Call mechanism by programming several Model Specific Registers (MSRs).
| MSR | Purpose | Configuration |
|---|---|---|
| LSTAR | Long Syscall Target Address | Points tosyscall_entryassembly function |
| STAR | Syscall Target Address | Contains segment selectors for user/kernel code/data |
| SFMASK | Syscall Flag Mask | Masks specific RFLAGS during syscall execution |
| EFER | Extended Feature Enable | Enables System Call Extensions (SCE bit) |
flowchart TD
subgraph subGraph3["MSR Configuration in init_syscall()"]
INIT_FUNC["init_syscall()"]
subgraph subGraph2["Flag Masking"]
TF["TRAP_FLAG"]
IF["INTERRUPT_FLAG"]
DF["DIRECTION_FLAG"]
IOPL["IOPL_LOW | IOPL_HIGH"]
AC["ALIGNMENT_CHECK"]
NT["NESTED_TASK"]
end
subgraph subGraph1["GDT Selectors"]
UCODE["UCODE64_SELECTOR"]
UDATA["UDATA_SELECTOR"]
KCODE["KCODE64_SELECTOR"]
KDATA["KDATA_SELECTOR"]
end
subgraph subGraph0["MSR Setup"]
LSTAR_SETUP["LStar::write(syscall_entry)"]
STAR_SETUP["Star::write(selectors)"]
SFMASK_SETUP["SFMask::write(flags)"]
EFER_SETUP["Efer::update(SCE)"]
KERNELGS_SETUP["KernelGsBase::write(0)"]
end
end
INIT_FUNC --> EFER_SETUP
INIT_FUNC --> KERNELGS_SETUP
INIT_FUNC --> LSTAR_SETUP
INIT_FUNC --> SFMASK_SETUP
INIT_FUNC --> STAR_SETUP
SFMASK_SETUP --> AC
SFMASK_SETUP --> DF
SFMASK_SETUP --> IF
SFMASK_SETUP --> IOPL
SFMASK_SETUP --> NT
SFMASK_SETUP --> TF
STAR_SETUP --> KCODE
STAR_SETUP --> KDATA
STAR_SETUP --> UCODE
STAR_SETUP --> UDATA
The SFMASK register masks flags with value 0x47700, ensuring that potentially dangerous flags like interrupts and trap flags are cleared during system call execution.
Sources: src/x86_64/syscall.rs(L22 - L48)
Assembly Entry Point
The syscall_entry function in assembly handles the low-level transition from user space to kernel space. It performs critical operations including GS base swapping, stack switching, and trap frame construction.
sequenceDiagram
participant UserSpace as "User Space"
participant CPUHardware as "CPU Hardware"
participant KernelSpace as "Kernel Space"
UserSpace ->> CPUHardware: syscall instruction
CPUHardware ->> KernelSpace: Jump to syscall_entry
Note over KernelSpace: swapgs (switch to kernel GS)
KernelSpace ->> KernelSpace: Save user RSP to percpu area
KernelSpace ->> KernelSpace: Load kernel stack from TSS
Note over KernelSpace: Build TrapFrame on stack
KernelSpace ->> KernelSpace: Push user RSP, RFLAGS, RIP
KernelSpace ->> KernelSpace: Push all general purpose registers
KernelSpace ->> KernelSpace: Call x86_syscall_handler(trapframe)
KernelSpace ->> KernelSpace: Pop all general purpose registers
KernelSpace ->> KernelSpace: Restore user RSP, RFLAGS, RIP
Note over KernelSpace: swapgs (switch to user GS)
KernelSpace ->> CPUHardware: sysretq instruction
CPUHardware ->> UserSpace: Return to user space
Key Assembly Operations
The entry point performs these critical steps:
- GS Base Switching:
swapgsswitches between user and kernel GS base registers - Stack Management: Saves user RSP and switches to kernel stack from TSS
- Register Preservation: Pushes all general-purpose registers to build a
TrapFrame - Handler Invocation: Calls
x86_syscall_handlerwith the trap frame pointer - Context Restoration: Restores all registers and user context
- Return: Uses
sysretqto return to user space
Sources: src/x86_64/syscall.S(L3 - L55)
System Call Handler
The Rust-based system call handler processes the actual system call request after the assembly entry point has set up the execution context.
flowchart TD
subgraph subGraph2["Per-CPU Data"]
USER_RSP["USER_RSP_OFFSET"]
TSS_STACK["TSS privilege_stack_table"]
end
subgraph subGraph1["Data Flow"]
TRAPFRAME_IN["TrapFrame (Input)"]
SYSCALL_NUM["tf.rax (System Call Number)"]
RESULT["Return Value"]
TRAPFRAME_OUT["tf.rax (Updated)"]
end
subgraph subGraph0["Handler Flow"]
ASM_ENTRY["syscall_entry (Assembly)"]
RUST_HANDLER["x86_syscall_handler"]
GENERIC_HANDLER["crate::trap::handle_syscall"]
RETURN["Return to Assembly"]
end
ASM_ENTRY --> RUST_HANDLER
ASM_ENTRY --> TSS_STACK
ASM_ENTRY --> USER_RSP
GENERIC_HANDLER --> RESULT
GENERIC_HANDLER --> RUST_HANDLER
RESULT --> TRAPFRAME_OUT
RUST_HANDLER --> GENERIC_HANDLER
RUST_HANDLER --> RETURN
SYSCALL_NUM --> GENERIC_HANDLER
TRAPFRAME_IN --> RUST_HANDLER
The handler function is minimal but critical:
#![allow(unused)] fn main() { #[unsafe(no_mangle)] pub(super) fn x86_syscall_handler(tf: &mut TrapFrame) { tf.rax = crate::trap::handle_syscall(tf, tf.rax as usize) as u64; } }
It extracts the system call number from rax, delegates to the generic system call handler, and stores the return value back in rax for return to user space.
Sources: src/x86_64/syscall.rs(L17 - L20)
Per-CPU State Management
The system call implementation uses per-CPU variables to manage state during the user-kernel transition:
| Variable | Purpose | Usage |
|---|---|---|
| USER_RSP_OFFSET | Stores user stack pointer | Saved during entry, restored during exit |
| TSS.privilege_stack_table | Kernel stack pointer | Loaded from TSS during stack switch |
flowchart TD
subgraph subGraph1["Assembly Operations"]
SAVE_USER_RSP["mov gs:[USER_RSP_OFFSET], rsp"]
LOAD_KERNEL_RSP["mov rsp, gs:[TSS + rsp0_offset]"]
RESTORE_USER_RSP["mov rsp, [rsp - 2 * 8]"]
end
subgraph subGraph0["Per-CPU Memory Layout"]
PERCPU_BASE["Per-CPU Base Address"]
USER_RSP_VAR["USER_RSP_OFFSET"]
TSS_VAR["TSS Structure"]
TSS_RSP0["privilege_stack_table[0]"]
end
PERCPU_BASE --> TSS_VAR
PERCPU_BASE --> USER_RSP_VAR
SAVE_USER_RSP --> RESTORE_USER_RSP
TSS_RSP0 --> LOAD_KERNEL_RSP
TSS_VAR --> TSS_RSP0
USER_RSP_VAR --> SAVE_USER_RSP
The per-CPU storage ensures that system calls work correctly in multi-CPU environments where each CPU needs its own temporary storage for user state.
Sources: src/x86_64/syscall.rs(L9 - L10) src/x86_64/syscall.S(L5 - L6) src/x86_64/syscall.S(L52)
Integration with Trap Framework
The x86_64 system call implementation integrates with the broader trap handling framework through the handle_syscall function, which provides architecture-independent system call processing.
flowchart TD
subgraph subGraph2["Return Path"]
RESULT_VAL["Return Value"]
RAX_UPDATE["tf.rax = result"]
SYSRET["sysretq"]
end
subgraph subGraph1["Generic Trap Framework"]
HANDLE_SYSCALL["crate::trap::handle_syscall"]
SYSCALL_DISPATCH["System Call Dispatch"]
SYSCALL_IMPL["Actual System Call Implementation"]
end
subgraph subGraph0["Architecture-Specific Layer"]
SYSCALL_ENTRY["syscall_entry.S"]
X86_HANDLER["x86_syscall_handler"]
TRAPFRAME["TrapFrame"]
end
HANDLE_SYSCALL --> SYSCALL_DISPATCH
RAX_UPDATE --> SYSRET
RESULT_VAL --> RAX_UPDATE
SYSCALL_DISPATCH --> SYSCALL_IMPL
SYSCALL_ENTRY --> X86_HANDLER
SYSCALL_IMPL --> RESULT_VAL
TRAPFRAME --> HANDLE_SYSCALL
X86_HANDLER --> TRAPFRAME
This layered approach allows the generic trap framework to handle system call logic while the x86_64-specific code manages the low-level hardware interface and calling conventions.
Sources: src/x86_64/syscall.rs(L18 - L19)