mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-23 02:05:57 +01:00
arm gic: get closer to working, still failing though
Define print functions for all system regs.
This commit is contained in:
158
README.adoc
158
README.adoc
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
11
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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user