blob: 902cf212176397efa9202799d0088655ca4161c1 [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
David Brazdil52256ff2019-08-23 15:15:15 +010017#include <array>
18#include <cstdio>
19#include <sstream>
20
David Brazdil7a462ec2019-08-15 12:27:47 +010021#include <gmock/gmock.h>
22
23extern "C" {
24#include "hf/manifest.h"
25}
26
27namespace
28{
29using ::testing::Eq;
30using ::testing::NotNull;
31
David Brazdil52256ff2019-08-23 15:15:15 +010032template <typename T>
David Brazdil0dbb41f2019-09-09 18:03:35 +010033void exec(const char *program, const char *args[], const T &stdin,
David Brazdil52256ff2019-08-23 15:15:15 +010034 std::vector<char> *stdout)
35{
36 /* Create two pipes, one for stdin and one for stdout. */
37 int pipes[2][2];
38 pipe(pipes[0]);
39 pipe(pipes[1]);
David Brazdil7a462ec2019-08-15 12:27:47 +010040
David Brazdil52256ff2019-08-23 15:15:15 +010041 /* Assign FDs for reading/writing by the parent/child. */
42 int parent_read_fd = pipes[1][0]; /* stdout pipe, read FD */
43 int parent_write_fd = pipes[0][1]; /* stdin pipe, write FD */
44 int child_read_fd = pipes[0][0]; /* stdin pipe, read FD */
45 int child_write_fd = pipes[1][1]; /* stdout pipe, write FD */
David Brazdil7a462ec2019-08-15 12:27:47 +010046
David Brazdil52256ff2019-08-23 15:15:15 +010047 if (fork()) {
48 /* Parent process. */
49 std::array<char, 128> buf;
50 ssize_t res;
51
52 /* Close child FDs which won't be used. */
53 close(child_read_fd);
54 close(child_write_fd);
55
56 /* Write to stdin. */
57 for (size_t count = 0; count < stdin.size();) {
58 res = write(parent_write_fd, stdin.data() + count,
59 stdin.size() - count);
60 if (res < 0) {
61 std::cerr << "IO error" << std::endl;
62 exit(1);
63 }
64 count += res;
65 }
66 close(parent_write_fd);
67
68 /* Read from stdout. */
69 while (true) {
70 res = read(parent_read_fd, buf.data(), buf.size());
71 if (res == 0) {
72 /* EOF */
73 break;
74 } else if (res < 0) {
75 std::cerr << "IO error" << std::endl;
76 exit(1);
77 }
78 stdout->insert(stdout->end(), buf.begin(),
79 buf.begin() + res);
80 }
81 close(parent_read_fd);
82 } else {
83 /* Child process. */
84
85 /* Redirect stdin/stdout to read/write FDs. */
86 dup2(child_read_fd, STDIN_FILENO);
87 dup2(child_write_fd, STDOUT_FILENO);
88
89 /* Close all FDs which are now unused. */
90 close(child_read_fd);
91 close(child_write_fd);
92 close(parent_read_fd);
93 close(parent_write_fd);
94
95 /* Execute the given program. */
David Brazdil0dbb41f2019-09-09 18:03:35 +010096 execv(program, const_cast<char *const *>(args));
David Brazdil52256ff2019-08-23 15:15:15 +010097 }
98}
99
100/**
101 * Class for programatically building a Device Tree.
102 *
103 * Usage:
104 * std::vector<char> dtb = ManifestDtBuilder()
105 * .Command1()
106 * .Command2()
107 * ...
108 * .CommandN()
109 * .Build();
110 */
111class ManifestDtBuilder
112{
113 public:
114 ManifestDtBuilder()
115 {
116 dts_ << "/dts-v1/;" << std::endl;
117 dts_ << std::endl;
118
119 /* Start root node. */
120 StartChild("/");
121 }
122
123 std::vector<char> Build()
124 {
David Brazdil0dbb41f2019-09-09 18:03:35 +0100125 const char *program = "./build/image/dtc.py";
126 const char *dtc_args[] = {program, "compile", NULL};
David Brazdil52256ff2019-08-23 15:15:15 +0100127 std::vector<char> dtc_stdout;
128
129 /* Finish root node. */
130 EndChild();
131
David Brazdil0dbb41f2019-09-09 18:03:35 +0100132 exec(program, dtc_args, dts_.str(), &dtc_stdout);
David Brazdil52256ff2019-08-23 15:15:15 +0100133 return dtc_stdout;
134 }
135
136 ManifestDtBuilder &StartChild(const std::string_view &name)
137 {
138 dts_ << name << " {" << std::endl;
139 return *this;
140 }
141
142 ManifestDtBuilder &EndChild()
143 {
144 dts_ << "};" << std::endl;
145 return *this;
146 }
147
David Brazdil74e9c3b2019-08-28 11:09:08 +0100148 ManifestDtBuilder &Compatible(const std::vector<std::string_view>
149 &value = {"hafnium,hafnium"})
150 {
151 return StringListProperty("compatible", value);
152 }
153
David Brazdil52256ff2019-08-23 15:15:15 +0100154 ManifestDtBuilder &DebugName(const std::string_view &value)
155 {
156 return StringProperty("debug_name", value);
157 }
158
159 ManifestDtBuilder &KernelFilename(const std::string_view &value)
160 {
161 return StringProperty("kernel_filename", value);
162 }
163
David Brazdile6f83222019-09-23 14:47:37 +0100164 ManifestDtBuilder &RamdiskFilename(const std::string_view &value)
165 {
166 return StringProperty("ramdisk_filename", value);
167 }
168
David Brazdil52256ff2019-08-23 15:15:15 +0100169 ManifestDtBuilder &VcpuCount(uint64_t value)
170 {
171 return IntegerProperty("vcpu_count", value);
172 }
173
174 ManifestDtBuilder &MemSize(uint64_t value)
175 {
176 return IntegerProperty("mem_size", value);
177 }
178
179 private:
180 ManifestDtBuilder &StringProperty(const std::string_view &name,
181 const std::string_view &value)
182 {
183 dts_ << name << " = \"" << value << "\";" << std::endl;
184 return *this;
185 }
186
David Brazdil74e9c3b2019-08-28 11:09:08 +0100187 ManifestDtBuilder &StringListProperty(
188 const std::string_view &name,
189 const std::vector<std::string_view> &value)
190 {
191 bool is_first = true;
192
193 dts_ << name << " = ";
194 for (const std::string_view &entry : value) {
195 if (is_first) {
196 is_first = false;
197 } else {
198 dts_ << ", ";
199 }
200 dts_ << "\"" << entry << "\"";
201 }
202 dts_ << ";" << std::endl;
203 return *this;
204 }
205
David Brazdil52256ff2019-08-23 15:15:15 +0100206 ManifestDtBuilder &IntegerProperty(const std::string_view &name,
207 uint64_t value)
208 {
209 dts_ << name << " = <" << value << ">;" << std::endl;
210 return *this;
211 }
212
213 std::stringstream dts_;
214};
215
David Brazdil0dbb41f2019-09-09 18:03:35 +0100216static bool get_fdt_root(const std::vector<char> &dtb,
217 struct fdt_node *fdt_root)
218{
219 const struct fdt_header *fdt_header;
220
221 fdt_header = reinterpret_cast<const struct fdt_header *>(dtb.data());
222 return fdt_root_node(fdt_root, fdt_header) &&
223 fdt_find_child(fdt_root, "");
224}
225
David Brazdil52256ff2019-08-23 15:15:15 +0100226TEST(manifest, no_hypervisor_node)
David Brazdil7a462ec2019-08-15 12:27:47 +0100227{
228 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100229 struct fdt_node fdt_root;
David Brazdil52256ff2019-08-23 15:15:15 +0100230 std::vector<char> dtb = ManifestDtBuilder().Build();
David Brazdil7a462ec2019-08-15 12:27:47 +0100231
David Brazdil0dbb41f2019-09-09 18:03:35 +0100232 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
233 ASSERT_EQ(manifest_init(&m, &fdt_root),
David Brazdil7a462ec2019-08-15 12:27:47 +0100234 MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
235}
236
David Brazdil74e9c3b2019-08-28 11:09:08 +0100237TEST(manifest, no_compatible_property)
David Brazdil7a462ec2019-08-15 12:27:47 +0100238{
239 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100240 struct fdt_node fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100241
David Brazdil52256ff2019-08-23 15:15:15 +0100242 /* clang-format off */
243 std::vector<char> dtb = ManifestDtBuilder()
244 .StartChild("hypervisor")
245 .EndChild()
246 .Build();
247 /* clang-format on */
248
David Brazdil0dbb41f2019-09-09 18:03:35 +0100249 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
250 ASSERT_EQ(manifest_init(&m, &fdt_root),
251 MANIFEST_ERROR_PROPERTY_NOT_FOUND);
David Brazdil7a462ec2019-08-15 12:27:47 +0100252}
253
David Brazdil74e9c3b2019-08-28 11:09:08 +0100254TEST(manifest, not_compatible)
David Brazdil7a462ec2019-08-15 12:27:47 +0100255{
256 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100257 struct fdt_node fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100258
David Brazdil52256ff2019-08-23 15:15:15 +0100259 /* clang-format off */
260 std::vector<char> dtb = ManifestDtBuilder()
261 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100262 .Compatible({ "foo,bar" })
263 .EndChild()
264 .Build();
265 /* clang-format on */
266
David Brazdil0dbb41f2019-09-09 18:03:35 +0100267 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
268 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_NOT_COMPATIBLE);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100269}
270
271TEST(manifest, compatible_one_of_many)
272{
273 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100274 struct fdt_node fdt_root;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100275
276 /* clang-format off */
277 std::vector<char> dtb = ManifestDtBuilder()
278 .StartChild("hypervisor")
279 .Compatible({ "foo,bar", "hafnium,hafnium" })
280 .StartChild("vm1")
281 .DebugName("primary")
282 .EndChild()
283 .EndChild()
284 .Build();
285 /* clang-format on */
286
David Brazdil0dbb41f2019-09-09 18:03:35 +0100287 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
288 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100289}
290
291TEST(manifest, no_vm_nodes)
292{
293 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100294 struct fdt_node fdt_root;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100295
296 /* clang-format off */
297 std::vector<char> dtb = ManifestDtBuilder()
298 .StartChild("hypervisor")
299 .Compatible()
300 .EndChild()
301 .Build();
302 /* clang-format on */
303
David Brazdil0dbb41f2019-09-09 18:03:35 +0100304 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
305 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_NO_PRIMARY_VM);
306}
307
308static std::vector<char> gen_long_string_dtb(bool valid)
309{
310 const char last_valid[] = "1234567890123456789012345678901";
311 const char first_invalid[] = "12345678901234567890123456789012";
David Brazdil136f2942019-09-23 14:11:03 +0100312 static_assert(sizeof(last_valid) == STRING_MAX_SIZE);
313 static_assert(sizeof(first_invalid) == STRING_MAX_SIZE + 1);
David Brazdil0dbb41f2019-09-09 18:03:35 +0100314
315 /* clang-format off */
316 return ManifestDtBuilder()
317 .StartChild("hypervisor")
318 .Compatible()
319 .StartChild("vm1")
320 .DebugName(valid ? last_valid : first_invalid)
321 .EndChild()
322 .EndChild()
323 .Build();
324 /* clang-format on */
325}
326
327TEST(manifest, long_string)
328{
329 struct manifest m;
330 struct fdt_node fdt_root;
331
332 std::vector<char> dtb_last_valid = gen_long_string_dtb(true);
333 std::vector<char> dtb_first_invalid = gen_long_string_dtb(false);
334
335 ASSERT_TRUE(get_fdt_root(dtb_last_valid, &fdt_root));
336 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
337
338 ASSERT_TRUE(get_fdt_root(dtb_first_invalid, &fdt_root));
339 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_STRING_TOO_LONG);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100340}
341
342TEST(manifest, reserved_vm_id)
343{
344 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100345 struct fdt_node fdt_root;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100346
347 /* clang-format off */
348 std::vector<char> dtb = ManifestDtBuilder()
349 .StartChild("hypervisor")
350 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100351 .StartChild("vm1")
352 .DebugName("primary_vm")
353 .EndChild()
354 .StartChild("vm0")
355 .DebugName("reserved_vm")
356 .VcpuCount(1)
357 .MemSize(0x1000)
358 .KernelFilename("kernel")
359 .EndChild()
360 .EndChild()
361 .Build();
362 /* clang-format on */
363
David Brazdil0dbb41f2019-09-09 18:03:35 +0100364 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
365 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_RESERVED_VM_ID);
David Brazdil7a462ec2019-08-15 12:27:47 +0100366}
367
David Brazdil52256ff2019-08-23 15:15:15 +0100368static std::vector<char> gen_vcpu_count_limit_dtb(uint64_t vcpu_count)
369{
370 /* clang-format off */
371 return ManifestDtBuilder()
372 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100373 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100374 .StartChild("vm1")
375 .DebugName("primary_vm")
376 .EndChild()
377 .StartChild("vm2")
378 .DebugName("secondary_vm")
379 .VcpuCount(vcpu_count)
380 .MemSize(0x1000)
381 .KernelFilename("kernel")
382 .EndChild()
383 .EndChild()
384 .Build();
385 /* clang-format on */
386}
David Brazdil7a462ec2019-08-15 12:27:47 +0100387
388TEST(manifest, vcpu_count_limit)
389{
390 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100391 struct fdt_node fdt_root;
David Brazdil52256ff2019-08-23 15:15:15 +0100392 std::vector<char> dtb_last_valid = gen_vcpu_count_limit_dtb(UINT16_MAX);
393 std::vector<char> dtb_first_invalid =
394 gen_vcpu_count_limit_dtb(UINT16_MAX + 1);
David Brazdil7a462ec2019-08-15 12:27:47 +0100395
David Brazdil0dbb41f2019-09-09 18:03:35 +0100396 ASSERT_TRUE(get_fdt_root(dtb_last_valid, &fdt_root));
397 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
David Brazdil0251b942019-09-10 15:59:50 +0100398 ASSERT_EQ(m.vm_count, 2);
David Brazdil7a462ec2019-08-15 12:27:47 +0100399 ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
400
David Brazdil0dbb41f2019-09-09 18:03:35 +0100401 ASSERT_TRUE(get_fdt_root(dtb_first_invalid, &fdt_root));
402 ASSERT_EQ(manifest_init(&m, &fdt_root),
403 MANIFEST_ERROR_INTEGER_OVERFLOW);
David Brazdil7a462ec2019-08-15 12:27:47 +0100404}
405
David Brazdile6f83222019-09-23 14:47:37 +0100406TEST(manifest, no_ramdisk_primary)
407{
408 struct manifest m;
409 struct fdt_node fdt_root;
410
411 /* clang-format off */
412 std::vector<char> dtb = ManifestDtBuilder()
413 .StartChild("hypervisor")
414 .Compatible()
415 .StartChild("vm1")
416 .DebugName("primary_vm")
417 .EndChild()
418 .EndChild()
419 .Build();
420 /* clang-format on */
421
422 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
423 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
424 ASSERT_EQ(m.vm_count, 1);
425 ASSERT_STREQ(string_data(&m.vm[0].debug_name), "primary_vm");
426 ASSERT_STREQ(string_data(&m.vm[0].primary.ramdisk_filename), "");
427}
428
David Brazdil7a462ec2019-08-15 12:27:47 +0100429TEST(manifest, valid)
430{
431 struct manifest m;
432 struct manifest_vm *vm;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100433 struct fdt_node fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100434
David Brazdil52256ff2019-08-23 15:15:15 +0100435 /* clang-format off */
436 std::vector<char> dtb = ManifestDtBuilder()
437 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100438 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100439 .StartChild("vm1")
440 .DebugName("primary_vm")
Andrew Scull72b43c02019-09-18 13:53:45 +0100441 .KernelFilename("primary_kernel")
David Brazdile6f83222019-09-23 14:47:37 +0100442 .RamdiskFilename("primary_ramdisk")
David Brazdil52256ff2019-08-23 15:15:15 +0100443 .EndChild()
444 .StartChild("vm3")
445 .DebugName("second_secondary_vm")
446 .VcpuCount(43)
447 .MemSize(0x12345)
Andrew Scull72b43c02019-09-18 13:53:45 +0100448 .KernelFilename("second_secondary_kernel")
David Brazdil52256ff2019-08-23 15:15:15 +0100449 .EndChild()
450 .StartChild("vm2")
451 .DebugName("first_secondary_vm")
452 .VcpuCount(42)
453 .MemSize(12345)
David Brazdil52256ff2019-08-23 15:15:15 +0100454 .EndChild()
455 .EndChild()
456 .Build();
457 /* clang-format on */
458
David Brazdil0dbb41f2019-09-09 18:03:35 +0100459 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
David Brazdil7a462ec2019-08-15 12:27:47 +0100460
David Brazdil0dbb41f2019-09-09 18:03:35 +0100461 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
David Brazdil0251b942019-09-10 15:59:50 +0100462 ASSERT_EQ(m.vm_count, 3);
David Brazdil7a462ec2019-08-15 12:27:47 +0100463
464 vm = &m.vm[0];
David Brazdil136f2942019-09-23 14:11:03 +0100465 ASSERT_STREQ(string_data(&vm->debug_name), "primary_vm");
466 ASSERT_STREQ(string_data(&vm->kernel_filename), "primary_kernel");
David Brazdile6f83222019-09-23 14:47:37 +0100467 ASSERT_STREQ(string_data(&vm->primary.ramdisk_filename),
468 "primary_ramdisk");
David Brazdil7a462ec2019-08-15 12:27:47 +0100469
470 vm = &m.vm[1];
David Brazdil136f2942019-09-23 14:11:03 +0100471 ASSERT_STREQ(string_data(&vm->debug_name), "first_secondary_vm");
472 ASSERT_STREQ(string_data(&vm->kernel_filename), "");
David Brazdil7a462ec2019-08-15 12:27:47 +0100473 ASSERT_EQ(vm->secondary.vcpu_count, 42);
474 ASSERT_EQ(vm->secondary.mem_size, 12345);
David Brazdil7a462ec2019-08-15 12:27:47 +0100475
476 vm = &m.vm[2];
David Brazdil136f2942019-09-23 14:11:03 +0100477 ASSERT_STREQ(string_data(&vm->debug_name), "second_secondary_vm");
478 ASSERT_STREQ(string_data(&vm->kernel_filename),
479 "second_secondary_kernel");
David Brazdil7a462ec2019-08-15 12:27:47 +0100480 ASSERT_EQ(vm->secondary.vcpu_count, 43);
481 ASSERT_EQ(vm->secondary.mem_size, 0x12345);
David Brazdil7a462ec2019-08-15 12:27:47 +0100482}
483
484} /* namespace */