blob: 7a8474b84eb41689467c76cd3ddb926ea61ea085 [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");
Andrew Walbran9daa57e2019-09-27 13:33:20 +010036static_assert(HF_TEE_VM_ID > MAX_VMS,
37 "TrustZone VM ID clashes with normal VM range.");
David Brazdil7a462ec2019-08-15 12:27:47 +010038
39/**
40 * Generates a string with the two letters "vm" followed by an integer.
41 * Assumes `buf` is of size VM_NAME_BUF_SIZE.
42 */
43static const char *generate_vm_node_name(char *buf, spci_vm_id_t vm_id)
44{
45 static const char *digits = "0123456789";
46 char *ptr = buf + VM_NAME_BUF_SIZE;
47
48 *(--ptr) = '\0';
49 do {
50 *(--ptr) = digits[vm_id % 10];
51 vm_id /= 10;
52 } while (vm_id);
53 *(--ptr) = 'm';
54 *(--ptr) = 'v';
55
56 return ptr;
57}
58
Andrew Scullae9962e2019-10-03 16:51:16 +010059/**
Andrew Scullb2c3a242019-11-04 13:52:36 +000060 * Read a boolean property: true if present; false if not. If present, the value
61 * of the property must be empty else it is considered malformed.
Andrew Scullae9962e2019-10-03 16:51:16 +010062 */
Andrew Scullb2c3a242019-11-04 13:52:36 +000063static enum manifest_return_code read_bool(const struct fdt_node *node,
64 const char *property, bool *out)
Andrew Scullae9962e2019-10-03 16:51:16 +010065{
66 const char *data;
67 uint32_t size;
Andrew Scullb2c3a242019-11-04 13:52:36 +000068 bool present = fdt_read_property(node, property, &data, &size);
Andrew Scullae9962e2019-10-03 16:51:16 +010069
Andrew Scullb2c3a242019-11-04 13:52:36 +000070 if (present && size != 0) {
71 return MANIFEST_ERROR_MALFORMED_BOOLEAN;
72 }
73
74 *out = present;
75 return MANIFEST_SUCCESS;
Andrew Scullae9962e2019-10-03 16:51:16 +010076}
77
Andrew Scull72b43c02019-09-18 13:53:45 +010078static enum manifest_return_code read_string(const struct fdt_node *node,
David Brazdil136f2942019-09-23 14:11:03 +010079 const char *property,
80 struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +010081{
82 const char *data;
83 uint32_t size;
84
85 if (!fdt_read_property(node, property, &data, &size)) {
86 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
87 }
88
David Brazdil136f2942019-09-23 14:11:03 +010089 switch (string_init(out, data, size)) {
90 case STRING_SUCCESS:
91 return MANIFEST_SUCCESS;
92 case STRING_ERROR_INVALID_INPUT:
93 return MANIFEST_ERROR_MALFORMED_STRING;
94 case STRING_ERROR_TOO_LONG:
95 return MANIFEST_ERROR_STRING_TOO_LONG;
96 }
Andrew Scull72b43c02019-09-18 13:53:45 +010097}
98
99static enum manifest_return_code read_optional_string(
David Brazdil136f2942019-09-23 14:11:03 +0100100 const struct fdt_node *node, const char *property, struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +0100101{
David Brazdil136f2942019-09-23 14:11:03 +0100102 enum manifest_return_code ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100103
David Brazdil136f2942019-09-23 14:11:03 +0100104 ret = read_string(node, property, out);
105 if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
106 string_init_empty(out);
107 ret = MANIFEST_SUCCESS;
Andrew Scull72b43c02019-09-18 13:53:45 +0100108 }
David Brazdil136f2942019-09-23 14:11:03 +0100109 return ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100110}
111
David Brazdil7a462ec2019-08-15 12:27:47 +0100112static enum manifest_return_code read_uint64(const struct fdt_node *node,
113 const char *property,
114 uint64_t *out)
115{
116 const char *data;
117 uint32_t size;
118
119 if (!fdt_read_property(node, property, &data, &size)) {
120 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
121 }
122
123 if (!fdt_parse_number(data, size, out)) {
124 return MANIFEST_ERROR_MALFORMED_INTEGER;
125 }
126
127 return MANIFEST_SUCCESS;
128}
129
David Brazdil080ee312020-02-25 15:30:30 -0800130static enum manifest_return_code read_optional_uint64(
131 const struct fdt_node *node, const char *property,
132 uint64_t default_value, uint64_t *out)
133{
134 enum manifest_return_code ret;
135
136 ret = read_uint64(node, property, out);
137 if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
138 *out = default_value;
139 return MANIFEST_SUCCESS;
140 }
141 return ret;
142}
143
David Brazdil7a462ec2019-08-15 12:27:47 +0100144static enum manifest_return_code read_uint16(const struct fdt_node *node,
145 const char *property,
146 uint16_t *out)
147{
148 uint64_t value;
149
150 TRY(read_uint64(node, property, &value));
151
152 if (value > UINT16_MAX) {
153 return MANIFEST_ERROR_INTEGER_OVERFLOW;
154 }
155
156 *out = (uint16_t)value;
157 return MANIFEST_SUCCESS;
158}
159
Andrew Scullae9962e2019-10-03 16:51:16 +0100160struct uint32list_iter {
161 struct memiter mem_it;
162};
163
164static enum manifest_return_code read_optional_uint32list(
165 const struct fdt_node *node, const char *property,
166 struct uint32list_iter *out)
167{
168 const char *data;
169 uint32_t size;
170
171 if (!fdt_read_property(node, property, &data, &size)) {
172 memiter_init(&out->mem_it, NULL, 0);
173 return MANIFEST_SUCCESS;
174 }
175
176 if ((size % sizeof(uint32_t)) != 0) {
177 return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
178 }
179
180 memiter_init(&out->mem_it, data, size);
181 return MANIFEST_SUCCESS;
182}
183
David Brazdil74e9c3b2019-08-28 11:09:08 +0100184/**
185 * Represents the value of property whose type is a list of strings. These are
186 * encoded as one contiguous byte buffer with NULL-separated entries.
187 */
188struct stringlist_iter {
189 struct memiter mem_it;
190};
191
192static enum manifest_return_code read_stringlist(const struct fdt_node *node,
193 const char *property,
194 struct stringlist_iter *out)
195{
196 const char *data;
197 uint32_t size;
198
199 if (!fdt_read_property(node, property, &data, &size)) {
200 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
201 }
202
203 /*
204 * Require that the value ends with a NULL terminator. Other NULL
205 * characters separate the string list entries.
206 */
207 if (data[size - 1] != '\0') {
208 return MANIFEST_ERROR_MALFORMED_STRING_LIST;
209 }
210
211 memiter_init(&out->mem_it, data, size - 1);
212 return MANIFEST_SUCCESS;
213}
214
Andrew Scullae9962e2019-10-03 16:51:16 +0100215static bool uint32list_has_next(const struct uint32list_iter *list)
216{
217 return memiter_size(&list->mem_it) > 0;
218}
219
David Brazdil5ea99462020-03-25 13:01:47 +0000220static enum manifest_return_code uint32list_get_next(
221 struct uint32list_iter *list, uint32_t *out)
Andrew Scullae9962e2019-10-03 16:51:16 +0100222{
223 const char *mem_base = memiter_base(&list->mem_it);
224 uint64_t num;
225
226 CHECK(uint32list_has_next(list));
227
228 if (!fdt_parse_number(mem_base, sizeof(uint32_t), &num)) {
229 return MANIFEST_ERROR_MALFORMED_INTEGER;
230 }
231
232 memiter_advance(&list->mem_it, sizeof(uint32_t));
David Brazdil5ea99462020-03-25 13:01:47 +0000233 *out = (uint32_t)num;
234 return MANIFEST_SUCCESS;
Andrew Scullae9962e2019-10-03 16:51:16 +0100235}
236
David Brazdil74e9c3b2019-08-28 11:09:08 +0100237static bool stringlist_has_next(const struct stringlist_iter *list)
238{
239 return memiter_size(&list->mem_it) > 0;
240}
241
242static void stringlist_get_next(struct stringlist_iter *list,
243 struct memiter *out)
244{
245 const char *mem_base = memiter_base(&list->mem_it);
246 size_t mem_size = memiter_size(&list->mem_it);
247 const char *null_term;
248
249 CHECK(stringlist_has_next(list));
250
251 null_term = memchr(mem_base, '\0', mem_size);
252 if (null_term == NULL) {
253 /*
254 * NULL terminator not found, this is the last entry.
255 * Set entry memiter to the entire byte range and advance list
256 * memiter to the end of the byte range.
257 */
258 memiter_init(out, mem_base, mem_size);
259 memiter_advance(&list->mem_it, mem_size);
260 } else {
261 /*
262 * Found NULL terminator. Set entry memiter to byte range
263 * [base, null) and move list memiter past the terminator.
264 */
265 size_t entry_size = null_term - mem_base;
266
267 memiter_init(out, mem_base, entry_size);
268 memiter_advance(&list->mem_it, entry_size + 1);
269 }
270}
271
272static bool stringlist_contains(const struct stringlist_iter *list,
273 const char *str)
274{
275 struct stringlist_iter it = *list;
276 struct memiter entry;
277
278 while (stringlist_has_next(&it)) {
279 stringlist_get_next(&it, &entry);
280 if (memiter_iseq(&entry, str)) {
281 return true;
282 }
283 }
284 return false;
285}
286
David Brazdil7a462ec2019-08-15 12:27:47 +0100287static enum manifest_return_code parse_vm(struct fdt_node *node,
288 struct manifest_vm *vm,
289 spci_vm_id_t vm_id)
290{
Andrew Scullae9962e2019-10-03 16:51:16 +0100291 struct uint32list_iter smcs;
David Brazdil5ea99462020-03-25 13:01:47 +0000292 size_t idx;
Andrew Scullae9962e2019-10-03 16:51:16 +0100293
David Brazdil136f2942019-09-23 14:11:03 +0100294 TRY(read_string(node, "debug_name", &vm->debug_name));
295 TRY(read_optional_string(node, "kernel_filename",
296 &vm->kernel_filename));
Andrew Scullae9962e2019-10-03 16:51:16 +0100297
298 TRY(read_optional_uint32list(node, "smc_whitelist", &smcs));
299 while (uint32list_has_next(&smcs) &&
300 vm->smc_whitelist.smc_count < MAX_SMCS) {
David Brazdil5ea99462020-03-25 13:01:47 +0000301 idx = vm->smc_whitelist.smc_count++;
302 TRY(uint32list_get_next(&smcs, &vm->smc_whitelist.smcs[idx]));
Andrew Scullae9962e2019-10-03 16:51:16 +0100303 }
304
305 if (uint32list_has_next(&smcs)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000306 dlog_warning("%s SMC whitelist too long.\n", vm->debug_name);
Andrew Scullae9962e2019-10-03 16:51:16 +0100307 }
308
Andrew Scullb2c3a242019-11-04 13:52:36 +0000309 TRY(read_bool(node, "smc_whitelist_permissive",
310 &vm->smc_whitelist.permissive));
Andrew Scullae9962e2019-10-03 16:51:16 +0100311
David Brazdile6f83222019-09-23 14:47:37 +0100312 if (vm_id == HF_PRIMARY_VM_ID) {
313 TRY(read_optional_string(node, "ramdisk_filename",
314 &vm->primary.ramdisk_filename));
David Brazdil080ee312020-02-25 15:30:30 -0800315 TRY(read_optional_uint64(node, "boot_address",
316 MANIFEST_INVALID_ADDRESS,
317 &vm->primary.boot_address));
David Brazdile6f83222019-09-23 14:47:37 +0100318 } else {
David Brazdil7a462ec2019-08-15 12:27:47 +0100319 TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
320 TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
321 }
322 return MANIFEST_SUCCESS;
323}
324
325/**
326 * Parse manifest from FDT.
327 */
328enum manifest_return_code manifest_init(struct manifest *manifest,
David Brazdila2358d42020-01-27 18:51:38 +0000329 struct memiter *manifest_fdt)
David Brazdil7a462ec2019-08-15 12:27:47 +0100330{
331 char vm_name_buf[VM_NAME_BUF_SIZE];
David Brazdila2358d42020-01-27 18:51:38 +0000332 const struct fdt_header *fdt;
David Brazdil7a462ec2019-08-15 12:27:47 +0100333 struct fdt_node hyp_node;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100334 struct stringlist_iter compatible_list;
David Brazdil7a462ec2019-08-15 12:27:47 +0100335 size_t i = 0;
336 bool found_primary_vm = false;
337
338 memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
339
David Brazdila2358d42020-01-27 18:51:38 +0000340 fdt = (const struct fdt_header *)memiter_base(manifest_fdt);
341 if (memiter_size(manifest_fdt) != fdt_total_size(fdt)) {
342 return MANIFEST_ERROR_FILE_SIZE;
343 }
344
David Brazdil7a462ec2019-08-15 12:27:47 +0100345 /* Find hypervisor node. */
David Brazdila2358d42020-01-27 18:51:38 +0000346 if (!fdt_root_node(&hyp_node, fdt)) {
347 return MANIFEST_ERROR_NO_ROOT_NODE;
348 }
349 if (!fdt_find_child(&hyp_node, "")) {
350 return MANIFEST_ERROR_NO_ROOT_NODE;
351 }
David Brazdil7a462ec2019-08-15 12:27:47 +0100352 if (!fdt_find_child(&hyp_node, "hypervisor")) {
353 return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
354 }
355
David Brazdil74e9c3b2019-08-28 11:09:08 +0100356 /* Check "compatible" property. */
357 TRY(read_stringlist(&hyp_node, "compatible", &compatible_list));
358 if (!stringlist_contains(&compatible_list, "hafnium,hafnium")) {
359 return MANIFEST_ERROR_NOT_COMPATIBLE;
360 }
361
David Brazdil7a462ec2019-08-15 12:27:47 +0100362 /* Iterate over reserved VM IDs and check no such nodes exist. */
363 for (i = 0; i < HF_VM_ID_OFFSET; i++) {
364 spci_vm_id_t vm_id = (spci_vm_id_t)i;
365 struct fdt_node vm_node = hyp_node;
366 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
367
368 if (fdt_find_child(&vm_node, vm_name)) {
369 return MANIFEST_ERROR_RESERVED_VM_ID;
370 }
371 }
372
373 /* Iterate over VM nodes until we find one that does not exist. */
374 for (i = 0; i <= MAX_VMS; ++i) {
375 spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
376 struct fdt_node vm_node = hyp_node;
377 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
378
379 if (!fdt_find_child(&vm_node, vm_name)) {
380 break;
381 }
382
383 if (i == MAX_VMS) {
384 return MANIFEST_ERROR_TOO_MANY_VMS;
385 }
386
387 if (vm_id == HF_PRIMARY_VM_ID) {
388 CHECK(found_primary_vm == false); /* sanity check */
389 found_primary_vm = true;
390 }
391
David Brazdil0251b942019-09-10 15:59:50 +0100392 manifest->vm_count = i + 1;
David Brazdil7a462ec2019-08-15 12:27:47 +0100393 TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
394 }
395
396 if (!found_primary_vm) {
397 return MANIFEST_ERROR_NO_PRIMARY_VM;
398 }
399
400 return MANIFEST_SUCCESS;
401}
402
403const char *manifest_strerror(enum manifest_return_code ret_code)
404{
405 switch (ret_code) {
406 case MANIFEST_SUCCESS:
407 return "Success";
David Brazdila2358d42020-01-27 18:51:38 +0000408 case MANIFEST_ERROR_FILE_SIZE:
409 return "Total size in header does not match file size";
410 case MANIFEST_ERROR_NO_ROOT_NODE:
411 return "Could not find root node in manifest";
David Brazdil7a462ec2019-08-15 12:27:47 +0100412 case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
413 return "Could not find \"hypervisor\" node in manifest";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100414 case MANIFEST_ERROR_NOT_COMPATIBLE:
415 return "Hypervisor manifest entry not compatible with Hafnium";
David Brazdil7a462ec2019-08-15 12:27:47 +0100416 case MANIFEST_ERROR_RESERVED_VM_ID:
417 return "Manifest defines a VM with a reserved ID";
418 case MANIFEST_ERROR_NO_PRIMARY_VM:
419 return "Manifest does not contain a primary VM entry";
420 case MANIFEST_ERROR_TOO_MANY_VMS:
421 return "Manifest specifies more VMs than Hafnium has "
422 "statically allocated space for";
423 case MANIFEST_ERROR_PROPERTY_NOT_FOUND:
424 return "Property not found";
425 case MANIFEST_ERROR_MALFORMED_STRING:
426 return "Malformed string property";
David Brazdil0dbb41f2019-09-09 18:03:35 +0100427 case MANIFEST_ERROR_STRING_TOO_LONG:
428 return "String too long";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100429 case MANIFEST_ERROR_MALFORMED_STRING_LIST:
430 return "Malformed string list property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100431 case MANIFEST_ERROR_MALFORMED_INTEGER:
432 return "Malformed integer property";
433 case MANIFEST_ERROR_INTEGER_OVERFLOW:
434 return "Integer overflow";
Andrew Scullae9962e2019-10-03 16:51:16 +0100435 case MANIFEST_ERROR_MALFORMED_INTEGER_LIST:
436 return "Malformed integer list property";
Andrew Scullb2c3a242019-11-04 13:52:36 +0000437 case MANIFEST_ERROR_MALFORMED_BOOLEAN:
438 return "Malformed boolean property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100439 }
440
441 panic("Unexpected manifest return code.");
442}