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.
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-07-30 00:00:01 +00:00
parent 8f73910dd1
commit 3b93a2d65a
5 changed files with 166 additions and 38 deletions

View File

@@ -15539,26 +15539,61 @@ With <<qemu-d-tracing>>:
.... ....
./run \ ./run \
--arch aarch64 \ --arch aarch64 \
--baremetal baremetal/arch/aarch64/svc_asm.S --baremetal baremetal/arch/aarch64/svc.c \
-- -d in_asm,int \ -- -d in_asm,int \
; ;
.... ....
the output contains: the output at 8f73910dd1fc1fa6dc6904ae406b7598cdcd96d7 contains:
.... ....
---------------- ----------------
IN: IN: main
0x40002060: d4000001 svc #0 0x40002098: d41579a1 svc #0xabcd
Taking exception 2 [SVC] Taking exception 2 [SVC]
...from EL1 to EL1 ...from EL1 to EL1
...with ESR 0x15/0x56000000 ...with ESR 0x15/0x5600abcd
...with ELR 0x40002064 ...with ELR 0x4000209c
...to EL1 PC 0x40000a00 PSTATE 0x3c5 ...to EL1 PC 0x40000a00 PSTATE 0x3c5
---------------- ----------------
IN: IN:
0x40000a00: 14000225 b #0x40001294 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 <<gem5-tracing>>: And with <<gem5-tracing>>:
@@ -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) 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 <<baremetal-bootloaders,in the bootloader>>.
* 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 <<armarm8>> Table D1-7 "Vector offsets from vector table base address". The vector table format is described on <<armarm8>> 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. * `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." * `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. * `CNTV_CVAL_EL0`: "Counter-timer Virtual Timer CompareValue register". The interrupt happens when `CNTVCT_EL0` reaches the value in this register.
Bibliography: 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/51094092/how-to-make-timer-irq-work-on-qemu-machine-virt-cpu-cortex-a57
* https://stackoverflow.com/questions/44198483/arm-timers-and-interrupts * 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 ==== 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 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. 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>; reg = <0x0 0x8000000 0x0 0x10000 0x0 0x80a0000 0x0 0xf60000>;

View File

