blob: 0a23745af756fb392eaed80d28c60808932c86af [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
147 ManifestDtBuilder &DebugName(const std::string_view &value)
148 {
149 return StringProperty("debug_name", value);
150 }
151
152 ManifestDtBuilder &KernelFilename(const std::string_view &value)
153 {
154 return StringProperty("kernel_filename", value);
155 }
156
157 ManifestDtBuilder &VcpuCount(uint64_t value)
158 {
159 return IntegerProperty("vcpu_count", value);
160 }
161
162 ManifestDtBuilder &MemSize(uint64_t value)
163 {
164 return IntegerProperty("mem_size", value);
165 }
166
167 private:
168 ManifestDtBuilder &StringProperty(const std::string_view &name,
169 const std::string_view &value)
170 {
171 dts_ << name << " = \"" << value << "\";" << std::endl;
172 return *this;
173 }
174
175 ManifestDtBuilder &IntegerProperty(const std::string_view &name,
176 uint64_t value)
177 {
178 dts_ << name << " = <" << value << ">;" << std::endl;
179 return *this;
180 }
181
182 std::stringstream dts_;
183};
184
185TEST(manifest, no_hypervisor_node)
David Brazdil7a462ec2019-08-15 12:27:47 +0100186{
187 struct manifest m;
188 struct memiter it;
David Brazdil52256ff2019-08-23 15:15:15 +0100189 std::vector<char> dtb = ManifestDtBuilder().Build();
David Brazdil7a462ec2019-08-15 12:27:47 +0100190
David Brazdil52256ff2019-08-23 15:15:15 +0100191 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100192 ASSERT_EQ(manifest_init(&m, &it),
193 MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
194}
195
David Brazdil7a462ec2019-08-15 12:27:47 +0100196TEST(manifest, no_vms)
197{
198 struct manifest m;
199 struct memiter it;
200
David Brazdil52256ff2019-08-23 15:15:15 +0100201 /* clang-format off */
202 std::vector<char> dtb = ManifestDtBuilder()
203 .StartChild("hypervisor")
204 .EndChild()
205 .Build();
206 /* clang-format on */
207
208 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100209 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_NO_PRIMARY_VM);
210}
211
David Brazdil7a462ec2019-08-15 12:27:47 +0100212TEST(manifest, reserved_vmid)
213{
214 struct manifest m;
215 struct memiter it;
216
David Brazdil52256ff2019-08-23 15:15:15 +0100217 /* clang-format off */
218 std::vector<char> dtb = ManifestDtBuilder()
219 .StartChild("hypervisor")
220 .StartChild("vm1")
221 .DebugName("primary_vm")
222 .EndChild()
223 .StartChild("vm0")
224 .DebugName("reserved_vm")
225 .VcpuCount(1)
226 .MemSize(0x1000)
227 .KernelFilename("kernel")
228 .EndChild()
229 .EndChild()
230 .Build();
231 /* clang-format on */
232
233 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100234 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_RESERVED_VM_ID);
235}
236
David Brazdil52256ff2019-08-23 15:15:15 +0100237static std::vector<char> gen_vcpu_count_limit_dtb(uint64_t vcpu_count)
238{
239 /* clang-format off */
240 return ManifestDtBuilder()
241 .StartChild("hypervisor")
242 .StartChild("vm1")
243 .DebugName("primary_vm")
244 .EndChild()
245 .StartChild("vm2")
246 .DebugName("secondary_vm")
247 .VcpuCount(vcpu_count)
248 .MemSize(0x1000)
249 .KernelFilename("kernel")
250 .EndChild()
251 .EndChild()
252 .Build();
253 /* clang-format on */
254}
David Brazdil7a462ec2019-08-15 12:27:47 +0100255
256TEST(manifest, vcpu_count_limit)
257{
258 struct manifest m;
259 struct memiter it;
David Brazdil52256ff2019-08-23 15:15:15 +0100260 std::vector<char> dtb_last_valid = gen_vcpu_count_limit_dtb(UINT16_MAX);
261 std::vector<char> dtb_first_invalid =
262 gen_vcpu_count_limit_dtb(UINT16_MAX + 1);
David Brazdil7a462ec2019-08-15 12:27:47 +0100263
David Brazdil52256ff2019-08-23 15:15:15 +0100264 memiter_init(&it, dtb_last_valid.data(), dtb_last_valid.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100265 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
266 ASSERT_EQ(m.num_vms, 2);
267 ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
268
David Brazdil52256ff2019-08-23 15:15:15 +0100269 memiter_init(&it, dtb_first_invalid.data(), dtb_first_invalid.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100270 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_INTEGER_OVERFLOW);
271}
272
David Brazdil7a462ec2019-08-15 12:27:47 +0100273TEST(manifest, valid)
274{
275 struct manifest m;
276 struct manifest_vm *vm;
277 struct memiter it;
278
David Brazdil52256ff2019-08-23 15:15:15 +0100279 /* clang-format off */
280 std::vector<char> dtb = ManifestDtBuilder()
281 .StartChild("hypervisor")
282 .StartChild("vm1")
283 .DebugName("primary_vm")
284 .EndChild()
285 .StartChild("vm3")
286 .DebugName("second_secondary_vm")
287 .VcpuCount(43)
288 .MemSize(0x12345)
289 .KernelFilename("second_kernel")
290 .EndChild()
291 .StartChild("vm2")
292 .DebugName("first_secondary_vm")
293 .VcpuCount(42)
294 .MemSize(12345)
295 .KernelFilename("first_kernel")
296 .EndChild()
297 .EndChild()
298 .Build();
299 /* clang-format on */
300
301 memiter_init(&it, dtb.data(), dtb.size());
David Brazdil7a462ec2019-08-15 12:27:47 +0100302
303 ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
304 ASSERT_EQ(m.num_vms, 3);
305
306 vm = &m.vm[0];
307 ASSERT_TRUE(memiter_iseq(&vm->debug_name, "primary_vm"));
308
309 vm = &m.vm[1];
310 ASSERT_TRUE(memiter_iseq(&vm->debug_name, "first_secondary_vm"));
311 ASSERT_EQ(vm->secondary.vcpu_count, 42);
312 ASSERT_EQ(vm->secondary.mem_size, 12345);
313 ASSERT_TRUE(
314 memiter_iseq(&vm->secondary.kernel_filename, "first_kernel"));
315
316 vm = &m.vm[2];
317 ASSERT_TRUE(memiter_iseq(&vm->debug_name, "second_secondary_vm"));
318 ASSERT_EQ(vm->secondary.vcpu_count, 43);
319 ASSERT_EQ(vm->secondary.mem_size, 0x12345);
320 ASSERT_TRUE(
321 memiter_iseq(&vm->secondary.kernel_filename, "second_kernel"));
322}
323
324} /* namespace */