AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/config.h"
2#include "hv/gicv3.h"
3#include "hv/log.h"
4#include "hv/mmio.h"
5#include "hv/uart.h"
6#include "hv/vcpu.h"
7
8#define GICD_CTLR 0x0000u
9#define GICD_TYPER 0x0004u
10#define GICR_CTLR 0x0000u
11#define GICR_WAKER 0x0014u
12#define GICR_WAKER_PROCESSOR_SLEEP (1u << 1)
13#define GICR_WAKER_CHILDREN_ASLEEP (1u << 2)
14#define ICH_LR_STATE(lr) (((lr) >> 62u) & 0x3u)
15#define ICH_LR_INTID(lr) ((uint32_t)((lr) & 0xffffffffu))
16
17static uintptr_t gicd(uint32_t off)
18{
19 return (uintptr_t)(CONFIG_GICD_BASE + (uint64_t)off);
20}
21
22static uintptr_t gicr(uint32_t off)
23{
24 return (uintptr_t)(CONFIG_GICR_BASE + (uint64_t)off);
25}
26
27void gicv3_init(void)
28{
29 uint32_t typer = mmio_read32(gicd(GICD_TYPER));
30 mmio_write32(gicd(GICD_CTLR), 0u);
31
32 uint32_t waker = mmio_read32(gicr(GICR_WAKER));
33 waker &= ~GICR_WAKER_PROCESSOR_SLEEP;
34 mmio_write32(gicr(GICR_WAKER), waker);
35 for (uint32_t i = 0; i < 100000u; i++) {
36 if ((mmio_read32(gicr(GICR_WAKER)) & GICR_WAKER_CHILDREN_ASLEEP) == 0u) {
37 break;
38 }
39 }
40
41 __asm__ volatile(
42 "msr ICC_SRE_EL2, %0\n"
43 "isb\n"
44 "msr ICC_SRE_EL1, %0\n"
45 "isb\n"
46 "msr ICC_PMR_EL1, %1\n"
47 "msr ICC_IGRPEN1_EL1, %0\n"
48 "isb\n"
49 :: "r"(1ull), "r"(0xffull)
50 : "memory");
51
52 mmio_write32(gicd(GICD_CTLR), 0x3u);
53 log_simple(LOG_BOOT, 20u, typer, 0u);
54 gicv3_vcpu_init();
55}
56
57void gicv3_dump(void)
58{
59 uart_puts("gicv3 gicd=");
60 uart_put_hex64(CONFIG_GICD_BASE);
61 uart_puts(" gicr=");
62 uart_put_hex64(CONFIG_GICR_BASE);
63 uart_puts(" typer=");
64 uart_put_hex64(mmio_read32(gicd(GICD_TYPER)));
65 uart_puts("\n");
66}
67
68uint32_t gicv3_ack_irq(void)
69{
70 uint64_t irq;
71 __asm__ volatile("mrs %0, ICC_IAR1_EL1" : "=r"(irq));
72 return (uint32_t)(irq & 0x3ffu);
73}
74
75void gicv3_eoi(uint32_t irq)
76{
77 __asm__ volatile("msr ICC_EOIR1_EL1, %0\nisb" :: "r"((uint64_t)irq) : "memory");
78}
79
80void gicv3_vcpu_init(void)
81{
82 __asm__ volatile(
83 "msr ICH_HCR_EL2, %0\n"
84 "msr ICH_VMCR_EL2, %1\n"
85 "msr ICH_LR0_EL2, xzr\n"
86 "isb\n"
87 :: "r"(1ull), "r"(0xff0000ull)
88 : "memory");
89}
90
91void gicv3_sync_vcpu_state(struct vcpu *vcpu)
92{
93 uint64_t lr;
94 uint64_t state;
95
96 if (vcpu == (struct vcpu *)0) {
97 return;
98 }
99
100 __asm__ volatile("mrs %0, ICH_LR0_EL2" : "=r"(lr));
101 state = ICH_LR_STATE(lr);
102 if (state == 0u && vcpu->active_virtual_irq != 0u) {
103 vcpu_clear_pending_irq(vcpu, (uint32_t)vcpu->active_virtual_irq);
104 vcpu->active_virtual_irq = 0u;
105 vcpu->stats.virtual_irqs_completed++;
106 } else if (state != 0u) {
107 vcpu->active_virtual_irq = ICH_LR_INTID(lr);
108 }
109}
110
111void gicv3_flush_vcpu_state(struct vcpu *vcpu)
112{
113 if (vcpu == (struct vcpu *)0) {
114 return;
115 }
116
117 if ((vcpu->pending_virtual_irq & (1ull << CONFIG_VTIMER_IRQ)) == 0u) {
118 vcpu->active_virtual_irq = 0u;
119 __asm__ volatile("msr ICH_LR0_EL2, xzr\nisb" ::: "memory");
120 return;
121 }
122
123 uint64_t lr = (uint64_t)CONFIG_VTIMER_IRQ | (1ull << 62u) | (1ull << 60u);
124 if (vcpu->active_virtual_irq != CONFIG_VTIMER_IRQ) {
125 vcpu->stats.virtual_irqs_injected++;
126 }
127 vcpu->active_virtual_irq = CONFIG_VTIMER_IRQ;
128 __asm__ volatile("msr ICH_LR0_EL2, %0\nisb" :: "r"(lr) : "memory");
129}