blob: 4cf490a927b27858931e2eeb894605e4d9eaa3c5 [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
56static enum manifest_return_code read_string(const struct fdt_node *node,
57 const char *property,
58 struct memiter *out)
59{
60 const char *data;
61 uint32_t size;
62
63 if (!fdt_read_property(node, property, &data, &size)) {
64 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
65 }
66
David Brazdil74e9c3b2019-08-28 11:09:08 +010067 /*
68 * Require that the value contains exactly one NULL character and that
69 * it is the last byte.
70 */
71 if (memchr(data, '\0', size) != &data[size - 1]) {
David Brazdil7a462ec2019-08-15 12:27:47 +010072 return MANIFEST_ERROR_MALFORMED_STRING;
73 }
74
75 memiter_init(out, data, size - 1);
76 return MANIFEST_SUCCESS;
77}
78
79static enum manifest_return_code read_uint64(const struct fdt_node *node,
80 const char *property,
81 uint64_t *out)
82{
83 const char *data;
84 uint32_t size;
85
86 if (!fdt_read_property(node, property, &data, &size)) {
87 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
88 }
89
90 if (!fdt_parse_number(data, size, out)) {
91 return MANIFEST_ERROR_MALFORMED_INTEGER;
92 }
93
94 return MANIFEST_SUCCESS;
95}
96
97static enum manifest_return_code read_uint16(const struct fdt_node *node,
98 const char *property,
99 uint16_t *out)
100{
101 uint64_t value;
102
103 TRY(read_uint64(node, property, &value));
104
105 if (value > UINT16_MAX) {
106 return MANIFEST_ERROR_INTEGER_OVERFLOW;
107 }
108
109 *out = (uint16_t)value;
110 return MANIFEST_SUCCESS;
111}
112
David Brazdil74e9c3b2019-08-28 11:09:08 +0100113/**
114 * Represents the value of property whose type is a list of strings. These are
115 * encoded as one contiguous byte buffer with NULL-separated entries.
116 */
117struct stringlist_iter {
118 struct memiter mem_it;
119};
120
121static enum manifest_return_code read_stringlist(const struct fdt_node *node,
122 const char *property,
123 struct stringlist_iter *out)
124{
125 const char *data;
126 uint32_t size;
127
128 if (!fdt_read_property(node, property, &data, &size)) {
129 return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
130 }
131
132 /*
133 * Require that the value ends with a NULL terminator. Other NULL
134 * characters separate the string list entries.
135 */
136 if (data[size - 1] != '\0') {
137 return MANIFEST_ERROR_MALFORMED_STRING_LIST;
138 }
139
140 memiter_init(&out->mem_it, data, size - 1);
141 return MANIFEST_SUCCESS;
142}
143
144static bool stringlist_has_next(const struct stringlist_iter *list)
145{
146 return memiter_size(&list->mem_it) > 0;
147}
148
149static void stringlist_get_next(struct stringlist_iter *list,
150 struct memiter *out)
151{
152 const char *mem_base = memiter_base(&list->mem_it);
153 size_t mem_size = memiter_size(&list->mem_it);
154 const char *null_term;
155
156 CHECK(stringlist_has_next(list));
157
158 null_term = memchr(mem_base, '\0', mem_size);
159 if (null_term == NULL) {
160 /*
161 * NULL terminator not found, this is the last entry.
162 * Set entry memiter to the entire byte range and advance list
163 * memiter to the end of the byte range.
164 */
165 memiter_init(out, mem_base, mem_size);
166 memiter_advance(&list->mem_it, mem_size);
167 } else {
168 /*
169 * Found NULL terminator. Set entry memiter to byte range
170 * [base, null) and move list memiter past the terminator.
171 */
172 size_t entry_size = null_term - mem_base;
173
174 memiter_init(out, mem_base, entry_size);
175 memiter_advance(&list->mem_it, entry_size + 1);
176 }
177}
178
179static bool stringlist_contains(const struct stringlist_iter *list,
180 const char *str)
181{
182 struct stringlist_iter it = *list;
183 struct memiter entry;
184
185 while (stringlist_has_next(&it)) {
186 stringlist_get_next(&it, &entry);
187 if (memiter_iseq(&entry, str)) {
188 return true;
189 }
190 }
191 return false;
192}
193
David Brazdil7a462ec2019-08-15 12:27:47 +0100194static enum manifest_return_code parse_vm(struct fdt_node *node,
195 struct manifest_vm *vm,
196 spci_vm_id_t vm_id)
197{
198 TRY(read_string(node, "debug_name", &vm->debug_name));
199 if (vm_id != HF_PRIMARY_VM_ID) {
200 TRY(read_string(node, "kernel_filename",
201 &vm->secondary.kernel_filename));
202 TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
203 TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
204 }
205 return MANIFEST_SUCCESS;
206}
207
208/**
209 * Parse manifest from FDT.
210 */
211enum manifest_return_code manifest_init(struct manifest *manifest,
212 struct memiter *fdt)
213{
214 char vm_name_buf[VM_NAME_BUF_SIZE];
215 struct fdt_node hyp_node;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100216 struct stringlist_iter compatible_list;
David Brazdil7a462ec2019-08-15 12:27:47 +0100217 size_t i = 0;
218 bool found_primary_vm = false;
219
220 memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
221
222 /* Find hypervisor node. */
223 if (!fdt_root_node(&hyp_node,
224 (const struct fdt_header *)memiter_base(fdt))) {
225 return MANIFEST_ERROR_CORRUPTED_FDT;
226 }
227 if (!fdt_find_child(&hyp_node, "")) {
228 return MANIFEST_ERROR_NO_ROOT_FDT_NODE;
229 }
230 if (!fdt_find_child(&hyp_node, "hypervisor")) {
231 return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
232 }
233
David Brazdil74e9c3b2019-08-28 11:09:08 +0100234 /* Check "compatible" property. */
235 TRY(read_stringlist(&hyp_node, "compatible", &compatible_list));
236 if (!stringlist_contains(&compatible_list, "hafnium,hafnium")) {
237 return MANIFEST_ERROR_NOT_COMPATIBLE;
238 }
239
David Brazdil7a462ec2019-08-15 12:27:47 +0100240 /* Iterate over reserved VM IDs and check no such nodes exist. */
241 for (i = 0; i < HF_VM_ID_OFFSET; i++) {
242 spci_vm_id_t vm_id = (spci_vm_id_t)i;
243 struct fdt_node vm_node = hyp_node;
244 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
245
246 if (fdt_find_child(&vm_node, vm_name)) {
247 return MANIFEST_ERROR_RESERVED_VM_ID;
248 }
249 }
250
251 /* Iterate over VM nodes until we find one that does not exist. */
252 for (i = 0; i <= MAX_VMS; ++i) {
253 spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
254 struct fdt_node vm_node = hyp_node;
255 const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
256
257 if (!fdt_find_child(&vm_node, vm_name)) {
258 break;
259 }
260
261 if (i == MAX_VMS) {
262 return MANIFEST_ERROR_TOO_MANY_VMS;
263 }
264
265 if (vm_id == HF_PRIMARY_VM_ID) {
266 CHECK(found_primary_vm == false); /* sanity check */
267 found_primary_vm = true;
268 }
269
270 manifest->num_vms = i + 1;
271 TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
272 }
273
274 if (!found_primary_vm) {
275 return MANIFEST_ERROR_NO_PRIMARY_VM;
276 }
277
278 return MANIFEST_SUCCESS;
279}
280
281const char *manifest_strerror(enum manifest_return_code ret_code)
282{
283 switch (ret_code) {
284 case MANIFEST_SUCCESS:
285 return "Success";
286 case MANIFEST_ERROR_CORRUPTED_FDT:
287 return "Manifest failed FDT validation";
288 case MANIFEST_ERROR_NO_ROOT_FDT_NODE:
289 return "Could not find root node of manifest";
290 case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
291 return "Could not find \"hypervisor\" node in manifest";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100292 case MANIFEST_ERROR_NOT_COMPATIBLE:
293 return "Hypervisor manifest entry not compatible with Hafnium";
David Brazdil7a462ec2019-08-15 12:27:47 +0100294 case MANIFEST_ERROR_RESERVED_VM_ID:
295 return "Manifest defines a VM with a reserved ID";
296 case MANIFEST_ERROR_NO_PRIMARY_VM:
297 return "Manifest does not contain a primary VM entry";
298 case MANIFEST_ERROR_TOO_MANY_VMS:
299 return "Manifest specifies more VMs than Hafnium has "
300 "statically allocated space for";
301 case MANIFEST_ERROR_PROPERTY_NOT_FOUND:
302 return "Property not found";
303 case MANIFEST_ERROR_MALFORMED_STRING:
304 return "Malformed string property";
David Brazdil74e9c3b2019-08-28 11:09:08 +0100305 case MANIFEST_ERROR_MALFORMED_STRING_LIST:
306 return "Malformed string list property";
David Brazdil7a462ec2019-08-15 12:27:47 +0100307 case MANIFEST_ERROR_MALFORMED_INTEGER:
308 return "Malformed integer property";
309 case MANIFEST_ERROR_INTEGER_OVERFLOW:
310 return "Integer overflow";
311 }
312
313 panic("Unexpected manifest return code.");
314}