AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/arch.h"
2#include "hv/config.h"
3#include "hv/fdt.h"
4#include "hv/guest_loader.h"
5#include "hv/log.h"
6#include "hv/string.h"
7
8#define ARM64_IMAGE_MAGIC 0x644d5241u
9#define ARM64_IMAGE_MAGIC_OFFSET 0x38u
10#define ARM64_IMAGE_TEXT_OFFSET 0x08u
11#define ARM64_IMAGE_SIZE_OFFSET 0x10u
12#define LINUX_BOOTARGS "console=ttyAMA0 earlycon"
13
14static uint32_t read_le32(const uint8_t *p)
15{
16 return (uint32_t)p[0] | ((uint32_t)p[1] << 8u) |
17 ((uint32_t)p[2] << 16u) | ((uint32_t)p[3] << 24u);
18}
19
20static uint64_t read_le64(const uint8_t *p)
21{
22 return (uint64_t)read_le32(p) | ((uint64_t)read_le32(p + 4u) << 32u);
23}
24
25bool guest_load_diagnostic(struct guest_image_info *info)
26{
27 uintptr_t guest_start;
28 uintptr_t guest_end;
29 uint64_t guest_size;
30
31 if (info == (struct guest_image_info *)0) {
32 return false;
33 }
34
35 guest_start = (uintptr_t)__guest_image_start;
36 guest_end = (uintptr_t)__guest_image_end;
37 guest_size = (uint64_t)(guest_end - guest_start);
38
39 if (guest_size == 0u || guest_size > CONFIG_GUEST_RAM_SIZE) {
40 return false;
41 }
42 if (hv_range_overlaps(CONFIG_GUEST_RAM_BASE, CONFIG_GUEST_RAM_SIZE,
43 (uint64_t)(uintptr_t)__image_start,
44 (uint64_t)((uintptr_t)__image_end - (uintptr_t)__image_start))) {
45 return false;
46 }
47
48 hv_memset((void *)(uintptr_t)CONFIG_GUEST_RAM_BASE, 0, CONFIG_GUEST_RAM_SIZE);
49 hv_memcpy((void *)(uintptr_t)CONFIG_GUEST_RAM_BASE, (const void *)guest_start, (size_t)guest_size);
50
51 info->kind = GUEST_PAYLOAD_DIAGNOSTIC;
52 info->ipa_entry = CONFIG_GUEST_ENTRY;
53 info->ipa_load = CONFIG_GUEST_IPA_BASE;
54 info->size = guest_size;
55 info->dtb_ipa = 0u;
56 info->initrd_ipa = 0u;
57 info->initrd_size = 0u;
58 return true;
59}
60
61bool guest_validate_linux_image_header(const void *image, uint64_t size,
62 struct guest_image_info *info)
63{
64 const uint8_t *p = (const uint8_t *)image;
65 uint64_t text_offset;
66 uint64_t image_size;
67 uint64_t entry;
68
69 if (image == (const void *)0 || info == (struct guest_image_info *)0 || size < 0x40u) {
70 return false;
71 }
72 if (read_le32(p + ARM64_IMAGE_MAGIC_OFFSET) != ARM64_IMAGE_MAGIC) {
73 return false;
74 }
75
76 text_offset = read_le64(p + ARM64_IMAGE_TEXT_OFFSET);
77 image_size = read_le64(p + ARM64_IMAGE_SIZE_OFFSET);
78 if (image_size == 0u) {
79 image_size = size;
80 }
81 if (image_size > CONFIG_GUEST_RAM_SIZE || text_offset >= CONFIG_GUEST_RAM_SIZE) {
82 return false;
83 }
84 if (hv_add_overflow_u64(CONFIG_GUEST_IPA_BASE, text_offset, &entry)) {
85 return false;
86 }
87
88 info->kind = GUEST_PAYLOAD_LINUX_IMAGE;
89 info->ipa_entry = entry;
90 info->ipa_load = entry;
91 info->size = image_size;
92 info->dtb_ipa = 0u;
93 info->initrd_ipa = 0u;
94 info->initrd_size = 0u;
95 return true;
96}
97
98bool guest_load_external_linux(struct guest_image_info *info, paddr_t image_pa,
99 uint64_t image_max_size, paddr_t dtb_pa,
100 uint64_t dtb_max_size, paddr_t initrd_pa,
101 uint64_t initrd_size)
102{
103 struct fdt_platform_info dtb_info;
104 uint64_t initrd_start = 0u;
105 uint64_t initrd_end = 0u;
106
107 if (info == (struct guest_image_info *)0 ||
108 image_max_size == 0u || image_max_size > CONFIG_GUEST_RAM_SIZE) {
109 return false;
110 }
111 if (!guest_validate_linux_image_header((const void *)(uintptr_t)image_pa,
112 image_max_size, info)) {
113 return false;
114 }
115 if (!fdt_probe(dtb_pa, &dtb_info) || dtb_info.size > dtb_max_size) {
116 return false;
117 }
118 if (initrd_size != 0u) {
119 if (initrd_size > CONFIG_LINUX_INITRD_MAX_SIZE ||
120 initrd_pa != CONFIG_LINUX_INITRD_PA ||
121 hv_add_overflow_u64(CONFIG_LINUX_INITRD_IPA, initrd_size, &initrd_end) ||
122 initrd_end > CONFIG_LINUX_DTB_IPA) {
123 return false;
124 }
125 initrd_start = CONFIG_LINUX_INITRD_IPA;
126 }
127 if (!fdt_patch_memory(dtb_pa, CONFIG_GUEST_IPA_BASE, CONFIG_GUEST_RAM_SIZE)) {
128 return false;
129 }
130 if (!fdt_patch_linux_guest(dtb_pa, dtb_max_size, LINUX_BOOTARGS,
131 initrd_start, initrd_end)) {
132 return false;
133 }
134 if (!fdt_probe(dtb_pa, &dtb_info)) {
135 return false;
136 }
137 log_simple(LOG_BOOT, 33u, dtb_info.size, dtb_info.memory_size);
138 if (dtb_info.initrd_start != 0u || dtb_info.initrd_end != 0u) {
139 log_simple(LOG_BOOT, 34u, dtb_info.initrd_start, dtb_info.initrd_end);
140 }
141 fdt_dump(&dtb_info);
142
143 info->kind = GUEST_PAYLOAD_LINUX_IMAGE;
144 info->ipa_load = CONFIG_GUEST_IPA_BASE;
145 info->dtb_ipa = CONFIG_LINUX_DTB_IPA;
146 if (initrd_start != 0u && initrd_end > initrd_start) {
147 info->initrd_ipa = initrd_start;
148 info->initrd_size = initrd_end - initrd_start;
149 }
150 return true;
151}