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 <stdbool.h>
31 
32 #include "dm.h"
33 #include "vmmapi.h"
34 #include "sw_load.h"
35 #include "acpi.h"
36 #include "log.h"
37 
38 
39 /* If the vsbl is loaded by DM, the User VM memory layout will be like:
40  *
41  * | ...                                              |
42  * +--------------------------------------------------+
43  * | offset: 0xf2400 (ACPI table)                     |
44  * +--------------------------------------------------+
45  * | ...                                              |
46  * +--------------------------------------------------+
47  * | offset: 16MB (vsbl image)                        |
48  * +--------------------------------------------------+
49  * | ...                                              |
50  * +--------------------------------------------------+
51  * | offset: lowmem - 16K (partition blob)            |
52  * +--------------------------------------------------+
53  * | offset: lowmem - 12K (e820 table)                |
54  * +--------------------------------------------------+
55  * | offset: lowmem - 8K (boot_args_address)          |
56  * +--------------------------------------------------+
57  * | offset: lowmem - 6K (vsbl entry address)         |
58  * +--------------------------------------------------+
59  * | offset: lowmem - 4K (config_page with e820 table)|
60  * +--------------------------------------------------+
61  */
62 
63 /*                 vsbl binary layout:
64  *
65  * +--------------------------------------------------+ <--vSBL Top
66  * |             |offset: Top - 0x10 (reset vector)   |
67  * + STAGEINIT   |------------------------------------+
68  * | (0x10000)   |other                               |
69  * +--------------------------------------------------+
70  * |                                                  |
71  * + PAYLOAD                                          +
72  * |(0x100000)                                        |
73  * +--------------------------------------------------+
74  * |                                                  |
75  * + vFastboot                                        +
76  * |(0x200000)                                        |
77  * +--------------------------------------------------+
78  */
79 
80 /* Check default e820 table in sw_load_common.c for info about ctx->lowmem */
81 #define	CONFIGPAGE_OFF(ctx)	((ctx)->lowmem - 4*KB)
82 #define	VSBL_ENTRY_OFF(ctx)	((ctx)->lowmem - 6*KB)
83 #define	BOOTARGS_OFF(ctx)	((ctx)->lowmem - 8*KB)
84 #define	E820_TABLE_OFF(ctx)	((ctx)->lowmem - 12*KB)
85 #define	GUEST_PART_INFO_OFF(ctx)	((ctx)->lowmem - 16*KB)
86 /* vsbl real entry is reset vector, which is (VSBL_TOP - 16) */
87 #define	VSBL_TOP(ctx)		(64*MB)
88 
89 struct vsbl_para {
90 	uint64_t		e820_table_address;
91 	uint64_t		e820_entries;
92 	uint64_t		acpi_table_address;
93 	uint64_t		acpi_table_size;
94 	uint64_t		guest_part_info_address;
95 	uint64_t		guest_part_info_size;
96 	uint64_t		vsbl_address;
97 	uint64_t		vsbl_size;
98 	uint64_t		bootargs_address;
99 	uint32_t		trusty_enabled;
100 	uint32_t		key_info_lock;
101 	uint32_t		reserved;
102 	uint32_t		boot_device_address;
103 };
104 
105 static char guest_part_info_path[STR_LEN];
106 static size_t guest_part_info_size;
107 static bool with_guest_part_info;
108 
109 static char vsbl_path[STR_LEN];
110 static size_t vsbl_size;
111 
112 static int boot_blk_bdf;
113 
114 extern int init_cmos_vrpmb(struct vmctx *ctx);
115 
116 #define	LOW_8BIT(x)	((x) & 0xFF)
117 void
vsbl_set_bdf(int bnum,int snum,int fnum)118 vsbl_set_bdf(int bnum, int snum, int fnum)
119 {
120 	boot_blk_bdf = (LOW_8BIT(bnum) << 16) | (LOW_8BIT(snum) << 8) |
121 		LOW_8BIT(fnum);
122 }
123 
124 int
acrn_parse_guest_part_info(char * arg)125 acrn_parse_guest_part_info(char *arg)
126 {
127 	int error = -1;
128 	size_t len = strnlen(arg, STR_LEN);
129 
130 	if (len < STR_LEN) {
131 		strncpy(guest_part_info_path, arg, len + 1);
132 		if (check_image(guest_part_info_path, 0, &guest_part_info_size) == 0) {
133 			with_guest_part_info = true;
134 			pr_info("SW_LOAD: get partition blob path %s\n",
135 						guest_part_info_path);
136 			error = 0;
137 		}
138 	}
139 	return error;
140 }
141 
142 static int
acrn_prepare_guest_part_info(struct vmctx * ctx)143 acrn_prepare_guest_part_info(struct vmctx *ctx)
144 {
145 	FILE *fp;
146 	long len;
147 	size_t read;
148 
149 	fp = fopen(guest_part_info_path, "r");
150 	if (fp == NULL) {
151 		pr_err("SW_LOAD ERR: could not open partition blob %s\n",
152 			guest_part_info_path);
153 		return -1;
154 	}
155 
156 	fseek(fp, 0, SEEK_END);
157 	len = ftell(fp);
158 
159 	if (len != guest_part_info_size) {
160 		pr_err("SW_LOAD ERR: partition blob changed\n");
161 		fclose(fp);
162 		return -1;
163 	}
164 
165 	if ((len + GUEST_PART_INFO_OFF(ctx)) > BOOTARGS_OFF(ctx)) {
166 		pr_err("SW_LOAD ERR: too large partition blob\n");
167 		fclose(fp);
168 		return -1;
169 	}
170 
171 	fseek(fp, 0, SEEK_SET);
172 	read = fread(ctx->baseaddr + GUEST_PART_INFO_OFF(ctx),
173 		sizeof(char), len, fp);
174 	if (read < len) {
175 		pr_err("SW_LOAD ERR: could not read whole partition blob\n");
176 		fclose(fp);
177 		return -1;
178 	}
179 	fclose(fp);
180 	pr_info("SW_LOAD: partition blob %s size %lu copy to guest 0x%lx\n",
181 		guest_part_info_path, guest_part_info_size,
182 		GUEST_PART_INFO_OFF(ctx));
183 
184 	return 0;
185 }
186 
187 int
acrn_parse_vsbl(char * arg)188 acrn_parse_vsbl(char *arg)
189 {
190 	int error = -1;
191 	size_t len = strnlen(arg, STR_LEN);
192 
193 	if (len < STR_LEN) {
194 		strncpy(vsbl_path, arg, len + 1);
195 		if (check_image(vsbl_path, 8 * MB, &vsbl_size) == 0) {
196 			vsbl_file_name = vsbl_path;
197 			pr_notice("SW_LOAD: get vsbl path %s\n", vsbl_path);
198 			error = 0;
199 		}
200 	}
201 	return error;
202 }
203 
204 static int
acrn_prepare_vsbl(struct vmctx * ctx)205 acrn_prepare_vsbl(struct vmctx *ctx)
206 {
207 	FILE *fp;
208 	size_t read;
209 
210 	fp = fopen(vsbl_path, "r");
211 	if (fp == NULL) {
212 		pr_err("SW_LOAD ERR: could not open vsbl file: %s\n",
213 			vsbl_path);
214 		return -1;
215 	}
216 
217 	fseek(fp, 0, SEEK_END);
218 
219 	if (ftell(fp) != vsbl_size) {
220 		pr_err("SW_LOAD ERR: vsbl file changed\n");
221 		fclose(fp);
222 		return -1;
223 	}
224 
225 	fseek(fp, 0, SEEK_SET);
226 	read = fread(ctx->baseaddr + VSBL_TOP(ctx) - vsbl_size,
227 		sizeof(char), vsbl_size, fp);
228 	if (read < vsbl_size) {
229 		pr_err("SW_LOAD ERR: could not read whole partition blob\n");
230 		fclose(fp);
231 		return -1;
232 	}
233 	fclose(fp);
234 	pr_info("SW_LOAD: partition blob %s size %lu copy to guest 0x%lx\n",
235 		vsbl_path, vsbl_size, VSBL_TOP(ctx) - vsbl_size);
236 
237 	return 0;
238 }
239 
240 int
acrn_sw_load_vsbl(struct vmctx * ctx)241 acrn_sw_load_vsbl(struct vmctx *ctx)
242 {
243 	int ret;
244 	struct e820_entry *e820;
245 	struct vsbl_para *vsbl_para;
246 
247 	init_cmos_vrpmb(ctx);
248 
249 	vsbl_para = (struct vsbl_para *)
250 		(ctx->baseaddr + CONFIGPAGE_OFF(ctx));
251 
252 	memset(vsbl_para, 0x0, sizeof(struct vsbl_para));
253 
254 	e820 = (struct e820_entry *)
255 		(ctx->baseaddr + E820_TABLE_OFF(ctx));
256 
257 	vsbl_para->e820_entries = acrn_create_e820_table(ctx, e820);
258 	vsbl_para->e820_table_address = E820_TABLE_OFF(ctx);
259 
260 	vsbl_para->acpi_table_address = get_acpi_base();
261 	vsbl_para->acpi_table_size = get_acpi_table_length();
262 
263 	if (with_bootargs) {
264 		strncpy(ctx->baseaddr + BOOTARGS_OFF(ctx), get_bootargs(), STR_LEN);
265 		vsbl_para->bootargs_address = BOOTARGS_OFF(ctx);
266 	} else {
267 		vsbl_para->bootargs_address = 0;
268 	}
269 
270 	if (with_guest_part_info) {
271 		ret = acrn_prepare_guest_part_info(ctx);
272 		if (ret)
273 			return ret;
274 		vsbl_para->guest_part_info_address = GUEST_PART_INFO_OFF(ctx);
275 		vsbl_para->guest_part_info_size = guest_part_info_size;
276 	} else {
277 		vsbl_para->guest_part_info_address = 0;
278 		vsbl_para->guest_part_info_size = 0;
279 	}
280 
281 	ret = acrn_prepare_vsbl(ctx);
282 	if (ret)
283 		return ret;
284 
285 	vsbl_para->vsbl_address = VSBL_TOP(ctx) - vsbl_size;
286 	vsbl_para->vsbl_size = vsbl_size;
287 
288 	vsbl_para->e820_entries = add_e820_entry(e820, vsbl_para->e820_entries,
289 		vsbl_para->vsbl_address, vsbl_size, E820_TYPE_RESERVED);
290 
291 	pr_info("SW_LOAD: vsbl_entry 0x%lx\n", VSBL_TOP(ctx) - 16);
292 
293 	vsbl_para->boot_device_address = boot_blk_bdf;
294 	vsbl_para->trusty_enabled = trusty_enabled;
295 
296 	/* set guest bsp state. Will call hypercall set bsp state
297 	 * after bsp is created.
298 	 */
299 	memset(&ctx->bsp_regs, 0, sizeof( struct acrn_vcpu_regs));
300 	ctx->bsp_regs.vcpu_id = 0;
301 
302 	/* CR0_ET | CR0_NE */
303 	ctx->bsp_regs.vcpu_regs.cr0 = 0x30U;
304 	ctx->bsp_regs.vcpu_regs.cs_ar = 0x009FU;
305 	ctx->bsp_regs.vcpu_regs.cs_sel = 0xF000U;
306 	ctx->bsp_regs.vcpu_regs.cs_limit = 0xFFFFU;
307 	ctx->bsp_regs.vcpu_regs.cs_base = (VSBL_TOP(ctx) - 16) &0xFFFF0000UL;
308 	ctx->bsp_regs.vcpu_regs.rip = (VSBL_TOP(ctx) - 16) & 0xFFFFUL;
309 	ctx->bsp_regs.vcpu_regs.gprs.rsi = CONFIGPAGE_OFF(ctx);
310 
311 	return 0;
312 }
313