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, 1001u },
9 { CONFIG_GICD_BASE, CONFIG_GICD_SIZE, "gicd", MMIO_POLICY_EMULATE_READONLY_DEVICE, 1002u },
10 { CONFIG_GITS_BASE, CONFIG_GITS_SIZE, "gits", MMIO_POLICY_EMULATE_READONLY_DEVICE, 1003u },
11 { CONFIG_GICR_BASE, CONFIG_GICR_SIZE, "gicr", MMIO_POLICY_EMULATE_READONLY_DEVICE, 1004u },
12 { CONFIG_RTC_BASE, CONFIG_RTC_SIZE, "pl031-rtc", MMIO_POLICY_EMULATE_READONLY_DEVICE, 1005u },
13 { CONFIG_FW_CFG_BASE, CONFIG_FW_CFG_SIZE, "fw-cfg", MMIO_POLICY_EMULATE_READONLY_DEVICE, 1006u },
14 { CONFIG_GPIO_BASE, CONFIG_GPIO_SIZE, "pl061-gpio", MMIO_POLICY_EMULATE_READONLY_DEVICE, 1007u },
15 { CONFIG_PCI_ECAM_BASE, CONFIG_PCI_ECAM_SIZE, "pci-ecam", MMIO_POLICY_EMULATE_ZERO, 1008u },
16 { 0x0a000000ull, 0x00004000ull, "virtio-mmio", MMIO_POLICY_EMULATE_VIRTIO_MMIO, 1009u },
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
69static bool emulate_readonly_probe(struct vcpu *vcpu, ipa_t ipa, bool is_write, uint32_t rt)
70{
71 if (is_write) {
72 return true;
73 }
74 vcpu_write_gpr(vcpu, rt, readonly_device_read32(ipa));
75 return true;
76}
77
78bool mmio_policy_emulate(struct vcpu *vcpu, ipa_t ipa, uint64_t esr_el2)
79{
80 const struct mmio_policy_region *region = mmio_policy_lookup(ipa);
81 bool is_write;
82 uint32_t rt;
83 uint64_t off;
84 uint64_t value = 0u;
85
86 if (vcpu == (struct vcpu *)0 || region == (const struct mmio_policy_region *)0 ||
87 !decode_abort_access(esr_el2, &is_write, &rt)) {
88 return false;
89 }
90
91 off = ipa - region->base;
92 if (region->action == MMIO_POLICY_EMULATE_PL011) {
93 if (is_write && off == 0u) {
94 uart_putc((char)(vcpu_read_gpr(vcpu, rt) & 0xffu));
95 } else if (!is_write && off == 0x18u) {
96 value = 0x10u;
97 }
98 } else if (region->action == MMIO_POLICY_EMULATE_VIRTIO_MMIO) {
99 if (!is_write) {
100 value = virtio_read(off);
101 }
102 } else if (region->action == MMIO_POLICY_EMULATE_READONLY_DEVICE) {
103 return emulate_readonly_probe(vcpu, ipa, is_write, rt);
104 } else if (region->action != MMIO_POLICY_EMULATE_ZERO) {
105 return false;
106 }
107
108 if (!is_write) {
109 vcpu_write_gpr(vcpu, rt, value);
110 }
111 return true;
112}
113
114void mmio_policy_dump(void)
115{
116 for (uint32_t i = 0; i < HV_ARRAY_SIZE(g_mmio_regions); i++) {
117 uart_puts("mmio name=");
118 uart_puts(g_mmio_regions[i].name);
119 uart_puts(" base=");
120 uart_put_hex64(g_mmio_regions[i].base);
121 uart_puts(" size=");
122 uart_put_hex64(g_mmio_regions[i].size);
123 uart_puts(" action=");
124 uart_put_dec64((uint64_t)g_mmio_regions[i].action);
125 uart_puts(" provenance=");
126 uart_put_dec64(g_mmio_regions[i].provenance);
127 uart_puts("\n");
128 }
129}