AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/config.h"
2#include "hv/mmio.h"
3#include "hv/mmio_policy.h"
4#include "hv/uart.h"
5#include "hv/vcpu.h"
6
7static const struct mmio_policy_region g_mmio_regions[] = {
8 { CONFIG_UART_BASE, CONFIG_UART_SIZE, "pl011-uart", MMIO_POLICY_EMULATE_PL011 },
9 { CONFIG_GICD_BASE, CONFIG_GICD_SIZE, "gicd", MMIO_POLICY_EMULATE_READONLY_DEVICE },
10 { CONFIG_GITS_BASE, CONFIG_GITS_SIZE, "gits", MMIO_POLICY_EMULATE_READONLY_DEVICE },
11 { CONFIG_GICR_BASE, CONFIG_GICR_SIZE, "gicr", MMIO_POLICY_EMULATE_READONLY_DEVICE },
12 { CONFIG_RTC_BASE, CONFIG_RTC_SIZE, "pl031-rtc", MMIO_POLICY_EMULATE_READONLY_DEVICE },
13 { CONFIG_FW_CFG_BASE, CONFIG_FW_CFG_SIZE, "fw-cfg", MMIO_POLICY_EMULATE_READONLY_DEVICE },
14 { CONFIG_GPIO_BASE, CONFIG_GPIO_SIZE, "pl061-gpio", MMIO_POLICY_EMULATE_READONLY_DEVICE },
15 { CONFIG_PCI_ECAM_BASE, CONFIG_PCI_ECAM_SIZE, "pci-ecam", MMIO_POLICY_EMULATE_ZERO },
16 { 0x0a000000ull, 0x00004000ull, "virtio-mmio", MMIO_POLICY_EMULATE_VIRTIO_MMIO },
17};
18
19const struct mmio_policy_region *mmio_policy_lookup(ipa_t ipa)
20{
21 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_mmio_regions); i++) {
22 uint64_t end;
23 if (hv_add_overflow_u64(g_mmio_regions[i].base, g_mmio_regions[i].size, &end)) {
24 continue;
25 }
26 if (ipa >= g_mmio_regions[i].base && ipa < end) {
27 return &g_mmio_regions[i];
28 }
29 }
30 return (const struct mmio_policy_region *)0;
31}
32
33static bool decode_abort_access(uint64_t esr, bool *is_write, uint32_t *rt)
34{
35 if ((esr & HV_BIT(24)) == 0u) {
36 return false;
37 }
38 *rt = (uint32_t)((esr >> 16u) & 0x1fu);
39 *is_write = (esr & HV_BIT(6)) != 0u;
40 return true;
41}
42
43static uint64_t virtio_read(uint64_t off)
44{
45 if ((off & ~0x1ffull) != 0u) {
46 return 0u;
47 }
48 off &= 0x1ffu;
49 switch (off) {
50 case 0x000u: return 0x74726976u;
51 case 0x004u: return 2u;
52 case 0x008u: return 3u;
53 case 0x00cu: return 0x41524945u;
54 case 0x010u: return 0u;
55 case 0x034u: return 0u;
56 case 0x070u: return 0u;
57 default: return 0u;
58 }
59}
60
61static uint64_t readonly_device_read32(ipa_t ipa)
62{
63 if (!hv_is_aligned_u64(ipa, 4u)) {
64 return 0u;
65 }
66 return (uint64_t)mmio_read32((uintptr_t)ipa);
67}
68
69bool mmio_policy_emulate(struct vcpu *vcpu, ipa_t ipa, uint64_t esr_el2)
70{
71 const struct mmio_policy_region *region = mmio_policy_lookup(ipa);
72 bool is_write;
73 uint32_t rt;
74 uint64_t off;
75 uint64_t value = 0u;
76
77 if (vcpu == (struct vcpu *)0 || region == (const struct mmio_policy_region *)0 ||
78 !decode_abort_access(esr_el2, &is_write, &rt)) {
79 return false;
80 }
81
82 off = ipa - region->base;
83 if (region->action == MMIO_POLICY_EMULATE_PL011) {
84 if (is_write && off == 0u) {
85 uart_putc((char)(vcpu_read_gpr(vcpu, rt) & 0xffu));
86 } else if (!is_write && off == 0x18u) {
87 value = 0x10u;
88 }
89 } else if (region->action == MMIO_POLICY_EMULATE_VIRTIO_MMIO) {
90 if (!is_write) {
91 value = virtio_read(off);
92 }
93 } else if (region->action == MMIO_POLICY_EMULATE_READONLY_DEVICE) {
94 if (!is_write) {
95 value = readonly_device_read32(ipa);
96 }
97 } else if (region->action != MMIO_POLICY_EMULATE_ZERO) {
98 return false;
99 }
100
101 if (!is_write) {
102 vcpu_write_gpr(vcpu, rt, value);
103 }
104 return true;
105}
106
107void mmio_policy_dump(void)
108{
109 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_mmio_regions); i++) {
110 uart_puts("mmio name=");
111 uart_puts(g_mmio_regions[i].name);
112 uart_puts(" base=");
113 uart_put_hex64(g_mmio_regions[i].base);
114 uart_puts(" size=");
115 uart_put_hex64(g_mmio_regions[i].size);
116 uart_puts(" action=");
117 uart_put_dec64((uint64_t)g_mmio_regions[i].action);
118 uart_puts("\n");
119 }
120}