diff options
Diffstat (limited to 'md')
| -rw-r--r-- | md/writeup/raspberry5_baremetal_gic_and_timers.md | 505 |
1 files changed, 502 insertions, 3 deletions
diff --git a/md/writeup/raspberry5_baremetal_gic_and_timers.md b/md/writeup/raspberry5_baremetal_gic_and_timers.md index 4f37be4..f86162f 100644 --- a/md/writeup/raspberry5_baremetal_gic_and_timers.md +++ b/md/writeup/raspberry5_baremetal_gic_and_timers.md @@ -268,7 +268,10 @@ vector_table: ventry dummy_vector ``` -Vector table address can be passed as +Vector table address need to be configured, each of the execution +levels can't see higher level registers, so for EL2 and EL3 different +registers need to be set + for EL2 ```arm @@ -284,13 +287,13 @@ isb ``` It took some time to figure out why EL3 part didn't worked, only after -checking execution level right approach was choosen(it was EL2). Also when I did +checking execution level right approach was chosen (it was EL2). Also when I did attempted to run stub I got EL3 rather then EL2. ### IRQ handler IRQ handler function will be called when generic IRQ is triggered, so it will need -to handle each of the IRQ's is some way +to handle each of the IRQ's in some way ```c void irq_handler() { @@ -311,6 +314,15 @@ void irq_handler() { } ``` +### Enabling global interrupts + +```arm +enable_global_interrupts: + msr daifclr, #7 + dmb osh + ret +``` + ## Sumup There is many parts that's need's to be combined to make IRQ's to work @@ -324,12 +336,499 @@ There is many parts that's need's to be combined to make IRQ's to work Some parts needs to be cleaned up +### AI usage + +There was needed some help fro LLM's to figure out why interrupts didn't fired. + +First LLM attempt was to blame that IRQ's for BCM timer is wrong, it tried to figure out IRQ's +used from kernel and blamed those are wrongs. Then it tried to implement generic ARM's +timer's that wasn't the goal. It's always assumed execution level is EL3, after trying to load +ARMv8 stub figured out that actually execution level was EL2, as armstub failed and crashed +kernel that how figured out that its in EL3 rather then EL2. + +After this thing's got better but +issue with IAR register that always showed at 0x3ff no internet search showed how to deal with +this, one of the things that LLM added is to list all pending IRQ's. As config where enabling all +IRQ's there is 3 extra IRQ's that popup's 129,141,260, after checking Linux kernel some seems +related to graphics, default config.txt have HDMI screen setup, so those comes from RP firmware +and those caused that if kernel loaded second time IAR was 0x3ff. This only can be fixed after restart. + + +Search didn't gave anything how to deal with it but with LLM suggest to reset APR registers, +cant find much doc's on those. Also suggested the IRQ drain loop. After this before loading kernel +could get rid of spurious interrupt state. Without LLM, set the all interrupts disabled and only +enabled ones for timer and things started to go well. + ## ARMv8 Stub's Raspberry Pi provide option to boot custom armstub's +I have tried to modify stub to update to register's from raspi5, but after loading stub, I dont think +its worked properly. + ## Source +Source code doesn't have the UART, linker script and Makefiles as those was explored in previous notes. + +__timer-bcm2835.h__ +```c +#ifndef __TIMER_BCM2835_H +#define __TIMER_BCM2835_H + +#include "io.h" + +//typedef unsigned char uint8_t; +//typedef unsigned short uint16_t; +//typedef unsigned int uint32_t; + +//#define ARM_IO_BASE 0x107C000000 +//#define RP1_IO_BASE 0x1c00000000UL +#define TM_BCM2835_REG_OFFSET (ARM_IO_BASE+0x7c003000) + + +#define BCM2835_CS 0x00 +#define BCM2835_CLO 0x04 +#define BCM2835_CHI 0x08 +#define BCM2835_C0 0x0c +#define BCM2835_C1 0x10 +#define BCM2835_C2 0x14 +#define BCM2835_C3 0x18 + +#define bcm2835_read32(offset) (*(volatile uint32_t *)(TM_BCM2835_REG_OFFSET+offset)) +#define bcm2835_write32(offset,val) (*(volatile uint32_t *)(TM_BCM2835_REG_OFFSET+offset)) = val + +#define TIMER_INTERVAL 1000000 + +#define GIC_SPI_IRQ_BCM2835_C0 96 +#define GIC_SPI_IRQ_BCM2835_C1 97 +#define GIC_SPI_IRQ_BCM2835_C2 98 +#define GIC_SPI_IRQ_BCM2835_C3 99 + + + +void timer_set_cmp1(uint32_t val); +void timer_get_cmp1(); +void timer_get_cnt(); +void timer_status(); +void timer_clr_stat_c0(); +void timer_clr_stat_c1(); +void timer_clr_stat(); +void timer_start_c0(); +void timer_start_c1(); +void timer_start_c3(); + +#endif + +``` + +__timer-bcm2835.c__ +```c +#include "timer-bcm2835.h" +#include "pl011.h" + +void timer_get_cnt() { + uint32_t lo = bcm2835_read32(BCM2835_CLO); + uint32_t hi = bcm2835_read32(BCM2835_CHI); + pl011_putu32(hi); + pl011_putu32(lo); + pl011_puts("\r\n"); +} + +void timer_status() { + uint32_t stat = bcm2835_read32(BCM2835_CS); + pl011_puts("timer status "); + pl011_putu32(stat); + pl011_puts("\r\n"); +} + +void timer_clr_stat_c1() { + bcm2835_write32(BCM2835_CS,0x2); +} +void timer_clr_stat_c0() { + bcm2835_write32(BCM2835_CS,0x1); +} + +void timer_clr_stat() { + bcm2835_write32(BCM2835_CS,0xF); +} + +void timer_start_c0() { + uint32_t t_lo = bcm2835_read32(BCM2835_CLO); + t_lo += TIMER_INTERVAL; + bcm2835_write32(BCM2835_C0, t_lo); +} + +void timer_start_c1() { + uint32_t t_lo = bcm2835_read32(BCM2835_CLO); + t_lo += TIMER_INTERVAL; + bcm2835_write32(BCM2835_C1, t_lo); +} + +void timer_start_c3() { + uint32_t t_lo = bcm2835_read32(BCM2835_CLO); + t_lo += TIMER_INTERVAL; + bcm2835_write32(BCM2835_C3, t_lo); +} + +``` + +__gic400.h__ +```c +#ifndef __GIC400_H +#define __GIC400_H + +#include "io.h" + +#define GIC400_REG_OFFSET (ARM_IO_BASE+0x0) + +#define GIC400_GICD_BASE (GIC400_REG_OFFSET+0x7fff9000) +#define GIC400_GICC_BASE (GIC400_REG_OFFSET+0x7fffa000) + +#define GICD_CTRL (GIC400_GICD_BASE+0x0) +#define GICD_IDR (GIC400_GICD_BASE+0x8) +#define GICD_IGROUPR(n) (GIC400_GICD_BASE+0x080+4*(n)) +#define GICD_ISENABLER(n) (GIC400_GICD_BASE+0x100+4*(n)) +#define GICD_ICENABLER(n) (GIC400_GICD_BASE+0x180+4*(n)) +#define GICD_ISPENDR(n) (GIC400_GICD_BASE+0x200+4*(n)) +#define GICD_ICPENDR(n) (GIC400_GICD_BASE+0x280+4*(n)) +#define GICD_ISACTIVER(n) (GIC400_GICD_BASE+0x300+4*(n)) +#define GICD_ICACTIVER(n) (GIC400_GICD_BASE+0x380+4*(n)) +#define GICD_IPRIORITYR(n) (GIC400_GICD_BASE+0x400+4*(n)) +#define GICD_ITARGETSR(n) (GIC400_GICD_BASE+0x800+4*(n)) +#define GICD_ICFGR(n) (GIC400_GICD_BASE+0xC00+4*(n)) + +#define GICC_CTRL (GIC400_GICC_BASE+0x0) +#define GICC_PMR (GIC400_GICC_BASE+0x4) +#define GICC_IAR (GIC400_GICC_BASE+0xC) +#define GICC_EOIR (GIC400_GICC_BASE+0x10) +#define GICC_RPR (GIC400_GICC_BASE+0x14) // running priority (read to diagnose) +#define GICC_APR0 (GIC400_GICC_BASE+0xD0) // active priorities (write 0 to unstick) +#define GICC_APR1 (GIC400_GICC_BASE+0xD4) +#define GICC_APR2 (GIC400_GICC_BASE+0xD8) +#define GICC_APR3 (GIC400_GICC_BASE+0xDC) + + +void gic400_init(); +void gic400_reset(); +uint32_t gic400_version(); +void gic400_enable_irq(unsigned int irqn); +void gic400_set_pending_irq(unsigned int irqn); +void gicc_drain(void); + +#endif +``` + +__gic400.c__ +```c +#include <gic400.h> + +void gicc_drain(void) { + uint32_t val; + while (((val = ioread32(GICC_IAR)) & 0x3ff) < 1020) + iowrite32(GICC_EOIR, val); +} + +void gic400_init() { + int i; + + iowrite32(GICD_CTRL, 0); + iowrite32(GICC_CTRL, 0); + + for (i = 0; i < 16; i++) { + iowrite32(GICD_ICENABLER(i), 0xffffffff); // disable + iowrite32(GICD_ICPENDR(i), 0xffffffff); // clear pending + iowrite32(GICD_ICACTIVER(i), 0xffffffff); // clear active + } + + for (i = 0; i < 16; i++) + iowrite32(GICD_IGROUPR(i), 0xffffffff); // all -> Group 1 (NS) + for (i = 0; i < 128; i++) { + iowrite32(GICD_IPRIORITYR(i), 0xa0a0a0a0); // mid priority + iowrite32(GICD_ITARGETSR(i), 0x01010101); // -> CPU0 + } + for (i = 0; i < 32; i++) + iowrite32(GICD_ICFGR(i), 0x55); // (SPIs: level-triggered) + + iowrite32(GICD_CTRL, 0x1); + + iowrite32(GICC_PMR, 0xff); + iowrite32(GICC_CTRL, 0x3); // EnableGrp0 | EnableGrp1 + + iowrite32(GICC_APR0, 0); + iowrite32(GICC_APR1, 0); + iowrite32(GICC_APR2, 0); + iowrite32(GICC_APR3, 0); + + gicc_drain(); +} + +uint32_t gic400_version() { + return ioread32(GICD_IDR); +} + +void gic400_enable_irq(unsigned int irqn) { + iowrite32(GICD_ISENABLER(irqn/32), 1 << (irqn % 32)); +} + +void gic400_set_pending_irq(unsigned int irqn) { + iowrite32(GICD_ISPENDR(irqn/32), 1 << (irqn % 32)); +} +``` + +__boot.S__ +```arm +// AArch64 mode + +// To keep this in the first portion of the binary. +.section ".text.boot" + +// Make _start global. +.globl _start + +// .org 0x80000 +// Entry point for the kernel. Registers: +// x0 -> 32 bit pointer to DTB in memory (primary core only) / 0 (secondary cores) +// x1 -> 0 +// x2 -> 0 +// x3 -> 0 +// x4 -> 32 bit kernel entry point, _start location +_start: + // set stack before our code + ldr x5, =_start + mov sp, x5 + + // clear bss + ldr x5, =__bss_start + ldr w6, =__bss_size +1: cbz w6, 2f + str xzr, [x5], #8 + sub w6, w6, #1 + cbnz w6, 1b + + + + // jump to C code, should not return +2: + //mrs x0, scr_el3 + //orr x0, x0, #(1<<1) // SCR_EL3.IRQ — take physical IRQ to EL3 + //msr scr_el3, x0 + //isb + + //set vector table (EL2!) + ldr x1, =vector_table + msr VBAR_EL2, x1 + isb + + // Route physical IRQ to EL2 (HCR_EL2.IMO = bit 4) + mrs x0, hcr_el2 + orr x0, x0, #(1 << 4) // IMO + msr hcr_el2, x0 + isb + + bl main + // for failsafe, halt this core +halt: + wfe + b halt + +.align 11 +sync_exception_handler: + bl kernel_panic + eret + +.macro ventry label + .align 7 + b \label +.endm + +dummy_vector: + b kernel_panic + +.align 11 +vector_table: + ventry sync_exception_handler + ventry handle_irq + ventry dummy_vector + ventry dummy_vector + + //EL1h + ventry dummy_vector + ventry handle_irq + ventry dummy_vector + ventry dummy_vector + + ventry sync_exception_handler + ventry handle_irq + ventry dummy_vector + ventry dummy_vector + + //EL1h + ventry dummy_vector + ventry handle_irq + ventry dummy_vector + ventry dummy_vector +``` + +__irq.S__ +```arm +.global enable_global_interrupts + +enable_global_interrupts: + msr daifclr, #7 + dmb osh + ret + +//push all the registers just after the stack pointer +.macro m__save_curr_context + //reserving 16*16 bytes of stack + sub sp, sp, #16*16 //30 regs, 8 bytes each + stp x0, x1, [sp, #16*0] + stp x2, x3, [sp, #16*1] + stp x4, x5, [sp, #16*2] + stp x6, x7, [sp, #16*3] + stp x8, x9, [sp, #16*4] + stp x10, x11, [sp, #16*5] + stp x12, x13, [sp, #16*6] + stp x14, x15, [sp, #16*7] + stp x16, x17, [sp, #16*8] + stp x18, x19, [sp, #16*9] + stp x20, x21, [sp, #16*10] + stp x22, x23, [sp, #16*11] + stp x24, x25, [sp, #16*12] + stp x26, x27, [sp, #16*13] + stp x28, x29, [sp, #16*14] + str x30, [sp, #16*15] +.endm + +//undo previous operation and start restoring +.macro m__resume_from_context + ldp x0, x1, [sp, #16*0] + ldp x2, x3, [sp, #16*1] + ldp x4, x5, [sp, #16*2] + ldp x6, x7, [sp, #16*3] + ldp x8, x9, [sp, #16*4] + ldp x10, x11, [sp, #16*5] + ldp x12, x13, [sp, #16*6] + ldp x14, x15, [sp, #16*7] + ldp x16, x17, [sp, #16*8] + ldp x18, x19, [sp, #16*9] + ldp x20, x21, [sp, #16*10] + ldp x22, x23, [sp, #16*11] + ldp x24, x25, [sp, #16*12] + ldp x26, x27, [sp, #16*13] + ldp x28, x29, [sp, #16*14] + ldr x30, [sp, #16*15] + add sp, sp, #16*16 //reclaim stack +.endm + +.global handle_irq +handle_irq: + m__save_curr_context + bl irq_handler + m__resume_from_context + eret +``` + +__kernel.c__ +```c +#include <pl011.h> +#include <timer-bcm2835.h> +#include <gic400.h> + +extern void enable_global_interrupts(); + +void kernel_panic(void) __attribute__ ((noreturn)); + +void kernel_panic() { + pl011_puts("Kernel panic\r\n"); + while(1) { + } +} + +volatile uint32_t spurious_count = 0; + +void irq_handler() { + uint32_t val = ioread32(GICC_IAR); + uint32_t irq = val & 0x3ff; + + if (irq >= 1020) { + spurious_count++; + return; + } + + pl011_puts("Got IRQ "); + pl011_putu32(irq); + pl011_puts("\r\n"); + bcm2835_write32(BCM2835_CS, 0xf); // clear all four match flags + timer_start_c1(); + + iowrite32(GICC_EOIR, val); +} + +static inline uint32_t current_el(void) { + uint64_t el; + __asm__ volatile("mrs %0, CurrentEL" : "=r"(el)); + return (uint32_t)((el >> 2) & 0x3); +} + +static void gic_drain(void) { + uint32_t val; + while (((val = ioread32(GICC_IAR)) & 0x3ff) < 1020) + iowrite32(GICC_EOIR, val); + +} + +static void put_dec(uint32_t v) { + char buf[10]; + int i = 0; + if (v == 0) { pl011_putc('0'); return; } + while (v) { buf[i++] = '0' + (v % 10); v /= 10; } + while (i) pl011_putc(buf[--i]); +} + +static void scan_pending(void) { + for (int n = 0; n < 16; n++) { + uint32_t p = ioread32(GICD_ISPENDR(n)); + for (int b = 0; b < 32; b++) { + if ((p >> b) & 1) { + pl011_puts(" pending IRQ "); + put_dec(32 * n + b); + pl011_puts("\r\n"); + } + } + } +} + +void main() +{ + pl011_puts("----------------------------\r\n"); + pl011_puts("Running at EL"); + pl011_putu32(current_el()); + pl011_puts("\r\n"); + + gic400_init(); + + bcm2835_write32(BCM2835_CS, 0xf); + gic400_enable_irq(97); + gic400_enable_irq(99); + enable_global_interrupts(); + timer_start_c1(); + timer_start_c3(); + + for (volatile int d = 0; d < 4000000; d++); + + pl011_puts("CS = "); + pl011_putu32(bcm2835_read32(BCM2835_CS)); + pl011_puts("\r\n"); + + scan_pending(); + + while (1) { + __asm__ volatile("wfi"); + } +} +``` + ## Links https://www.raspberrypi.com/documentation/computers/config_txt.html |
