blob: f83c0f90516e78b7bf96b806e53dd32e19433dc9 [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
David Brazdilb856be62020-03-25 10:14:55 +000034#define VM_ID_MAX (HF_VM_ID_OFFSET + MAX_VMS - 1)
35#define VM_ID_MAX_DIGITS (5)
36#define VM_NAME_EXTRA_CHARS (3) /* "vm" + number + '\0' */
37#define VM_NAME_MAX_SIZE (VM_ID_MAX_DIGITS + VM_NAME_EXTRA_CHARS)
38static_assert(VM_NAME_MAX_SIZE <= STRING_MAX_SIZE,
39 "VM name does not fit into a struct string.");
40static_assert(VM_ID_MAX <= 99999, "Insufficient VM_NAME_BUF_SIZE");
41static_assert(HF_TEE_VM_ID > VM_ID_MAX,
Andrew Walbran9daa57e2019-09-27 13:33:20 +010042 "TrustZone VM ID clashes with normal VM range.");
David Brazdil7a462ec2019-08-15 12:27:47 +010043
David Brazdilb856be62020-03-25 10:14:55 +000044static inline size_t count_digits(spci_vm_id_t vm_id)
45{
46 size_t digits = 0;
47
48 do {
49 digits++;
50 vm_id /= 10;
51 } while (vm_id);
52 return digits;
53}
54
David Brazdil7a462ec2019-08-15 12:27:47 +010055/**
56 * Generates a string with the two letters "vm" followed by an integer.
57 * Assumes `buf` is of size VM_NAME_BUF_SIZE.
58 */
David Brazdilb856be62020-03-25 10:14:55 +000059static void generate_vm_node_name(struct string *str, spci_vm_id_t vm_id)
David Brazdil7a462ec2019-08-15 12:27:47 +010060{
61 static const char *digits = "0123456789";
David Brazdilb856be62020-03-25 10:14:55 +000062 size_t vm_id_digits = count_digits(vm_id);
63 char *base = str->data;
64 char *ptr = base + (VM_NAME_EXTRA_CHARS + vm_id_digits);
David Brazdil7a462ec2019-08-15 12:27:47 +010065
David Brazdilb856be62020-03-25 10:14:55 +000066 CHECK(vm_id_digits <= VM_ID_MAX_DIGITS);
David Brazdil7a462ec2019-08-15 12:27:47 +010067 *(--ptr) = '\0';
68 do {
69 *(--ptr) = digits[vm_id % 10];
70 vm_id /= 10;
71 } while (vm_id);
72 *(--ptr) = 'm';
73 *(--ptr) = 'v';
David Brazdilb856be62020-03-25 10:14:55 +000074 CHECK(ptr == base);
David Brazdil7a462ec2019-08-15 12:27:47 +010075}
76
Andrew Scullae9962e2019-10-03 16:51:16 +010077/**
Andrew Scullb2c3a242019-11-04 13:52:36 +000078 * Read a boolean property: true if present; false if not. If present, the value
79 * of the property must be empty else it is considered malformed.
Andrew Scullae9962e2019-10-03 16:51:16 +010080 */
Andrew Scullb2c3a242019-11-04 13:52:36 +000081static enum manifest_return_code read_bool(const struct fdt_node *node,
82 const char *property, bool *out)
Andrew Scullae9962e2019-10-03 16:51:16 +010083{
David Brazdilb856be62020-03-25 10:14:55 +000084 struct memiter data;
85 bool present = fdt_read_property(node, property, &data);
Andrew Scullae9962e2019-10-03 16:51:16 +010086
David Brazdilb856be62020-03-25 10:14:55 +000087 if (present && memiter_size(&data) != 0) {
Andrew Scullb2c3a242019-11-04 13:52:36 +000088 return MANIFEST_ERROR_MALFORMED_BOOLEAN;
89 }
90
91 *out = present;
92 return MANIFEST_SUCCESS;
Andrew Scullae9962e2019-10-03 16:51:16 +010093}
94
Andrew Scull72b43c02019-09-18 13:53:45 +010095static enum manifest_return_code read_string(const struct fdt_node *node,
David Brazdil136f2942019-09-23 14:11:03 +010096 const char *property,
97 struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +010098{
David Brazdilb856be62020-03-25 10:14:55 +000099 struct memiter data;
Andrew Scull72b43c02019-09-18 13:53:45 +0100100
David Brazdilb856be62020-03-25 10:14:55 +0000101 if (!fdt_read_property(node, property, &data)) {
Andrew Scull72b43c02019-09-18 13:53:45 +0100102 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
103 }
104
David Brazdilb856be62020-03-25 10:14:55 +0000105 switch (string_init(out, &data)) {
David Brazdil136f2942019-09-23 14:11:03 +0100106 case STRING_SUCCESS:
107 return MANIFEST_SUCCESS;
108 case STRING_ERROR_INVALID_INPUT:
109 return MANIFEST_ERROR_MALFORMED_STRING;
110 case STRING_ERROR_TOO_LONG:
111 return MANIFEST_ERROR_STRING_TOO_LONG;
112 }
Andrew Scull72b43c02019-09-18 13:53:45 +0100113}
114
115static enum manifest_return_code read_optional_string(
David Brazdil136f2942019-09-23 14:11:03 +0100116 const struct fdt_node *node, const char *property, struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +0100117{
David Brazdil136f2942019-09-23 14:11:03 +0100118 enum manifest_return_code ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100119
David Brazdil136f2942019-09-23 14:11:03 +0100120 ret = read_string(node, property, out);
121 if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
122 string_init_empty(out);
123 ret = MANIFEST_SUCCESS;
Andrew Scull72b43c02019-09-18 13:53:45 +0100124 }
David Brazdil136f2942019-09-23 14:11:03 +0100125 return ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100126}
127
David Brazdil7a462ec2019-08-15 12:27:47 +0100128static enum manifest_return_code read_uint64(const struct fdt_node *node,
129 const char *property,
130 uint64_t *out)
131{
David Brazdilb856be62020-03-25 10:14:55 +0000132 struct memiter data;
David Brazdil7a462ec2019-08-15 12:27:47 +0100133
David Brazdilb856be62020-03-25 10:14:55 +0000134 if (!fdt_read_property(node, property, &data)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100135 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
136 }
137
David Brazdilb856be62020-03-25 10:14:55 +0000138 if (!fdt_parse_number(&data, memiter_size(&data), out)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100139 return MANIFEST_ERROR_MALFORMED_INTEGER;
140 }
141
142 return MANIFEST_SUCCESS;
143}
144
David Brazdil080ee312020-02-25 15:30:30 -0800145static enum manifest_return_code read_optional_uint64(
146 const struct fdt_node *node, const char *property,
147 uint64_t default_value, uint64_t *out)
148{
149 enum manifest_return_code ret;
150
151 ret = read_uint64(node, property, out);
152 if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
153 *out = default_value;
154 return MANIFEST_SUCCESS;
155 }
156 return ret;
157}
158
David Brazdil7a462ec2019-08-15 12:27:47 +0100159static enum manifest_return_code read_uint16(const struct fdt_node *node,
160 const char *property,
161 uint16_t *out)
162{
163 uint64_t value;
164
165 TRY(read_uint64(node, property, &value));
166
167 if (value > UINT16_MAX) {
168 return MANIFEST_ERROR_INTEGER_OVERFLOW;
169 }
170
171 *out = (uint16_t)value;
172 return MANIFEST_SUCCESS;
173}
174
Andrew Scullae9962e2019-10-03 16:51:16 +0100175struct uint32list_iter {
176 struct memiter mem_it;
177};
178
179static enum manifest_return_code read_optional_uint32list(
180 const struct fdt_node *node, const char *property,
181 struct uint32list_iter *out)
182{
David Brazdilb856be62020-03-25 10:14:55 +0000183 struct memiter data;
Andrew Scullae9962e2019-10-03 16:51:16 +0100184
David Brazdilb856be62020-03-25 10:14:55 +0000185 if (!fdt_read_property(node, property, &data)) {
Andrew Scullae9962e2019-10-03 16:51:16 +0100186 memiter_init(&out->mem_it, NULL, 0);
187 return MANIFEST_SUCCESS;
188 }
189
David Brazdilb856be62020-03-25 10:14:55 +0000190 if ((memiter_size(&data) % sizeof(uint32_t)) != 0) {
Andrew Scullae9962e2019-10-03 16:51:16 +0100191 return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
192 }
193
David Brazdilb856be62020-03-25 10:14:55 +0000194 out->mem_it = data;
Andrew Scullae9962e2019-10-03 16:51:16 +0100195 return MANIFEST_SUCCESS;
196}
197
David Brazdil74e9c3b2019-08-28 11:09:08 +0100198/**
199 * Represents the value of property whose type is a list of strings. These are
200 * encoded as one contiguous byte buffer with NULL-separated entries.
201 */
202struct stringlist_iter {
203 struct memiter mem_it;
204};
205
206static enum manifest_return_code read_stringlist(const struct fdt_node *node,
207 const char *property,
208 struct stringlist_iter *out)
209{
David Brazdilb856be62020-03-25 10:14:55 +0000210 struct memiter data;
211 const char *str;
212 size_t size;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100213
David Brazdilb856be62020-03-25 10:14:55 +0000214 if (!fdt_read_property(node, property, &data)) {
David Brazdil74e9c3b2019-08-28 11:09:08 +0100215 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
216 }
217
David Brazdilb856be62020-03-25 10:14:55 +0000218 str = memiter_base(&data);
219 size = memiter_size(&data);
220
David Brazdil74e9c3b2019-08-28 11:09:08 +0100221 /*
222 * Require that the value ends with a NULL terminator. Other NULL
223 * characters separate the string list entries.
224 */
David Brazdilb856be62020-03-25 10:14:55 +0000225 if ((size < 1) || (str[size - 1] != '\0')) {
David Brazdil74e9c3b2019-08-28 11:09:08 +0100226 return MANIFEST_ERROR_MALFORMED_STRING_LIST;
227 }
228
David Brazdilb856be62020-03-25 10:14:55 +0000229 CHECK(memiter_restrict(&data, 1));
230 out->mem_it = data;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100231 return MANIFEST_SUCCESS;
232}
233
Andrew Scullae9962e2019-10-03 16:51:16 +0100234static bool uint32list_has_next(const struct uint32list_iter *list)
235{
236 return memiter_size(&list->mem_it) > 0;
237}
238
David Brazdil5ea99462020-03-25 13:01:47 +0000239static enum manifest_return_code uint32list_get_next(
240 struct uint32list_iter *list, uint32_t *out)
Andrew Scullae9962e2019-10-03 16:51:16 +0100241{
Andrew Scullae9962e2019-10-03 16:51:16 +0100242 uint64_t num;
243
244 CHECK(uint32list_has_next(list));
David Brazdilb856be62020-03-25 10:14:55 +0000245 if (!fdt_parse_number(&list->mem_it, sizeof(uint32_t), &num)) {
Andrew Scullae9962e2019-10-03 16:51:16 +0100246 return MANIFEST_ERROR_MALFORMED_INTEGER;
247 }
248
David Brazdil5ea99462020-03-25 13:01:47 +0000249 *out = (uint32_t)num;
250 return MANIFEST_SUCCESS;
Andrew Scullae9962e2019-10-03 16:51:16 +0100251}
252
David Brazdil74e9c3b2019-08-28 11:09:08 +0100253static bool stringlist_has_next(const struct stringlist_iter *list)
254{
255 return memiter_size(&list->mem_it) > 0;
256}
257
258static void stringlist_get_next(struct stringlist_iter *list,
259 struct memiter *out)
260{
261 const char *mem_base = memiter_base(&list->mem_it);
262 size_t mem_size = memiter_size(&list->mem_it);
263 const char *null_term;
264
265 CHECK(stringlist_has_next(list));
266
267 null_term = memchr(mem_base, '\0', mem_size);
268 if (null_term == NULL) {
269 /*
270 * NULL terminator not found, this is the last entry.
271 * Set entry memiter to the entire byte range and advance list
272 * memiter to the end of the byte range.
273 */
274 memiter_init(out, mem_base, mem_size);
275 memiter_advance(&list->mem_it, mem_size);
276 } else {
277 /*
278 * Found NULL terminator. Set entry memiter to byte range
279 * [base, null) and move list memiter past the terminator.
280 */
281 size_t entry_size = null_term - mem_base;
282
283 memiter_init(out, mem_base, entry_size);
284 memiter_advance(&list->mem_it, entry_size + 1);
285 }
286}
287
288static bool stringlist_contains(const struct stringlist_iter *list,
289 const char *str)
290{
291 struct stringlist_iter it = *list;
292 struct memiter entry;
293
294 while (stringlist_has_next(&it)) {
295 stringlist_get_next(&it, &entry);
296 if (memiter_iseq(&entry, str)) {
297 return true;
298 }
299 }
300 return false;
301}
302
David Brazdilb856be62020-03-25 10:14:55 +0000303static enum manifest_return_code parse_vm(const struct fdt_node *node,
David Brazdil7a462ec2019-08-15 12:27:47 +0100304 struct manifest_vm *vm,
305 spci_vm_id_t vm_id)
306{
Andrew Scullae9962e2019-10-03 16:51:16 +0100307 struct uint32list_iter smcs;
David Brazdil5ea99462020-03-25 13:01:47 +0000308 size_t idx;
Andrew Scullae9962e2019-10-03 16:51:16 +0100309
David Brazdil136f2942019-09-23 14:11:03 +0100310 TRY(read_string(node, "debug_name", &vm->debug_name));
311 TRY(read_optional_string(node, "kernel_filename",
312 &vm->kernel_filename));
Andrew Scullae9962e2019-10-03 16:51:16 +0100313
314 TRY(read_optional_uint32list(node, "smc_whitelist", &smcs));
315 while (uint32list_has_next(&smcs) &&
316 vm->smc_whitelist.smc_count < MAX_SMCS) {
David Brazdil5ea99462020-03-25 13:01:47 +0000317 idx = vm->smc_whitelist.smc_count++;
318 TRY(uint32list_get_next(&smcs, &vm->smc_whitelist.smcs[idx]));
Andrew Scullae9962e2019-10-03 16:51:16 +0100319 }
320
321 if (uint32list_has_next(&smcs)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000322 dlog_warning("%s SMC whitelist too long.\n", vm->debug_name);
Andrew Scullae9962e2019-10-03 16:51:16 +0100323 }
324
Andrew Scullb2c3a242019-11-04 13:52:36 +0000325 TRY(read_bool(node, "smc_whitelist_permissive",
326 &vm->smc_whitelist.permissive));
Andrew Scullae9962e2019-10-03 16:51:16 +0100327
David Brazdile6f83222019-09-23 14:47:37 +0100328 if (vm_id == HF_PRIMARY_VM_ID) {
329 TRY(read_optional_string(node, "ramdisk_filename",
330 &vm->primary.ramdisk_filename));
David Brazdil080ee312020-02-25 15:30:30 -0800331 TRY(read_optional_uint64(node, "boot_address",
332 MANIFEST_INVALID_ADDRESS,
333 &vm->primary.boot_address));
David Brazdile6f83222019-09-23 14:47:37 +0100334 } else {
David Brazdil7a462ec2019-08-15 12:27:47 +0100335 TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
336 TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
337 }
338 return MANIFEST_SUCCESS;
339}
340
341/**
342 * Parse manifest from FDT.
343 */
344enum manifest_return_code manifest_init(struct manifest *manifest,
David Brazdila2358d42020-01-27 18:51:38 +0000345 struct memiter *manifest_fdt)
David Brazdil7a462ec2019-08-15 12:27:47 +0100346{
David Brazdilb856be62020-03-25 10:14:55 +0000347 struct string vm_name;
348 struct fdt fdt;
David Brazdil7a462ec2019-08-15 12:27:47 +0100349 struct fdt_node hyp_node;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100350 struct stringlist_iter compatible_list;
David Brazdil7a462ec2019-08-15 12:27:47 +0100351 size_t i = 0;
352 bool found_primary_vm = false;
353
354 memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
355
David Brazdilb856be62020-03-25 10:14:55 +0000356 if (!fdt_init_from_memiter(&fdt, manifest_fdt)) {
357 return MANIFEST_ERROR_FILE_SIZE; /* TODO */
David Brazdila2358d42020-01-27 18:51:38 +0000358 }
359
David Brazdil7a462ec2019-08-15 12:27:47 +0100360 /* Find hypervisor node. */
David Brazdilb856be62020-03-25 10:14:55 +0000361 if (!fdt_find_node(&fdt, "/hypervisor", &hyp_node)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100362 return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
363 }
364
David Brazdil74e9c3b2019-08-28 11:09:08 +0100365 /* Check "compatible" property. */
366 TRY(read_stringlist(&hyp_node, "compatible", &compatible_list));
367 if (!stringlist_contains(&compatible_list, "hafnium,hafnium")) {
368 return MANIFEST_ERROR_NOT_COMPATIBLE;
369 }
370
David Brazdil7a462ec2019-08-15 12:27:47 +0100371 /* Iterate over reserved VM IDs and check no such nodes exist. */
372 for (i = 0; i < HF_VM_ID_OFFSET; i++) {
373 spci_vm_id_t vm_id = (spci_vm_id_t)i;
374 struct fdt_node vm_node = hyp_node;
David Brazdil7a462ec2019-08-15 12:27:47 +0100375
David Brazdilb856be62020-03-25 10:14:55 +0000376 generate_vm_node_name(&vm_name, vm_id);
377 if (fdt_find_child(&vm_node, &vm_name)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100378 return MANIFEST_ERROR_RESERVED_VM_ID;
379 }
380 }
381
382 /* Iterate over VM nodes until we find one that does not exist. */
383 for (i = 0; i <= MAX_VMS; ++i) {
384 spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
385 struct fdt_node vm_node = hyp_node;
David Brazdil7a462ec2019-08-15 12:27:47 +0100386
David Brazdilb856be62020-03-25 10:14:55 +0000387 generate_vm_node_name(&vm_name, vm_id);
388 if (!fdt_find_child(&vm_node, &vm_name)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100389 break;
390 }
391
392 if (i == MAX_VMS) {
393 return MANIFEST_ERROR_TOO_MANY_VMS;
394 }
395
396 if (vm_id == HF_PRIMARY_VM_ID) {
397 CHECK(found_primary_vm == false); /* sanity check */
398 found_primary_vm = true;
399 }
400
David Brazdil0251b942019-09-10 15:59:50 +0100401 manifest->vm_count = i + 1;
David Brazdil7a462ec2019-08-15 12:27:47 +0100402 TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
403 }
404
405 if (!found_primary_vm) {
406 return MANIFEST_ERROR_NO_PRIMARY_VM;
407 }
408
409 return MANIFEST_SUCCESS;
410}
411
412const char *manifest_strerror(enum manifest_return_code ret_code)
413{
414 switch (ret_code) {
415 case MANIFEST_SUCCESS:
416 return "Success";
David Brazdila2358d42020-01-27 18:51:38 +0000417 case MANIFEST_ERROR_FILE_SIZE:
418 return "Total size in header does not match file size";
419 case MANIFEST_ERROR_NO_ROOT_NODE:
420 return "Could not find root node in manifest";
David Brazdil7a462ec2019-08-15 12:27:47 +0100421 case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
422 return "Could not find \"hypervisor\" node in manifest";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100423 case MANIFEST_ERROR_NOT_COMPATIBLE:
424 return "Hypervisor manifest entry not compatible with Hafnium";
David Brazdil7a462ec2019-08-15 12:27:47 +0100425 case MANIFEST_ERROR_RESERVED_VM_ID:
426 return "Manifest defines a VM with a reserved ID";
427 case MANIFEST_ERROR_NO_PRIMARY_VM:
428 return "Manifest does not contain a primary VM entry";
429 case MANIFEST_ERROR_TOO_MANY_VMS:
430 return "Manifest specifies more VMs than Hafnium has "
431 "statically allocated space for";
432 case MANIFEST_ERROR_PROPERTY_NOT_FOUND:
433 return "Property not found";
434 case MANIFEST_ERROR_MALFORMED_STRING:
435 return "Malformed string property";
David Brazdil0dbb41f2019-09-09 18:03:35 +0100436 case MANIFEST_ERROR_STRING_TOO_LONG:
437 return "String too long";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100438 case MANIFEST_ERROR_MALFORMED_STRING_LIST:
439 return "Malformed string list property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100440 case MANIFEST_ERROR_MALFORMED_INTEGER:
441 return "Malformed integer property";
442 case MANIFEST_ERROR_INTEGER_OVERFLOW:
443 return "Integer overflow";
Andrew Scullae9962e2019-10-03 16:51:16 +0100444 case MANIFEST_ERROR_MALFORMED_INTEGER_LIST:
445 return "Malformed integer list property";
Andrew Scullb2c3a242019-11-04 13:52:36 +0000446 case MANIFEST_ERROR_MALFORMED_BOOLEAN:
447 return "Malformed boolean property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100448 }
449
450 panic("Unexpected manifest return code.");
451}