AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/config.h"
2#include "hv/mmio.h"
3#include "hv/uart.h"
4
5#define UART_DR 0x000u
6#define UART_FR 0x018u
7#define UART_IBRD 0x024u
8#define UART_FBRD 0x028u
9#define UART_LCRH 0x02cu
10#define UART_CR 0x030u
11#define UART_IMSC 0x038u
12#define UART_ICR 0x044u
13#define UART_FR_TXFF (1u << 5)
14#define UART_FR_RXFE (1u << 4)
15
16static uintptr_t uart_addr(uint32_t off)
17{
18 return (uintptr_t)(CONFIG_UART_BASE + (uint64_t)off);
19}
20
21void uart_init(void)
22{
23 mmio_write32(uart_addr(UART_CR), 0u);
24 mmio_write32(uart_addr(UART_ICR), 0x7ffu);
25 mmio_write32(uart_addr(UART_IBRD), 1u);
26 mmio_write32(uart_addr(UART_FBRD), 40u);
27 mmio_write32(uart_addr(UART_LCRH), (3u << 5));
28 mmio_write32(uart_addr(UART_IMSC), 0u);
29 mmio_write32(uart_addr(UART_CR), (1u << 9) | (1u << 8) | 1u);
30}
31
32void uart_putc(char c)
33{
34 if (c == '\n') {
35 uart_putc('\r');
36 }
37 while ((mmio_read32(uart_addr(UART_FR)) & UART_FR_TXFF) != 0u) {
38 }
39 mmio_write32(uart_addr(UART_DR), (uint32_t)(unsigned char)c);
40}
41
42void uart_puts(const char *s)
43{
44 while (*s != '\0') {
45 uart_putc(*s++);
46 }
47}
48
49void uart_put_hex64(uint64_t value)
50{
51 static const char hex[] = "0123456789abcdef";
52 uart_puts("0x");
53 for (int shift = 60; shift >= 0; shift -= 4) {
54 uart_putc(hex[(value >> (unsigned)shift) & 0xfu]);
55 }
56}
57
58void uart_put_dec64(uint64_t value)
59{
60 char buf[21];
61 unsigned pos = 0;
62 if (value == 0u) {
63 uart_putc('0');
64 return;
65 }
66 while (value != 0u && pos < sizeof(buf)) {
67 buf[pos++] = (char)('0' + (value % 10u));
68 value /= 10u;
69 }
70 while (pos != 0u) {
71 uart_putc(buf[--pos]);
72 }
73}
74
75void uart_put_label_hex64(const char *label, uint64_t value)
76{
77 uart_puts(label);
78 uart_puts("=");
79 uart_put_hex64(value);
80 uart_puts("\n");
81}
82
83bool uart_getc_nonblocking(char *out)
84{
85 if ((mmio_read32(uart_addr(UART_FR)) & UART_FR_RXFE) != 0u) {
86 return false;
87 }
88 *out = (char)(mmio_read32(uart_addr(UART_DR)) & 0xffu);
89 return true;
90}