blob: 2b8be8bd49d90543a6ad521a4dde1a60b07bb27e [file] [log] [blame]
David Brazdil7a462ec2019-08-15 12:27:47 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
David Brazdil7a462ec2019-08-15 12:27:47 +01007 */
8
David Brazdil52256ff2019-08-23 15:15:15 +01009#include <array>
10#include <cstdio>
Andrew Scullae9962e2019-10-03 16:51:16 +010011#include <span>
David Brazdil52256ff2019-08-23 15:15:15 +010012#include <sstream>
13
David Brazdil7a462ec2019-08-15 12:27:47 +010014#include <gmock/gmock.h>
15
16extern "C" {
17#include "hf/manifest.h"
18}
19
20namespace
21{
Andrew Scullae9962e2019-10-03 16:51:16 +010022using ::testing::ElementsAre;
David Brazdil7a462ec2019-08-15 12:27:47 +010023using ::testing::Eq;
Andrew Scullae9962e2019-10-03 16:51:16 +010024using ::testing::IsEmpty;
David Brazdil7a462ec2019-08-15 12:27:47 +010025using ::testing::NotNull;
26
David Brazdil52256ff2019-08-23 15:15:15 +010027template <typename T>
David Brazdil0dbb41f2019-09-09 18:03:35 +010028void exec(const char *program, const char *args[], const T &stdin,
David Brazdil52256ff2019-08-23 15:15:15 +010029 std::vector<char> *stdout)
30{
31 /* Create two pipes, one for stdin and one for stdout. */
32 int pipes[2][2];
33 pipe(pipes[0]);
34 pipe(pipes[1]);
David Brazdil7a462ec2019-08-15 12:27:47 +010035
David Brazdil52256ff2019-08-23 15:15:15 +010036 /* Assign FDs for reading/writing by the parent/child. */
37 int parent_read_fd = pipes[1][0]; /* stdout pipe, read FD */
38 int parent_write_fd = pipes[0][1]; /* stdin pipe, write FD */
39 int child_read_fd = pipes[0][0]; /* stdin pipe, read FD */
40 int child_write_fd = pipes[1][1]; /* stdout pipe, write FD */
David Brazdil7a462ec2019-08-15 12:27:47 +010041
David Brazdil52256ff2019-08-23 15:15:15 +010042 if (fork()) {
43 /* Parent process. */
44 std::array<char, 128> buf;
45 ssize_t res;
46
47 /* Close child FDs which won't be used. */
48 close(child_read_fd);
49 close(child_write_fd);
50
51 /* Write to stdin. */
52 for (size_t count = 0; count < stdin.size();) {
53 res = write(parent_write_fd, stdin.data() + count,
54 stdin.size() - count);
55 if (res < 0) {
56 std::cerr << "IO error" << std::endl;
57 exit(1);
58 }
59 count += res;
60 }
61 close(parent_write_fd);
62
63 /* Read from stdout. */
64 while (true) {
65 res = read(parent_read_fd, buf.data(), buf.size());
66 if (res == 0) {
67 /* EOF */
68 break;
69 } else if (res < 0) {
70 std::cerr << "IO error" << std::endl;
71 exit(1);
72 }
73 stdout->insert(stdout->end(), buf.begin(),
74 buf.begin() + res);
75 }
76 close(parent_read_fd);
77 } else {
78 /* Child process. */
79
80 /* Redirect stdin/stdout to read/write FDs. */
81 dup2(child_read_fd, STDIN_FILENO);
82 dup2(child_write_fd, STDOUT_FILENO);
83
84 /* Close all FDs which are now unused. */
85 close(child_read_fd);
86 close(child_write_fd);
87 close(parent_read_fd);
88 close(parent_write_fd);
89
90 /* Execute the given program. */
David Brazdil0dbb41f2019-09-09 18:03:35 +010091 execv(program, const_cast<char *const *>(args));
David Brazdil52256ff2019-08-23 15:15:15 +010092 }
93}
94
95/**
96 * Class for programatically building a Device Tree.
97 *
98 * Usage:
99 * std::vector<char> dtb = ManifestDtBuilder()
100 * .Command1()
101 * .Command2()
102 * ...
103 * .CommandN()
104 * .Build();
105 */
106class ManifestDtBuilder
107{
108 public:
109 ManifestDtBuilder()
110 {
111 dts_ << "/dts-v1/;" << std::endl;
112 dts_ << std::endl;
113
114 /* Start root node. */
115 StartChild("/");
116 }
117
Andrew Scullae9962e2019-10-03 16:51:16 +0100118 std::vector<char> Build(bool dump = false)
David Brazdil52256ff2019-08-23 15:15:15 +0100119 {
David Brazdil0dbb41f2019-09-09 18:03:35 +0100120 const char *program = "./build/image/dtc.py";
121 const char *dtc_args[] = {program, "compile", NULL};
David Brazdil52256ff2019-08-23 15:15:15 +0100122 std::vector<char> dtc_stdout;
123
124 /* Finish root node. */
125 EndChild();
126
Andrew Scullae9962e2019-10-03 16:51:16 +0100127 if (dump) {
128 Dump();
129 }
130
David Brazdil0dbb41f2019-09-09 18:03:35 +0100131 exec(program, dtc_args, dts_.str(), &dtc_stdout);
David Brazdil52256ff2019-08-23 15:15:15 +0100132 return dtc_stdout;
133 }
134
Andrew Scullae9962e2019-10-03 16:51:16 +0100135 void Dump()
136 {
137 std::cerr << dts_.str() << std::endl;
138 }
139
David Brazdil52256ff2019-08-23 15:15:15 +0100140 ManifestDtBuilder &StartChild(const std::string_view &name)
141 {
142 dts_ << name << " {" << std::endl;
143 return *this;
144 }
145
146 ManifestDtBuilder &EndChild()
147 {
148 dts_ << "};" << std::endl;
149 return *this;
150 }
151
David Brazdil74e9c3b2019-08-28 11:09:08 +0100152 ManifestDtBuilder &Compatible(const std::vector<std::string_view>
153 &value = {"hafnium,hafnium"})
154 {
155 return StringListProperty("compatible", value);
156 }
157
David Brazdil52256ff2019-08-23 15:15:15 +0100158 ManifestDtBuilder &DebugName(const std::string_view &value)
159 {
160 return StringProperty("debug_name", value);
161 }
162
163 ManifestDtBuilder &KernelFilename(const std::string_view &value)
164 {
165 return StringProperty("kernel_filename", value);
166 }
167
David Brazdile6f83222019-09-23 14:47:37 +0100168 ManifestDtBuilder &RamdiskFilename(const std::string_view &value)
169 {
170 return StringProperty("ramdisk_filename", value);
171 }
172
David Brazdil080ee312020-02-25 15:30:30 -0800173 ManifestDtBuilder &BootAddress(uint64_t value)
174 {
175 return Integer64Property("boot_address", value);
176 }
177
Andrew Scullae9962e2019-10-03 16:51:16 +0100178 ManifestDtBuilder &VcpuCount(uint32_t value)
David Brazdil52256ff2019-08-23 15:15:15 +0100179 {
180 return IntegerProperty("vcpu_count", value);
181 }
182
Andrew Scullae9962e2019-10-03 16:51:16 +0100183 ManifestDtBuilder &MemSize(uint32_t value)
David Brazdil52256ff2019-08-23 15:15:15 +0100184 {
185 return IntegerProperty("mem_size", value);
186 }
187
Andrew Scullae9962e2019-10-03 16:51:16 +0100188 ManifestDtBuilder &SmcWhitelist(const std::vector<uint32_t> &value)
189 {
190 return IntegerListProperty("smc_whitelist", value);
191 }
192
193 ManifestDtBuilder &SmcWhitelistPermissive()
194 {
195 return BooleanProperty("smc_whitelist_permissive");
196 }
197
Olivier Deprez62d99e32020-01-09 15:58:07 +0100198 ManifestDtBuilder &LoadAddress(uint64_t value)
199 {
200 return Integer64Property("load_address", value);
201 }
202
203 ManifestDtBuilder &FfaPartition()
204 {
205 return BooleanProperty("is_ffa_partition");
206 }
207
Andrew Scullae9962e2019-10-03 16:51:16 +0100208 ManifestDtBuilder &Property(const std::string_view &name,
209 const std::string_view &value)
210 {
211 dts_ << name << " = " << value << ";" << std::endl;
212 return *this;
213 }
214
David Brazdil52256ff2019-08-23 15:15:15 +0100215 private:
216 ManifestDtBuilder &StringProperty(const std::string_view &name,
217 const std::string_view &value)
218 {
219 dts_ << name << " = \"" << value << "\";" << std::endl;
220 return *this;
221 }
222
David Brazdil74e9c3b2019-08-28 11:09:08 +0100223 ManifestDtBuilder &StringListProperty(
224 const std::string_view &name,
225 const std::vector<std::string_view> &value)
226 {
227 bool is_first = true;
228
229 dts_ << name << " = ";
230 for (const std::string_view &entry : value) {
231 if (is_first) {
232 is_first = false;
233 } else {
234 dts_ << ", ";
235 }
236 dts_ << "\"" << entry << "\"";
237 }
238 dts_ << ";" << std::endl;
239 return *this;
240 }
241
David Brazdil52256ff2019-08-23 15:15:15 +0100242 ManifestDtBuilder &IntegerProperty(const std::string_view &name,
Andrew Scullae9962e2019-10-03 16:51:16 +0100243 uint32_t value)
David Brazdil52256ff2019-08-23 15:15:15 +0100244 {
245 dts_ << name << " = <" << value << ">;" << std::endl;
246 return *this;
247 }
248
David Brazdil080ee312020-02-25 15:30:30 -0800249 ManifestDtBuilder &Integer64Property(const std::string_view &name,
250 uint64_t value)
251 {
252 uint32_t high = value >> 32;
253 uint32_t low = (uint32_t)value;
254 dts_ << name << " = <" << high << " " << low << ">;"
255 << std::endl;
256 return *this;
257 }
258
Andrew Scullae9962e2019-10-03 16:51:16 +0100259 ManifestDtBuilder &IntegerListProperty(
260 const std::string_view &name,
261 const std::vector<uint32_t> &value)
262 {
263 dts_ << name << " = < ";
264 for (const uint32_t entry : value) {
265 dts_ << entry << " ";
266 }
267 dts_ << ">;" << std::endl;
268 return *this;
269 }
270
271 ManifestDtBuilder &BooleanProperty(const std::string_view &name)
272 {
Andrew Scull5dc089e2019-11-04 13:21:03 +0000273 dts_ << name << ";" << std::endl;
274 return *this;
Andrew Scullae9962e2019-10-03 16:51:16 +0100275 }
276
David Brazdil52256ff2019-08-23 15:15:15 +0100277 std::stringstream dts_;
278};
279
David Brazdila2358d42020-01-27 18:51:38 +0000280static enum manifest_return_code manifest_from_vec(struct manifest *m,
281 const std::vector<char> &vec)
David Brazdil0dbb41f2019-09-09 18:03:35 +0100282{
David Brazdila2358d42020-01-27 18:51:38 +0000283 struct memiter it;
Olivier Deprez62d99e32020-01-09 15:58:07 +0100284 struct mpool ppool;
285 struct mm_stage1_locked mm_stage1_locked;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100286
David Brazdila2358d42020-01-27 18:51:38 +0000287 memiter_init(&it, vec.data(), vec.size());
Olivier Deprez62d99e32020-01-09 15:58:07 +0100288 return manifest_init(mm_stage1_locked, m, &it, &ppool);
David Brazdil0dbb41f2019-09-09 18:03:35 +0100289}
290
David Brazdil52256ff2019-08-23 15:15:15 +0100291TEST(manifest, no_hypervisor_node)
David Brazdil7a462ec2019-08-15 12:27:47 +0100292{
293 struct manifest m;
David Brazdil52256ff2019-08-23 15:15:15 +0100294 std::vector<char> dtb = ManifestDtBuilder().Build();
David Brazdil7a462ec2019-08-15 12:27:47 +0100295
David Brazdila2358d42020-01-27 18:51:38 +0000296 ASSERT_EQ(manifest_from_vec(&m, dtb),
David Brazdil7a462ec2019-08-15 12:27:47 +0100297 MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
298}
299
David Brazdil74e9c3b2019-08-28 11:09:08 +0100300TEST(manifest, no_compatible_property)
David Brazdil7a462ec2019-08-15 12:27:47 +0100301{
302 struct manifest m;
David Brazdil7a462ec2019-08-15 12:27:47 +0100303
David Brazdil52256ff2019-08-23 15:15:15 +0100304 /* clang-format off */
305 std::vector<char> dtb = ManifestDtBuilder()
306 .StartChild("hypervisor")
307 .EndChild()
308 .Build();
309 /* clang-format on */
310
David Brazdilf4925382020-03-25 13:33:51 +0000311 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_ERROR_NOT_COMPATIBLE);
David Brazdil7a462ec2019-08-15 12:27:47 +0100312}
313
David Brazdil74e9c3b2019-08-28 11:09:08 +0100314TEST(manifest, not_compatible)
David Brazdil7a462ec2019-08-15 12:27:47 +0100315{
316 struct manifest m;
David Brazdil7a462ec2019-08-15 12:27:47 +0100317
David Brazdil52256ff2019-08-23 15:15:15 +0100318 /* clang-format off */
319 std::vector<char> dtb = ManifestDtBuilder()
320 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100321 .Compatible({ "foo,bar" })
322 .EndChild()
323 .Build();
324 /* clang-format on */
325
David Brazdila2358d42020-01-27 18:51:38 +0000326 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_ERROR_NOT_COMPATIBLE);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100327}
328
329TEST(manifest, compatible_one_of_many)
330{
331 struct manifest m;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100332
333 /* clang-format off */
334 std::vector<char> dtb = ManifestDtBuilder()
335 .StartChild("hypervisor")
336 .Compatible({ "foo,bar", "hafnium,hafnium" })
337 .StartChild("vm1")
338 .DebugName("primary")
339 .EndChild()
340 .EndChild()
341 .Build();
342 /* clang-format on */
343
David Brazdila2358d42020-01-27 18:51:38 +0000344 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_SUCCESS);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100345}
346
347TEST(manifest, no_vm_nodes)
348{
349 struct manifest m;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100350
351 /* clang-format off */
352 std::vector<char> dtb = ManifestDtBuilder()
353 .StartChild("hypervisor")
354 .Compatible()
355 .EndChild()
356 .Build();
357 /* clang-format on */
358
David Brazdila2358d42020-01-27 18:51:38 +0000359 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_ERROR_NO_PRIMARY_VM);
David Brazdil0dbb41f2019-09-09 18:03:35 +0100360}
361
362static std::vector<char> gen_long_string_dtb(bool valid)
363{
364 const char last_valid[] = "1234567890123456789012345678901";
365 const char first_invalid[] = "12345678901234567890123456789012";
David Brazdil136f2942019-09-23 14:11:03 +0100366 static_assert(sizeof(last_valid) == STRING_MAX_SIZE);
367 static_assert(sizeof(first_invalid) == STRING_MAX_SIZE + 1);
David Brazdil0dbb41f2019-09-09 18:03:35 +0100368
369 /* clang-format off */
370 return ManifestDtBuilder()
371 .StartChild("hypervisor")
372 .Compatible()
373 .StartChild("vm1")
374 .DebugName(valid ? last_valid : first_invalid)
375 .EndChild()
376 .EndChild()
377 .Build();
378 /* clang-format on */
379}
380
381TEST(manifest, long_string)
382{
383 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100384 std::vector<char> dtb_last_valid = gen_long_string_dtb(true);
385 std::vector<char> dtb_first_invalid = gen_long_string_dtb(false);
386
David Brazdila2358d42020-01-27 18:51:38 +0000387 ASSERT_EQ(manifest_from_vec(&m, dtb_last_valid), MANIFEST_SUCCESS);
388 ASSERT_EQ(manifest_from_vec(&m, dtb_first_invalid),
389 MANIFEST_ERROR_STRING_TOO_LONG);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100390}
391
392TEST(manifest, reserved_vm_id)
393{
394 struct manifest m;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100395
396 /* clang-format off */
397 std::vector<char> dtb = ManifestDtBuilder()
398 .StartChild("hypervisor")
399 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100400 .StartChild("vm1")
401 .DebugName("primary_vm")
402 .EndChild()
403 .StartChild("vm0")
404 .DebugName("reserved_vm")
405 .VcpuCount(1)
406 .MemSize(0x1000)
407 .KernelFilename("kernel")
408 .EndChild()
409 .EndChild()
410 .Build();
411 /* clang-format on */
412
David Brazdila2358d42020-01-27 18:51:38 +0000413 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_ERROR_RESERVED_VM_ID);
David Brazdil7a462ec2019-08-15 12:27:47 +0100414}
415
Andrew Scullae9962e2019-10-03 16:51:16 +0100416static std::vector<char> gen_vcpu_count_limit_dtb(uint32_t vcpu_count)
David Brazdil52256ff2019-08-23 15:15:15 +0100417{
418 /* clang-format off */
419 return ManifestDtBuilder()
420 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100421 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100422 .StartChild("vm1")
423 .DebugName("primary_vm")
424 .EndChild()
425 .StartChild("vm2")
426 .DebugName("secondary_vm")
427 .VcpuCount(vcpu_count)
428 .MemSize(0x1000)
429 .KernelFilename("kernel")
430 .EndChild()
431 .EndChild()
432 .Build();
433 /* clang-format on */
434}
David Brazdil7a462ec2019-08-15 12:27:47 +0100435
436TEST(manifest, vcpu_count_limit)
437{
438 struct manifest m;
David Brazdil52256ff2019-08-23 15:15:15 +0100439 std::vector<char> dtb_last_valid = gen_vcpu_count_limit_dtb(UINT16_MAX);
440 std::vector<char> dtb_first_invalid =
441 gen_vcpu_count_limit_dtb(UINT16_MAX + 1);
David Brazdil7a462ec2019-08-15 12:27:47 +0100442
David Brazdila2358d42020-01-27 18:51:38 +0000443 ASSERT_EQ(manifest_from_vec(&m, dtb_last_valid), MANIFEST_SUCCESS);
David Brazdil0251b942019-09-10 15:59:50 +0100444 ASSERT_EQ(m.vm_count, 2);
David Brazdil7a462ec2019-08-15 12:27:47 +0100445 ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
446
David Brazdila2358d42020-01-27 18:51:38 +0000447 ASSERT_EQ(manifest_from_vec(&m, dtb_first_invalid),
David Brazdil0dbb41f2019-09-09 18:03:35 +0100448 MANIFEST_ERROR_INTEGER_OVERFLOW);
David Brazdil7a462ec2019-08-15 12:27:47 +0100449}
450
David Brazdile6f83222019-09-23 14:47:37 +0100451TEST(manifest, no_ramdisk_primary)
452{
453 struct manifest m;
David Brazdile6f83222019-09-23 14:47:37 +0100454
455 /* clang-format off */
456 std::vector<char> dtb = ManifestDtBuilder()
457 .StartChild("hypervisor")
458 .Compatible()
459 .StartChild("vm1")
460 .DebugName("primary_vm")
461 .EndChild()
462 .EndChild()
463 .Build();
464 /* clang-format on */
465
David Brazdila2358d42020-01-27 18:51:38 +0000466 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_SUCCESS);
David Brazdile6f83222019-09-23 14:47:37 +0100467 ASSERT_EQ(m.vm_count, 1);
468 ASSERT_STREQ(string_data(&m.vm[0].debug_name), "primary_vm");
469 ASSERT_STREQ(string_data(&m.vm[0].primary.ramdisk_filename), "");
470}
471
David Brazdil080ee312020-02-25 15:30:30 -0800472TEST(manifest, no_boot_address_primary)
473{
474 struct manifest m;
475
476 /* clang-format off */
477 std::vector<char> dtb = ManifestDtBuilder()
478 .StartChild("hypervisor")
479 .Compatible()
480 .StartChild("vm1")
481 .DebugName("primary_vm")
482 .EndChild()
483 .EndChild()
484 .Build();
485 /* clang-format on */
486
487 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_SUCCESS);
488 ASSERT_EQ(m.vm_count, 1);
489 ASSERT_STREQ(string_data(&m.vm[0].debug_name), "primary_vm");
490 ASSERT_EQ(m.vm[0].primary.boot_address, MANIFEST_INVALID_ADDRESS);
491}
492
493TEST(manifest, boot_address_primary)
494{
495 struct manifest m;
496 const uint64_t addr = UINT64_C(0x12345678ABCDEFEF);
497
498 /* clang-format off */
499 std::vector<char> dtb = ManifestDtBuilder()
500 .StartChild("hypervisor")
501 .Compatible()
502 .StartChild("vm1")
503 .DebugName("primary_vm")
504 .BootAddress(addr)
505 .EndChild()
506 .EndChild()
507 .Build();
508 /* clang-format on */
509
510 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_SUCCESS);
511 ASSERT_EQ(m.vm_count, 1);
512 ASSERT_STREQ(string_data(&m.vm[0].debug_name), "primary_vm");
513 ASSERT_EQ(m.vm[0].primary.boot_address, addr);
514}
515
Andrew Scullb2c3a242019-11-04 13:52:36 +0000516static std::vector<char> gen_malformed_boolean_dtb(
517 const std::string_view &value)
Andrew Scullae9962e2019-10-03 16:51:16 +0100518{
Andrew Scullae9962e2019-10-03 16:51:16 +0100519 /* clang-format off */
Andrew Scullb2c3a242019-11-04 13:52:36 +0000520 return ManifestDtBuilder()
Andrew Scullae9962e2019-10-03 16:51:16 +0100521 .StartChild("hypervisor")
522 .Compatible()
523 .StartChild("vm1")
524 .DebugName("primary_vm")
Andrew Scullb2c3a242019-11-04 13:52:36 +0000525 .Property("smc_whitelist_permissive", value)
Andrew Scull5dc089e2019-11-04 13:21:03 +0000526 .EndChild()
Andrew Scullae9962e2019-10-03 16:51:16 +0100527 .EndChild()
528 .Build();
529 /* clang-format on */
Andrew Scullb2c3a242019-11-04 13:52:36 +0000530}
Andrew Scullae9962e2019-10-03 16:51:16 +0100531
Andrew Scullb2c3a242019-11-04 13:52:36 +0000532TEST(manifest, malformed_booleans)
533{
534 struct manifest m;
Andrew Scullae9962e2019-10-03 16:51:16 +0100535
Andrew Scullb2c3a242019-11-04 13:52:36 +0000536 std::vector<char> dtb_false = gen_malformed_boolean_dtb("\"false\"");
537 std::vector<char> dtb_true = gen_malformed_boolean_dtb("\"true\"");
538 std::vector<char> dtb_0 = gen_malformed_boolean_dtb("\"<0>\"");
539 std::vector<char> dtb_1 = gen_malformed_boolean_dtb("\"<1>\"");
Andrew Scullae9962e2019-10-03 16:51:16 +0100540
David Brazdila2358d42020-01-27 18:51:38 +0000541 ASSERT_EQ(manifest_from_vec(&m, dtb_false),
Andrew Scullb2c3a242019-11-04 13:52:36 +0000542 MANIFEST_ERROR_MALFORMED_BOOLEAN);
David Brazdila2358d42020-01-27 18:51:38 +0000543 ASSERT_EQ(manifest_from_vec(&m, dtb_true),
Andrew Scullb2c3a242019-11-04 13:52:36 +0000544 MANIFEST_ERROR_MALFORMED_BOOLEAN);
David Brazdila2358d42020-01-27 18:51:38 +0000545 ASSERT_EQ(manifest_from_vec(&m, dtb_0),
Andrew Scullb2c3a242019-11-04 13:52:36 +0000546 MANIFEST_ERROR_MALFORMED_BOOLEAN);
David Brazdila2358d42020-01-27 18:51:38 +0000547 ASSERT_EQ(manifest_from_vec(&m, dtb_1),
Andrew Scullb2c3a242019-11-04 13:52:36 +0000548 MANIFEST_ERROR_MALFORMED_BOOLEAN);
Andrew Scullae9962e2019-10-03 16:51:16 +0100549}
550
David Brazdil7a462ec2019-08-15 12:27:47 +0100551TEST(manifest, valid)
552{
553 struct manifest m;
554 struct manifest_vm *vm;
David Brazdil7a462ec2019-08-15 12:27:47 +0100555
David Brazdil52256ff2019-08-23 15:15:15 +0100556 /* clang-format off */
557 std::vector<char> dtb = ManifestDtBuilder()
558 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100559 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100560 .StartChild("vm1")
561 .DebugName("primary_vm")
Andrew Scull72b43c02019-09-18 13:53:45 +0100562 .KernelFilename("primary_kernel")
David Brazdile6f83222019-09-23 14:47:37 +0100563 .RamdiskFilename("primary_ramdisk")
Andrew Scullae9962e2019-10-03 16:51:16 +0100564 .SmcWhitelist({0x32000000, 0x33001111})
David Brazdil52256ff2019-08-23 15:15:15 +0100565 .EndChild()
566 .StartChild("vm3")
567 .DebugName("second_secondary_vm")
568 .VcpuCount(43)
569 .MemSize(0x12345)
Andrew Scull72b43c02019-09-18 13:53:45 +0100570 .KernelFilename("second_secondary_kernel")
David Brazdil52256ff2019-08-23 15:15:15 +0100571 .EndChild()
572 .StartChild("vm2")
573 .DebugName("first_secondary_vm")
574 .VcpuCount(42)
575 .MemSize(12345)
Andrew Scullae9962e2019-10-03 16:51:16 +0100576 .SmcWhitelist({0x04000000, 0x30002222, 0x31445566})
577 .SmcWhitelistPermissive()
David Brazdil52256ff2019-08-23 15:15:15 +0100578 .EndChild()
579 .EndChild()
580 .Build();
581 /* clang-format on */
582
David Brazdila2358d42020-01-27 18:51:38 +0000583 ASSERT_EQ(manifest_from_vec(&m, dtb), MANIFEST_SUCCESS);
David Brazdil0251b942019-09-10 15:59:50 +0100584 ASSERT_EQ(m.vm_count, 3);
David Brazdil7a462ec2019-08-15 12:27:47 +0100585
586 vm = &m.vm[0];
David Brazdil136f2942019-09-23 14:11:03 +0100587 ASSERT_STREQ(string_data(&vm->debug_name), "primary_vm");
588 ASSERT_STREQ(string_data(&vm->kernel_filename), "primary_kernel");
David Brazdile6f83222019-09-23 14:47:37 +0100589 ASSERT_STREQ(string_data(&vm->primary.ramdisk_filename),
590 "primary_ramdisk");
Andrew Scullae9962e2019-10-03 16:51:16 +0100591 ASSERT_THAT(
592 std::span(vm->smc_whitelist.smcs, vm->smc_whitelist.smc_count),
593 ElementsAre(0x32000000, 0x33001111));
594 ASSERT_FALSE(vm->smc_whitelist.permissive);
David Brazdil7a462ec2019-08-15 12:27:47 +0100595
596 vm = &m.vm[1];
David Brazdil136f2942019-09-23 14:11:03 +0100597 ASSERT_STREQ(string_data(&vm->debug_name), "first_secondary_vm");
598 ASSERT_STREQ(string_data(&vm->kernel_filename), "");
David Brazdil7a462ec2019-08-15 12:27:47 +0100599 ASSERT_EQ(vm->secondary.vcpu_count, 42);
600 ASSERT_EQ(vm->secondary.mem_size, 12345);
Andrew Scullae9962e2019-10-03 16:51:16 +0100601 ASSERT_THAT(
602 std::span(vm->smc_whitelist.smcs, vm->smc_whitelist.smc_count),
603 ElementsAre(0x04000000, 0x30002222, 0x31445566));
604 ASSERT_TRUE(vm->smc_whitelist.permissive);
David Brazdil7a462ec2019-08-15 12:27:47 +0100605
606 vm = &m.vm[2];
David Brazdil136f2942019-09-23 14:11:03 +0100607 ASSERT_STREQ(string_data(&vm->debug_name), "second_secondary_vm");
608 ASSERT_STREQ(string_data(&vm->kernel_filename),
609 "second_secondary_kernel");
David Brazdil7a462ec2019-08-15 12:27:47 +0100610 ASSERT_EQ(vm->secondary.vcpu_count, 43);
611 ASSERT_EQ(vm->secondary.mem_size, 0x12345);
Andrew Scullae9962e2019-10-03 16:51:16 +0100612 ASSERT_THAT(
613 std::span(vm->smc_whitelist.smcs, vm->smc_whitelist.smc_count),
614 IsEmpty());
615 ASSERT_FALSE(vm->smc_whitelist.permissive);
David Brazdil7a462ec2019-08-15 12:27:47 +0100616}
617
Olivier Deprez62d99e32020-01-09 15:58:07 +0100618/**
619 * Class for programatically building a Partition package.
620 */
621class Partition_package
622{
623 public:
624 __attribute__((aligned(PAGE_SIZE))) struct sp_pkg_header spkg;
625 char manifest_dtb[PAGE_SIZE] = {};
626
627 Partition_package(const std::vector<char> &vec)
628 {
629 // Initialise header field
630 spkg.magic = SP_PKG_HEADER_MAGIC;
631 spkg.version = SP_PKG_HEADER_VERSION;
632 spkg.pm_offset = sizeof(struct sp_pkg_header);
633 spkg.pm_size = vec.size();
634
635 // Copy dtb into package
636 std::copy(vec.begin(), vec.end(), manifest_dtb);
637 }
638};
639
640static enum manifest_return_code ffa_manifest_from_vec(
641 struct manifest *m, const std::vector<char> &vec)
642{
643 struct memiter it;
644 struct mpool ppool;
645 struct mm_stage1_locked mm_stage1_locked;
646
647 Partition_package spkg(vec);
648
649 /* clang-format off */
650 std::vector<char> core_dtb = ManifestDtBuilder()
651 .StartChild("hypervisor")
652 .Compatible()
653 .StartChild("vm1")
654 .DebugName("primary_vm")
655 .FfaPartition()
656 .LoadAddress((uint64_t)&spkg)
657 .EndChild()
658 .EndChild()
659 .Build();
660 /* clang-format on */
661
662 memiter_init(&it, core_dtb.data(), core_dtb.size());
663 return manifest_init(mm_stage1_locked, m, &it, &ppool);
664}
665
666TEST(manifest, ffa_not_compatible)
667{
668 struct manifest m;
669
670 /* clang-format off */
671 std::vector<char> dtb = ManifestDtBuilder()
672 .Compatible({ "arm,ffa-manifest-2.0" })
673 .Property("ffa-version", "<0x10000>")
674 .Property("uuid", "<0xb4b5671e 0x4a904fe1 0xb81ffb13 0xdae1dacb>")
675 .Property("execution-ctx-count", "<1>")
676 .Property("exception-level", "<2>")
677 .Property("execution-state", "<0>")
678 .Property("load-address", "<0x7000000>")
679 .Property("entrypoint-offset", "<0x00001000>")
680 .Property("xlat-granule", "<0>")
681 .Property("messaging-method", "<1>")
682 .Build();
683 /* clang-format on */
684
685 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
686 MANIFEST_ERROR_NOT_COMPATIBLE);
687}
688
689TEST(manifest, ffa_missing_property)
690{
691 struct manifest m;
692
693 /* clang-format off */
694 std::vector<char> dtb = ManifestDtBuilder()
695 .Compatible({ "arm,ffa-manifest-1.0" })
696 .Property("ffa-version", "<0x10000>")
697 .Build();
698 /* clang-format on */
699
700 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
701 MANIFEST_ERROR_PROPERTY_NOT_FOUND);
702}
703
704TEST(manifest, ffa_validate_sanity_check)
705{
706 struct manifest m;
707
708 /* Incompatible version */
709 /* clang-format off */
710 std::vector<char> dtb = ManifestDtBuilder()
711 .Compatible({ "arm,ffa-manifest-1.0" })
712 .Property("ffa-version", "<0xa1>")
713 .Property("uuid", "<0xb4b5671e 0x4a904fe1 0xb81ffb13 0xdae1dacb>")
714 .Property("execution-ctx-count", "<1>")
715 .Property("exception-level", "<2>")
716 .Property("execution-state", "<0>")
717 .Property("load-address", "<0x7000000>")
718 .Property("entrypoint-offset", "<0x00001000>")
719 .Property("xlat-granule", "<0>")
720 .Property("messaging-method", "<1>")
721 .Build();
722 /* clang-format on */
723 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
724 MANIFEST_ERROR_NOT_COMPATIBLE);
725
726 /* Incompatible translation granule */
727 /* clang-format off */
728 dtb = ManifestDtBuilder()
729 .Compatible({ "arm,ffa-manifest-1.0" })
730 .Property("ffa-version", "<0x10000>")
731 .Property("uuid", "<0xb4b5671e 0x4a904fe1 0xb81ffb13 0xdae1dacb>")
732 .Property("execution-ctx-count", "<1>")
733 .Property("exception-level", "<2>")
734 .Property("execution-state", "<0>")
735 .Property("load-address", "<0x7000000>")
736 .Property("entrypoint-offset", "<0x00001000>")
737 .Property("xlat-granule", "<3>")
738 .Property("messaging-method", "<1>")
739 .Build();
740 /* clang-format on */
741 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
742 MANIFEST_ERROR_NOT_COMPATIBLE);
743
744 /* Incompatible exeption level */
745 /* clang-format off */
746 dtb = ManifestDtBuilder()
747 .Compatible({ "arm,ffa-manifest-1.0" })
748 .Property("ffa-version", "<0x10000>")
749 .Property("uuid", "<0xb4b5671e 0x4a904fe1 0xb81ffb13 0xdae1dacb>")
750 .Property("execution-ctx-count", "<1>")
751 .Property("exception-level", "<6>")
752 .Property("execution-state", "<0>")
753 .Property("load-address", "<0x7000000>")
754 .Property("entrypoint-offset", "<0x00001000>")
755 .Property("xlat-granule", "<0>")
756 .Property("messaging-method", "<1>")
757 .Build();
758 /* clang-format on */
759 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
760 MANIFEST_ERROR_NOT_COMPATIBLE);
761
762 /* Incompatible execution state */
763 /* clang-format off */
764 dtb = ManifestDtBuilder()
765 .Compatible({ "arm,ffa-manifest-1.0" })
766 .Property("ffa-version", "<0x10000>")
767 .Property("uuid", "<0xb4b5671e 0x4a904fe1 0xb81ffb13 0xdae1dacb>")
768 .Property("execution-ctx-count", "<1>")
769 .Property("exception-level", "<2>")
770 .Property("execution-state", "<2>")
771 .Property("load-address", "<0x7000000>")
772 .Property("entrypoint-offset", "<0x00001000>")
773 .Property("xlat-granule", "<0>")
774 .Property("messaging-method", "<1>")
775 .Build();
776 /* clang-format on */
777 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
778 MANIFEST_ERROR_NOT_COMPATIBLE);
779
780 /* Incompatible messaging method */
781 /* clang-format off */
782 dtb = ManifestDtBuilder()
783 .Compatible({ "arm,ffa-manifest-1.0" })
784 .Property("ffa-version", "<0x10000>")
785 .Property("uuid", "<0xb4b5671e 0x4a904fe1 0xb81ffb13 0xdae1dacb>")
786 .Property("execution-ctx-count", "<1>")
787 .Property("exception-level", "<2>")
788 .Property("execution-state", "<0>")
789 .Property("load-address", "<0x7000000>")
790 .Property("entrypoint-offset", "<0x00001000>")
791 .Property("xlat-granule", "<0>")
792 .Property("messaging-method", "<3>")
793 .Build();
794 /* clang-format on */
795 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
796 MANIFEST_ERROR_NOT_COMPATIBLE);
797}
798
799TEST(manifest, ffa_valid)
800{
801 struct manifest m;
802
803 /* clang-format off */
804 std::vector<char> dtb = ManifestDtBuilder()
805 .Compatible({ "arm,ffa-manifest-1.0" })
806 .Property("ffa-version", "<0x10000>")
807 .Property("uuid", "<0xb4b5671e 0x4a904fe1 0xb81ffb13 0xdae1dacb>")
808 .Property("execution-ctx-count", "<1>")
809 .Property("exception-level", "<2>")
810 .Property("execution-state", "<0>")
811 .Property("load-address", "<0x7000000>")
812 .Property("entrypoint-offset", "<0x00001000>")
813 .Property("xlat-granule", "<0>")
814 .Property("messaging-method", "<1>")
815 .Build();
816 /* clang-format on */
817
818 ASSERT_EQ(ffa_manifest_from_vec(&m, dtb), MANIFEST_SUCCESS);
819
820 ASSERT_EQ(m.vm[0].sp.ffa_version, 0x10000);
821 ASSERT_THAT(
Fuad Tabbae4efcc32020-07-16 15:37:27 +0100822 std::span(m.vm[0].sp.uuid.uuid, 4),
Olivier Deprez62d99e32020-01-09 15:58:07 +0100823 ElementsAre(0xb4b5671e, 0x4a904fe1, 0xb81ffb13, 0xdae1dacb));
824 ASSERT_EQ(m.vm[0].sp.execution_ctx_count, 1);
825 ASSERT_EQ(m.vm[0].sp.run_time_el, S_EL1);
826 ASSERT_EQ(m.vm[0].sp.execution_state, AARCH64);
827 ASSERT_EQ(m.vm[0].sp.load_addr, 0x7000000);
828 ASSERT_EQ(m.vm[0].sp.ep_offset, 0x00001000);
829 ASSERT_EQ(m.vm[0].sp.xlat_granule, PAGE_4KB);
830 ASSERT_EQ(m.vm[0].sp.messaging_method, INDIRECT_MESSAGING);
831}
832
David Brazdil7a462ec2019-08-15 12:27:47 +0100833} /* namespace */