@@ -68,7 +68,7 @@ int main(void) {
printf("SPSEL 0x%" PRIX32 "\n", lkmc_sysreg_spsel_read()); printf("SPSEL 0x%" PRIX32 "\n", lkmc_sysreg_spsel_read());
printf("VBAR_EL1 0x%" PRIX64 "\n", lkmc_sysreg_vbar_el1_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 */ /* 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); assert(myvar == 0);
/* Max 16-bits. */ /* Max 16-bits. */
LKMC_SVC(0xABCD); LKMC_SVC(0xABCD);

View File

@@ -8,20 +8,16 @@ void lkmc_vector_trap_handler(LkmcVectorExceptionFrame *exception __attribute__(
printf("CNTVCT_EL0 0x%" PRIX64 "\n", lkmc_sysreg_cntvct_el0_read()); 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 */ /* DAIF, Interrupt Mask Bits */
#define DAIF_DBG_BIT (1<<3) /* Debug mask bit */ #define DAIF_DBG_BIT (1<<3) /* Debug mask bit */
#define DAIF_ABT_BIT (1<<2) /* Asynchronous abort mask bit */ #define DAIF_ABT_BIT (1<<2) /* Asynchronous abort mask bit */
#define DAIF_IRQ_BIT (1<<1) /* IRQ mask bit */ #define DAIF_IRQ_BIT (1<<1) /* IRQ mask bit */
#define DAIF_FIQ_BIT (1<<0) /* FIQ mask bit */ #define DAIF_FIQ_BIT (1<<0) /* FIQ mask bit */
#define wfi() __asm__ __volatile__ ("wfi" : : : "memory") #define wfi() __asm__ __volatile__ ("wfi" : : : "memory")
void enable_cntv(void) { 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) { void enable_irq(void) {
@@ -42,25 +38,22 @@ int main(void) {
/**/ /**/
gic_v3_initialize(); gic_v3_initialize();
{ {
uint64_t ticks, current_cnt; /*uint64_t ticks, current_cnt;*/
uint32_t cntfrq; /*uint32_t cntfrq;*/
cntfrq = lkmc_sysreg_cntfrq_el0_read(); lkmc_sysreg_cntfrq_el0_write(1);
ticks = cntfrq; /*ticks = cntfrq;*/
current_cnt = lkmc_sysreg_cntvct_el0_read(); /*current_cnt = lkmc_sysreg_cntvct_el0_read();*/
lkmc_sysreg_cntv_cval_el0_write(current_cnt + ticks); /*lkmc_sysreg_cntv_cval_el0_write(current_cnt + ticks);*/
enable_cntv(); enable_cntv();
enable_irq(); enable_irq();
} }
while (1) { /*while (1) {*/
/*puts("qwer");*/ /*puts("qwer");*/
/*current_cnt = raw_read_cntvct_el0();*/ /*current_cnt = raw_read_cntvct_el0();*/
/*val = raw_read_cntv_ctl();*/ /*val = raw_read_cntv_ctl();*/
/*printf("CNTVCT_EL0 = ");*/ /*printf("CNTVCT_EL0 = ");*/
/*uart_puthex(current_cnt);*/
/*uart_puts(", CNTV_CTL_EL0 = ");*/
/*uart_puthex(val);*/
/*wfi();*/ /*wfi();*/
} /*}*/
#if 0 #if 0
/* TODO crashes gem5. */ /* TODO crashes gem5. */

View File

@@ -65,6 +65,11 @@ main: \
main_after_prologue: \ 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 /* LKMC_VECTOR_TABLE
* *
* Adapted from: https://github.com/takeharukato/sample-tsk-sw/blob/ce7973aa5d46c9eedb58309de43df3b09d4f8d8d/hal/aarch64/vector.S * 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_ #define LKMC_VECTOR_SYMBOL_PREFIX lkmc_vector_
/* Push several registers on the stack to match LkmcVectorExceptionFrame. */ /* 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 x29, x30, [sp, -16]!; \
stp x27, x28, [sp, -16]!; \ stp x27, x28, [sp, -16]!; \
stp x25, x26, [sp, -16]!; \ stp x25, x26, [sp, -16]!; \
@@ -170,12 +176,12 @@ main_after_prologue: \
#define LKMC_VECTOR_ENTRY(func_name) \ #define LKMC_VECTOR_ENTRY(func_name) \
.align 7; \ .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) \ #define LKMC_VECTOR_FUNC(func_name, func_id) \
LKMC_VECTOR_FUNC_ALIGN; \ LKMC_VECTOR_FUNC_ALIGN; \
LKMC_VECTOR_SYMBOL_PREFIX ## func_name:; \ LKMC_CONCAT(LKMC_CONCAT(LKMC_VECTOR_SYMBOL_PREFIX, entry_), func_name):; \
LKMC_VECTOR_BUILD_TRAPFRAME(func_id); \ LKMC_VECTOR_BUILD_TRAPFRAME(func_id, func_name); \
LKMC_VECTOR_STORE_TRAPED_SP; \ LKMC_VECTOR_STORE_TRAPED_SP; \
LKMC_VECTOR_CALL_TRAP_HANDLER; \ LKMC_VECTOR_CALL_TRAP_HANDLER; \
LKMC_VECTOR_RESTORE_TRAPED_SP; \ LKMC_VECTOR_RESTORE_TRAPED_SP; \
@@ -183,8 +189,8 @@ LKMC_VECTOR_SYMBOL_PREFIX ## func_name:; \
#define LKMC_VECTOR_FUNC_NESTED(func_name, func_id) \ #define LKMC_VECTOR_FUNC_NESTED(func_name, func_id) \
LKMC_VECTOR_FUNC_ALIGN; \ LKMC_VECTOR_FUNC_ALIGN; \
LKMC_VECTOR_SYMBOL_PREFIX ## func_name:; \ LKMC_CONCAT(LKMC_CONCAT(LKMC_VECTOR_SYMBOL_PREFIX, entry_), func_name):; \
LKMC_VECTOR_BUILD_TRAPFRAME(func_id); \ LKMC_VECTOR_BUILD_TRAPFRAME(func_id, func_name); \
LKMC_VECTOR_STORE_NESTED_SP; \ LKMC_VECTOR_STORE_NESTED_SP; \
LKMC_VECTOR_CALL_TRAP_HANDLER; \ LKMC_VECTOR_CALL_TRAP_HANDLER; \
LKMC_VECTOR_RESTORE_TRAPFRAME LKMC_VECTOR_RESTORE_TRAPFRAME

View File

@@ -14,16 +14,21 @@
#include <unistd.h> #include <unistd.h>
int main(int argc, char **argv) { int main(int argc, char **argv) {
unsigned long i, max; unsigned long i, max, sleep_time;
if (argc > 1) { if (argc > 1) {
max = strtoll(argv[1], NULL, 0); max = strtoll(argv[1], NULL, 0);
} else { } else {
max = 1; max = 1;
} }
if (argc > 2) {
sleep_time = strtoll(argv[2], NULL, 0);
} else {
sleep_time = 2;
}
i = 0; i = 0;
while (i < max) { while (i < max) {
printf("%lu\n", i); printf("%lu\n", i);
i++; i++;
sleep(1); sleep(sleep_time);
} }
} }