blob: 432b8f8fd812c597ef7c2069948d13c943992f0e [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>
33void exec(const char *program, char *const args[], const T &stdin,
34 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. */
96 execv(program, args);
97 }
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 {
125 char *const dtc_args[] = {NULL};
126 std::vector<char> dtc_stdout;
127
128 /* Finish root node. */
129 EndChild();
130
131 exec("./build/image/dtc.py", dtc_args, dts_.str(), &dtc_stdout);
132 return dtc_stdout;
133 }
134
135 ManifestDtBuilder &StartChild(const std::string_view &name)
136 {
137 dts_ << name << " {" << std::endl;
138 return *this;
139 }
140
141 ManifestDtBuilder &EndChild()
142 {
143 dts_ << "};" << std::endl;
144 return *this;
145 }
146
David Brazdil74e9c3b2019-08-28 11:09:08 +0100147 ManifestDtBuilder &Compatible(const std::vector<std::string_view>
148 &value = {"hafnium,hafnium"})
149 {
150 return StringListProperty("compatible", value);
151 }
152
David Brazdil52256ff2019-08-23 15:15:15 +0100153 ManifestDtBuilder &DebugName(const std::string_view &value)
154 {
155 return StringProperty("debug_name", value);
156 }
157
158 ManifestDtBuilder &KernelFilename(const std::string_view &value)
159 {
160 return StringProperty("kernel_filename", value);
161 }
162
163 ManifestDtBuilder &VcpuCount(uint64_t value)
164 {
165 return IntegerProperty("vcpu_count", value);
166 }
167
168 ManifestDtBuilder &MemSize(uint64_t value)
169 {
170 return IntegerProperty("mem_size", value);
171 }
172
173 private:
174 ManifestDtBuilder &StringProperty(const std::string_view &name,
175 const std::string_view &value)
176 {
177 dts_ << name << " = \"" << value << "\";" << std::endl;
178 return *this;
179 }
180
David Brazdil74e9c3b2019-08-28 11:09:08 +0100181 ManifestDtBuilder &StringListProperty(
182 const std::string_view &name,
183 const std::vector<std::string_view> &value)
184 {
185 bool is_first = true;
186
187 dts_ << name << " = ";
188 for (const std::string_view &entry : value) {
189 if (is_first) {
190 is_first = false;
191 } else {
192 dts_ << ", ";
193 }
194 dts_ << "\"" << entry << "\"";
195 }
196 dts_ << ";" << std::endl;
197 return *this;
198 }
199
David Brazdil52256ff2019-08-23 15:15:15 +0100200 ManifestDtBuilder &IntegerProperty(const std::string_view &name,
201 uint64_t value)
202 {
203 dts_ << name << " = <" << value << ">;" << std::endl;
204 return *this;
205 }
206
207 std::stringstream dts_;
208};
209
210TEST(manifest, no_hypervisor_node)
David Brazdil7a462ec2019-08-15 12:27:47 +0100211{
212 struct manifest m;
213 struct memiter it;
David Brazdil52256ff2019-08-23 15:15:15 +0100214 std::vector<char> dtb = ManifestDtBuilder().Build();
David Brazdil7a462ec2019-08-15 12:27:47 +0100215
David Brazdil52256ff2019-08-23 15:15:15 +0100216 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100217 ASSERT_EQ(manifest_init(&m, &it),
218 MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
219}
220
David Brazdil74e9c3b2019-08-28 11:09:08 +0100221TEST(manifest, no_compatible_property)
David Brazdil7a462ec2019-08-15 12:27:47 +0100222{
223 struct manifest m;
224 struct memiter it;
225
David Brazdil52256ff2019-08-23 15:15:15 +0100226 /* clang-format off */
227 std::vector<char> dtb = ManifestDtBuilder()
228 .StartChild("hypervisor")
229 .EndChild()
230 .Build();
231 /* clang-format on */
232
233 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil74e9c3b2019-08-28 11:09:08 +0100234 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_PROPERTY_NOT_FOUND);
David Brazdil7a462ec2019-08-15 12:27:47 +0100235}
236
David Brazdil74e9c3b2019-08-28 11:09:08 +0100237TEST(manifest, not_compatible)
David Brazdil7a462ec2019-08-15 12:27:47 +0100238{
239 struct manifest m;
240 struct memiter it;
241
David Brazdil52256ff2019-08-23 15:15:15 +0100242 /* clang-format off */
243 std::vector<char> dtb = ManifestDtBuilder()
244 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100245 .Compatible({ "foo,bar" })
246 .EndChild()
247 .Build();
248 /* clang-format on */
249
250 memiter_init(&it, dtb.data(), dtb.size());
251 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_NOT_COMPATIBLE);
252}
253
254TEST(manifest, compatible_one_of_many)
255{
256 struct manifest m;
257 struct memiter it;
258
259 /* clang-format off */
260 std::vector<char> dtb = ManifestDtBuilder()
261 .StartChild("hypervisor")
262 .Compatible({ "foo,bar", "hafnium,hafnium" })
263 .StartChild("vm1")
264 .DebugName("primary")
265 .EndChild()
266 .EndChild()
267 .Build();
268 /* clang-format on */
269
270 memiter_init(&it, dtb.data(), dtb.size());
271 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
272}
273
274TEST(manifest, no_vm_nodes)
275{
276 struct manifest m;
277 struct memiter it;
278
279 /* clang-format off */
280 std::vector<char> dtb = ManifestDtBuilder()
281 .StartChild("hypervisor")
282 .Compatible()
283 .EndChild()
284 .Build();
285 /* clang-format on */
286
287 memiter_init(&it, dtb.data(), dtb.size());
288 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_NO_PRIMARY_VM);
289}
290
291TEST(manifest, reserved_vm_id)
292{
293 struct manifest m;
294 struct memiter it;
295
296 /* clang-format off */
297 std::vector<char> dtb = ManifestDtBuilder()
298 .StartChild("hypervisor")
299 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100300 .StartChild("vm1")
301 .DebugName("primary_vm")
302 .EndChild()
303 .StartChild("vm0")
304 .DebugName("reserved_vm")
305 .VcpuCount(1)
306 .MemSize(0x1000)
307 .KernelFilename("kernel")
308 .EndChild()
309 .EndChild()
310 .Build();
311 /* clang-format on */
312
313 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100314 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_RESERVED_VM_ID);
315}
316
David Brazdil52256ff2019-08-23 15:15:15 +0100317static std::vector<char> gen_vcpu_count_limit_dtb(uint64_t vcpu_count)
318{
319 /* clang-format off */
320 return ManifestDtBuilder()
321 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100322 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100323 .StartChild("vm1")
324 .DebugName("primary_vm")
325 .EndChild()
326 .StartChild("vm2")
327 .DebugName("secondary_vm")
328 .VcpuCount(vcpu_count)
329 .MemSize(0x1000)
330 .KernelFilename("kernel")
331 .EndChild()
332 .EndChild()
333 .Build();
334 /* clang-format on */
335}
David Brazdil7a462ec2019-08-15 12:27:47 +0100336
337TEST(manifest, vcpu_count_limit)
338{
339 struct manifest m;
340 struct memiter it;
David Brazdil52256ff2019-08-23 15:15:15 +0100341 std::vector<char> dtb_last_valid = gen_vcpu_count_limit_dtb(UINT16_MAX);
342 std::vector<char> dtb_first_invalid =
343 gen_vcpu_count_limit_dtb(UINT16_MAX + 1);
David Brazdil7a462ec2019-08-15 12:27:47 +0100344
David Brazdil52256ff2019-08-23 15:15:15 +0100345 memiter_init(&it, dtb_last_valid.data(), dtb_last_valid.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100346 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
347 ASSERT_EQ(m.num_vms, 2);
348 ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
349
David Brazdil52256ff2019-08-23 15:15:15 +0100350 memiter_init(&it, dtb_first_invalid.data(), dtb_first_invalid.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100351 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_INTEGER_OVERFLOW);
352}
353
David Brazdil7a462ec2019-08-15 12:27:47 +0100354TEST(manifest, valid)
355{
356 struct manifest m;
357 struct manifest_vm *vm;
358 struct memiter it;
359
David Brazdil52256ff2019-08-23 15:15:15 +0100360 /* clang-format off */
361 std::vector<char> dtb = ManifestDtBuilder()
362 .StartChild("hypervisor")
David Brazdil74e9c3b2019-08-28 11:09:08 +0100363 .Compatible()
David Brazdil52256ff2019-08-23 15:15:15 +0100364 .StartChild("vm1")
365 .DebugName("primary_vm")
366 .EndChild()
367 .StartChild("vm3")
368 .DebugName("second_secondary_vm")
369 .VcpuCount(43)
370 .MemSize(0x12345)
371 .KernelFilename("second_kernel")
372 .EndChild()
373 .StartChild("vm2")
374 .DebugName("first_secondary_vm")
375 .VcpuCount(42)
376 .MemSize(12345)
377 .KernelFilename("first_kernel")
378 .EndChild()
379 .EndChild()
380 .Build();
381 /* clang-format on */
382
383 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100384
385 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
386 ASSERT_EQ(m.num_vms, 3);
387
388 vm = &m.vm[0];
389 ASSERT_TRUE(memiter_iseq(&vm->debug_name, "primary_vm"));
390
391 vm = &m.vm[1];
392 ASSERT_TRUE(memiter_iseq(&vm->debug_name, "first_secondary_vm"));
393 ASSERT_EQ(vm->secondary.vcpu_count, 42);
394 ASSERT_EQ(vm->secondary.mem_size, 12345);
395 ASSERT_TRUE(
396 memiter_iseq(&vm->secondary.kernel_filename, "first_kernel"));
397
398 vm = &m.vm[2];
399 ASSERT_TRUE(memiter_iseq(&vm->debug_name, "second_secondary_vm"));
400 ASSERT_EQ(vm->secondary.vcpu_count, 43);
401 ASSERT_EQ(vm->secondary.mem_size, 0x12345);
402 ASSERT_TRUE(
403 memiter_iseq(&vm->secondary.kernel_filename, "second_kernel"));
404}
405
406} /* namespace */