blob: d36ac21835741da77dc583936b1ad3dbbbbd44d6 [file] [log] [blame]
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001#include "load.h"
2
3#include <stdbool.h>
4
5#include "api.h"
6#include "dlog.h"
7#include "memiter.h"
8#include "mm.h"
9#include "std.h"
10#include "vm.h"
11
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 Scull265ada92018-07-30 15:19:01 +010018 vaddr_t begin = mm_va_from_pa(to);
19 vaddr_t end = va_add(begin, size);
20
21 if (!mm_identity_map(begin, end, MM_MODE_W)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010022 return false;
23 }
24
Andrew Scull265ada92018-07-30 15:19:01 +010025 memcpy(mm_ptr_from_va(begin), from, size);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010026
Andrew Scull265ada92018-07-30 15:19:01 +010027 mm_unmap(begin, end, 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010028
29 return true;
30}
31
32/**
33 * Moves the kernel of the primary VM to its final destination.
34 */
35static bool relocate(const char *from, size_t size)
36{
37 /* TODO: This is a hack. We must read the alignment from the binary. */
38 extern char bin_end[];
39 size_t tmp = (size_t)&bin_end[0];
Andrew Scull265ada92018-07-30 15:19:01 +010040 paddr_t dest = pa_init((tmp + 0x80000 - 1) & ~(0x80000 - 1));
41 dlog("bin_end is at %p, copying to %p\n", &bin_end[0], pa_addr(dest));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010042 return copy_to_unmaped(dest, from, size);
43}
44
45/**
46 * Looks for a file in the given cpio archive. The filename is not
47 * null-terminated, so we use a memory iterator to represent it. The file, if
48 * found, is returned in the "it" argument.
49 */
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010050static bool memiter_find_file(const struct memiter *cpio,
51 const struct memiter *filename,
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010052 struct memiter *it)
53{
54 const char *fname;
55 const void *fcontents;
56 size_t fsize;
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010057 struct memiter iter = *cpio;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010058
59 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
60 if (memiter_iseq(filename, fname)) {
61 memiter_init(it, fcontents, fsize);
62 return true;
63 }
64 }
65
66 return false;
67}
68
69/**
70 * Looks for a file in the given cpio archive. The file, if found, is returned
71 * in the "it" argument.
72 */
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010073static bool find_file(const struct memiter *cpio, const char *name,
74 struct memiter *it)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010075{
76 const char *fname;
77 const void *fcontents;
78 size_t fsize;
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010079 struct memiter iter = *cpio;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010080
81 while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
82 if (!strcmp(fname, name)) {
83 memiter_init(it, fcontents, fsize);
84 return true;
85 }
86 }
87
88 return false;
89}
90
91/**
92 * Loads the primary VM.
93 */
Andrew Scull265ada92018-07-30 15:19:01 +010094// TODO: kernel_arg is a size_t???
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +010095bool load_primary(const struct memiter *cpio, size_t kernel_arg,
96 struct memiter *initrd)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010097{
98 struct memiter it;
99
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100100 if (!find_file(cpio, "vmlinuz", &it)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100101 dlog("Unable to find vmlinuz\n");
102 return false;
103 }
104
105 if (!relocate(it.next, it.limit - it.next)) {
106 dlog("Unable to relocate kernel for primary vm.\n");
107 return false;
108 }
109
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100110 if (!find_file(cpio, "initrd.img", initrd)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100111 dlog("Unable to find initrd.img\n");
112 return false;
113 }
114
115 {
116 size_t tmp = (size_t)&load_primary;
117 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100118 if (!vm_init(&primary_vm, 0, MAX_CPUS)) {
119 dlog("Unable to initialise primary vm\n");
120 return false;
121 }
122
123 /* Map the 1TB of memory. */
124 /* TODO: We should do a whitelist rather than a blacklist. */
Andrew Scull265ada92018-07-30 15:19:01 +0100125 if (!mm_ptable_identity_map(
126 &primary_vm.ptable, va_init(0),
127 va_init(1024ull * 1024 * 1024 * 1024),
128 MM_MODE_R | MM_MODE_W | MM_MODE_X |
129 MM_MODE_NOINVALIDATE)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100130 dlog("Unable to initialise memory for primary vm\n");
131 return false;
132 }
133
134 if (!mm_ptable_unmap_hypervisor(&primary_vm.ptable,
135 MM_MODE_NOINVALIDATE)) {
136 dlog("Unable to unmap hypervisor from primary vm\n");
137 return false;
138 }
139
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100140 vm_start_vcpu(&primary_vm, 0, tmp, kernel_arg, true);
141 }
142
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100143 return true;
144}
145
146/**
147 * Loads all secondary VMs in the given memory range. "mem_end" is updated to
148 * reflect the fact that some of the memory isn't available to the primary VM
149 * anymore.
150 */
Andrew Scull265ada92018-07-30 15:19:01 +0100151bool load_secondary(const struct memiter *cpio, paddr_t mem_begin,
152 paddr_t *mem_end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100153{
154 struct memiter it;
155 struct memiter str;
156 uint64_t mem;
157 uint64_t cpu;
158 uint32_t count;
159
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100160 if (!find_file(cpio, "vms.txt", &it)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100161 dlog("vms.txt is missing\n");
162 return true;
163 }
164
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100165 /* Round the last address down to the page size. */
Andrew Scull265ada92018-07-30 15:19:01 +0100166 *mem_end = pa_init(pa_addr(*mem_end) & ~(PAGE_SIZE - 1));
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100167
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100168 for (count = 0;
169 memiter_parse_uint(&it, &mem) && memiter_parse_uint(&it, &cpu) &&
170 memiter_parse_str(&it, &str) && count < MAX_VMS;
171 count++) {
172 struct memiter kernel;
173
Wedson Almeida Filho9ee60e92018-07-23 18:56:56 +0100174 if (!memiter_find_file(cpio, &str, &kernel)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100175 dlog("Unable to load kernel for vm %u\n", count);
176 continue;
177 }
178
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100179 /* Round up to page size. */
180 mem = (mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
Andrew Scull265ada92018-07-30 15:19:01 +0100181 if (mem > pa_addr(*mem_end) - pa_addr(mem_begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100182 dlog("Not enough memory for vm %u (%u bytes)\n", count,
183 mem);
184 continue;
185 }
186
187 if (mem < kernel.limit - kernel.next) {
188 dlog("Kernel is larger than available memory for vm "
189 "%u\n",
190 count);
191 continue;
192 }
193
Andrew Scull265ada92018-07-30 15:19:01 +0100194 *mem_end = pa_init(pa_addr(*mem_end) - mem);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100195 if (!copy_to_unmaped(*mem_end, kernel.next,
196 kernel.limit - kernel.next)) {
197 dlog("Unable to copy kernel for vm %u\n", count);
198 continue;
199 }
200
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100201 if (!vm_init(secondary_vm + count, count + 1, cpu)) {
202 dlog("Unable to initialise vm %u\n", count);
203 continue;
204 }
205
206 /* TODO: Remove this. */
207 /* Grant VM access to uart. */
Andrew Scullfe636b12018-07-30 14:15:54 +0100208 mm_ptable_identity_map_page(&secondary_vm[count].ptable,
Andrew Scull265ada92018-07-30 15:19:01 +0100209 va_init(PL011_BASE),
Andrew Scullfe636b12018-07-30 14:15:54 +0100210 MM_MODE_R | MM_MODE_W | MM_MODE_D |
211 MM_MODE_NOINVALIDATE);
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100212
213 /* Grant the VM access to the memory. */
Andrew Scull265ada92018-07-30 15:19:01 +0100214 if (!mm_ptable_identity_map(
215 &secondary_vm[count].ptable,
216 mm_va_from_pa(*mem_end),
217 va_add(mm_va_from_pa(*mem_end), mem),
218 MM_MODE_R | MM_MODE_W | MM_MODE_X |
219 MM_MODE_NOINVALIDATE)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100220 dlog("Unable to initialise memory for vm %u\n", count);
221 continue;
222 }
223
224 /* Deny the primary VM access to this memory. */
Andrew Scull265ada92018-07-30 15:19:01 +0100225 if (!mm_ptable_unmap(&primary_vm.ptable,
226 mm_va_from_pa(*mem_end),
227 va_add(mm_va_from_pa(*mem_end), mem),
228 MM_MODE_NOINVALIDATE)) {
Wedson Almeida Filho84a30a02018-07-23 20:05:05 +0100229 dlog("Unable to unmap secondary VM from primary VM\n");
230 return false;
231 }
232
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100233 dlog("Loaded VM%u with %u vcpus, entry at 0x%x\n", count, cpu,
Andrew Scull265ada92018-07-30 15:19:01 +0100234 pa_addr(*mem_end));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100235
Andrew Scull265ada92018-07-30 15:19:01 +0100236 // TODO: entry is a size_t which seems to be wrong, what should
237 // it be?
238 vm_start_vcpu(secondary_vm + count, 0, pa_addr(*mem_end), 0,
239 false);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100240 }
241
242 secondary_vm_count = count;
243
244 return true;
245}