blob: 80382ee0018c29731792d8f398ca8a4bcf807d32 [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"
21#include "hf/fdt.h"
22#include "hf/static_assert.h"
23#include "hf/std.h"
24
25#define TRY(expr) \
26 do { \
27 enum manifest_return_code ret_code = (expr); \
28 if (ret_code != MANIFEST_SUCCESS) { \
29 return ret_code; \
30 } \
31 } while (0)
32
33#define VM_NAME_BUF_SIZE (2 + 5 + 1) /* "vm" + number + null terminator */
34static_assert(MAX_VMS <= 99999, "Insufficient VM_NAME_BUF_SIZE");
35
36/**
37 * Generates a string with the two letters "vm" followed by an integer.
38 * Assumes `buf` is of size VM_NAME_BUF_SIZE.
39 */
40static const char *generate_vm_node_name(char *buf, spci_vm_id_t vm_id)
41{
42 static const char *digits = "0123456789";
43 char *ptr = buf + VM_NAME_BUF_SIZE;
44
45 *(--ptr) = '\0';
46 do {
47 *(--ptr) = digits[vm_id % 10];
48 vm_id /= 10;
49 } while (vm_id);
50 *(--ptr) = 'm';
51 *(--ptr) = 'v';
52
53 return ptr;
54}
55
Andrew Scull72b43c02019-09-18 13:53:45 +010056static enum manifest_return_code extract_string(const char *data, uint32_t size,
57 char *out, rsize_t out_sz)
David Brazdil7a462ec2019-08-15 12:27:47 +010058{
David Brazdil74e9c3b2019-08-28 11:09:08 +010059 /*
60 * Require that the value contains exactly one NULL character and that
61 * it is the last byte.
62 */
63 if (memchr(data, '\0', size) != &data[size - 1]) {
David Brazdil7a462ec2019-08-15 12:27:47 +010064 return MANIFEST_ERROR_MALFORMED_STRING;
65 }
66
David Brazdil0dbb41f2019-09-09 18:03:35 +010067 /* Check that the string fits into the buffer. */
68 if (size > out_sz) {
69 return MANIFEST_ERROR_STRING_TOO_LONG;
70 }
71
72 memcpy_s(out, out_sz, data, size);
David Brazdil7a462ec2019-08-15 12:27:47 +010073 return MANIFEST_SUCCESS;
74}
75
Andrew Scull72b43c02019-09-18 13:53:45 +010076static enum manifest_return_code read_string(const struct fdt_node *node,
77 const char *property, char *out,
78 rsize_t out_sz)
79{
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
87 return extract_string(data, size, out, out_sz);
88}
89
90static enum manifest_return_code read_optional_string(
91 const struct fdt_node *node, const char *property, char *out,
92 rsize_t out_sz)
93{
94 const char *data;
95 uint32_t size;
96
97 if (!fdt_read_property(node, property, &data, &size)) {
98 if (out_sz < 1) {
99 return MANIFEST_ERROR_STRING_TOO_LONG;
100 }
101
102 *out = '\0';
103 return MANIFEST_SUCCESS;
104 }
105
106 return extract_string(data, size, out, out_sz);
107}
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
David Brazdil74e9c3b2019-08-28 11:09:08 +0100143/**
144 * Represents the value of property whose type is a list of strings. These are
145 * encoded as one contiguous byte buffer with NULL-separated entries.
146 */
147struct stringlist_iter {
148 struct memiter mem_it;
149};
150
151static enum manifest_return_code read_stringlist(const struct fdt_node *node,
152 const char *property,
153 struct stringlist_iter *out)
154{
155 const char *data;
156 uint32_t size;
157
158 if (!fdt_read_property(node, property, &data, &size)) {
159 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
160 }
161
162 /*
163 * Require that the value ends with a NULL terminator. Other NULL
164 * characters separate the string list entries.
165 */
166 if (data[size - 1] != '\0') {
167 return MANIFEST_ERROR_MALFORMED_STRING_LIST;
168 }
169
170 memiter_init(&out->mem_it, data, size - 1);
171 return MANIFEST_SUCCESS;
172}
173
174static bool stringlist_has_next(const struct stringlist_iter *list)
175{
176 return memiter_size(&list->mem_it) > 0;
177}
178
179static void stringlist_get_next(struct stringlist_iter *list,
180 struct memiter *out)
181{
182 const char *mem_base = memiter_base(&list->mem_it);
183 size_t mem_size = memiter_size(&list->mem_it);
184 const char *null_term;
185
186 CHECK(stringlist_has_next(list));
187
188 null_term = memchr(mem_base, '\0', mem_size);
189 if (null_term == NULL) {
190 /*
191 * NULL terminator not found, this is the last entry.
192 * Set entry memiter to the entire byte range and advance list
193 * memiter to the end of the byte range.
194 */
195 memiter_init(out, mem_base, mem_size);
196 memiter_advance(&list->mem_it, mem_size);
197 } else {
198 /*
199 * Found NULL terminator. Set entry memiter to byte range
200 * [base, null) and move list memiter past the terminator.
201 */
202 size_t entry_size = null_term - mem_base;
203
204 memiter_init(out, mem_base, entry_size);
205 memiter_advance(&list->mem_it, entry_size + 1);
206 }
207}
208
209static bool stringlist_contains(const struct stringlist_iter *list,
210 const char *str)
211{
212 struct stringlist_iter it = *list;
213 struct memiter entry;
214
215 while (stringlist_has_next(&it)) {
216 stringlist_get_next(&it, &entry);
217 if (memiter_iseq(&entry, str)) {
218 return true;
219 }
220 }
221 return false;
222}
223
David Brazdil7a462ec2019-08-15 12:27:47 +0100224static enum manifest_return_code parse_vm(struct fdt_node *node,
225 struct manifest_vm *vm,
226 spci_vm_id_t vm_id)
227{
David Brazdil0dbb41f2019-09-09 18:03:35 +0100228 TRY(read_string(node, "debug_name", vm->debug_name,
229 sizeof(vm->debug_name)));
Andrew Scull72b43c02019-09-18 13:53:45 +0100230 TRY(read_optional_string(node, "kernel_filename", vm->kernel_filename,
231 sizeof(vm->kernel_filename)));
David Brazdil7a462ec2019-08-15 12:27:47 +0100232 if (vm_id != HF_PRIMARY_VM_ID) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100233 TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
234 TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
235 }
236 return MANIFEST_SUCCESS;
237}
238
239/**
240 * Parse manifest from FDT.
241 */
242enum manifest_return_code manifest_init(struct manifest *manifest,
David Brazdil0dbb41f2019-09-09 18:03:35 +0100243 const struct fdt_node *fdt_root)
David Brazdil7a462ec2019-08-15 12:27:47 +0100244{
245 char vm_name_buf[VM_NAME_BUF_SIZE];
246 struct fdt_node hyp_node;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100247 struct stringlist_iter compatible_list;
David Brazdil7a462ec2019-08-15 12:27:47 +0100248 size_t i = 0;
249 bool found_primary_vm = false;
250
251 memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
252
253 /* Find hypervisor node. */
David Brazdil0dbb41f2019-09-09 18:03:35 +0100254 hyp_node = *fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100255 if (!fdt_find_child(&hyp_node, "hypervisor")) {
256 return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
257 }
258
David Brazdil74e9c3b2019-08-28 11:09:08 +0100259 /* Check "compatible" property. */
260 TRY(read_stringlist(&hyp_node, "compatible", &compatible_list));
261 if (!stringlist_contains(&compatible_list, "hafnium,hafnium")) {
262 return MANIFEST_ERROR_NOT_COMPATIBLE;
263 }
264
David Brazdil7a462ec2019-08-15 12:27:47 +0100265 /* Iterate over reserved VM IDs and check no such nodes exist. */
266 for (i = 0; i < HF_VM_ID_OFFSET; i++) {
267 spci_vm_id_t vm_id = (spci_vm_id_t)i;
268 struct fdt_node vm_node = hyp_node;
269 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
270
271 if (fdt_find_child(&vm_node, vm_name)) {
272 return MANIFEST_ERROR_RESERVED_VM_ID;
273 }
274 }
275
276 /* Iterate over VM nodes until we find one that does not exist. */
277 for (i = 0; i <= MAX_VMS; ++i) {
278 spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
279 struct fdt_node vm_node = hyp_node;
280 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
281
282 if (!fdt_find_child(&vm_node, vm_name)) {
283 break;
284 }
285
286 if (i == MAX_VMS) {
287 return MANIFEST_ERROR_TOO_MANY_VMS;
288 }
289
290 if (vm_id == HF_PRIMARY_VM_ID) {
291 CHECK(found_primary_vm == false); /* sanity check */
292 found_primary_vm = true;
293 }
294
David Brazdil0251b942019-09-10 15:59:50 +0100295 manifest->vm_count = i + 1;
David Brazdil7a462ec2019-08-15 12:27:47 +0100296 TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
297 }
298
299 if (!found_primary_vm) {
300 return MANIFEST_ERROR_NO_PRIMARY_VM;
301 }
302
303 return MANIFEST_SUCCESS;
304}
305
306const char *manifest_strerror(enum manifest_return_code ret_code)
307{
308 switch (ret_code) {
309 case MANIFEST_SUCCESS:
310 return "Success";
David Brazdil7a462ec2019-08-15 12:27:47 +0100311 case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
312 return "Could not find \"hypervisor\" node in manifest";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100313 case MANIFEST_ERROR_NOT_COMPATIBLE:
314 return "Hypervisor manifest entry not compatible with Hafnium";
David Brazdil7a462ec2019-08-15 12:27:47 +0100315 case MANIFEST_ERROR_RESERVED_VM_ID:
316 return "Manifest defines a VM with a reserved ID";
317 case MANIFEST_ERROR_NO_PRIMARY_VM:
318 return "Manifest does not contain a primary VM entry";
319 case MANIFEST_ERROR_TOO_MANY_VMS:
320 return "Manifest specifies more VMs than Hafnium has "
321 "statically allocated space for";
322 case MANIFEST_ERROR_PROPERTY_NOT_FOUND:
323 return "Property not found";
324 case MANIFEST_ERROR_MALFORMED_STRING:
325 return "Malformed string property";
David Brazdil0dbb41f2019-09-09 18:03:35 +0100326 case MANIFEST_ERROR_STRING_TOO_LONG:
327 return "String too long";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100328 case MANIFEST_ERROR_MALFORMED_STRING_LIST:
329 return "Malformed string list property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100330 case MANIFEST_ERROR_MALFORMED_INTEGER:
331 return "Malformed integer property";
332 case MANIFEST_ERROR_INTEGER_OVERFLOW:
333 return "Integer overflow";
334 }
335
336 panic("Unexpected manifest return code.");
337}