arm gic: get closer to working, still failing though

Define print functions for all system regs.
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-07-31 00:00:00 +00:00
parent 3b93a2d65a
commit 75e2582970
6 changed files with 229 additions and 107 deletions

View File

@@ -15444,6 +15444,18 @@ in link:baremetal/lib/arm.S[]. That patch however enables SIMD in baremetal, whi
According to <<armarm7>>, 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 <<armarm8-db>> 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 <<armv8-exception-vector-table-format>>, 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 <<arm-exception-levels,from EL1 to EL1>>.
+
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.
* 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 <<armarm8>> 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 <<armv8-programmers-guide>> 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 <<arm-exception-levels,EL1>> 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 <<armarm8>> 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 <<armv8-programmers-guide>> 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 <<arm-svc-instruction>>.
+
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: <<arm-timer>>
* TODO FIQ vs IRQ
* TODO SError
* EL changes: <<arm-change-exception-level>>
* SP0 vs SPx: <<arm-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 <<arm-exception-levels,<starts in EL1>>, and so we kept just the EL1 part, and it worked. Related:
We then found out that QEMU <<arm-exception-levels,starts in EL1>>, 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

View File

@@ -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);

View File

@@ -4,63 +4,84 @@
#include <lkmc.h>
#include <lkmc/gicv3.h>
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;
}

11
lkmc.c
View File

@@ -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

View File

@@ -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