blob: eb6074d1dbcd45cc810b4f4a01b1b929bcc7cd31 [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 */
16static bool copy_to_unmaped(paddr_t to, const void *from, size_t size)
17{
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));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010043 return copy_to_unmaped(dest, from, size);
44}
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 Scull80871322018-08-06 12:04:09 +0100126 if (!mm_vm_identity_map(&primary_vm.ptable, pa_init(0),
127 pa_init(1024ull * 1024 * 1024 * 1024),
128 MM_MODE_R | MM_MODE_W | MM_MODE_X |
129 MM_MODE_NOINVALIDATE,
130 NULL)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100131 dlog("Unable to initialise memory for primary vm\n");
132 return false;
133 }
134
135 if (!mm_ptable_unmap_hypervisor(&primary_vm.ptable,
136 MM_MODE_NOINVALIDATE)) {
137 dlog("Unable to unmap hypervisor from primary vm\n");
138 return false;
139 }
140
Andrew Scull89a75242018-08-06 17:04:55 +0100141 vm_start_vcpu(&primary_vm, 0, ipa_init(tmp), kernel_arg);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100142 }
143
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100144 return true;
145}
146
147/**
148 * Loads all secondary VMs in the given memory range. "mem_end" is updated to
149 * reflect the fact that some of the memory isn't available to the primary VM
150 * anymore.
151 */
Andrew Scull265ada92018-07-30 15:19:01 +0100152bool load_secondary(const struct memiter *cpio, paddr_t mem_begin,
153 paddr_t *mem_end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100154{
155 struct memiter it;
156 struct memiter str;
157 uint64_t mem;
158 uint64_t cpu;
159 uint32_t count;
160
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100161 if (!find_file(cpio, "vms.txt", &it)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100162 dlog("vms.txt is missing\n");
163 return true;
164 }
165
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100166 /* Round the last address down to the page size. */
Andrew Scull265ada92018-07-30 15:19:01 +0100167 *mem_end = pa_init(pa_addr(*mem_end) & ~(PAGE_SIZE - 1));
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100168
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100169 for (count = 0;
170 memiter_parse_uint(&it, &mem) && memiter_parse_uint(&it, &cpu) &&
171 memiter_parse_str(&it, &str) && count < MAX_VMS;
172 count++) {
173 struct memiter kernel;
Andrew Scull80871322018-08-06 12:04:09 +0100174 paddr_t secondary_mem_begin;
175 paddr_t secondary_mem_end;
176 ipaddr_t secondary_entry;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100177
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100178 if (!memiter_find_file(cpio, &str, &kernel)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100179 dlog("Unable to load kernel for vm %u\n", count);
180 continue;
181 }
182
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100183 /* Round up to page size. */
184 mem = (mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
Andrew Scull265ada92018-07-30 15:19:01 +0100185 if (mem > pa_addr(*mem_end) - pa_addr(mem_begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100186 dlog("Not enough memory for vm %u (%u bytes)\n", count,
187 mem);
188 continue;
189 }
190
191 if (mem < kernel.limit - kernel.next) {
192 dlog("Kernel is larger than available memory for vm "
193 "%u\n",
194 count);
195 continue;
196 }
197
Andrew Scull80871322018-08-06 12:04:09 +0100198 secondary_mem_end = *mem_end;
Andrew Scull265ada92018-07-30 15:19:01 +0100199 *mem_end = pa_init(pa_addr(*mem_end) - mem);
Andrew Scull80871322018-08-06 12:04:09 +0100200 secondary_mem_begin = *mem_end;
201
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100202 if (!copy_to_unmaped(*mem_end, kernel.next,
203 kernel.limit - kernel.next)) {
204 dlog("Unable to copy kernel for vm %u\n", count);
205 continue;
206 }
207
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100208 if (!vm_init(secondary_vm + count, count + 1, cpu)) {
209 dlog("Unable to initialise vm %u\n", count);
210 continue;
211 }
212
213 /* TODO: Remove this. */
214 /* Grant VM access to uart. */
Andrew Scull80871322018-08-06 12:04:09 +0100215 mm_vm_identity_map_page(&secondary_vm[count].ptable,
216 pa_init(PL011_BASE),
217 MM_MODE_R | MM_MODE_W | MM_MODE_D |
218 MM_MODE_NOINVALIDATE,
219 NULL);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100220
221 /* Grant the VM access to the memory. */
Andrew Scull80871322018-08-06 12:04:09 +0100222 if (!mm_vm_identity_map(&secondary_vm[count].ptable,
223 secondary_mem_begin, secondary_mem_end,
224 MM_MODE_R | MM_MODE_W | MM_MODE_X |
225 MM_MODE_NOINVALIDATE,
226 &secondary_entry)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100227 dlog("Unable to initialise memory for vm %u\n", count);
228 continue;
229 }
230
231 /* Deny the primary VM access to this memory. */
Andrew Scull80871322018-08-06 12:04:09 +0100232 if (!mm_vm_unmap(&primary_vm.ptable, secondary_mem_begin,
233 secondary_mem_end, MM_MODE_NOINVALIDATE)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100234 dlog("Unable to unmap secondary VM from primary VM\n");
235 return false;
236 }
237
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100238 dlog("Loaded VM%u with %u vcpus, entry at 0x%x\n", count, cpu,
Andrew Scull265ada92018-07-30 15:19:01 +0100239 pa_addr(*mem_end));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100240
Andrew Scull80871322018-08-06 12:04:09 +0100241 vm_start_vcpu(secondary_vm + count, 0, secondary_entry, 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100242 }
243
244 secondary_vm_count = count;
245
246 return true;
247}