1 /*-
2 * Copyright (c) 2018-2022 Intel Corporation.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 */
26
27 #include <string.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/mman.h>
34
35 #include "dm.h"
36 #include "vmmapi.h"
37 #include "sw_load.h"
38 #include "log.h"
39
40
41 /* ovmf binary layout:
42 *
43 * +--------------------------------------------------+ <--OVMF Top
44 * | |offset: Top - 0x10 (reset vector) |
45 * + SECFV |------------------------------------+
46 * | |other |
47 * +--------------------------------------------------+
48 * | |
49 * + FVMAIN_COMPACT +
50 * | |
51 * +--------------------------------------------------+
52 * | |
53 * + NV data storage +
54 * | |
55 * +--------------------------------------------------+ <--OVMF offset 0
56 */
57
58 /* ovmf real entry is reset vector, which is (OVMF_TOP - 16) */
59 #define OVMF_TOP(ctx) (4*GB)
60
61 /* ovmf image size limit */
62 #define OVMF_SZ_LIMIT (2*MB)
63
64 /* ovmf split images size limit */
65 #define OVMF_VARS_SZ_LIMIT (128*KB)
66 #define OVMF_CODE_SZ_LIMIT (OVMF_SZ_LIMIT - OVMF_VARS_SZ_LIMIT)
67
68 /* ovmf NV storage begins at offset 0 */
69 #define OVMF_NVSTORAGE_OFFSET (OVMF_TOP(ctx) - ovmf_image_size())
70
71 /* ovmf NV storage size */
72 #define OVMF_NVSTORAGE_SZ (ovmf_file_name ? OVMF_VARS_SZ_LIMIT : ovmf_vars_size)
73
74 /* located in the ROM area */
75 #define OVMF_E820_BASE 0x000EF000UL
76
77 static char ovmf_path[STR_LEN];
78 static char ovmf_code_path[STR_LEN];
79 static char ovmf_vars_path[STR_LEN];
80 static size_t ovmf_size;
81 static size_t ovmf_code_size;
82 static size_t ovmf_vars_size;
83 static char *mmap_vars;
84 static bool writeback_nv_storage;
85
86 extern int init_cmos_vrpmb(struct vmctx *ctx);
87
88 size_t
ovmf_image_size(void)89 ovmf_image_size(void)
90 {
91 size_t size = 0;
92
93 if (ovmf_file_name)
94 size = ovmf_size;
95 else if (ovmf_code_file_name && ovmf_vars_file_name)
96 size = ovmf_code_size + ovmf_vars_size;
97
98 return size;
99 }
100
101 int
acrn_parse_ovmf(char * arg)102 acrn_parse_ovmf(char *arg)
103 {
104 int error = -1;
105 char *str, *cp, *token;
106
107 if (strnlen(arg, STR_LEN) < STR_LEN) {
108 str = cp = strdup(arg);
109
110 while ((token = strsep(&cp, ",")) != NULL) {
111 if (!strcmp(token, "w")) {
112 writeback_nv_storage = true;
113 } else if (!strncmp(token, "code=", sizeof("code=") - 1)) {
114 token += sizeof("code=") - 1;
115 strncpy(ovmf_code_path, token, sizeof(ovmf_code_path));
116 if (check_image(ovmf_code_path, OVMF_CODE_SZ_LIMIT,
117 &ovmf_code_size) != 0)
118 break;
119 ovmf_code_file_name = ovmf_code_path;
120 pr_notice("SW_LOAD: get ovmf code path %s, size 0x%lx\n",
121 ovmf_code_path, ovmf_code_size);
122 } else if (!strncmp(token, "vars=", sizeof("vars=") - 1)) {
123 token += sizeof("vars=") - 1;
124 strncpy(ovmf_vars_path, token, sizeof(ovmf_vars_path));
125 if (check_image(ovmf_vars_path, OVMF_VARS_SZ_LIMIT,
126 &ovmf_vars_size) != 0)
127 break;
128 ovmf_vars_file_name = ovmf_vars_path;
129 pr_notice("SW_LOAD: get ovmf vars path %s, size 0x%lx\n",
130 ovmf_vars_path, ovmf_vars_size);
131 } else {
132 strncpy(ovmf_path, token, sizeof(ovmf_path));
133 if (check_image(ovmf_path, OVMF_SZ_LIMIT, &ovmf_size) != 0)
134 break;
135 ovmf_file_name = ovmf_path;
136 pr_notice("SW_LOAD: get ovmf path %s, size 0x%lx\n",
137 ovmf_path, ovmf_size);
138 }
139 }
140 free(str);
141 }
142
143 if ((ovmf_file_name != NULL) ^ (ovmf_code_file_name && ovmf_vars_file_name))
144 error = 0;
145
146 return error;
147 }
148
149 static int
acrn_prepare_ovmf(struct vmctx * ctx)150 acrn_prepare_ovmf(struct vmctx *ctx)
151 {
152 int i, flags, fd;
153 char *path, *addr;
154 size_t size, size_limit, cur_size, read;
155 struct flock fl;
156 FILE *fp;
157
158 if (ovmf_file_name) {
159 path = ovmf_file_name;
160 size = ovmf_size;
161 size_limit = OVMF_SZ_LIMIT;
162 } else {
163 path = ovmf_vars_file_name;
164 size = ovmf_vars_size;
165 size_limit = OVMF_VARS_SZ_LIMIT;
166 }
167
168 flags = writeback_nv_storage ? O_RDWR : O_RDONLY;
169 addr = ctx->baseaddr + OVMF_TOP(ctx) - ovmf_image_size();
170
171 for (i = 0; i < 2; i++) {
172 fd = open(path, flags);
173
174 if (fd == -1) {
175 pr_err("SW_LOAD ERR: could not open ovmf file: %s (%s)\n",
176 path, strerror(errno));
177 return -1;
178 }
179
180 /* acquire read lock over the entire file */
181 memset(&fl, 0, sizeof(fl));
182 fl.l_type = F_RDLCK;
183 fl.l_whence = SEEK_SET;
184 fl.l_start = 0;
185 fl.l_len = 0;
186
187 if (fcntl(fd, F_SETLK, &fl)) {
188 pr_err("SW_LOAD ERR: could not fcntl(F_RDLCK) "
189 "ovmf file: %s (%s)\n",
190 path, strerror(errno));
191 close(fd);
192 return -1;
193 }
194
195 if (check_image(path, size_limit, &cur_size) != 0) {
196 close(fd);
197 return -1;
198 }
199
200 if (cur_size != size) {
201 pr_err("SW_LOAD ERR: ovmf file %s changed\n", path);
202 close(fd);
203 return -1;
204 }
205
206 if (flags == O_RDWR) {
207 /* upgrade to write lock */
208 memset(&fl, 0, sizeof(fl));
209 fl.l_type = F_WRLCK;
210 fl.l_whence = SEEK_SET;
211 fl.l_start = 0;
212 fl.l_len = OVMF_NVSTORAGE_SZ;
213
214 if (fcntl(fd, F_SETLK, &fl)) {
215 pr_err("SW_LOAD ERR: could not fcntl(F_WRLCK) "
216 "ovmf file: %s (%s)\n",
217 path, strerror(errno));
218 close(fd);
219 return -1;
220 }
221
222 mmap_vars = mmap(NULL, OVMF_NVSTORAGE_SZ, PROT_WRITE,
223 MAP_SHARED, fd, 0);
224
225 if (mmap_vars == MAP_FAILED) {
226 pr_err("SW_LOAD ERR: could not mmap "
227 "ovmf file: %s (%s)\n",
228 path, strerror(errno));
229 close(fd);
230 return -1;
231 }
232 }
233
234 fp = fdopen(fd, "r");
235
236 if (fp == NULL) {
237 pr_err("SW_LOAD ERR: could not fdopen "
238 "ovmf file: %s (%s)\n",
239 path, strerror(errno));
240 close(fd);
241 return -1;
242 }
243
244 fseek(fp, 0, SEEK_SET);
245 read = fread(addr, sizeof(char), size, fp);
246 fclose(fp);
247
248 if (read < size) {
249 pr_err("SW_LOAD ERR: could not read whole partition blob %s\n",
250 path);
251 return -1;
252 }
253
254 pr_info("SW_LOAD: partition blob %s size 0x%lx copied to addr %p\n",
255 path, size, addr);
256
257 if (!ovmf_file_name) {
258 addr += size;
259 path = ovmf_code_file_name;
260 size = ovmf_code_size;
261 size_limit = OVMF_CODE_SZ_LIMIT;
262 flags = O_RDONLY;
263 } else
264 break;
265 }
266
267 return 0;
268 }
269
270 int
acrn_sw_load_ovmf(struct vmctx * ctx)271 acrn_sw_load_ovmf(struct vmctx *ctx)
272 {
273 int ret;
274 struct {
275 char signature[4];
276 uint32_t nentries;
277 struct e820_entry map[];
278 } __attribute__((packed)) *e820;
279
280 init_cmos_vrpmb(ctx);
281
282 ret = acrn_prepare_ovmf(ctx);
283
284 if (ret)
285 return ret;
286
287 e820 = paddr_guest2host(ctx, OVMF_E820_BASE,
288 e820_default_entries[LOWRAM_E820_ENTRY].baseaddr -
289 OVMF_E820_BASE);
290 if (e820 == NULL)
291 return -1;
292
293 strncpy(e820->signature, "820", sizeof(e820->signature));
294 e820->nentries = acrn_create_e820_table(ctx, e820->map);
295
296 pr_info("SW_LOAD: ovmf_entry 0x%lx\n", OVMF_TOP(ctx) - 16);
297
298 /* set guest bsp state. Will call hypercall set bsp state
299 * after bsp is created.
300 */
301 memset(&ctx->bsp_regs, 0, sizeof(struct acrn_vcpu_regs));
302 ctx->bsp_regs.vcpu_id = 0;
303
304 /* CR0_ET | CR0_NE */
305 ctx->bsp_regs.vcpu_regs.cr0 = 0x30U;
306 ctx->bsp_regs.vcpu_regs.cs_ar = 0x009FU;
307 ctx->bsp_regs.vcpu_regs.cs_sel = 0xF000U;
308 ctx->bsp_regs.vcpu_regs.cs_limit = 0xFFFFU;
309 ctx->bsp_regs.vcpu_regs.cs_base = (OVMF_TOP(ctx) - 16) & 0xFFFF0000UL;
310 ctx->bsp_regs.vcpu_regs.rip = (OVMF_TOP(ctx) - 16) & 0xFFFFUL;
311
312 return 0;
313 }
314
315 /*
316 * The NV data section is the first 128KB in the OVMF image. At runtime,
317 * it's copied into guest memory and behave as RAM to OVMF. It can be
318 * accessed and updated by OVMF. To preserve NV section (referred to
319 * as Non-Volatile Data Store section in the OVMF spec), we're flushing
320 * in-memory data back to the NV data section of the OVMF image file
321 * at designated points.
322 */
323 int
acrn_writeback_ovmf_nvstorage(struct vmctx * ctx)324 acrn_writeback_ovmf_nvstorage(struct vmctx *ctx)
325 {
326 int i, fd, ret = 0;
327 char *path;
328 struct flock fl;
329
330 if (!writeback_nv_storage)
331 return 0;
332
333 memcpy(mmap_vars, ctx->baseaddr + OVMF_NVSTORAGE_OFFSET,
334 OVMF_NVSTORAGE_SZ);
335
336 if (munmap(mmap_vars, OVMF_NVSTORAGE_SZ)) {
337 pr_err("SW_LOAD ERR: could not munmap (%s)\n",
338 strerror(errno));
339 ret = -1;
340 }
341
342 mmap_vars = NULL;
343
344 path = ovmf_file_name ? ovmf_file_name : ovmf_vars_file_name;
345 pr_info("OVMF_WRITEBACK: OVMF has been written back "
346 "to partition blob %s size 0x%lx @ gpa %p\n",
347 path, OVMF_NVSTORAGE_SZ, (void *)OVMF_NVSTORAGE_OFFSET);
348
349 for (i = 0; i < 2; i++) {
350 fd = open(path, O_RDONLY);
351
352 if (fd == -1) {
353 pr_err("SW_LOAD ERR: could not open ovmf file: %s (%s)\n",
354 path, strerror(errno));
355 ret = -1;
356 goto next;
357 }
358
359 /* unlock the entire file */
360 memset(&fl, 0, sizeof(fl));
361 fl.l_type = F_UNLCK;
362 fl.l_whence = SEEK_SET;
363 fl.l_start = 0;
364 fl.l_len = 0;
365
366 if (fcntl(fd, F_SETLK, &fl)) {
367 pr_err("SW_LOAD ERR: could not fcntl(F_UNLCK) "
368 "ovmf file: %s (%s)\n",
369 path, strerror(errno));
370 ret = -1;
371 }
372
373 close(fd);
374
375 next:
376 if (!ovmf_file_name)
377 path = ovmf_code_file_name;
378 else
379 break;
380 }
381
382 return ret;
383 }
384