blob: cd81237c3fa270d717fc83e28957bd6c34a18ddf [file] [log] [blame]
Andrew Scull18c78fc2018-08-20 12:57:41 +01001#include "hf/load.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01002
3#include <stdbool.h>
4
Andrew Scull18c78fc2018-08-20 12:57:41 +01005#include "hf/api.h"
6#include "hf/dlog.h"
7#include "hf/memiter.h"
8#include "hf/mm.h"
9#include "hf/std.h"
10#include "hf/vm.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010011
12/**
13 * Copies data to an unmapped location by mapping it for write, copying the
14 * data, then unmapping it.
15 */
Andrew Walbranfd265ec2018-09-12 17:50:55 +010016static bool copy_to_unmapped(paddr_t to, const void *from, size_t size)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010017{
Andrew Scull80871322018-08-06 12:04:09 +010018 paddr_t to_end = pa_add(to, size);
19 void *ptr;
Andrew Scull265ada92018-07-30 15:19:01 +010020
Andrew Scull80871322018-08-06 12:04:09 +010021 ptr = mm_identity_map(to, to_end, MM_MODE_W);
22 if (!ptr) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010023 return false;
24 }
25
Andrew Scull80871322018-08-06 12:04:09 +010026 memcpy(ptr, from, size);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010027
Andrew Scull80871322018-08-06 12:04:09 +010028 mm_unmap(to, to_end, 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010029
30 return true;
31}
32
33/**
34 * Moves the kernel of the primary VM to its final destination.
35 */
36static bool relocate(const char *from, size_t size)
37{
38 /* TODO: This is a hack. We must read the alignment from the binary. */
39 extern char bin_end[];
40 size_t tmp = (size_t)&bin_end[0];
Andrew Scull265ada92018-07-30 15:19:01 +010041 paddr_t dest = pa_init((tmp + 0x80000 - 1) & ~(0x80000 - 1));
42 dlog("bin_end is at %p, copying to %p\n", &bin_end[0], pa_addr(dest));
Andrew Walbranfd265ec2018-09-12 17:50:55 +010043 return copy_to_unmapped(dest, from, size);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010044}
45
46/**
47 * Looks for a file in the given cpio archive. The filename is not
48 * null-terminated, so we use a memory iterator to represent it. The file, if
49 * found, is returned in the "it" argument.
50 */
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010051static bool memiter_find_file(const struct memiter *cpio,
52 const struct memiter *filename,
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010053 struct memiter *it)
54{
55 const char *fname;
56 const void *fcontents;
57 size_t fsize;
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010058 struct memiter iter = *cpio;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010059
60 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
61 if (memiter_iseq(filename, fname)) {
62 memiter_init(it, fcontents, fsize);
63 return true;
64 }
65 }
66
67 return false;
68}
69
70/**
71 * Looks for a file in the given cpio archive. The file, if found, is returned
72 * in the "it" argument.
73 */
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010074static bool find_file(const struct memiter *cpio, const char *name,
75 struct memiter *it)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010076{
77 const char *fname;
78 const void *fcontents;
79 size_t fsize;
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010080 struct memiter iter = *cpio;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010081
82 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
83 if (!strcmp(fname, name)) {
84 memiter_init(it, fcontents, fsize);
85 return true;
86 }
87 }
88
89 return false;
90}
91
92/**
93 * Loads the primary VM.
94 */
Andrew Scull265ada92018-07-30 15:19:01 +010095// TODO: kernel_arg is a size_t???
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010096bool load_primary(const struct memiter *cpio, size_t kernel_arg,
97 struct memiter *initrd)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010098{
99 struct memiter it;
100
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100101 if (!find_file(cpio, "vmlinuz", &it)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100102 dlog("Unable to find vmlinuz\n");
103 return false;
104 }
105
106 if (!relocate(it.next, it.limit - it.next)) {
107 dlog("Unable to relocate kernel for primary vm.\n");
108 return false;
109 }
110
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100111 if (!find_file(cpio, "initrd.img", initrd)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100112 dlog("Unable to find initrd.img\n");
113 return false;
114 }
115
116 {
Andrew Scull1b8d0442018-08-06 15:47:04 +0100117 uintpaddr_t tmp = (uintpaddr_t)&load_primary;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100118 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100119 if (!vm_init(&primary_vm, 0, MAX_CPUS)) {
120 dlog("Unable to initialise primary vm\n");
121 return false;
122 }
123
124 /* Map the 1TB of memory. */
125 /* TODO: We should do a whitelist rather than a blacklist. */
Andrew Scull78d6fd92018-09-06 15:08:36 +0100126 if (!mm_vm_identity_map(
127 &primary_vm.ptable, pa_init(0),
128 pa_init(UINT64_C(1024) * 1024 * 1024 * 1024),
129 MM_MODE_R | MM_MODE_W | MM_MODE_X |
130 MM_MODE_NOINVALIDATE,
131 NULL)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100132 dlog("Unable to initialise memory for primary vm\n");
133 return false;
134 }
135
136 if (!mm_ptable_unmap_hypervisor(&primary_vm.ptable,
137 MM_MODE_NOINVALIDATE)) {
138 dlog("Unable to unmap hypervisor from primary vm\n");
139 return false;
140 }
141
Andrew Scull89a75242018-08-06 17:04:55 +0100142 vm_start_vcpu(&primary_vm, 0, ipa_init(tmp), kernel_arg);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100143 }
144
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100145 return true;
146}
147
148/**
149 * Loads all secondary VMs in the given memory range. "mem_end" is updated to
150 * reflect the fact that some of the memory isn't available to the primary VM
151 * anymore.
152 */
Andrew Scull265ada92018-07-30 15:19:01 +0100153bool load_secondary(const struct memiter *cpio, paddr_t mem_begin,
154 paddr_t *mem_end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100155{
156 struct memiter it;
157 struct memiter str;
158 uint64_t mem;
159 uint64_t cpu;
160 uint32_t count;
161
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100162 if (!find_file(cpio, "vms.txt", &it)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100163 dlog("vms.txt is missing\n");
164 return true;
165 }
166
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100167 /* Round the last address down to the page size. */
Andrew Scull265ada92018-07-30 15:19:01 +0100168 *mem_end = pa_init(pa_addr(*mem_end) & ~(PAGE_SIZE - 1));
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100169
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100170 for (count = 0;
171 memiter_parse_uint(&it, &mem) && memiter_parse_uint(&it, &cpu) &&
172 memiter_parse_str(&it, &str) && count < MAX_VMS;
173 count++) {
174 struct memiter kernel;
Andrew Scull80871322018-08-06 12:04:09 +0100175 paddr_t secondary_mem_begin;
176 paddr_t secondary_mem_end;
177 ipaddr_t secondary_entry;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100178
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100179 if (!memiter_find_file(cpio, &str, &kernel)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100180 dlog("Unable to load kernel for vm %u\n", count);
181 continue;
182 }
183
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100184 /* Round up to page size. */
185 mem = (mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
Andrew Scull265ada92018-07-30 15:19:01 +0100186 if (mem > pa_addr(*mem_end) - pa_addr(mem_begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100187 dlog("Not enough memory for vm %u (%u bytes)\n", count,
188 mem);
189 continue;
190 }
191
192 if (mem < kernel.limit - kernel.next) {
193 dlog("Kernel is larger than available memory for vm "
194 "%u\n",
195 count);
196 continue;
197 }
198
Andrew Scull80871322018-08-06 12:04:09 +0100199 secondary_mem_end = *mem_end;
Andrew Scull265ada92018-07-30 15:19:01 +0100200 *mem_end = pa_init(pa_addr(*mem_end) - mem);
Andrew Scull80871322018-08-06 12:04:09 +0100201 secondary_mem_begin = *mem_end;
202
Andrew Walbranfd265ec2018-09-12 17:50:55 +0100203 if (!copy_to_unmapped(*mem_end, kernel.next,
204 kernel.limit - kernel.next)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100205 dlog("Unable to copy kernel for vm %u\n", count);
206 continue;
207 }
208
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100209 if (!vm_init(secondary_vm + count, count + 1, cpu)) {
210 dlog("Unable to initialise vm %u\n", count);
211 continue;
212 }
213
214 /* TODO: Remove this. */
215 /* Grant VM access to uart. */
Andrew Scull80871322018-08-06 12:04:09 +0100216 mm_vm_identity_map_page(&secondary_vm[count].ptable,
217 pa_init(PL011_BASE),
218 MM_MODE_R | MM_MODE_W | MM_MODE_D |
219 MM_MODE_NOINVALIDATE,
220 NULL);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100221
222 /* Grant the VM access to the memory. */
Andrew Scull80871322018-08-06 12:04:09 +0100223 if (!mm_vm_identity_map(&secondary_vm[count].ptable,
224 secondary_mem_begin, secondary_mem_end,
225 MM_MODE_R | MM_MODE_W | MM_MODE_X |
226 MM_MODE_NOINVALIDATE,
227 &secondary_entry)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100228 dlog("Unable to initialise memory for vm %u\n", count);
229 continue;
230 }
231
232 /* Deny the primary VM access to this memory. */
Andrew Scull80871322018-08-06 12:04:09 +0100233 if (!mm_vm_unmap(&primary_vm.ptable, secondary_mem_begin,
234 secondary_mem_end, MM_MODE_NOINVALIDATE)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100235 dlog("Unable to unmap secondary VM from primary VM\n");
236 return false;
237 }
238
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100239 dlog("Loaded VM%u with %u vcpus, entry at 0x%x\n", count, cpu,
Andrew Scull265ada92018-07-30 15:19:01 +0100240 pa_addr(*mem_end));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100241
Andrew Scull80871322018-08-06 12:04:09 +0100242 vm_start_vcpu(secondary_vm + count, 0, secondary_entry, 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100243 }
244
245 secondary_vm_count = count;
246
247 return true;
248}