aboutsummaryrefslogtreecommitdiff
path: root/mirai.old/asm/entry.zig
blob: 4f9adf6a9b51a76903df4a0a6c754372a3e8cf3b (plain)
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
//! System Call Entry Point
//! Assembly stub for handling SYSCALL instruction from userspace

// External handlers
extern fn handle_syscall(ctx: u64) void;
extern fn get_kernel_stack() u64;

/// Address of syscall entry point for LSTAR MSR
pub fn get_entry_address() u64 {
    return @intFromPtr(&syscall_entry_asm);
}

extern fn syscall_entry_asm() void;

comptime {
    asm (
        \\.global syscall_entry_asm
        \\syscall_entry_asm:
        \\  # SYSCALL has already:
        \\  #   - Saved RIP to RCX
        \\  #   - Saved RFLAGS to R11
        \\  #   - Loaded CS/SS from STAR
        \\  #   - Jumped here (LSTAR)
        \\  #   - Masked RFLAGS per FMASK
        \\  #
        \\  # We need to:
        \\  #   - Save user RSP
        \\  #   - Switch to kernel stack (from TSS)
        \\  #   - Save all registers
        \\  #   - Call handler
        \\  #   - Restore registers
        \\  #   - Use sysret to return
        \\
        \\  # At entry: RCX = user RIP, R11 = user RFLAGS, RSP = user RSP, RAX = syscall#
        \\  
        \\  # Strategy: Use SWAPGS-like approach but with stack
        \\  # Save everything on user stack, switch to kernel stack, copy over
        \\  
        \\  # First, save user RSP by pushing all regs then calculating
        \\  push %rax
        \\  push %rbx
        \\  push %rcx
        \\  push %rdx
        \\  push %rsi
        \\  push %rdi
        \\  push %rbp
        \\  push %r8
        \\  push %r9
        \\  push %r10
        \\  push %r11
        \\  push %r12
        \\  push %r13
        \\  push %r14
        \\  push %r15
        \\  
        \\  # Get kernel stack (returns in RAX, RSP unchanged after ret)
        \\  call get_kernel_stack
        \\  
        \\  # RAX now has kernel stack pointer
        \\  # RSP points to saved R15 (call/ret balanced)
        \\  mov %rsp, %r15         # R15 = pointer to saved registers
        \\  lea 120(%r15), %r14    # R14 = original user RSP (before 15 pushes)
        \\  mov %rax, %rsp         # Switch to kernel stack
        \\  
        \\  # Build struct on kernel stack by reading from user stack (via R15)
        \\  # User stack has (from low to high addr): [rax][rbx][rcx][rdx][rsi][rdi][rbp][r8-r15]
        \\  # We push in REVERSE order so struct layout matches (stack grows down)
        \\  # NOTE: RCX and R11 already have user_rip and user_rflags from SYSCALL!
        \\  push %r14              # user_rsp (last in struct, push first)
        \\  push 32(%r15)          # user_rflags (R11 value before syscall)
        \\  push $0x1B             # user_ss
        \\  push 96(%r15)          # user_rip (RCX value before syscall)
        \\  push $0x23             # user_cs
        \\  push 0(%r15)           # r15
        \\  push 8(%r15)           # r14
        \\  push 16(%r15)          # r13
        \\  push 24(%r15)          # r12
        \\  push 32(%r15)          # r11 (value before syscall)
        \\  push 40(%r15)          # r10
        \\  push 48(%r15)          # r9
        \\  push 56(%r15)          # r8
        \\  push 64(%r15)          # rbp
        \\  push 72(%r15)          # rdi
        \\  push 80(%r15)          # rsi
        \\  push 88(%r15)          # rdx
        \\  push 96(%r15)          # rcx (value before syscall)
        \\  push 104(%r15)         # rbx
        \\  push 112(%r15)         # rax (first in struct, push last)
        \\
        \\  # Re-enable interrupts (we're on kernel stack now)
        \\  sti
        \\
        \\  # Call invocation handler
        \\  mov %rsp, %rdi        # Pass context pointer
        \\  call handle_syscall
        \\
        \\  # Disable interrupts before returning to user
        \\  cli
        \\
        \\  # Restore registers (RAX might be modified with return value)
        \\  # NOTE: Don't restore RCX and R11 - they're needed for sysret!
        \\  pop %rax
        \\  pop %rbx
        \\  add $8, %rsp           # Skip RCX (will be loaded from saved RIP)
        \\  pop %rdx
        \\  pop %rsi
        \\  pop %rdi
        \\  pop %rbp
        \\  pop %r8
        \\  pop %r9
        \\  pop %r10
        \\  add $8, %rsp           # Skip R11 (will be loaded from saved RFLAGS)
        \\  pop %r12
        \\  pop %r13
        \\  pop %r14
        \\  pop %r15
        \\
        \\  # Pop saved context
        \\  add $8, %rsp           # Skip user CS (sysret will set it)
        \\  pop %rcx               # User RIP for sysret
        \\  add $8, %rsp           # Skip user SS (sysret will set it)
        \\  pop %r11               # User RFLAGS for sysret
        \\  pop %rsp               # Restore user RSP
        \\
        \\  # Return to userspace
        \\  sysretq
    );
}