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