blob: 1933d10cc2b9160f0ebbd41e3c2538b1d98dfdb1 [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/**
58 * Read a boolean property: true if present; false if not. The value of the
59 * property is ignored.
60 *
61 * This is the convention used by Linux but beware of things like the following
62 * that will actually be considered as `true`.
63 *
64 * true-property0 = <0>;
65 * true-property1 = "false";
66 */
67static bool read_bool(const struct fdt_node *node, const char *property)
68{
69 const char *data;
70 uint32_t size;
71
72 return fdt_read_property(node, property, &data, &size);
73}
74
Andrew Scull72b43c02019-09-18 13:53:45 +010075static enum manifest_return_code read_string(const struct fdt_node *node,
David Brazdil136f2942019-09-23 14:11:03 +010076 const char *property,
77 struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +010078{
79 const char *data;
80 uint32_t size;
81
82 if (!fdt_read_property(node, property, &data, &size)) {
83 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
84 }
85
David Brazdil136f2942019-09-23 14:11:03 +010086 switch (string_init(out, data, size)) {
87 case STRING_SUCCESS:
88 return MANIFEST_SUCCESS;
89 case STRING_ERROR_INVALID_INPUT:
90 return MANIFEST_ERROR_MALFORMED_STRING;
91 case STRING_ERROR_TOO_LONG:
92 return MANIFEST_ERROR_STRING_TOO_LONG;
93 }
Andrew Scull72b43c02019-09-18 13:53:45 +010094}
95
96static enum manifest_return_code read_optional_string(
David Brazdil136f2942019-09-23 14:11:03 +010097 const struct fdt_node *node, const char *property, struct string *out)
Andrew Scull72b43c02019-09-18 13:53:45 +010098{
David Brazdil136f2942019-09-23 14:11:03 +010099 enum manifest_return_code ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100100
David Brazdil136f2942019-09-23 14:11:03 +0100101 ret = read_string(node, property, out);
102 if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
103 string_init_empty(out);
104 ret = MANIFEST_SUCCESS;
Andrew Scull72b43c02019-09-18 13:53:45 +0100105 }
David Brazdil136f2942019-09-23 14:11:03 +0100106 return ret;
Andrew Scull72b43c02019-09-18 13:53:45 +0100107}
108
David Brazdil7a462ec2019-08-15 12:27:47 +0100109static enum manifest_return_code read_uint64(const struct fdt_node *node,
110 const char *property,
111 uint64_t *out)
112{
113 const char *data;
114 uint32_t size;
115
116 if (!fdt_read_property(node, property, &data, &size)) {
117 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
118 }
119
120 if (!fdt_parse_number(data, size, out)) {
121 return MANIFEST_ERROR_MALFORMED_INTEGER;
122 }
123
124 return MANIFEST_SUCCESS;
125}
126
127static enum manifest_return_code read_uint16(const struct fdt_node *node,
128 const char *property,
129 uint16_t *out)
130{
131 uint64_t value;
132
133 TRY(read_uint64(node, property, &value));
134
135 if (value > UINT16_MAX) {
136 return MANIFEST_ERROR_INTEGER_OVERFLOW;
137 }
138
139 *out = (uint16_t)value;
140 return MANIFEST_SUCCESS;
141}
142
Andrew Scullae9962e2019-10-03 16:51:16 +0100143struct uint32list_iter {
144 struct memiter mem_it;
145};
146
147static enum manifest_return_code read_optional_uint32list(
148 const struct fdt_node *node, const char *property,
149 struct uint32list_iter *out)
150{
151 const char *data;
152 uint32_t size;
153
154 if (!fdt_read_property(node, property, &data, &size)) {
155 memiter_init(&out->mem_it, NULL, 0);
156 return MANIFEST_SUCCESS;
157 }
158
159 if ((size % sizeof(uint32_t)) != 0) {
160 return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
161 }
162
163 memiter_init(&out->mem_it, data, size);
164 return MANIFEST_SUCCESS;
165}
166
David Brazdil74e9c3b2019-08-28 11:09:08 +0100167/**
168 * Represents the value of property whose type is a list of strings. These are
169 * encoded as one contiguous byte buffer with NULL-separated entries.
170 */
171struct stringlist_iter {
172 struct memiter mem_it;
173};
174
175static enum manifest_return_code read_stringlist(const struct fdt_node *node,
176 const char *property,
177 struct stringlist_iter *out)
178{
179 const char *data;
180 uint32_t size;
181
182 if (!fdt_read_property(node, property, &data, &size)) {
183 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
184 }
185
186 /*
187 * Require that the value ends with a NULL terminator. Other NULL
188 * characters separate the string list entries.
189 */
190 if (data[size - 1] != '\0') {
191 return MANIFEST_ERROR_MALFORMED_STRING_LIST;
192 }
193
194 memiter_init(&out->mem_it, data, size - 1);
195 return MANIFEST_SUCCESS;
196}
197
Andrew Scullae9962e2019-10-03 16:51:16 +0100198static bool uint32list_has_next(const struct uint32list_iter *list)
199{
200 return memiter_size(&list->mem_it) > 0;
201}
202
203static uint32_t uint32list_get_next(struct uint32list_iter *list)
204{
205 const char *mem_base = memiter_base(&list->mem_it);
206 uint64_t num;
207
208 CHECK(uint32list_has_next(list));
209
210 if (!fdt_parse_number(mem_base, sizeof(uint32_t), &num)) {
211 return MANIFEST_ERROR_MALFORMED_INTEGER;
212 }
213
214 memiter_advance(&list->mem_it, sizeof(uint32_t));
215 return num;
216}
217
David Brazdil74e9c3b2019-08-28 11:09:08 +0100218static bool stringlist_has_next(const struct stringlist_iter *list)
219{
220 return memiter_size(&list->mem_it) > 0;
221}
222
223static void stringlist_get_next(struct stringlist_iter *list,
224 struct memiter *out)
225{
226 const char *mem_base = memiter_base(&list->mem_it);
227 size_t mem_size = memiter_size(&list->mem_it);
228 const char *null_term;
229
230 CHECK(stringlist_has_next(list));
231
232 null_term = memchr(mem_base, '\0', mem_size);
233 if (null_term == NULL) {
234 /*
235 * NULL terminator not found, this is the last entry.
236 * Set entry memiter to the entire byte range and advance list
237 * memiter to the end of the byte range.
238 */
239 memiter_init(out, mem_base, mem_size);
240 memiter_advance(&list->mem_it, mem_size);
241 } else {
242 /*
243 * Found NULL terminator. Set entry memiter to byte range
244 * [base, null) and move list memiter past the terminator.
245 */
246 size_t entry_size = null_term - mem_base;
247
248 memiter_init(out, mem_base, entry_size);
249 memiter_advance(&list->mem_it, entry_size + 1);
250 }
251}
252
253static bool stringlist_contains(const struct stringlist_iter *list,
254 const char *str)
255{
256 struct stringlist_iter it = *list;
257 struct memiter entry;
258
259 while (stringlist_has_next(&it)) {
260 stringlist_get_next(&it, &entry);
261 if (memiter_iseq(&entry, str)) {
262 return true;
263 }
264 }
265 return false;
266}
267
David Brazdil7a462ec2019-08-15 12:27:47 +0100268static enum manifest_return_code parse_vm(struct fdt_node *node,
269 struct manifest_vm *vm,
270 spci_vm_id_t vm_id)
271{
Andrew Scullae9962e2019-10-03 16:51:16 +0100272 struct uint32list_iter smcs;
273
David Brazdil136f2942019-09-23 14:11:03 +0100274 TRY(read_string(node, "debug_name", &vm->debug_name));
275 TRY(read_optional_string(node, "kernel_filename",
276 &vm->kernel_filename));
Andrew Scullae9962e2019-10-03 16:51:16 +0100277
278 TRY(read_optional_uint32list(node, "smc_whitelist", &smcs));
279 while (uint32list_has_next(&smcs) &&
280 vm->smc_whitelist.smc_count < MAX_SMCS) {
281 vm->smc_whitelist.smcs[vm->smc_whitelist.smc_count++] =
282 uint32list_get_next(&smcs);
283 }
284
285 if (uint32list_has_next(&smcs)) {
286 dlog("%s SMC whitelist too long.\n", vm->debug_name);
287 }
288
289 vm->smc_whitelist.permissive =
290 read_bool(node, "smc_whitelist_permissive");
291
David Brazdile6f83222019-09-23 14:47:37 +0100292 if (vm_id == HF_PRIMARY_VM_ID) {
293 TRY(read_optional_string(node, "ramdisk_filename",
294 &vm->primary.ramdisk_filename));
295 } else {
David Brazdil7a462ec2019-08-15 12:27:47 +0100296 TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
297 TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
298 }
299 return MANIFEST_SUCCESS;
300}
301
302/**
303 * Parse manifest from FDT.
304 */
305enum manifest_return_code manifest_init(struct manifest *manifest,
David Brazdil0dbb41f2019-09-09 18:03:35 +0100306 const struct fdt_node *fdt_root)
David Brazdil7a462ec2019-08-15 12:27:47 +0100307{
308 char vm_name_buf[VM_NAME_BUF_SIZE];
309 struct fdt_node hyp_node;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100310 struct stringlist_iter compatible_list;
David Brazdil7a462ec2019-08-15 12:27:47 +0100311 size_t i = 0;
312 bool found_primary_vm = false;
313
314 memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
315
316 /* Find hypervisor node. */
David Brazdil0dbb41f2019-09-09 18:03:35 +0100317 hyp_node = *fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100318 if (!fdt_find_child(&hyp_node, "hypervisor")) {
319 return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
320 }
321
David Brazdil74e9c3b2019-08-28 11:09:08 +0100322 /* Check "compatible" property. */
323 TRY(read_stringlist(&hyp_node, "compatible", &compatible_list));
324 if (!stringlist_contains(&compatible_list, "hafnium,hafnium")) {
325 return MANIFEST_ERROR_NOT_COMPATIBLE;
326 }
327
David Brazdil7a462ec2019-08-15 12:27:47 +0100328 /* Iterate over reserved VM IDs and check no such nodes exist. */
329 for (i = 0; i < HF_VM_ID_OFFSET; i++) {
330 spci_vm_id_t vm_id = (spci_vm_id_t)i;
331 struct fdt_node vm_node = hyp_node;
332 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
333
334 if (fdt_find_child(&vm_node, vm_name)) {
335 return MANIFEST_ERROR_RESERVED_VM_ID;
336 }
337 }
338
339 /* Iterate over VM nodes until we find one that does not exist. */
340 for (i = 0; i <= MAX_VMS; ++i) {
341 spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
342 struct fdt_node vm_node = hyp_node;
343 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
344
345 if (!fdt_find_child(&vm_node, vm_name)) {
346 break;
347 }
348
349 if (i == MAX_VMS) {
350 return MANIFEST_ERROR_TOO_MANY_VMS;
351 }
352
353 if (vm_id == HF_PRIMARY_VM_ID) {
354 CHECK(found_primary_vm == false); /* sanity check */
355 found_primary_vm = true;
356 }
357
David Brazdil0251b942019-09-10 15:59:50 +0100358 manifest->vm_count = i + 1;
David Brazdil7a462ec2019-08-15 12:27:47 +0100359 TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
360 }
361
362 if (!found_primary_vm) {
363 return MANIFEST_ERROR_NO_PRIMARY_VM;
364 }
365
366 return MANIFEST_SUCCESS;
367}
368
369const char *manifest_strerror(enum manifest_return_code ret_code)
370{
371 switch (ret_code) {
372 case MANIFEST_SUCCESS:
373 return "Success";
David Brazdil7a462ec2019-08-15 12:27:47 +0100374 case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
375 return "Could not find \"hypervisor\" node in manifest";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100376 case MANIFEST_ERROR_NOT_COMPATIBLE:
377 return "Hypervisor manifest entry not compatible with Hafnium";
David Brazdil7a462ec2019-08-15 12:27:47 +0100378 case MANIFEST_ERROR_RESERVED_VM_ID:
379 return "Manifest defines a VM with a reserved ID";
380 case MANIFEST_ERROR_NO_PRIMARY_VM:
381 return "Manifest does not contain a primary VM entry";
382 case MANIFEST_ERROR_TOO_MANY_VMS:
383 return "Manifest specifies more VMs than Hafnium has "
384 "statically allocated space for";
385 case MANIFEST_ERROR_PROPERTY_NOT_FOUND:
386 return "Property not found";
387 case MANIFEST_ERROR_MALFORMED_STRING:
388 return "Malformed string property";
David Brazdil0dbb41f2019-09-09 18:03:35 +0100389 case MANIFEST_ERROR_STRING_TOO_LONG:
390 return "String too long";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100391 case MANIFEST_ERROR_MALFORMED_STRING_LIST:
392 return "Malformed string list property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100393 case MANIFEST_ERROR_MALFORMED_INTEGER:
394 return "Malformed integer property";
395 case MANIFEST_ERROR_INTEGER_OVERFLOW:
396 return "Integer overflow";
Andrew Scullae9962e2019-10-03 16:51:16 +0100397 case MANIFEST_ERROR_MALFORMED_INTEGER_LIST:
398 return "Malformed integer list property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100399 }
400
401 panic("Unexpected manifest return code.");
402}