| 1 |
// vectors.S — AArch64 Exception Vector Table |
| 2 |
// Each entry is 128 bytes (32 instructions), aligned to 2KB (0x800) |
| 3 |
// |
| 4 |
// Vector table layout: |
| 5 |
// 0x000 Current EL with SP0: sync, irq, fiq, serror |
| 6 |
// 0x200 Current EL with SPx: sync, irq, fiq, serror |
| 7 |
// 0x400 Lower EL AArch64: sync, irq, fiq, serror |
| 8 |
// 0x600 Lower EL AArch32: sync, irq, fiq, serror |
| 9 |
|
| 10 |
.section .text |
| 11 |
.align 11 // 2KB alignment (2^11 = 2048 = 0x800) |
| 12 |
|
| 13 |
.globl vector_table |
| 14 |
vector_table: |
| 15 |
// ================================================================ |
| 16 |
// 0x000: Current EL with SP0 (shouldn't happen in normal operation) |
| 17 |
// ================================================================ |
| 18 |
.org 0x000 |
| 19 |
b . // sync |
| 20 |
.org 0x080 |
| 21 |
b . // irq |
| 22 |
.org 0x100 |
| 23 |
b . // fiq |
| 24 |
.org 0x180 |
| 25 |
b . // serror |
| 26 |
|
| 27 |
// ================================================================ |
| 28 |
// 0x200: Current EL with SPx (kernel mode exceptions) |
| 29 |
// ================================================================ |
| 30 |
.org 0x200 |
| 31 |
b el1_sync_handler // sync (kernel faults, SVC from kernel) |
| 32 |
.org 0x280 |
| 33 |
b el1_irq_handler // irq (timer, peripherals while in kernel) |
| 34 |
.org 0x300 |
| 35 |
b . // fiq |
| 36 |
.org 0x380 |
| 37 |
b . // serror |
| 38 |
|
| 39 |
// ================================================================ |
| 40 |
// 0x400: Lower EL with AArch64 (userspace → kernel transitions) |
| 41 |
// ================================================================ |
| 42 |
.org 0x400 |
| 43 |
b el0_sync_handler // sync (SVC instruction, data abort, etc.) |
| 44 |
.org 0x480 |
| 45 |
b el0_irq_handler // irq (timer interrupt from userspace) |
| 46 |
.org 0x500 |
| 47 |
b . // fiq |
| 48 |
.org 0x580 |
| 49 |
b . // serror |
| 50 |
|
| 51 |
// ================================================================ |
| 52 |
// 0x600: Lower EL with AArch32 (unused — we're AArch64 only) |
| 53 |
// ================================================================ |
| 54 |
.org 0x600 |
| 55 |
b . |
| 56 |
.org 0x680 |
| 57 |
b . |
| 58 |
.org 0x700 |
| 59 |
b . |
| 60 |
.org 0x780 |
| 61 |
b . |
| 62 |
|
| 63 |
// ================================================================ |
| 64 |
// Helper: save all general-purpose registers |
| 65 |
// ================================================================ |
| 66 |
.macro save_all_regs |
| 67 |
stp x0, x1, [sp, #-16]! |
| 68 |
stp x2, x3, [sp, #-16]! |
| 69 |
stp x4, x5, [sp, #-16]! |
| 70 |
stp x6, x7, [sp, #-16]! |
| 71 |
stp x8, x9, [sp, #-16]! |
| 72 |
stp x10, x11, [sp, #-16]! |
| 73 |
stp x12, x13, [sp, #-16]! |
| 74 |
stp x14, x15, [sp, #-16]! |
| 75 |
stp x16, x17, [sp, #-16]! |
| 76 |
stp x18, x19, [sp, #-16]! |
| 77 |
stp x20, x21, [sp, #-16]! |
| 78 |
stp x22, x23, [sp, #-16]! |
| 79 |
stp x24, x25, [sp, #-16]! |
| 80 |
stp x26, x27, [sp, #-16]! |
| 81 |
stp x28, x29, [sp, #-16]! |
| 82 |
stp x30, xzr, [sp, #-16]! // LR + padding |
| 83 |
.endm |
| 84 |
|
| 85 |
.macro restore_all_regs |
| 86 |
ldp x30, xzr, [sp], #16 |
| 87 |
ldp x28, x29, [sp], #16 |
| 88 |
ldp x26, x27, [sp], #16 |
| 89 |
ldp x24, x25, [sp], #16 |
| 90 |
ldp x22, x23, [sp], #16 |
| 91 |
ldp x20, x21, [sp], #16 |
| 92 |
ldp x18, x19, [sp], #16 |
| 93 |
ldp x16, x17, [sp], #16 |
| 94 |
ldp x14, x15, [sp], #16 |
| 95 |
ldp x12, x13, [sp], #16 |
| 96 |
ldp x10, x11, [sp], #16 |
| 97 |
ldp x8, x9, [sp], #16 |
| 98 |
ldp x6, x7, [sp], #16 |
| 99 |
ldp x4, x5, [sp], #16 |
| 100 |
ldp x2, x3, [sp], #16 |
| 101 |
ldp x0, x1, [sp], #16 |
| 102 |
.endm |
| 103 |
|
| 104 |
// ================================================================ |
| 105 |
// EL1 Sync Handler (kernel mode synchronous exceptions) |
| 106 |
// ================================================================ |
| 107 |
el1_sync_handler: |
| 108 |
save_all_regs |
| 109 |
mrs x0, esr_el1 // Exception Syndrome Register |
| 110 |
mrs x1, elr_el1 // Exception Link Register |
| 111 |
mov x2, sp |
| 112 |
bl el1_sync_dispatch |
| 113 |
restore_all_regs |
| 114 |
eret |
| 115 |
|
| 116 |
// ================================================================ |
| 117 |
// EL1 IRQ Handler (kernel mode interrupts — timer, etc.) |
| 118 |
// ================================================================ |
| 119 |
el1_irq_handler: |
| 120 |
save_all_regs |
| 121 |
bl gic_acknowledge // returns IRQ number in x0 |
| 122 |
mov x19, x0 // save IRQ number |
| 123 |
bl el1_irq_dispatch // handle the IRQ |
| 124 |
mov x0, x19 // restore IRQ number |
| 125 |
bl gic_eoi // end of interrupt |
| 126 |
restore_all_regs |
| 127 |
eret |
| 128 |
|
| 129 |
// ================================================================ |
| 130 |
// EL0 Sync Handler (userspace synchronous — SVC instruction) |
| 131 |
// ================================================================ |
| 132 |
el0_sync_handler: |
| 133 |
save_all_regs |
| 134 |
mrs x0, esr_el1 // Exception Syndrome Register |
| 135 |
mrs x1, elr_el1 // Exception Link Register (user PC) |
| 136 |
mrs x2, sp_el0 // User stack pointer |
| 137 |
mov x3, sp // Kernel stack (for passing to C) |
| 138 |
bl el0_sync_dispatch |
| 139 |
restore_all_regs |
| 140 |
eret |
| 141 |
|
| 142 |
// ================================================================ |
| 143 |
// EL0 IRQ Handler (userspace interrupts — timer from EL0) |
| 144 |
// ================================================================ |
| 145 |
el0_irq_handler: |
| 146 |
save_all_regs |
| 147 |
bl gic_acknowledge // returns IRQ number in x0 |
| 148 |
mov x19, x0 // save IRQ number |
| 149 |
bl el0_irq_dispatch // handle the IRQ |
| 150 |
mov x0, x19 |
| 151 |
bl gic_eoi |
| 152 |
restore_all_regs |
| 153 |
eret |
| 154 |
|
| 155 |
// ================================================================ |
| 156 |
// C dispatch functions (called from assembly) |
| 157 |
// ================================================================ |
| 158 |
|
| 159 |
// el1_sync_dispatch(esr, elr, sp) — kernel sync exception |
| 160 |
.globl el1_sync_dispatch |
| 161 |
el1_sync_dispatch: |
| 162 |
// For now, just return. We'll handle faults later. |
| 163 |
ret |
| 164 |
|
| 165 |
// el1_irq_dispatch(irq_num) — kernel IRQ |
| 166 |
.globl el1_irq_dispatch |
| 167 |
el1_irq_dispatch: |
| 168 |
// Check if timer IRQ |
| 169 |
cmp x0, #30 // IRQ_TIMER_CNTP |
| 170 |
b.ne .Lnot_timer_kern |
| 171 |
b timer_irq // tail call to timer_irq() |
| 172 |
.Lnot_timer_kern: |
| 173 |
ret |
| 174 |
|
| 175 |
// el0_sync_dispatch(esr, elr, sp_el0, sp) — userspace sync |
| 176 |
.globl el0_sync_dispatch |
| 177 |
el0_sync_dispatch: |
| 178 |
// Check if SVC instruction (ESR bits [31:26] = 0x15) |
| 179 |
lsr x4, x0, #26 |
| 180 |
and x4, x4, #0x3F |
| 181 |
cmp x4, #0x15 // SVC from AArch64 |
| 182 |
b.ne .Lnot_svc |
| 183 |
// TODO: dispatch syscall based on x8 register |
| 184 |
// For now, just return |
| 185 |
ret |
| 186 |
.Lnot_svc: |
| 187 |
ret |
| 188 |
|
| 189 |
// el0_irq_dispatch(irq_num) — userspace IRQ |
| 190 |
.globl el0_irq_dispatch |
| 191 |
el0_irq_dispatch: |
| 192 |
cmp x0, #30 |
| 193 |
b.ne .Lnot_timer_user |
| 194 |
b timer_irq |
| 195 |
.Lnot_timer_user: |
| 196 |
ret |
| 197 |
|