Files
2026-07-19 12:38:20 +08:00

596 lines
14 KiB
ArmAsm

.section .bss
.comm _per_cpu, 32, 8
.comm _proc_mgr, 8192, 16
.comm _proc_mgr_ptr, 8, 8
.comm _saved_cr3, 8, 8
.comm _yield_cnt, 4, 4
.comm _yield_lock, 4, 4
.comm _fs_lock, 4, 4
.comm _fault_rip, 8, 8
.comm _fault_err, 8, 8
.section .text
.intel_syntax noprefix
.globl isr_default
isr_default:
push rax
push rdx
mov al, 0x20
out 0xA0, al
out 0x20, al
pop rdx
pop rax
iretq
# =============================================================================
# ISR macros with Ring 3 support
#
# When CPU takes interrupt/exception:
# From Ring 0: pushes RFLAGS, CS, RIP (3 qwords)
# From Ring 3: pushes SS, RSP, RFLAGS, CS, RIP (5 qwords)
# CPU also switches to TSS RSP0 stack when coming from Ring 3.
#
# Strategy:
# 1. Check CS RPL to determine if from Ring 3
# 2. If from Ring 3: swapgs (make gs: point to per_cpu), save user RSP
# 3. Save full interrupt frame into registers
# 4. Re-push in uniform format with is_user flag
# 5. Call handler
# 6. On return, rebuild correct iretq frame based on is_user
# 7. If returning to Ring 3: swapgs back
# =============================================================================
.macro ISR_NOERR num
.globl isr\num
isr\num:
# Check CS RPL to determine if from Ring 3
mov rax, [rsp + 8] # CS from interrupt frame
test al, 3 # RPL bits: 0 = Ring 0, 3 = Ring 3
jnz isr\num\()_from_user
isr\num\()_from_kernel:
# From Ring 0: no swapgs needed, already on kernel stack
mov r8, [rsp] # RIP
mov r9, [rsp + 8] # CS
mov r10, [rsp + 16] # RFLAGS
xor r11, r11 # SS = 0
xor rax, rax # RSP = 0
add rsp, 24 # remove the 3 CPU-pushed qwords
jmp isr\num\()_frame_ready
isr\num\()_from_user:
# From Ring 3: swapgs to make gs: accessible, CPU already switched to TSS RSP0
swapgs
# Save user RSP to per_cpu.user_rsp (gs:[0x0])
# Note: CPU has already switched RSP to TSS RSP0, so the user RSP is on this stack
mov r8, [rsp] # RIP
mov r9, [rsp + 8] # CS
mov r10, [rsp + 16] # RFLAGS
mov rax, [rsp + 24] # RSP (user)
mov r11, [rsp + 32] # SS (user)
# Save user RSP to per_cpu for potential later use
mov gs:[0x0], rax
add rsp, 40 # remove the 5 CPU-pushed qwords
isr\num\()_frame_ready:
# r8=RIP, r9=CS, r10=RFLAGS, rax=RSP(0 if kernel), r11=SS(0 if kernel)
# Build uniform frame on stack for iretq later
push r11 # SS
push rax # RSP
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
# Push is_user flag (1 if from Ring 3, 0 if from Ring 0)
xor ecx, ecx
test r9b, 3 # test CS RPL
setnz cl # cl = 1 if from Ring 3
push rcx # is_user
# Push all general-purpose registers
push rax
push rbx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
# Save faulting RIP and error code for debug
mov [_fault_rip], r8
mov [_fault_err], rdx
call isr\num\()_handler
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rbx
pop rax
# Pop is_user flag
pop rcx # is_user
# Pop the uniform frame into registers
pop r8 # RIP
pop r9 # CS
pop r10 # RFLAGS
pop rax # RSP (user)
pop r11 # SS (user)
test rcx, rcx
jnz isr\num\()_ret_user
isr\num\()_ret_kernel:
# Return to Ring 0: push RFLAGS, CS, RIP only
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
iretq
isr\num\()_ret_user:
# Return to Ring 3: swapgs back, then push SS, RSP, RFLAGS, CS, RIP
swapgs
# DIAG+FIX: if RFLAGS.NT (bit 14) is set, iretq performs hardware task-return
# which loads TSS selector 0x28 -> #GP(err=0x28). Detect and clear it.
test r10, 0x4000
jz 1f
btr r10, 14
mov dx, 0x3F8
mov al, 0x4E # 'N' marker = NT was set (now cleared)
out dx, al
1:
cmp r9, 0x1B
jne 8f
cmp r11, 0x23
jne 8f
push r11 # SS
push rax # RSP
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
iretq
8:
mov dx, 0x3F8
mov al, 0x42 # 'B' = bad CS/SS
out dx, al
mov al, 0x20 # space
out dx, al
# Output CS (r9) low byte as 2 hex digits
mov rcx, r9
mov rax, rcx
shr rax, 4
and rax, 0xF
add al, 0x30
cmp al, 0x39
jle 2f
add al, 7
2:
out dx, al
mov rax, rcx
and rax, 0xF
add al, 0x30
cmp al, 0x39
jle 3f
add al, 7
3:
out dx, al
mov al, 0x20 # space
out dx, al
# Output SS (r11) low byte as 2 hex digits
mov rcx, r11
mov rax, rcx
shr rax, 4
and rax, 0xF
add al, 0x30
cmp al, 0x39
jle 4f
add al, 7
4:
out dx, al
mov rax, rcx
and rax, 0xF
add al, 0x30
cmp al, 0x39
jle 5f
add al, 7
5:
out dx, al
mov al, 0x0A # newline
out dx, al
cli
7:
hlt
jmp 7b
.endm
.macro ISR_ERR num
.globl isr\num
isr\num:
# Stack at entry (with error code):
# From Ring 0: [rsp]=error_code, [rsp+8]=RIP, [rsp+16]=CS, [rsp+24]=RFLAGS
# From Ring 3: [rsp]=error_code, [rsp+8]=RIP, [rsp+16]=CS, [rsp+24]=RFLAGS, [rsp+32]=RSP, [rsp+40]=SS
# Check CS RPL
mov rax, [rsp + 16] # CS from interrupt frame
test al, 3
jnz isr\num\()_from_user
isr\num\()_from_kernel:
# From Ring 0: no swapgs needed
mov rdx, [rsp] # error_code
mov r8, [rsp + 8] # RIP
mov r9, [rsp + 16] # CS
mov r10, [rsp + 24] # RFLAGS
xor r11, r11 # SS = 0
xor rax, rax # RSP = 0
add rsp, 32 # remove 4 CPU-pushed qwords (error + 3 frame)
jmp isr\num\()_frame_ready
isr\num\()_from_user:
# From Ring 3: swapgs, CPU already on TSS RSP0
swapgs
mov rdx, [rsp] # error_code
mov r8, [rsp + 8] # RIP
mov r9, [rsp + 16] # CS
mov r10, [rsp + 24] # RFLAGS
mov rax, [rsp + 32] # RSP (user)
mov r11, [rsp + 40] # SS (user)
mov gs:[0x0], rax # save user RSP
add rsp, 48 # remove 6 CPU-pushed qwords (error + 5 frame)
isr\num\()_frame_ready:
# rdx=error_code, r8=RIP, r9=CS, r10=RFLAGS, rax=RSP, r11=SS
# Build uniform frame on stack
push r11 # SS
push rax # RSP
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
push rdx # error_code
# Push is_user flag
xor ecx, ecx
test r9b, 3
setnz cl
push rcx # is_user
# Push all general-purpose registers
push rax
push rbx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
# Save faulting RIP and error code for debug
mov [_fault_rip], r8
mov [_fault_err], rdx
call isr\num\()_handler
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rbx
pop rax
# Pop is_user flag
pop rcx # is_user
add rsp, 8 # skip error_code (handler already consumed it)
pop r8 # RIP
pop r9 # CS
pop r10 # RFLAGS
pop rax # RSP (user)
pop r11 # SS (user)
test rcx, rcx
jnz isr\num\()_ret_user
isr\num\()_ret_kernel:
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
iretq
isr\num\()_ret_user:
swapgs
push r11 # SS
push rax # RSP
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
iretq
.endm
ISR_NOERR 0
ISR_NOERR 1
ISR_NOERR 2
ISR_NOERR 3
ISR_NOERR 4
ISR_NOERR 5
ISR_NOERR 6
ISR_NOERR 7
ISR_ERR 8
ISR_NOERR 9
ISR_ERR 10
ISR_ERR 11
ISR_ERR 12
ISR_ERR 13
ISR_ERR 14
ISR_NOERR 15
ISR_NOERR 16
ISR_ERR 17
ISR_NOERR 18
ISR_NOERR 19
ISR_NOERR 20
ISR_NOERR 21
ISR_NOERR 22
ISR_NOERR 23
ISR_NOERR 24
ISR_NOERR 25
ISR_NOERR 26
ISR_NOERR 27
ISR_NOERR 28
ISR_NOERR 29
ISR_NOERR 30
ISR_NOERR 31
ISR_NOERR 32
ISR_NOERR 33
ISR_NOERR 34
ISR_NOERR 35
ISR_NOERR 36
ISR_NOERR 37
ISR_NOERR 38
ISR_NOERR 39
ISR_NOERR 40
ISR_NOERR 41
ISR_NOERR 42
ISR_NOERR 43
ISR_NOERR 44
ISR_NOERR 45
ISR_NOERR 46
ISR_NOERR 47
.globl syscall_entry
syscall_entry:
mov rax, rsp
shr rax, 47
test rax, rax
jnz syscall_from_kernel
syscall_from_user:
swapgs
mov gs:[0x0], rsp
mov rsp, gs:[0x8]
jmp syscall_common
syscall_from_kernel:
syscall_common:
push rcx
push r11
push rbx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r12
push r13
push r14
push r15
mov rbp, rsp
and rsp, ~15
push r9
mov r9, r8
mov r8, r10
mov rcx, rdx
mov rdx, rsi
mov rsi, rdi
mov rdi, rax
call syscall_handler
add rsp, 8
mov rsp, rbp
pop r15
pop r14
pop r13
pop r12
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rbx
pop r11
pop rcx
mov rax, rcx
shr rax, 47
test rax, rax
jnz syscall_ret_kernel
syscall_ret_user:
mov rsp, gs:[0x0]
swapgs
push r11
popfq
sysretq
syscall_ret_kernel:
push r11
popfq
jmp rcx
# =============================================================================
# ISR 0x80 - int 0x80 syscall handler for user-mode ELF apps
#
# int 0x80 calling convention (ViperOS):
# rax = syscall number
# rbx = arg1
# rcx = arg2
# rdx = arg3
# r10 = arg4
#
# Translated to syscall_handler calling convention:
# rdi = n (syscall number)
# rsi = arg1
# rdx = arg2
# rcx = arg3
# r8 = arg4
# r9 = arg5 (0)
# stack = arg6 (0)
#
# Return value in rax.
# =============================================================================
.globl isr80
isr80:
# Check CS RPL to determine if from Ring 3
mov rax, [rsp + 8] # CS from interrupt frame
test al, 3
jnz isr80_from_user
isr80_from_kernel:
mov r8, [rsp] # RIP
mov r9, [rsp + 8] # CS
mov r10, [rsp + 16] # RFLAGS
xor r11, r11 # SS = 0
xor rax, rax # RSP = 0
add rsp, 24
jmp isr80_frame_ready
isr80_from_user:
swapgs
mov r8, [rsp] # RIP
mov r9, [rsp + 8] # CS
mov r10, [rsp + 16] # RFLAGS
mov rax, [rsp + 24] # RSP (user)
mov r11, [rsp + 32] # SS (user)
mov gs:[0x0], rax
add rsp, 40
isr80_frame_ready:
# Build uniform frame on stack
push r11 # SS
push rax # RSP
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
# Save ALL registers FIRST (including original rcx = arg2)
# Order: rax, rbx, rcx, rdx, rsi, rdi, rbp, r8, r9, r10, r11, r12, r13, r14, r15
push rax # [rsp+112] rax (syscall number)
push rbx # [rsp+104] rbx (arg1)
push rcx # [rsp+96] rcx (arg2)
push rdx # [rsp+88] rdx (arg3)
push rsi # [rsp+80]
push rdi # [rsp+72]
push rbp # [rsp+64]
push r8 # [rsp+56]
push r9 # [rsp+48]
push r10 # [rsp+40] r10 (arg4)
push r11 # [rsp+32]
push r12 # [rsp+24]
push r13 # [rsp+16]
push r14 # [rsp+8]
push r15 # [rsp+0]
# Calculate is_user from saved CS on stack
# CS is at [rsp + 15*8 + 16] = [rsp + 136]
mov rax, [rsp + 136] # CS from frame
xor ecx, ecx
test al, 3
setnz cl
push rcx # is_user (pushed AFTER all registers)
# Stack layout now:
# [rsp+0] = is_user
# [rsp+8] = r15 [rsp+16] = r14 [rsp+24] = r13 [rsp+32] = r12
# [rsp+40] = r11 [rsp+48] = r10 [rsp+56] = r9 [rsp+64] = r8
# [rsp+72] = rbp [rsp+80] = rdi [rsp+88] = rsi [rsp+96] = rdx
# [rsp+104] = rcx [rsp+112] = rbx [rsp+120] = rax
# Translate int 0x80 calling convention to syscall_handler convention
mov rdi, [rsp + 120] # original rax = syscall number -> rdi
mov rsi, [rsp + 112] # original rbx = arg1 -> rsi
mov rdx, [rsp + 104] # original rcx = arg2 -> rdx
mov rcx, [rsp + 96] # original rdx = arg3 -> rcx
mov r8, [rsp + 48] # original r10 = arg4 -> r8
xor r9, r9 # arg5 = 0
push 0 # arg6 = 0 (on stack)
call syscall_handler
add rsp, 8 # remove arg6
# Save return value by overwriting saved rax on stack
mov [rsp + 120], rax
# Pop is_user flag
pop rcx # is_user
# Restore registers
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx # restore original rcx (arg2)
pop rbx
pop rax # now has the return value
# Pop frame
pop r8 # RIP
pop r9 # CS
pop r10 # RFLAGS
pop rax # RSP (user)
pop r11 # SS (user)
test rcx, rcx
jnz isr80_ret_user
isr80_ret_kernel:
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
iretq
isr80_ret_user:
swapgs
push r11 # SS
push rax # RSP
push r10 # RFLAGS
push r9 # CS
push r8 # RIP
iretq