#include "hv/arch.h" #include "hv/config.h" #include "hv/log.h" #include "hv/string.h" #include "hv/sysreg.h" #include "hv/timer.h" #include "hv/uart.h" struct log_ring { struct log_event events[CONFIG_LOG_CAPACITY]; volatile unsigned lock; uint64_t next_seq; uint32_t write_index; bool wrapped; }; static struct log_ring g_log; static struct log_ring g_log_selftest_backup; static void lock_log(void) { while (__atomic_test_and_set(&g_log.lock, __ATOMIC_ACQUIRE)) { } } static void unlock_log(void) { __atomic_clear(&g_log.lock, __ATOMIC_RELEASE); } void log_init(void) { hv_memset(&g_log, 0, sizeof(g_log)); } void log_event_commit(const struct log_event *event) { if (event == (const struct log_event *)0) { return; } uint64_t irq_flags = arch_irq_save(); lock_log(); struct log_event *slot = &g_log.events[g_log.write_index]; *slot = *event; slot->seq = g_log.next_seq++; slot->cpu = arch_mpidr_el1() & 0xffu; slot->timestamp = timer_now(); g_log.write_index++; if (g_log.write_index == CONFIG_LOG_CAPACITY) { g_log.write_index = 0; g_log.wrapped = true; } unlock_log(); arch_irq_restore(irq_flags); } void log_simple(enum log_kind kind, uint32_t reason, uint64_t a, uint64_t b) { struct log_event ev; hv_memset(&ev, 0, sizeof(ev)); ev.kind = (uint32_t)kind; ev.reason = reason; ev.iss = a; ev.elr = b; log_event_commit(&ev); } static const char *kind_name(uint32_t kind) { switch (kind) { case LOG_BOOT: return "boot"; case LOG_TRAP: return "trap"; case LOG_POLICY: return "policy"; case LOG_HVC: return "hvc"; case LOG_ABORT: return "abort"; case LOG_MONITOR: return "monitor"; case LOG_PANIC: return "panic"; case LOG_SELFTEST: return "selftest"; default: return "unknown"; } } void log_dump(unsigned limit) { uint64_t irq_flags = arch_irq_save(); lock_log(); uint32_t count = g_log.wrapped ? CONFIG_LOG_CAPACITY : g_log.write_index; if (limit != 0u && limit < count) { count = limit; } uint32_t start = g_log.wrapped ? g_log.write_index : 0u; if (g_log.wrapped && count < CONFIG_LOG_CAPACITY) { start = (g_log.write_index + CONFIG_LOG_CAPACITY - count) % CONFIG_LOG_CAPACITY; } for (uint32_t i = 0; i < count; i++) { const struct log_event *ev = &g_log.events[(start + i) % CONFIG_LOG_CAPACITY]; uart_puts("event seq="); uart_put_dec64(ev->seq); uart_puts(" cpu="); uart_put_dec64(ev->cpu); uart_puts(" vcpu="); uart_put_dec64(ev->vcpu); uart_puts(" ts="); uart_put_dec64(ev->timestamp); uart_puts(" kind="); uart_puts(kind_name(ev->kind)); uart_puts(" ec="); uart_put_hex64(ev->ec); uart_puts(" ecname="); uart_puts(esr_ec_name(ev->ec)); uart_puts(" iss="); uart_put_hex64(ev->iss); uart_puts(" elr="); uart_put_hex64(ev->elr); uart_puts(" far="); uart_put_hex64(ev->far); uart_puts(" hpfar="); uart_put_hex64(ev->hpfar); uart_puts(" sysreg="); uart_put_hex64(ev->sysreg_key); uart_puts(" name="); uart_puts(ev->name != (const char *)0 ? ev->name : "-"); uart_puts(" dir="); uart_puts(ev->is_read != 0u ? "read" : "write"); uart_puts(" rt="); uart_put_dec64(ev->rt); uart_puts(" operand="); uart_put_hex64(ev->operand); uart_puts(" result="); uart_put_hex64(ev->result); uart_puts(" action="); uart_put_dec64(ev->action); uart_puts(" class="); uart_put_dec64(ev->policy_class); uart_puts(" pstate="); uart_put_hex64(ev->pstate); uart_puts(" reason="); uart_put_dec64(ev->reason); uart_puts("\n"); } unlock_log(); arch_irq_restore(irq_flags); } void log_clear(void) { uint64_t irq_flags = arch_irq_save(); lock_log(); hv_memset(g_log.events, 0, sizeof(g_log.events)); g_log.write_index = 0; g_log.wrapped = false; unlock_log(); arch_irq_restore(irq_flags); } uint64_t log_next_sequence(void) { return g_log.next_seq; } uint32_t log_event_count(void) { return g_log.wrapped ? CONFIG_LOG_CAPACITY : g_log.write_index; } bool log_selftest(void) { g_log_selftest_backup = g_log; log_init(); for (uint32_t i = 0; i < CONFIG_LOG_CAPACITY + 3u; i++) { log_simple(LOG_SELFTEST, i, i, i + 1u); } bool ok = g_log.wrapped && log_next_sequence() == (uint64_t)CONFIG_LOG_CAPACITY + 3u && log_event_count() == CONFIG_LOG_CAPACITY; g_log = g_log_selftest_backup; return ok; }