// ============================================================================ // Pactor64 — Raspberry Pi 5 (Cortex A76) Kernel Entry // AArch64 assembly, clang assembler syntax // // GPU firmware loads kernel8.img to 0x80000 and starts CPU in EL2. // We transition to EL1, set up stack, init UART, and call kernel_main. // ============================================================================ .section .text // ============================================================================ // Constants // ============================================================================ // PL011 UART (BCM2712, Pi 5) .equ UART0_BASE, 0xFE201000 .equ UART_DR, 0x00 // Data register .equ UART_FR, 0x18 // Flag register .equ UART_IBRD, 0x24 // Integer baud rate divisor .equ UART_FBRD, 0x28 // Fractional baud rate divisor .equ UART_LCR_H, 0x2C // Line control register .equ UART_CR, 0x30 // Control register .equ UART_IMSC, 0x38 // Interrupt mask .equ UART_ICR, 0x44 // Interrupt clear // PL011 CR bits .equ CR_UARTEN, (1 << 0) .equ CR_TXE, (1 << 8) .equ CR_RXE, (1 << 9) // PL011 FR bits .equ FR_TXFF, (1 << 5) // TX FIFO full .equ FR_RXFE, (1 << 4) // RX FIFO empty // PL011 LCR_H bits .equ LCR_H_WLEN_8, (3 << 5) // 8-bit word .equ LCR_H_FEN, (1 << 4) // FIFO enable // Exception levels .equ EL2, 8 .equ EL1, 4 .equ EL0, 0 // Kernel load address .equ KERNEL_BASE, 0x80000 // ============================================================================ // Entry Point // ============================================================================ .globl _start _start: // ------------------------------------------------------------------ // Step 1: Check current exception level // ------------------------------------------------------------------ mrs x0, CurrentEL and x0, x0, #0xC // Extract EL bits [3:2] lsr x0, x0, #2 // EL = CurrentEL >> 2 // If already EL1, skip EL2→EL1 transition cmp x0, #1 beq .Lsetup_el1 // ------------------------------------------------------------------ // Step 2: EL2 → EL1 transition // ------------------------------------------------------------------ // Configure HCR_EL2: set RW bit (EL1 is AArch64) mrs x1, hcr_el2 orr x1, x1, #(1 << 31) // HCR_EL2.RW = 1 (AArch64 at EL1) msr hcr_el2, x1 // Configure SCTLR_EL1 to known state (all disabled initially) mov x1, #0x0800 // RES1 bits movk x1, #0x30D0, lsl #16 // RES1 bits msr sctlr_el1, x1 // Configure SPSR_EL2 for return to EL1 // SPSR: DAIF masked, EL1h mode mov x1, #0x3C5 // EL1h | DAIF masked msr spsr_el2, x1 // Set return address for ERET adr x1, .Lsetup_el1 msr elr_el2, x1 // Drop to EL1 eret .Lsetup_el1: // ------------------------------------------------------------------ // Step 3: Set up stack pointer (EL1) // ------------------------------------------------------------------ // Stack grows down from kernel load address // (We'll set up a proper stack in BSS later) ldr x0, =_stack_top mov sp, x0 // Also set SP_EL0 (for userspace later) ldr x0, =0x1000000 // 16MB — userspace stack base msr sp_el0, x0 // ------------------------------------------------------------------ // Step 4: Zero BSS section // ------------------------------------------------------------------ ldr x0, =__bss_start ldr x1, =__bss_end .Lbss_loop: cmp x0, x1 bge .Lbss_done str xzr, [x0], #8 b .Lbss_loop .Lbss_done: // ------------------------------------------------------------------ // Step 5: Initialize PL011 UART // ------------------------------------------------------------------ bl uart_init // ------------------------------------------------------------------ // Step 6: Print boot banner // ------------------------------------------------------------------ ldr x0, =msg_banner bl uart_puts // ------------------------------------------------------------------ // Step 7: Call kernel_main // ------------------------------------------------------------------ bl kernel_main // If kernel_main returns, halt .Lhalt: wfe b .Lhalt // ============================================================================ // uart_init — Initialize PL011 UART for serial output // BCM2712 UART0 at 0xFE201000 // Baud rate: 115200, 8N1 // ============================================================================ .globl uart_init uart_init: ldr x0, =UART0_BASE // Disable UART str wzr, [x0, #UART_CR] // Wait for TX to finish .Luart_wait: ldr w1, [x0, #UART_FR] tbnz w1, #3, .Luart_wait // BUSY bit // Clear interrupts mov w1, #0x7FF str w1, [x0, #UART_ICR] // Set baud rate: 115200 // IBRD = 48000000 / (16 * 115200) = 26.0416... // IBRD = 26, FBRD = round(0.0416 * 64) = 3 mov w1, #26 str w1, [x0, #UART_IBRD] mov w1, #3 str w1, [x0, #UART_FBRD] // 8-bit, no parity, 1 stop, FIFO enabled mov w1, #(LCR_H_WLEN_8 | LCR_H_FEN) str w1, [x0, #UART_LCR_H] // Mask all interrupts str wzr, [x0, #UART_IMSC] // Enable UART, TX, RX mov w1, #(CR_UARTEN | CR_TXE | CR_RXE) str w1, [x0, #UART_CR] ret // ============================================================================ // uart_putc — Write a character to UART // x0 = character // ============================================================================ .globl uart_putc uart_putc: ldr x1, =UART0_BASE .Lputc_wait: ldr w2, [x1, #UART_FR] tbnz w2, #5, .Lputc_wait // Wait while TX FIFO full str w0, [x1, #UART_DR] ret // ============================================================================ // uart_puts — Write a null-terminated string to UART // x0 = pointer to string // ============================================================================ .globl uart_puts uart_puts: stp x29, x30, [sp, #-16]! mov x29, sp mov x19, x0 // Save string pointer .Lputs_loop: ldrb w0, [x19], #1 cbz w0, .Lputs_done bl uart_putc b .Lputs_loop .Lputs_done: ldp x29, x30, [sp], #16 ret // ============================================================================ // uart_getc — Read a character from UART (blocking) // Returns character in x0, or -1 if no data // ============================================================================ .globl uart_getc uart_getc: ldr x1, =UART0_BASE ldr w2, [x1, #UART_FR] tbnz w2, #4, .Lgetc_empty // RX FIFO empty ldr w0, [x1, #UART_DR] and w0, w0, #0xFF ret .Lgetc_empty: mov x0, #-1 ret // ============================================================================ // uart_poll — Check if UART has data available // Returns 1 if data available, 0 otherwise // ============================================================================ .globl uart_poll uart_poll: ldr x0, =UART0_BASE ldr w1, [x0, #UART_FR] lsr w1, w1, #4 // FR_RXFE is bit 4 and w0, w1, #1 eor w0, w0, #1 // Invert: 1 = data available ret // ============================================================================ // Data Section // ============================================================================ .section .rodata msg_banner: .asciz "\r\n" .asciz " ================================\r\n" .asciz " Pactor64 on Raspberry Pi 5\r\n" .asciz " Cortex A76 (AArch64)\r\n" .asciz " ================================\r\n" .asciz "\r\n" // ============================================================================ // BSS — Kernel Stack (16KB) // ============================================================================ .section .bss .align 12 // 4KB aligned .space 16384 // 16KB stack _stack_top: