blob: c3b5566c990e6199ea1787b1faeeedf17a5912db [file] [log] [blame]
David Brazdil7a462ec2019-08-15 12:27:47 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "hf/manifest.h"
18
19#include "hf/addr.h"
20#include "hf/check.h"
Andrew Scullae9962e2019-10-03 16:51:16 +010021#include "hf/dlog.h"
David Brazdil7a462ec2019-08-15 12:27:47 +010022#include "hf/fdt.h"
23#include "hf/static_assert.h"
24#include "hf/std.h"
25
26#define TRY(expr) \
27 do { \
28 enum manifest_return_code ret_code = (expr); \
29 if (ret_code != MANIFEST_SUCCESS) { \
30 return ret_code; \
31 } \
32 } while (0)
33
34#define VM_NAME_BUF_SIZE (2 + 5 + 1) /* "vm" + number + null terminator */
35static_assert(MAX_VMS <= 99999, "Insufficient VM_NAME_BUF_SIZE");
36
37/**
38 * Generates a string with the two letters "vm" followed by an integer.
39 * Assumes `buf` is of size VM_NAME_BUF_SIZE.
40 */
41static const char *generate_vm_node_name(char *buf, spci_vm_id_t vm_id)
42{
43 static const char *digits = "0123456789";
44 char *ptr = buf + VM_NAME_BUF_SIZE;
45
46 *(--ptr) = '\0';
47 do {
48 *(--ptr) = digits[vm_id % 10];
49 vm_id /= 10;
50 } while (vm_id);
51 *(--ptr) = 'm';
52 *(--ptr) = 'v';
53
54 return ptr;
55}
56
Andrew Scullae9962e2019-10-03 16:51:16 +010057/**
Andrew Scullb2c3a242019-11-04 13:52:36 +000058 * Read a boolean property: true if present; false if not. If present, the value
59 * of the property must be empty else it is considered malformed.
Andrew Scullae9962e2019-10-03 16:51:16 +010060 */
Andrew Scullb2c3a242019-11-04 13:52:36 +000061static enum manifest_return_code read_bool(const struct fdt_node *node,
62 const char *property, bool *out)
Andrew Scullae9962e2019-10-03 16:51:16 +010063{
64 const char *data;
65 uint32_t size;
Andrew Scullb2c3a242019-11-04 13:52:36 +000066 bool present = fdt_read_property(node, property, &data, &size);
Andrew Scullae9962e2019-10-03 16:51:16 +010067
Andrew Scullb2c3a242019-11-04 13:52:36 +000068 if (present && size != 0) {
69 return MANIFEST_ERROR_MALFORMED_BOOLEAN;
70 }
71
72 *out = present;
73 return MANIFEST_SUCCESS;
Andrew Scullae9962e2019-10-03 16:51:16 +010074}
75
Andrew Scull72b43c02019-09-18 13:53:45 +010076static enum manifest_return_code read_string(const struct fdt_node *node,
David Brazdil136f2942019-09-23 14:11:03 +010077 const char *property,
78 struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +010079{
80 const char *data;
81 uint32_t size;
82
83 if (!fdt_read_property(node, property, &data, &size)) {
84 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
85 }
86
David Brazdil136f2942019-09-23 14:11:03 +010087 switch (string_init(out, data, size)) {
88 case STRING_SUCCESS:
89 return MANIFEST_SUCCESS;
90 case STRING_ERROR_INVALID_INPUT:
91 return MANIFEST_ERROR_MALFORMED_STRING;
92 case STRING_ERROR_TOO_LONG:
93 return MANIFEST_ERROR_STRING_TOO_LONG;
94 }
Andrew Scull72b43c02019-09-18 13:53:45 +010095}
96
97static enum manifest_return_code read_optional_string(
David Brazdil136f2942019-09-23 14:11:03 +010098 const struct fdt_node *node, const char *property, struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +010099{
David Brazdil136f2942019-09-23 14:11:03 +0100100 enum manifest_return_code ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100101
David Brazdil136f2942019-09-23 14:11:03 +0100102 ret = read_string(node, property, out);
103 if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
104 string_init_empty(out);
105 ret = MANIFEST_SUCCESS;
Andrew Scull72b43c02019-09-18 13:53:45 +0100106 }
David Brazdil136f2942019-09-23 14:11:03 +0100107 return ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100108}
109
David Brazdil7a462ec2019-08-15 12:27:47 +0100110static enum manifest_return_code read_uint64(const struct fdt_node *node,
111 const char *property,
112 uint64_t *out)
113{
114 const char *data;
115 uint32_t size;
116
117 if (!fdt_read_property(node, property, &data, &size)) {
118 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
119 }
120
121 if (!fdt_parse_number(data, size, out)) {
122 return MANIFEST_ERROR_MALFORMED_INTEGER;
123 }
124
125 return MANIFEST_SUCCESS;
126}
127
128static enum manifest_return_code read_uint16(const struct fdt_node *node,
129 const char *property,
130 uint16_t *out)
131{
132 uint64_t value;
133
134 TRY(read_uint64(node, property, &value));
135
136 if (value > UINT16_MAX) {
137 return MANIFEST_ERROR_INTEGER_OVERFLOW;
138 }
139
140 *out = (uint16_t)value;
141 return MANIFEST_SUCCESS;
142}
143
Andrew Scullae9962e2019-10-03 16:51:16 +0100144struct uint32list_iter {
145 struct memiter mem_it;
146};
147
148static enum manifest_return_code read_optional_uint32list(
149 const struct fdt_node *node, const char *property,
150 struct uint32list_iter *out)
151{
152 const char *data;
153 uint32_t size;
154
155 if (!fdt_read_property(node, property, &data, &size)) {
156 memiter_init(&out->mem_it, NULL, 0);
157 return MANIFEST_SUCCESS;
158 }
159
160 if ((size % sizeof(uint32_t)) != 0) {
161 return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
162 }
163
164 memiter_init(&out->mem_it, data, size);
165 return MANIFEST_SUCCESS;
166}
167
David Brazdil74e9c3b2019-08-28 11:09:08 +0100168/**
169 * Represents the value of property whose type is a list of strings. These are
170 * encoded as one contiguous byte buffer with NULL-separated entries.
171 */
172struct stringlist_iter {
173 struct memiter mem_it;
174};
175
176static enum manifest_return_code read_stringlist(const struct fdt_node *node,
177 const char *property,
178 struct stringlist_iter *out)
179{
180 const char *data;
181 uint32_t size;
182
183 if (!fdt_read_property(node, property, &data, &size)) {
184 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
185 }
186
187 /*
188 * Require that the value ends with a NULL terminator. Other NULL
189 * characters separate the string list entries.
190 */
191 if (data[size - 1] != '\0') {
192 return MANIFEST_ERROR_MALFORMED_STRING_LIST;
193 }
194
195 memiter_init(&out->mem_it, data, size - 1);
196 return MANIFEST_SUCCESS;
197}
198
Andrew Scullae9962e2019-10-03 16:51:16 +0100199static bool uint32list_has_next(const struct uint32list_iter *list)
200{
201 return memiter_size(&list->mem_it) > 0;
202}
203
204static uint32_t uint32list_get_next(struct uint32list_iter *list)
205{
206 const char *mem_base = memiter_base(&list->mem_it);
207 uint64_t num;
208
209 CHECK(uint32list_has_next(list));
210
211 if (!fdt_parse_number(mem_base, sizeof(uint32_t), &num)) {
212 return MANIFEST_ERROR_MALFORMED_INTEGER;
213 }
214
215 memiter_advance(&list->mem_it, sizeof(uint32_t));
216 return num;
217}
218
David Brazdil74e9c3b2019-08-28 11:09:08 +0100219static bool stringlist_has_next(const struct stringlist_iter *list)
220{
221 return memiter_size(&list->mem_it) > 0;
222}
223
224static void stringlist_get_next(struct stringlist_iter *list,
225 struct memiter *out)
226{
227 const char *mem_base = memiter_base(&list->mem_it);
228 size_t mem_size = memiter_size(&list->mem_it);
229 const char *null_term;
230
231 CHECK(stringlist_has_next(list));
232
233 null_term = memchr(mem_base, '\0', mem_size);
234 if (null_term == NULL) {
235 /*
236 * NULL terminator not found, this is the last entry.
237 * Set entry memiter to the entire byte range and advance list
238 * memiter to the end of the byte range.
239 */
240 memiter_init(out, mem_base, mem_size);
241 memiter_advance(&list->mem_it, mem_size);
242 } else {
243 /*
244 * Found NULL terminator. Set entry memiter to byte range
245 * [base, null) and move list memiter past the terminator.
246 */
247 size_t entry_size = null_term - mem_base;
248
249 memiter_init(out, mem_base, entry_size);
250 memiter_advance(&list->mem_it, entry_size + 1);
251 }
252}
253
254static bool stringlist_contains(const struct stringlist_iter *list,
255 const char *str)
256{
257 struct stringlist_iter it = *list;
258 struct memiter entry;
259
260 while (stringlist_has_next(&it)) {
261 stringlist_get_next(&it, &entry);
262 if (memiter_iseq(&entry, str)) {
263 return true;
264 }
265 }
266 return false;
267}
268
David Brazdil7a462ec2019-08-15 12:27:47 +0100269static enum manifest_return_code parse_vm(struct fdt_node *node,
270 struct manifest_vm *vm,
271 spci_vm_id_t vm_id)
272{
Andrew Scullae9962e2019-10-03 16:51:16 +0100273 struct uint32list_iter smcs;
274
David Brazdil136f2942019-09-23 14:11:03 +0100275 TRY(read_string(node, "debug_name", &vm->debug_name));
276 TRY(read_optional_string(node, "kernel_filename",
277 &vm->kernel_filename));
Andrew Scullae9962e2019-10-03 16:51:16 +0100278
279 TRY(read_optional_uint32list(node, "smc_whitelist", &smcs));
280 while (uint32list_has_next(&smcs) &&
281 vm->smc_whitelist.smc_count < MAX_SMCS) {
282 vm->smc_whitelist.smcs[vm->smc_whitelist.smc_count++] =
283 uint32list_get_next(&smcs);
284 }
285
286 if (uint32list_has_next(&smcs)) {
287 dlog("%s SMC whitelist too long.\n", vm->debug_name);
288 }
289
Andrew Scullb2c3a242019-11-04 13:52:36 +0000290 TRY(read_bool(node, "smc_whitelist_permissive",
291 &vm->smc_whitelist.permissive));
Andrew Scullae9962e2019-10-03 16:51:16 +0100292
David Brazdile6f83222019-09-23 14:47:37 +0100293 if (vm_id == HF_PRIMARY_VM_ID) {
294 TRY(read_optional_string(node, "ramdisk_filename",
295 &vm->primary.ramdisk_filename));
296 } else {
David Brazdil7a462ec2019-08-15 12:27:47 +0100297 TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
298 TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
299 }
300 return MANIFEST_SUCCESS;
301}
302
303/**
304 * Parse manifest from FDT.
305 */
306enum manifest_return_code manifest_init(struct manifest *manifest,
David Brazdil0dbb41f2019-09-09 18:03:35 +0100307 const struct fdt_node *fdt_root)
David Brazdil7a462ec2019-08-15 12:27:47 +0100308{
309 char vm_name_buf[VM_NAME_BUF_SIZE];
310 struct fdt_node hyp_node;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100311 struct stringlist_iter compatible_list;
David Brazdil7a462ec2019-08-15 12:27:47 +0100312 size_t i = 0;
313 bool found_primary_vm = false;
314
315 memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
316
317 /* Find hypervisor node. */
David Brazdil0dbb41f2019-09-09 18:03:35 +0100318 hyp_node = *fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100319 if (!fdt_find_child(&hyp_node, "hypervisor")) {
320 return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
321 }
322
David Brazdil74e9c3b2019-08-28 11:09:08 +0100323 /* Check "compatible" property. */
324 TRY(read_stringlist(&hyp_node, "compatible", &compatible_list));
325 if (!stringlist_contains(&compatible_list, "hafnium,hafnium")) {
326 return MANIFEST_ERROR_NOT_COMPATIBLE;
327 }
328
David Brazdil7a462ec2019-08-15 12:27:47 +0100329 /* Iterate over reserved VM IDs and check no such nodes exist. */
330 for (i = 0; i < HF_VM_ID_OFFSET; i++) {
331 spci_vm_id_t vm_id = (spci_vm_id_t)i;
332 struct fdt_node vm_node = hyp_node;
333 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
334
335 if (fdt_find_child(&vm_node, vm_name)) {
336 return MANIFEST_ERROR_RESERVED_VM_ID;
337 }
338 }
339
340 /* Iterate over VM nodes until we find one that does not exist. */
341 for (i = 0; i <= MAX_VMS; ++i) {
342 spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
343 struct fdt_node vm_node = hyp_node;
344 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
345
346 if (!fdt_find_child(&vm_node, vm_name)) {
347 break;
348 }
349
350 if (i == MAX_VMS) {
351 return MANIFEST_ERROR_TOO_MANY_VMS;
352 }
353
354 if (vm_id == HF_PRIMARY_VM_ID) {
355 CHECK(found_primary_vm == false); /* sanity check */
356 found_primary_vm = true;
357 }
358
David Brazdil0251b942019-09-10 15:59:50 +0100359 manifest->vm_count = i + 1;
David Brazdil7a462ec2019-08-15 12:27:47 +0100360 TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
361 }
362
363 if (!found_primary_vm) {
364 return MANIFEST_ERROR_NO_PRIMARY_VM;
365 }
366
367 return MANIFEST_SUCCESS;
368}
369
370const char *manifest_strerror(enum manifest_return_code ret_code)
371{
372 switch (ret_code) {
373 case MANIFEST_SUCCESS:
374 return "Success";
David Brazdil7a462ec2019-08-15 12:27:47 +0100375 case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
376 return "Could not find \"hypervisor\" node in manifest";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100377 case MANIFEST_ERROR_NOT_COMPATIBLE:
378 return "Hypervisor manifest entry not compatible with Hafnium";
David Brazdil7a462ec2019-08-15 12:27:47 +0100379 case MANIFEST_ERROR_RESERVED_VM_ID:
380 return "Manifest defines a VM with a reserved ID";
381 case MANIFEST_ERROR_NO_PRIMARY_VM:
382 return "Manifest does not contain a primary VM entry";
383 case MANIFEST_ERROR_TOO_MANY_VMS:
384 return "Manifest specifies more VMs than Hafnium has "
385 "statically allocated space for";
386 case MANIFEST_ERROR_PROPERTY_NOT_FOUND:
387 return "Property not found";
388 case MANIFEST_ERROR_MALFORMED_STRING:
389 return "Malformed string property";
David Brazdil0dbb41f2019-09-09 18:03:35 +0100390 case MANIFEST_ERROR_STRING_TOO_LONG:
391 return "String too long";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100392 case MANIFEST_ERROR_MALFORMED_STRING_LIST:
393 return "Malformed string list property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100394 case MANIFEST_ERROR_MALFORMED_INTEGER:
395 return "Malformed integer property";
396 case MANIFEST_ERROR_INTEGER_OVERFLOW:
397 return "Integer overflow";
Andrew Scullae9962e2019-10-03 16:51:16 +0100398 case MANIFEST_ERROR_MALFORMED_INTEGER_LIST:
399 return "Malformed integer list property";
Andrew Scullb2c3a242019-11-04 13:52:36 +0000400 case MANIFEST_ERROR_MALFORMED_BOOLEAN:
401 return "Malformed boolean property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100402 }
403
404 panic("Unexpected manifest return code.");
405}