From 75e2582970510813fecedc01f210bacf71e74676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Wed, 31 Jul 2019 00:00:00 +0000 Subject: [PATCH] arm gic: get closer to working, still failing though Define print functions for all system regs. --- README.adoc | 158 +++++++++++++++++++++++++-------- baremetal/arch/aarch64/svc.c | 6 +- baremetal/arch/aarch64/timer.c | 103 ++++++++++++--------- lkmc.c | 11 ++- lkmc/aarch64.h | 54 +++++++---- lkmc/gicv3.h | 4 +- 6 files changed, 229 insertions(+), 107 deletions(-) diff --git a/README.adoc b/README.adoc index 5821921..c937edc 100644 --- a/README.adoc +++ b/README.adoc @@ -15444,6 +15444,18 @@ in link:baremetal/lib/arm.S[]. That patch however enables SIMD in baremetal, whi According to <>, access to that register is controlled by other registers `NSACR.{CP11, CP10}` and `HCPTR` so those must be turned off, but I'm lazy to investigate now, even just trying to dump those registers in link:userland/arch/arm/dump_regs.c[] also leads to exceptions... +===== ARM change exception level + +TODO. Create a minimal runnable example of going into EL0 and jumping to EL1. + +===== ARM SP0 vs SPx + +See <> D1.6.2 "The stack pointer registers". + +TODO create a minimal runnable example. + +TODO: how to select to use SP0 in an exception handler? + ==== ARM SVC instruction This is the most basic example of exception handling we have. @@ -15618,12 +15630,23 @@ the output contains: So we see in both cases that the: * SVC is done -* an exception happens, and the PC jumps to address 0x40000a00. From our custom terminal prints further on, we see that this equals `VBAR_EL1 + 0x200`. TODO why + 0x200? +* an exception happens, and the PC jumps to address 0x40000a00. From our custom terminal prints further on, we see that this equals `VBAR_EL1 + 0x200`. ++ +According to the format of the <>, we see that the `+ 0x200` means that we are jumping in the Current EL with SPx. ++ +This can also be deduced from the message `exc_type is LKMC_VECTOR_SYNC_SPX`: we just manually store a different integer for every exception vector type in our handler code to be able to tell what happened. ++ +This is the one used because we are jumping <>. + We set VBAR_EL1 to that address ourselves <>. -* at 0x40000a00 a `b #0x40001294` is done and at 0x40001294 boilerplate preparation is done for lkmc_vector_trap_handler. +* at 0x40000a00 a `b #0x40001294` is done and then at 0x40001294 boilerplate preparation is done for lkmc_vector_trap_handler starting with several STP instructions. + -We have coded both of those in our vector table macro madness. +We have coded both of those in our vector table macro madness. As of LKMC 8f73910dd1fc1fa6dc6904ae406b7598cdcd96d7, both come from link:lkmc/aarch64.h[]: ++ +** `b #0x40001294` comes from: `LKMC_VECTOR_ENTRY` +** the STP come from: `LKMC_VECTOR_BUILD_TRAPFRAME` ++ +We jump immediately from inside `LKMC_VECTOR_ENTRY` to `LKMC_VECTOR_BUILD_TRAPFRAME` because we can only use 0x80 bytes of instructions for each one before reaching the next handler, so we might as well get it over with by jumping into a memory region without those constraints. + TODO: why doesn't QEMU show our nice symbol names? gem5 shows them fine, and `nm` says they are there! + @@ -15681,40 +15704,6 @@ which does an `eret` and jumps back to 0x4000209c, which is 4 bytes and therefor In QEMU, and then we just continue running from the exception handler address. -The vector table format is described on <> Table D1-7 "Vector offsets from vector table base address". - -A good representation of the format of the vector table can also be found at <> Table 10-2 "Vector table offsets from vector table base address". - -The first part of the table contains: xref:table-armv8-vector-handlers[xrefstyle=full]. - -[[table-armv8-vector-handlers]] -.Summary of ARMv8 vector handlers -[options="header"] -|=== -|Address |Exception type |Description - -|VBAR_ELn + 0x000 -|Synchronous -|Current EL with SP0 - -|VBAR_ELn + 0x080 -|IRQ/vIRQ + 0x100 -|Current EL with SP0 - -|VBAR_ELn + 0x100 -|FIQ/vFIQ -|Current EL with SP0 - -|VBAR_ELn + 0x180 -|SError/vSError -|Current EL with SP0 - -|=== - -and the following other parts are analogous, but referring to SPx and lower ELs. - -We are going to do everything in <> for now. - On the terminal output, we observe the initial values of: * DAIF: 0x3c0, i.e. 4 bits (6 to 9) set to 1, which means that exceptions are masked for each exception type: Synchronous, System error, IRQ and FIQ. @@ -15735,6 +15724,99 @@ Bibliography: * https://stackoverflow.com/questions/24162109/arm-assembly-code-and-svc-numbering/57064062#57064062 * https://stackoverflow.com/questions/44991264/armv8-exception-vectors-and-handling +===== ARMv8 exception vector table format + +The vector table format is described on <> Table D1-7 "Vector offsets from vector table base address". + +A good representation of the format of the vector table can also be found at <> Table 10-2 "Vector table offsets from vector table base address". + +The first part of the table contains: xref:table-armv8-vector-handlers[xrefstyle=full]. + +[[table-armv8-vector-handlers]] +.Summary of ARMv8 vector handlers +[options="header"] +|=== +|Address |Exception type |Description + +|VBAR_ELn + 0x000 +|Synchronous +|Current EL with SP0 + +|VBAR_ELn + 0x080 +|IRQ/vIRQ +|Current EL with SP0 + +|VBAR_ELn + 0x100 +|FIQ/vFIQ +|Current EL with SP0 + +|VBAR_ELn + 0x180 +|SError/vSError +|Current EL with SP0 + +|VBAR_ELn + 0x200 +|Synchronous +|Current EL with SPx + +|VBAR_ELn + 0x280 +|IRQ/vIRQ +|Current EL with SPx + +|VBAR_ELn + 0x300 +|FIQ/vFIQ +|Current EL with SPx + +|VBAR_ELn + 0x380 +|SError/vSError +|Lower EL using AArch64 + +|VBAR_ELn + 0x400 +|Synchronous +|Lower EL using AArch64 + +|VBAR_ELn + 0x480 +|IRQ/vIRQ +|Lower EL using AArch64 + +|VBAR_ELn + 0x500 +|FIQ/vFIQ +|Lower EL using AArch64 + +|VBAR_ELn + 0x580 +|SError/vSError +|Lower EL using AArch64 + +|VBAR_ELn + 0x600 +|Synchronous +|Lower EL using AArch32 + +|VBAR_ELn + 0x680 +|IRQ/vIRQ +|Lower EL using AArch32 + +|VBAR_ELn + 0x700 +|FIQ/vFIQ +|Lower EL using AArch32 + +|VBAR_ELn + 0x780 +|SError/vSError +|Lower EL using AArch32 + +|=== + +and the following other parts are analogous, but referring to SPx and lower ELs. + +Now, to fully understand this table, we need the following concepts: + +* Synchronous: what happens for example when we do an <>. ++ +It is called synchronous because the CPU is generating it itself from an instruction, unlike an interrupt generated by a device like a keyboard, which ends up in an IRQ or FIQ +* IRQ: an example can be found at: <> +* TODO FIQ vs IRQ +* TODO SError +* EL changes: <> +* SP0 vs SPx: <>. + ===== ARM ESR register Exception Syndrome Register. @@ -16143,7 +16225,7 @@ ISB but it entered an exception loop at `MSR CPTR_EL3, XZR`. -We then found out that QEMU <>, and so we kept just the EL1 part, and it worked. Related: +We then found out that QEMU <>, and so we kept just the EL1 part, and it worked. Related: * https://stackoverflow.com/questions/42824706/qemu-system-aarch64-entering-el1-when-emulating-a53-power-up * https://stackoverflow.com/questions/37299524/neon-support-in-armv8-system-mode-qemu diff --git a/baremetal/arch/aarch64/svc.c b/baremetal/arch/aarch64/svc.c index 60d4b18..09808d8 100644 --- a/baremetal/arch/aarch64/svc.c +++ b/baremetal/arch/aarch64/svc.c @@ -64,9 +64,9 @@ void lkmc_vector_trap_handler(LkmcVectorExceptionFrame *exception) { int main(void) { /* View initial relevant register values. */ - printf("DAIF 0x%" PRIX32 "\n", lkmc_sysreg_daif_read()); - printf("SPSEL 0x%" PRIX32 "\n", lkmc_sysreg_spsel_read()); - printf("VBAR_EL1 0x%" PRIX64 "\n", lkmc_sysreg_vbar_el1_read()); + lkmc_sysreg_print_daif(); + lkmc_sysreg_print_spsel(); + lkmc_sysreg_print_vbar_el1(); /* https://stackoverflow.com/questions/1777990/is-it-possible-to-store-the-address-of-a-label-in-a-variable-and-use-goto-to-jum */ printf("&after_svc %p\n", &&after_svc); assert(myvar == 0); diff --git a/baremetal/arch/aarch64/timer.c b/baremetal/arch/aarch64/timer.c index f4f71db..367c1ea 100644 --- a/baremetal/arch/aarch64/timer.c +++ b/baremetal/arch/aarch64/timer.c @@ -4,63 +4,84 @@ #include #include -void lkmc_vector_trap_handler(LkmcVectorExceptionFrame *exception __attribute__((unused))) { - printf("CNTVCT_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntvct_el0_read()); -} - -/* DAIF, Interrupt Mask Bits */ -#define DAIF_DBG_BIT (1<<3) /* Debug mask bit */ -#define DAIF_ABT_BIT (1<<2) /* Asynchronous abort mask bit */ -#define DAIF_IRQ_BIT (1<<1) /* IRQ mask bit */ -#define DAIF_FIQ_BIT (1<<0) /* FIQ mask bit */ - -#define wfi() __asm__ __volatile__ ("wfi" : : : "memory") - -void enable_cntv(void) { - lkmc_sysreg_cntv_ctl_el0_write(lkmc_sysreg_cntv_ctl_el0_read() | LKMC_CNTV_CTL_ENABLE); -} +#define IRQ_FOUND (0) +#define IRQ_NOT_FOUND (1) void enable_irq(void) { - __asm__ __volatile__ ("msr DAIFClr, %0" : : "i" (DAIF_IRQ_BIT) : "memory"); + __asm__ __volatile__("msr DAIFClr, %0\n\t" : : "i" (LKMC_SYSREG_BITS_DAIF_IRQ) : "memory"); +} + +void disable_irq(void) { + __asm__ __volatile__("msr DAIFSet, %0\n\t" : : "i" (LKMC_SYSREG_BITS_DAIF_IRQ) : "memory"); +} + +/* Processor status word. */ +void psw_disable_and_save_interrupt(uint64_t *pswp) { + uint64_t psw; + psw = lkmc_sysreg_read_daif(); + disable_irq(); + *pswp = psw; +} + +/* Processor status word. */ +void psw_restore_interrupt(uint64_t *pswp) { + uint64_t psw; + psw = *pswp; + lkmc_sysreg_write_daif(psw); +} + +void lkmc_vector_trap_handler( + LkmcVectorExceptionFrame *exception __attribute__((unused)) +) { + uint64_t psw; + irq_no irq; + int rc; + if ((exception->exc_type & 0xff) == LKMC_VECTOR_IRQ_SPX) { + psw_disable_and_save_interrupt(&psw); + rc = gic_v3_find_pending_irq(exception, &irq); + if (rc != IRQ_FOUND) { + puts("IRQ not found!"); + goto restore_irq_out; + } else { + printf("IRQ found: 0x%" PRIX32 "\n", irq); + } + gicd_disable_int(irq); + gic_v3_eoi(irq); + printf("CNTVCT_EL0 0x%" PRIX64 "\n", lkmc_sysreg_read_cntvct_el0()); + gicd_enable_int(irq); +restore_irq_out: + psw_restore_interrupt(&psw); + } +} + +void enable_cntv(void) { + lkmc_sysreg_write_cntv_ctl_el0(lkmc_sysreg_read_cntv_ctl_el0() | LKMC_CNTV_CTL_ENABLE); } int main(void) { /* Initial state. */ - printf("CNTV_CTL_EL0 0x%" PRIX32 "\n", lkmc_sysreg_cntv_ctl_el0_read()); - printf("CNTFRQ_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntfrq_el0_read()); - printf("CNTV_CVAL_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntv_cval_el0_read()); + lkmc_sysreg_print_cntv_ctl_el0(); + lkmc_sysreg_print_cntfrq_el0(); + lkmc_sysreg_print_cntv_cval_el0(); /* Get the counter value many times to watch the time pass. */ - printf("CNTVCT_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntvct_el0_read()); - printf("CNTVCT_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntvct_el0_read()); - printf("CNTVCT_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntvct_el0_read()); + lkmc_sysreg_print_cntvct_el0(); + lkmc_sysreg_print_cntvct_el0(); + lkmc_sysreg_print_cntvct_el0(); - /**/ gic_v3_initialize(); { /*uint64_t ticks, current_cnt;*/ /*uint32_t cntfrq;*/ - lkmc_sysreg_cntfrq_el0_write(1); + lkmc_sysreg_write_cntfrq_el0(1); /*ticks = cntfrq;*/ - /*current_cnt = lkmc_sysreg_cntvct_el0_read();*/ - /*lkmc_sysreg_cntv_cval_el0_write(current_cnt + ticks);*/ + /*current_cnt = lkmc_sysreg_read_cntvct_el0();*/ + /*lkmc_sysreg_write_cntv_cval_el0(current_cnt + ticks);*/ enable_cntv(); enable_irq(); } - /*while (1) {*/ - /*puts("qwer");*/ - /*current_cnt = raw_read_cntvct_el0();*/ - /*val = raw_read_cntv_ctl();*/ - /*printf("CNTVCT_EL0 = ");*/ - /*wfi();*/ - /*}*/ - -#if 0 - /* TODO crashes gem5. */ - puts("cntfrq_el0 = 1"); - lkmc_sysreg_cntfrq_el0_write(1); - printf("cntfrq_el0 0x%" PRIX64 "\n", lkmc_sysreg_cntfrq_el0_read()); -#endif - + while (1) { + LKMC_WFI; + } return 0; } diff --git a/lkmc.c b/lkmc.c index 21abf07..405adc5 100644 --- a/lkmc.c +++ b/lkmc.c @@ -69,14 +69,17 @@ void lkmc_print_newline() { } #if defined(__aarch64__) -#define LKMC_SYSREG_READ_WRITE(type, name) \ - type LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _read(void)) { \ - type name; \ +#define LKMC_SYSREG_READ_WRITE(nbits, name) \ + LKMC_CONCAT(LKMC_CONCAT(uint, nbits), _t) LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, read_), name)(void) { \ + LKMC_CONCAT(LKMC_CONCAT(uint, nbits), _t) name; \ __asm__ __volatile__("mrs %0, " #name : "=r" (name) : : ); \ return name; \ } \ - void LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _write(type name)) { \ + void LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, write_), name)(LKMC_CONCAT(LKMC_CONCAT(uint, nbits), _t) name) { \ __asm__ __volatile__("msr " #name ", %0" : : "r" (name) : ); \ + } \ + void LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, print_), name)(void) { \ + printf("name 0x%" PRIX ## nbits "\n", LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, read_), name)()); \ } LKMC_SYSREG_OPS #undef LKMC_SYSREG_READ_WRITE diff --git a/lkmc/aarch64.h b/lkmc/aarch64.h index 0d9960c..aec3c58 100644 --- a/lkmc/aarch64.h +++ b/lkmc/aarch64.h @@ -65,14 +65,22 @@ main: \ main_after_prologue: \ ; -/* Counter: https://github.com/cirosantilli/linux-kernel-module-cheat#arm-timer */ -#define LKMC_CNTV_CTL_ENABLE (1 << 0) -#define LKMC_CNTV_CTL_IMASK (1 << 1) -#define LKMC_CNTV_CTL_ISTATUS (1 << 2) +/** Fields of system registers. */ -/* LKMC_VECTOR_TABLE +/* Counter: https://github.com/cirosantilli/linux-kernel-module-cheat#arm-timer */ +#define LKMC_SYSREG_CNTV_CTL_ENABLE (1 << 0) +#define LKMC_SYSREG_CNTV_CTL_IMASK (1 << 1) +#define LKMC_SYSREG_CNTV_CTL_ISTATUS (1 << 2) + +/* DAIF. */ +#define LKMC_SYSREG_BITS_DAIF_FIQ (1<<0) +#define LKMC_SYSREG_BITS_DAIF_IRQ (1<<1) +#define LKMC_SYSREG_BITS_DAIF_ABT (1<<2) +#define LKMC_SYSREG_BITS_DAIF_DBG (1<<3) + +/* LKMC_VECTOR_* * - * Adapted from: https://github.com/takeharukato/sample-tsk-sw/blob/ce7973aa5d46c9eedb58309de43df3b09d4f8d8d/hal/aarch64/vector.S + * https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-exception-vector-table-format */ #define LKMC_VECTOR_SYNC_SP0 (0x1) @@ -285,25 +293,33 @@ typedef struct { void lkmc_vector_trap_handler(LkmcVectorExceptionFrame *exception); +/* Mistc assembly instructions. */ #define LKMC_SVC(immediate) __asm__ __volatile__("svc " #immediate : : : ) +#define LKMC_WFI() __asm__ __volatile__ ("wfi" : : : "memory") +/* Sysreg read and write functions, e.g.: + * + * * lkmc_sysreg_read_daif(void); + * * lkmc_sysreg_write_daif(uint64_t); + * * lkmc_sysreg_print_daif(void); + */ #define LKMC_SYSREG_SYMBOL_PREFIX lkmc_sysreg_ -#define LKMC_SYSREG_READ_WRITE(type, name) \ - type LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _read)(void); \ - void LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _write)(type name); +#define LKMC_SYSREG_READ_WRITE(nbits, name) \ + LKMC_CONCAT(LKMC_CONCAT(uint, nbits), _t) LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, read_), name)(void); \ + void LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, write_), name)(LKMC_CONCAT(LKMC_CONCAT(uint, nbits), _t) name); \ + void LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, print_), name)(void); #define LKMC_SYSREG_OPS \ - LKMC_SYSREG_READ_WRITE(uint32_t, cntv_ctl_el0) \ - LKMC_SYSREG_READ_WRITE(uint32_t, daif) \ - LKMC_SYSREG_READ_WRITE(uint32_t, spsel) \ - LKMC_SYSREG_READ_WRITE(uint64_t, cntfrq_el0) \ - LKMC_SYSREG_READ_WRITE(uint64_t, cntv_cval_el0) \ - LKMC_SYSREG_READ_WRITE(uint64_t, cntv_tval_el0) \ - LKMC_SYSREG_READ_WRITE(uint64_t, cntvct_el0) \ - LKMC_SYSREG_READ_WRITE(uint64_t, sp_el1) \ - LKMC_SYSREG_READ_WRITE(uint64_t, vbar_el1) + LKMC_SYSREG_READ_WRITE(32, cntv_ctl_el0) \ + LKMC_SYSREG_READ_WRITE(64, daif) \ + LKMC_SYSREG_READ_WRITE(32, spsel) \ + LKMC_SYSREG_READ_WRITE(64, cntfrq_el0) \ + LKMC_SYSREG_READ_WRITE(64, cntv_cval_el0) \ + LKMC_SYSREG_READ_WRITE(64, cntv_tval_el0) \ + LKMC_SYSREG_READ_WRITE(64, cntvct_el0) \ + LKMC_SYSREG_READ_WRITE(64, sp_el1) \ + LKMC_SYSREG_READ_WRITE(64, vbar_el1) LKMC_SYSREG_OPS #undef LKMC_SYSREG_READ_WRITE #endif - #endif diff --git a/lkmc/gicv3.h b/lkmc/gicv3.h index 832b3b2..69a135c 100644 --- a/lkmc/gicv3.h +++ b/lkmc/gicv3.h @@ -303,8 +303,8 @@ int gic_v3_find_pending_irq( ) { int rc; irq_no i; - for( i = 0; GIC_INT_MAX > i; ++i) { - if ( gicd_probe_pending(i) ) { + for (i = 0; GIC_INT_MAX > i; ++i) { + if (gicd_probe_pending(i)) { rc = 1; *irqp = i; goto found;