From 3b93a2d65a832e26d59a1ce720aae892bc9f104d 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: Tue, 30 Jul 2019 00:00:01 +0000 Subject: [PATCH] avm svc: fully study and understand QEMU traces Preparation for timer. Timer is almost working it seems, just need to ACK the interrupt most likely: it is firing like mad that's all. --- README.adoc | 142 ++++++++++++++++++++++++++++++--- baremetal/arch/aarch64/svc.c | 2 +- baremetal/arch/aarch64/timer.c | 33 +++----- lkmc/aarch64.h | 18 +++-- userland/posix/count_to.c | 9 ++- 5 files changed, 166 insertions(+), 38 deletions(-) diff --git a/README.adoc b/README.adoc index 9bd7afd..5821921 100644 --- a/README.adoc +++ b/README.adoc @@ -15539,26 +15539,61 @@ With <>: .... ./run \ --arch aarch64 \ - --baremetal baremetal/arch/aarch64/svc_asm.S + --baremetal baremetal/arch/aarch64/svc.c \ -- -d in_asm,int \ ; .... -the output contains: +the output at 8f73910dd1fc1fa6dc6904ae406b7598cdcd96d7 contains: .... + ---------------- -IN: -0x40002060: d4000001 svc #0 +IN: main +0x40002098: d41579a1 svc #0xabcd Taking exception 2 [SVC] ...from EL1 to EL1 -...with ESR 0x15/0x56000000 -...with ELR 0x40002064 +...with ESR 0x15/0x5600abcd +...with ELR 0x4000209c ...to EL1 PC 0x40000a00 PSTATE 0x3c5 ---------------- IN: 0x40000a00: 14000225 b #0x40001294 + +---------------- +IN: +0x40001294: a9bf7bfd stp x29, x30, [sp, #-0x10]! +0x40001298: a9bf73fb stp x27, x28, [sp, #-0x10]! +0x4000129c: a9bf6bf9 stp x25, x26, [sp, #-0x10]! +0x400012a0: a9bf63f7 stp x23, x24, [sp, #-0x10]! +0x400012a4: a9bf5bf5 stp x21, x22, [sp, #-0x10]! +0x400012a8: a9bf53f3 stp x19, x20, [sp, #-0x10]! +0x400012ac: a9bf4bf1 stp x17, x18, [sp, #-0x10]! +0x400012b0: a9bf43ef stp x15, x16, [sp, #-0x10]! +0x400012b4: a9bf3bed stp x13, x14, [sp, #-0x10]! +0x400012b8: a9bf33eb stp x11, x12, [sp, #-0x10]! +0x400012bc: a9bf2be9 stp x9, x10, [sp, #-0x10]! +0x400012c0: a9bf23e7 stp x7, x8, [sp, #-0x10]! +0x400012c4: a9bf1be5 stp x5, x6, [sp, #-0x10]! +0x400012c8: a9bf13e3 stp x3, x4, [sp, #-0x10]! +0x400012cc: a9bf0be1 stp x1, x2, [sp, #-0x10]! +0x400012d0: d5384015 mrs x21, spsr_el1 +0x400012d4: a9bf03f5 stp x21, x0, [sp, #-0x10]! +0x400012d8: d5384035 mrs x21, elr_el1 +0x400012dc: a9bf57ff stp xzr, x21, [sp, #-0x10]! +0x400012e0: d2800235 movz x21, #0x11 +0x400012e4: d5385216 mrs x22, esr_el1 +0x400012e8: a9bf5bf5 stp x21, x22, [sp, #-0x10]! +0x400012ec: 910003f5 mov x21, sp +0x400012f0: 910482b5 add x21, x21, #0x120 +0x400012f4: f9000bf5 str x21, [sp, #0x10] +0x400012f8: 910003e0 mov x0, sp +0x400012fc: 9400023f bl #0x40001bf8 + +---------------- +IN: lkmc_vector_trap_handler +0x40001bf8: a9bd7bfd stp x29, x30, [sp, #-0x30]! .... And with <>: @@ -15580,7 +15615,71 @@ the output contains: 4500: system.cpu A0 T0 : @vector_table+512 : b <_curr_el_spx_sync> : IntAlu : flags=(IsControl|IsDirectControl|IsUncondControl) .... -So we see in both cases that the SVC is done, then an exception happens, and then we just continue running from the exception handler address. +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? ++ +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. ++ +We have coded both of those in our vector table macro madness. ++ +TODO: why doesn't QEMU show our nice symbol names? gem5 shows them fine, and `nm` says they are there! ++ +.... +0000000040000800 T lkmc_vector_table +0000000040001294 T lkmc_vector_build_trapframe_curr_el_spx_sync +.... + +The exception return happens at the end of `lkmc_vector_trap_handler`: + +.... +---------------- +IN: lkmc_vector_trap_handler +0x40002000: d503201f nop +0x40002004: a8c37bfd ldp x29, x30, [sp], #0x30 +0x40002008: d65f03c0 ret + +---------------- +IN: +0x40001300: 910043ff add sp, sp, #0x10 +0x40001304: a8c15bf5 ldp x21, x22, [sp], #0x10 +0x40001308: d5184036 msr elr_el1, x22 + +---------------- +IN: +0x4000130c: a8c103f5 ldp x21, x0, [sp], #0x10 +0x40001310: d5184015 msr spsr_el1, x21 + +---------------- +IN: +0x40001314: a8c10be1 ldp x1, x2, [sp], #0x10 +0x40001318: a8c113e3 ldp x3, x4, [sp], #0x10 +0x4000131c: a8c11be5 ldp x5, x6, [sp], #0x10 +0x40001320: a8c123e7 ldp x7, x8, [sp], #0x10 +0x40001324: a8c12be9 ldp x9, x10, [sp], #0x10 +0x40001328: a8c133eb ldp x11, x12, [sp], #0x10 +0x4000132c: a8c13bed ldp x13, x14, [sp], #0x10 +0x40001330: a8c143ef ldp x15, x16, [sp], #0x10 +0x40001334: a8c14bf1 ldp x17, x18, [sp], #0x10 +0x40001338: a8c153f3 ldp x19, x20, [sp], #0x10 +0x4000133c: a8c15bf5 ldp x21, x22, [sp], #0x10 +0x40001340: a8c163f7 ldp x23, x24, [sp], #0x10 +0x40001344: a8c16bf9 ldp x25, x26, [sp], #0x10 +0x40001348: a8c173fb ldp x27, x28, [sp], #0x10 +0x4000134c: a8c17bfd ldp x29, x30, [sp], #0x10 +0x40001350: d69f03e0 eret + +Exception return from AArch64 EL1 to AArch64 EL1 PC 0x4000209c +---------------- +IN: main +0x4000209c: d0000040 adrp x0, #0x4000c000 +.... + +which does an `eret` and jumps back to 0x4000209c, which is 4 bytes and therefore one instruction after where SVC was taken at 0x40002098. + +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". @@ -15792,7 +15891,10 @@ The key registers to keep in mind are: * `CNTVCT_EL0`: "Counter-timer Virtual Count register". The increasing current counter value. * `CNTFRQ_EL0`: "Counter-timer Frequency register". "Indicates the system counter clock frequency, in Hz." -* `CNTV_CTL_EL0`: "Counter-timer Virtual Timer Control register" +* `CNTV_CTL_EL0`: "Counter-timer Virtual Timer Control register". This control register is very simple and only has three fields: +** `CNTV_CTL_EL0.ISTATUS` bit: set to 1 when the timer condition is met +** `CNTV_CTL_EL0.IMASK` bit: if 1, the interrupt does not happen when `ISTATUS` becomes one +** `CNTV_CTL_EL0.ENABLE` bit: if 0, the counter is turned off, interrupts don't happen * `CNTV_CVAL_EL0`: "Counter-timer Virtual Timer CompareValue register". The interrupt happens when `CNTVCT_EL0` reaches the value in this register. Bibliography: @@ -15800,6 +15902,28 @@ Bibliography: * https://stackoverflow.com/questions/51094092/how-to-make-timer-irq-work-on-qemu-machine-virt-cpu-cortex-a57 * https://stackoverflow.com/questions/44198483/arm-timers-and-interrupts +==== ARM GIC + +Generic Interrupt Controller. + +Examples: + +* xref:arm-timer[] + +ARM publishes both a GIC standard architecture specification, and specific implementations of these specifications. + +The specification can be found at: https://developer.arm.com/docs/ihi0069/latest + +As of 2019Q2 the latest version if v4.0, often called GICv4: https://static.docs.arm.com/ihi0069/e/Q1-IHI0069E_gic_architecture_specification_v3.1_19_01_21.pdf + +That document clarifies that GICv2 is a legacy specification only: + +.... +Version 2.0 (GICv2) is only described in terms of the GICv3 optional support for legacy operation +.... + +The specific models have names of type GIC-600, GIC-500, etc. + ==== ARM paging TODO create a minimal working aarch64 example analogous to the x86 one at: https://github.com/cirosantilli/x86-bare-metal-examples/blob/6dc9a73830fc05358d8d66128f740ef9906f7677/paging.S @@ -15857,7 +15981,7 @@ static void init_gicc(void) which tries to write to 0x8010000 according to GDB. -Without `-machine`, QEMU's DTB clearly states GICv2, so I'm starting to wonder if Nienfeng just made a mistake there? The QEMU GICv3 dtb contains: +Without `-machine`, QEMU's DTB clearly states GICv2, so I'm starting to wonder if Nienfeng just made a mistake there? The QEMU GICv3 DTB contains: .... reg = <0x0 0x8000000 0x0 0x10000 0x0 0x80a0000 0x0 0xf60000>; diff --git a/baremetal/arch/aarch64/svc.c b/baremetal/arch/aarch64/svc.c index a9337f7..60d4b18 100644 --- a/baremetal/arch/aarch64/svc.c +++ b/baremetal/arch/aarch64/svc.c @@ -68,7 +68,7 @@ int main(void) { printf("SPSEL 0x%" PRIX32 "\n", lkmc_sysreg_spsel_read()); printf("VBAR_EL1 0x%" PRIX64 "\n", lkmc_sysreg_vbar_el1_read()); /* 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); + printf("&after_svc %p\n", &&after_svc); assert(myvar == 0); /* Max 16-bits. */ LKMC_SVC(0xABCD); diff --git a/baremetal/arch/aarch64/timer.c b/baremetal/arch/aarch64/timer.c index 3bf6b5d..f4f71db 100644 --- a/baremetal/arch/aarch64/timer.c +++ b/baremetal/arch/aarch64/timer.c @@ -8,20 +8,16 @@ void lkmc_vector_trap_handler(LkmcVectorExceptionFrame *exception __attribute__( printf("CNTVCT_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntvct_el0_read()); } -#define CNTV_CTL_ENABLE (1 << 0) /* Enables the timer */ -#define CNTV_CTL_IMASK (1 << 1) /* Timer interrupt mask bit */ -#define CNTV_CTL_ISTATUS (1 << 2) /* The status of the timer interrupt. This bit is read-only */ - /* 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 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() | CNTV_CTL_ENABLE); + lkmc_sysreg_cntv_ctl_el0_write(lkmc_sysreg_cntv_ctl_el0_read() | LKMC_CNTV_CTL_ENABLE); } void enable_irq(void) { @@ -42,25 +38,22 @@ int main(void) { /**/ gic_v3_initialize(); { - uint64_t ticks, current_cnt; - uint32_t cntfrq; - cntfrq = lkmc_sysreg_cntfrq_el0_read(); - ticks = cntfrq; - current_cnt = lkmc_sysreg_cntvct_el0_read(); - lkmc_sysreg_cntv_cval_el0_write(current_cnt + ticks); + /*uint64_t ticks, current_cnt;*/ + /*uint32_t cntfrq;*/ + lkmc_sysreg_cntfrq_el0_write(1); + /*ticks = cntfrq;*/ + /*current_cnt = lkmc_sysreg_cntvct_el0_read();*/ + /*lkmc_sysreg_cntv_cval_el0_write(current_cnt + ticks);*/ enable_cntv(); enable_irq(); } - while (1) { + /*while (1) {*/ /*puts("qwer");*/ /*current_cnt = raw_read_cntvct_el0();*/ /*val = raw_read_cntv_ctl();*/ /*printf("CNTVCT_EL0 = ");*/ - /*uart_puthex(current_cnt);*/ - /*uart_puts(", CNTV_CTL_EL0 = ");*/ - /*uart_puthex(val);*/ /*wfi();*/ - } + /*}*/ #if 0 /* TODO crashes gem5. */ diff --git a/lkmc/aarch64.h b/lkmc/aarch64.h index 2856686..0d9960c 100644 --- a/lkmc/aarch64.h +++ b/lkmc/aarch64.h @@ -65,6 +65,11 @@ 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) + /* LKMC_VECTOR_TABLE * * Adapted from: https://github.com/takeharukato/sample-tsk-sw/blob/ce7973aa5d46c9eedb58309de43df3b09d4f8d8d/hal/aarch64/vector.S @@ -104,7 +109,8 @@ main_after_prologue: \ #define LKMC_VECTOR_SYMBOL_PREFIX lkmc_vector_ /* Push several registers on the stack to match LkmcVectorExceptionFrame. */ -#define LKMC_VECTOR_BUILD_TRAPFRAME(exc_type) \ +#define LKMC_VECTOR_BUILD_TRAPFRAME(exc_type, func_name) \ +LKMC_GLOBAL(LKMC_CONCAT(LKMC_CONCAT(LKMC_VECTOR_SYMBOL_PREFIX, build_trapframe_), func_name)) \ stp x29, x30, [sp, -16]!; \ stp x27, x28, [sp, -16]!; \ stp x25, x26, [sp, -16]!; \ @@ -170,12 +176,12 @@ main_after_prologue: \ #define LKMC_VECTOR_ENTRY(func_name) \ .align 7; \ - b LKMC_VECTOR_SYMBOL_PREFIX ## func_name + b LKMC_CONCAT(LKMC_CONCAT(LKMC_VECTOR_SYMBOL_PREFIX, entry_), func_name) #define LKMC_VECTOR_FUNC(func_name, func_id) \ LKMC_VECTOR_FUNC_ALIGN; \ -LKMC_VECTOR_SYMBOL_PREFIX ## func_name:; \ - LKMC_VECTOR_BUILD_TRAPFRAME(func_id); \ +LKMC_CONCAT(LKMC_CONCAT(LKMC_VECTOR_SYMBOL_PREFIX, entry_), func_name):; \ + LKMC_VECTOR_BUILD_TRAPFRAME(func_id, func_name); \ LKMC_VECTOR_STORE_TRAPED_SP; \ LKMC_VECTOR_CALL_TRAP_HANDLER; \ LKMC_VECTOR_RESTORE_TRAPED_SP; \ @@ -183,8 +189,8 @@ LKMC_VECTOR_SYMBOL_PREFIX ## func_name:; \ #define LKMC_VECTOR_FUNC_NESTED(func_name, func_id) \ LKMC_VECTOR_FUNC_ALIGN; \ -LKMC_VECTOR_SYMBOL_PREFIX ## func_name:; \ - LKMC_VECTOR_BUILD_TRAPFRAME(func_id); \ +LKMC_CONCAT(LKMC_CONCAT(LKMC_VECTOR_SYMBOL_PREFIX, entry_), func_name):; \ + LKMC_VECTOR_BUILD_TRAPFRAME(func_id, func_name); \ LKMC_VECTOR_STORE_NESTED_SP; \ LKMC_VECTOR_CALL_TRAP_HANDLER; \ LKMC_VECTOR_RESTORE_TRAPFRAME diff --git a/userland/posix/count_to.c b/userland/posix/count_to.c index 2238a6f..9501aab 100644 --- a/userland/posix/count_to.c +++ b/userland/posix/count_to.c @@ -14,16 +14,21 @@ #include int main(int argc, char **argv) { - unsigned long i, max; + unsigned long i, max, sleep_time; if (argc > 1) { max = strtoll(argv[1], NULL, 0); } else { max = 1; } + if (argc > 2) { + sleep_time = strtoll(argv[2], NULL, 0); + } else { + sleep_time = 2; + } i = 0; while (i < max) { printf("%lu\n", i); i++; - sleep(1); + sleep(sleep_time); } }