AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/arch.h"
2#include "hv/config.h"
3#include "hv/log.h"
4#include "hv/string.h"
5#include "hv/sysreg.h"
6#include "hv/timer.h"
7#include "hv/uart.h"
8
9struct log_ring {
10 struct log_event events[CONFIG_LOG_CAPACITY];
11 volatile unsigned lock;
12 uint64_t next_seq;
13 uint32_t write_index;
14 bool wrapped;
15};
16
17static struct log_ring g_log;
18static struct log_ring g_log_selftest_backup;
19
20static void lock_log(void)
21{
22 while (__atomic_test_and_set(&g_log.lock, __ATOMIC_ACQUIRE)) {
23 }
24}
25
26static void unlock_log(void)
27{
28 __atomic_clear(&g_log.lock, __ATOMIC_RELEASE);
29}
30
31void log_init(void)
32{
33 hv_memset(&g_log, 0, sizeof(g_log));
34}
35
36void log_event_commit(const struct log_event *event)
37{
38 if (event == (const struct log_event *)0) {
39 return;
40 }
41
42 uint64_t irq_flags = arch_irq_save();
43 lock_log();
44 struct log_event *slot = &g_log.events[g_log.write_index];
45 *slot = *event;
46 slot->seq = g_log.next_seq++;
47 slot->cpu = arch_mpidr_el1() & 0xffu;
48 slot->timestamp = timer_now();
49 g_log.write_index++;
50 if (g_log.write_index == CONFIG_LOG_CAPACITY) {
51 g_log.write_index = 0;
52 g_log.wrapped = true;
53 }
54 unlock_log();
55 arch_irq_restore(irq_flags);
56}
57
58void log_simple(enum log_kind kind, uint32_t reason, uint64_t a, uint64_t b)
59{
60 struct log_event ev;
61 hv_memset(&ev, 0, sizeof(ev));
62 ev.kind = (uint32_t)kind;
63 ev.reason = reason;
64 ev.iss = a;
65 ev.elr = b;
66 log_event_commit(&ev);
67}
68
69static const char *kind_name(uint32_t kind)
70{
71 switch (kind) {
72 case LOG_BOOT: return "boot";
73 case LOG_TRAP: return "trap";
74 case LOG_POLICY: return "policy";
75 case LOG_HVC: return "hvc";
76 case LOG_ABORT: return "abort";
77 case LOG_MONITOR: return "monitor";
78 case LOG_PANIC: return "panic";
79 case LOG_SELFTEST: return "selftest";
80 default: return "unknown";
81 }
82}
83
84void log_dump(unsigned limit)
85{
86 uint64_t irq_flags = arch_irq_save();
87 lock_log();
88 uint32_t count = g_log.wrapped ? CONFIG_LOG_CAPACITY : g_log.write_index;
89 if (limit != 0u && limit < count) {
90 count = limit;
91 }
92
93 uint32_t start = g_log.wrapped ? g_log.write_index : 0u;
94 if (g_log.wrapped && count < CONFIG_LOG_CAPACITY) {
95 start = (g_log.write_index + CONFIG_LOG_CAPACITY - count) % CONFIG_LOG_CAPACITY;
96 }
97
98 for (uint32_t i = 0; i < count; i++) {
99 const struct log_event *ev = &g_log.events[(start + i) % CONFIG_LOG_CAPACITY];
100 uart_puts("event seq=");
101 uart_put_dec64(ev->seq);
102 uart_puts(" cpu=");
103 uart_put_dec64(ev->cpu);
104 uart_puts(" vcpu=");
105 uart_put_dec64(ev->vcpu);
106 uart_puts(" ts=");
107 uart_put_dec64(ev->timestamp);
108 uart_puts(" kind=");
109 uart_puts(kind_name(ev->kind));
110 uart_puts(" ec=");
111 uart_put_hex64(ev->ec);
112 uart_puts(" ecname=");
113 uart_puts(esr_ec_name(ev->ec));
114 uart_puts(" iss=");
115 uart_put_hex64(ev->iss);
116 uart_puts(" elr=");
117 uart_put_hex64(ev->elr);
118 uart_puts(" far=");
119 uart_put_hex64(ev->far);
120 uart_puts(" hpfar=");
121 uart_put_hex64(ev->hpfar);
122 uart_puts(" sysreg=");
123 uart_put_hex64(ev->sysreg_key);
124 uart_puts(" name=");
125 uart_puts(ev->name != (const char *)0 ? ev->name : "-");
126 uart_puts(" dir=");
127 uart_puts(ev->is_read != 0u ? "read" : "write");
128 uart_puts(" rt=");
129 uart_put_dec64(ev->rt);
130 uart_puts(" operand=");
131 uart_put_hex64(ev->operand);
132 uart_puts(" result=");
133 uart_put_hex64(ev->result);
134 uart_puts(" action=");
135 uart_put_dec64(ev->action);
136 uart_puts(" pstate=");
137 uart_put_hex64(ev->pstate);
138 uart_puts(" reason=");
139 uart_put_dec64(ev->reason);
140 uart_puts("\n");
141 }
142 unlock_log();
143 arch_irq_restore(irq_flags);
144}
145
146void log_clear(void)
147{
148 uint64_t irq_flags = arch_irq_save();
149 lock_log();
150 hv_memset(g_log.events, 0, sizeof(g_log.events));
151 g_log.write_index = 0;
152 g_log.wrapped = false;
153 unlock_log();
154 arch_irq_restore(irq_flags);
155}
156
157bool log_selftest(void)
158{
159 g_log_selftest_backup = g_log;
160 log_init();
161 for (uint32_t i = 0; i < CONFIG_LOG_CAPACITY + 3u; i++) {
162 log_simple(LOG_SELFTEST, i, i, i + 1u);
163 }
164 bool ok = g_log.wrapped && g_log.next_seq == (uint64_t)CONFIG_LOG_CAPACITY + 3u;
165 g_log = g_log_selftest_backup;
166 return ok;
167}