blob: 14eef49cb506124acb8aee22193e4e38fde4bd3a [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
164 ManifestDtBuilder &VcpuCount(uint64_t value)
165 {
166 return IntegerProperty("vcpu_count", value);
167 }
168
169 ManifestDtBuilder &MemSize(uint64_t value)
170 {
171 return IntegerProperty("mem_size", value);
172 }
173
174 private:
175 ManifestDtBuilder &StringProperty(const std::string_view &name,
176 const std::string_view &value)
177 {
178 dts_ << name << " = \"" << value << "\";" << std::endl;
179 return *this;
180 }
181
David Brazdil74e9c3b2019-08-28 11:09:08 +0100182 ManifestDtBuilder &StringListProperty(
183 const std::string_view &name,
184 const std::vector<std::string_view> &value)
185 {
186 bool is_first = true;
187
188 dts_ << name << " = ";
189 for (const std::string_view &entry : value) {
190 if (is_first) {
191 is_first = false;
192 } else {
193 dts_ << ", ";
194 }
195 dts_ << "\"" << entry << "\"";
196 }
197 dts_ << ";" << std::endl;
198 return *this;
199 }
200
David Brazdil52256ff2019-08-23 15:15:15 +0100201 ManifestDtBuilder &IntegerProperty(const std::string_view &name,
202 uint64_t value)
203 {
204 dts_ << name << " = <" << value << ">;" << std::endl;
205 return *this;
206 }
207
208 std::stringstream dts_;
209};
210
David Brazdil0dbb41f2019-09-09 18:03:35 +0100211static bool get_fdt_root(const std::vector<char> &dtb,
212 struct fdt_node *fdt_root)
213{
214 const struct fdt_header *fdt_header;
215
216 fdt_header = reinterpret_cast<const struct fdt_header *>(dtb.data());
217 return fdt_root_node(fdt_root, fdt_header) &&
218 fdt_find_child(fdt_root, "");
219}
220
David Brazdil52256ff2019-08-23 15:15:15 +0100221TEST(manifest, no_hypervisor_node)
David Brazdil7a462ec2019-08-15 12:27:47 +0100222{
223 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100224 struct fdt_node fdt_root;
David Brazdil52256ff2019-08-23 15:15:15 +0100225 std::vector<char> dtb = ManifestDtBuilder().Build();
David Brazdil7a462ec2019-08-15 12:27:47 +0100226
David Brazdil0dbb41f2019-09-09 18:03:35 +0100227 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
228 ASSERT_EQ(manifest_init(&m, &fdt_root),
David Brazdil7a462ec2019-08-15 12:27:47 +0100229 MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
230}
231
David Brazdil74e9c3b2019-08-28 11:09:08 +0100232TEST(manifest, no_compatible_property)
David Brazdil7a462ec2019-08-15 12:27:47 +0100233{
234 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100235 struct fdt_node fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100236
David Brazdil52256ff2019-08-23 15:15:15 +0100237 /* clang-format off */
238 std::vector<char> dtb = ManifestDtBuilder()
239 .StartChild("hypervisor")
240 .EndChild()
241 .Build();
242 /* clang-format on */
243
David Brazdil0dbb41f2019-09-09 18:03:35 +0100244 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
245 ASSERT_EQ(manifest_init(&m, &fdt_root),
246 MANIFEST_ERROR_PROPERTY_NOT_FOUND);
David Brazdil7a462ec2019-08-15 12:27:47 +0100247}
248
David Brazdil74e9c3b2019-08-28 11:09:08 +0100249TEST(manifest, not_compatible)
David Brazdil7a462ec2019-08-15 12:27:47 +0100250{
251 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100252 struct fdt_node fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100253
David Brazdil52256ff2019-08-23 15:15:15 +0100254 /* clang-format off */
255 std::vector<char> dtb = ManifestDtBuilder()
256 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100257 .Compatible({ "foo,bar" })
258 .EndChild()
259 .Build();
260 /* clang-format on */
261
David Brazdil0dbb41f2019-09-09 18:03:35 +0100262 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
263 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_NOT_COMPATIBLE);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100264}
265
266TEST(manifest, compatible_one_of_many)
267{
268 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100269 struct fdt_node fdt_root;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100270
271 /* clang-format off */
272 std::vector<char> dtb = ManifestDtBuilder()
273 .StartChild("hypervisor")
274 .Compatible({ "foo,bar", "hafnium,hafnium" })
275 .StartChild("vm1")
276 .DebugName("primary")
277 .EndChild()
278 .EndChild()
279 .Build();
280 /* clang-format on */
281
David Brazdil0dbb41f2019-09-09 18:03:35 +0100282 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
283 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100284}
285
286TEST(manifest, no_vm_nodes)
287{
288 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100289 struct fdt_node fdt_root;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100290
291 /* clang-format off */
292 std::vector<char> dtb = ManifestDtBuilder()
293 .StartChild("hypervisor")
294 .Compatible()
295 .EndChild()
296 .Build();
297 /* clang-format on */
298
David Brazdil0dbb41f2019-09-09 18:03:35 +0100299 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
300 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_NO_PRIMARY_VM);
301}
302
303static std::vector<char> gen_long_string_dtb(bool valid)
304{
305 const char last_valid[] = "1234567890123456789012345678901";
306 const char first_invalid[] = "12345678901234567890123456789012";
307 static_assert(sizeof(last_valid) == MANIFEST_MAX_STRING_LENGTH);
308 static_assert(sizeof(first_invalid) == MANIFEST_MAX_STRING_LENGTH + 1);
309
310 /* clang-format off */
311 return ManifestDtBuilder()
312 .StartChild("hypervisor")
313 .Compatible()
314 .StartChild("vm1")
315 .DebugName(valid ? last_valid : first_invalid)
316 .EndChild()
317 .EndChild()
318 .Build();
319 /* clang-format on */
320}
321
322TEST(manifest, long_string)
323{
324 struct manifest m;
325 struct fdt_node fdt_root;
326
327 std::vector<char> dtb_last_valid = gen_long_string_dtb(true);
328 std::vector<char> dtb_first_invalid = gen_long_string_dtb(false);
329
330 ASSERT_TRUE(get_fdt_root(dtb_last_valid, &fdt_root));
331 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
332
333 ASSERT_TRUE(get_fdt_root(dtb_first_invalid, &fdt_root));
334 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_STRING_TOO_LONG);
David Brazdil74e9c3b2019-08-28 11:09:08 +0100335}
336
337TEST(manifest, reserved_vm_id)
338{
339 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100340 struct fdt_node fdt_root;
David Brazdil74e9c3b2019-08-28 11:09:08 +0100341
342 /* clang-format off */
343 std::vector<char> dtb = ManifestDtBuilder()
344 .StartChild("hypervisor")
345 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100346 .StartChild("vm1")
347 .DebugName("primary_vm")
348 .EndChild()
349 .StartChild("vm0")
350 .DebugName("reserved_vm")
351 .VcpuCount(1)
352 .MemSize(0x1000)
353 .KernelFilename("kernel")
354 .EndChild()
355 .EndChild()
356 .Build();
357 /* clang-format on */
358
David Brazdil0dbb41f2019-09-09 18:03:35 +0100359 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
360 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_ERROR_RESERVED_VM_ID);
David Brazdil7a462ec2019-08-15 12:27:47 +0100361}
362
David Brazdil52256ff2019-08-23 15:15:15 +0100363static std::vector<char> gen_vcpu_count_limit_dtb(uint64_t vcpu_count)
364{
365 /* clang-format off */
366 return ManifestDtBuilder()
367 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100368 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100369 .StartChild("vm1")
370 .DebugName("primary_vm")
371 .EndChild()
372 .StartChild("vm2")
373 .DebugName("secondary_vm")
374 .VcpuCount(vcpu_count)
375 .MemSize(0x1000)
376 .KernelFilename("kernel")
377 .EndChild()
378 .EndChild()
379 .Build();
380 /* clang-format on */
381}
David Brazdil7a462ec2019-08-15 12:27:47 +0100382
383TEST(manifest, vcpu_count_limit)
384{
385 struct manifest m;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100386 struct fdt_node fdt_root;
David Brazdil52256ff2019-08-23 15:15:15 +0100387 std::vector<char> dtb_last_valid = gen_vcpu_count_limit_dtb(UINT16_MAX);
388 std::vector<char> dtb_first_invalid =
389 gen_vcpu_count_limit_dtb(UINT16_MAX + 1);
David Brazdil7a462ec2019-08-15 12:27:47 +0100390
David Brazdil0dbb41f2019-09-09 18:03:35 +0100391 ASSERT_TRUE(get_fdt_root(dtb_last_valid, &fdt_root));
392 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
David Brazdil0251b942019-09-10 15:59:50 +0100393 ASSERT_EQ(m.vm_count, 2);
David Brazdil7a462ec2019-08-15 12:27:47 +0100394 ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
395
David Brazdil0dbb41f2019-09-09 18:03:35 +0100396 ASSERT_TRUE(get_fdt_root(dtb_first_invalid, &fdt_root));
397 ASSERT_EQ(manifest_init(&m, &fdt_root),
398 MANIFEST_ERROR_INTEGER_OVERFLOW);
David Brazdil7a462ec2019-08-15 12:27:47 +0100399}
400
David Brazdil7a462ec2019-08-15 12:27:47 +0100401TEST(manifest, valid)
402{
403 struct manifest m;
404 struct manifest_vm *vm;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100405 struct fdt_node fdt_root;
David Brazdil7a462ec2019-08-15 12:27:47 +0100406
David Brazdil52256ff2019-08-23 15:15:15 +0100407 /* clang-format off */
408 std::vector<char> dtb = ManifestDtBuilder()
409 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100410 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100411 .StartChild("vm1")
412 .DebugName("primary_vm")
Andrew Scull72b43c02019-09-18 13:53:45 +0100413 .KernelFilename("primary_kernel")
David Brazdil52256ff2019-08-23 15:15:15 +0100414 .EndChild()
415 .StartChild("vm3")
416 .DebugName("second_secondary_vm")
417 .VcpuCount(43)
418 .MemSize(0x12345)
Andrew Scull72b43c02019-09-18 13:53:45 +0100419 .KernelFilename("second_secondary_kernel")
David Brazdil52256ff2019-08-23 15:15:15 +0100420 .EndChild()
421 .StartChild("vm2")
422 .DebugName("first_secondary_vm")
423 .VcpuCount(42)
424 .MemSize(12345)
David Brazdil52256ff2019-08-23 15:15:15 +0100425 .EndChild()
426 .EndChild()
427 .Build();
428 /* clang-format on */
429
David Brazdil0dbb41f2019-09-09 18:03:35 +0100430 ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
David Brazdil7a462ec2019-08-15 12:27:47 +0100431
David Brazdil0dbb41f2019-09-09 18:03:35 +0100432 ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
David Brazdil0251b942019-09-10 15:59:50 +0100433 ASSERT_EQ(m.vm_count, 3);
David Brazdil7a462ec2019-08-15 12:27:47 +0100434
435 vm = &m.vm[0];
David Brazdil0dbb41f2019-09-09 18:03:35 +0100436 ASSERT_STREQ(vm->debug_name, "primary_vm");
Andrew Scull72b43c02019-09-18 13:53:45 +0100437 ASSERT_STREQ(vm->kernel_filename, "primary_kernel");
David Brazdil7a462ec2019-08-15 12:27:47 +0100438
439 vm = &m.vm[1];
David Brazdil0dbb41f2019-09-09 18:03:35 +0100440 ASSERT_STREQ(vm->debug_name, "first_secondary_vm");
Andrew Scull72b43c02019-09-18 13:53:45 +0100441 ASSERT_STREQ(vm->kernel_filename, "");
David Brazdil7a462ec2019-08-15 12:27:47 +0100442 ASSERT_EQ(vm->secondary.vcpu_count, 42);
443 ASSERT_EQ(vm->secondary.mem_size, 12345);
David Brazdil7a462ec2019-08-15 12:27:47 +0100444
445 vm = &m.vm[2];
David Brazdil0dbb41f2019-09-09 18:03:35 +0100446 ASSERT_STREQ(vm->debug_name, "second_secondary_vm");
Andrew Scull72b43c02019-09-18 13:53:45 +0100447 ASSERT_STREQ(vm->kernel_filename, "second_secondary_kernel");
David Brazdil7a462ec2019-08-15 12:27:47 +0100448 ASSERT_EQ(vm->secondary.vcpu_count, 43);
449 ASSERT_EQ(vm->secondary.mem_size, 0x12345);
David Brazdil7a462ec2019-08-15 12:27:47 +0100450}
451
452} /* namespace */