# x86_64 low-level entry code: the CPU-exception stubs, plus the GDT/IDT load # helpers. Kept in a dedicated assembly file rather than inline asm because these # need real labels and cross-symbol jumps/calls (isr_common, exceptionHandler), # and because `lgdt`/`lidt` memory operands aren't expressible in Zig inline asm. # # Each exception vector normalises the stack to a uniform trap frame — a dummy # error code where the CPU pushes none, then the vector number — and jumps to the # shared tail, which saves the general registers and calls the Zig handler with a # pointer to the frame (matching src/arch/x86_64/idt.zig's CpuState). .text # gdt_flush(rdi = *GDT descriptor): load the GDT, reload the data segment # registers to the data selector, and reload CS to the code selector. CS can't be # set with mov, so we far-return through the caller's own return address. .global gdt_flush gdt_flush: lgdt (%rdi) mov $0x10, %ax # kernel data selector mov %ax, %ds mov %ax, %es mov %ax, %ss mov %ax, %fs mov %ax, %gs pop %rax # caller's return address push $0x08 # kernel code selector (new CS) push %rax # return address (new RIP) lretq # idt_flush(rdi = *IDT descriptor): load the IDT. .global idt_flush idt_flush: lidt (%rdi) ret # load_tr(di = TSS selector): load the task register. .global load_tr load_tr: ltr %di ret # switch_context(rdi = &old_task.rsp, rsi = new_task.rsp) # Cooperative context switch: save the callee-saved registers on the current # stack, stash the stack pointer in the old task, load the new task's stack # pointer, restore its callee-saved registers, and return into it. Caller-saved # registers are the compiler's responsibility (this looks like a normal call). .global switch_context switch_context: push %rbx push %rbp push %r12 push %r13 push %r14 push %r15 mov %rsp, (%rdi) # save old stack pointer into old_task.rsp mov %rsi, %rsp # switch to the new task's stack pop %r15 pop %r14 pop %r13 pop %r12 pop %rbp pop %rbx ret # return into the new task's saved instruction pointer # task_trampoline: the first thing a freshly-spawned task runs. init_task_stack # leaves its entry function in r15. A fresh task is switched to with the big kernel # lock held (the hand-off rule in sync.zig) but has no enter/leave frame of its own, # so it releases the lock here before running its body. r15 survives the call (it's # callee-saved). New tasks then start with interrupts enabled. .extern releaseForFreshTask .global task_trampoline task_trampoline: call releaseForFreshTask # drop the kernel lock we inherited across the switch sti call *%r15 # call the task entry (fn() void) 1: hlt # if the entry returns, idle (still preemptible) jmp 1b # Stub for a vector the CPU does NOT push an error code for: push a dummy 0. .macro STUB_NOERR vec .global isr\vec isr\vec: pushq $0 pushq $\vec jmp isr_common .endm # Stub for a vector the CPU DOES push an error code for: leave it in place. .macro STUB_ERR vec .global isr\vec isr\vec: pushq $\vec jmp isr_common .endm STUB_NOERR 0 STUB_NOERR 1 STUB_NOERR 2 STUB_NOERR 3 STUB_NOERR 4 STUB_NOERR 5 STUB_NOERR 6 STUB_NOERR 7 STUB_ERR 8 STUB_NOERR 9 STUB_ERR 10 STUB_ERR 11 STUB_ERR 12 STUB_ERR 13 STUB_ERR 14 STUB_NOERR 15 STUB_NOERR 16 STUB_ERR 17 STUB_NOERR 18 STUB_NOERR 19 STUB_NOERR 20 STUB_ERR 21 STUB_NOERR 22 STUB_NOERR 23 STUB_NOERR 24 STUB_NOERR 25 STUB_NOERR 26 STUB_NOERR 27 STUB_NOERR 28 STUB_NOERR 29 STUB_NOERR 30 STUB_NOERR 31 # Device-interrupt vectors (timer, spurious, room for more). None push an error # code, so they all use the dummy-zero form. STUB_NOERR 32 STUB_NOERR 33 STUB_NOERR 34 STUB_NOERR 35 STUB_NOERR 36 STUB_NOERR 37 STUB_NOERR 38 STUB_NOERR 39 STUB_NOERR 40 STUB_NOERR 41 STUB_NOERR 42 STUB_NOERR 43 STUB_NOERR 44 STUB_NOERR 45 STUB_NOERR 46 STUB_NOERR 47 .extern interruptDispatch # Shared tail. Register push order here defines the CpuState field order. isr_common: 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 mov %rsp, %rdi # first argument: pointer to the trap frame call interruptDispatch 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 pop %rbx pop %rax add $16, %rsp # drop the vector and error code iretq