xref: /illumos-gate/usr/src/cmd/bhyve/qemu_loader.c (revision 32640292339b07090f10ce34d455f98711077343)
1*32640292SAndy Fiddaman /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3*32640292SAndy Fiddaman  *
4*32640292SAndy Fiddaman  * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
5*32640292SAndy Fiddaman  * Author: Corvin Köhne <c.koehne@beckhoff.com>
6*32640292SAndy Fiddaman  */
7*32640292SAndy Fiddaman 
8*32640292SAndy Fiddaman #include <sys/types.h>
9*32640292SAndy Fiddaman #include <sys/param.h>
10*32640292SAndy Fiddaman #include <sys/endian.h>
11*32640292SAndy Fiddaman #include <sys/queue.h>
12*32640292SAndy Fiddaman 
13*32640292SAndy Fiddaman #include <machine/vmm.h>
14*32640292SAndy Fiddaman 
15*32640292SAndy Fiddaman #include <err.h>
16*32640292SAndy Fiddaman #include <errno.h>
17*32640292SAndy Fiddaman #include <stdio.h>
18*32640292SAndy Fiddaman #include <stdlib.h>
19*32640292SAndy Fiddaman #include <string.h>
20*32640292SAndy Fiddaman #include <vmmapi.h>
21*32640292SAndy Fiddaman 
22*32640292SAndy Fiddaman #include "qemu_fwcfg.h"
23*32640292SAndy Fiddaman #include "qemu_loader.h"
24*32640292SAndy Fiddaman 
25*32640292SAndy Fiddaman #ifndef	__FreeBSD__
26*32640292SAndy Fiddaman /*
27*32640292SAndy Fiddaman  * This is better than the number of ifdef blocks that would be otherwise
28*32640292SAndy Fiddaman  * required throughout this code. Hopefully upstream will clear up the
29*32640292SAndy Fiddaman  * char* /uint8_t * confusion.
30*32640292SAndy Fiddaman  */
31*32640292SAndy Fiddaman #define	strlen(x)	strlen((char *)(x))
32*32640292SAndy Fiddaman #define	strncpy(p, q, n)	strncpy((char *)(p), (char *)(q), (n))
33*32640292SAndy Fiddaman #endif
34*32640292SAndy Fiddaman 
35*32640292SAndy Fiddaman struct qemu_loader_entry {
36*32640292SAndy Fiddaman 	uint32_t cmd_le;
37*32640292SAndy Fiddaman 	union {
38*32640292SAndy Fiddaman 		struct {
39*32640292SAndy Fiddaman 			uint8_t name[QEMU_FWCFG_MAX_NAME];
40*32640292SAndy Fiddaman 			uint32_t alignment_le;
41*32640292SAndy Fiddaman 			uint8_t zone;
42*32640292SAndy Fiddaman 		} alloc;
43*32640292SAndy Fiddaman 		struct {
44*32640292SAndy Fiddaman 			uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
45*32640292SAndy Fiddaman 			uint8_t src_name[QEMU_FWCFG_MAX_NAME];
46*32640292SAndy Fiddaman 			uint32_t off_le;
47*32640292SAndy Fiddaman 			uint8_t size;
48*32640292SAndy Fiddaman 		} add_pointer;
49*32640292SAndy Fiddaman 		struct {
50*32640292SAndy Fiddaman 			uint8_t name[QEMU_FWCFG_MAX_NAME];
51*32640292SAndy Fiddaman 			uint32_t off_le;
52*32640292SAndy Fiddaman 			uint32_t start_le;
53*32640292SAndy Fiddaman 			uint32_t len_le;
54*32640292SAndy Fiddaman 		} add_checksum;
55*32640292SAndy Fiddaman 		struct {
56*32640292SAndy Fiddaman 			uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
57*32640292SAndy Fiddaman 			uint8_t src_name[QEMU_FWCFG_MAX_NAME];
58*32640292SAndy Fiddaman 			uint32_t dest_off_le;
59*32640292SAndy Fiddaman 			uint32_t src_off_le;
60*32640292SAndy Fiddaman 			uint8_t size;
61*32640292SAndy Fiddaman 		} write_pointer;
62*32640292SAndy Fiddaman 
63*32640292SAndy Fiddaman 		/* padding */
64*32640292SAndy Fiddaman 		uint8_t pad[124];
65*32640292SAndy Fiddaman 	};
66*32640292SAndy Fiddaman } __packed;
67*32640292SAndy Fiddaman 
68*32640292SAndy Fiddaman enum qemu_loader_command {
69*32640292SAndy Fiddaman 	QEMU_LOADER_CMD_ALLOC = 0x1,
70*32640292SAndy Fiddaman 	QEMU_LOADER_CMD_ADD_POINTER = 0x2,
71*32640292SAndy Fiddaman 	QEMU_LOADER_CMD_ADD_CHECKSUM = 0x3,
72*32640292SAndy Fiddaman 	QEMU_LOADER_CMD_WRITE_POINTER = 0x4,
73*32640292SAndy Fiddaman };
74*32640292SAndy Fiddaman 
75*32640292SAndy Fiddaman struct qemu_loader_element {
76*32640292SAndy Fiddaman 	STAILQ_ENTRY(qemu_loader_element) chain;
77*32640292SAndy Fiddaman 	struct qemu_loader_entry entry;
78*32640292SAndy Fiddaman };
79*32640292SAndy Fiddaman 
80*32640292SAndy Fiddaman struct qemu_loader {
81*32640292SAndy Fiddaman 	uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
82*32640292SAndy Fiddaman 	STAILQ_HEAD(qemu_loader_list, qemu_loader_element) list;
83*32640292SAndy Fiddaman };
84*32640292SAndy Fiddaman 
85*32640292SAndy Fiddaman int
qemu_loader_alloc(struct qemu_loader * const loader,const uint8_t * name,const uint32_t alignment,const enum qemu_loader_zone zone)86*32640292SAndy Fiddaman qemu_loader_alloc(struct qemu_loader *const loader, const uint8_t *name,
87*32640292SAndy Fiddaman     const uint32_t alignment, const enum qemu_loader_zone zone)
88*32640292SAndy Fiddaman {
89*32640292SAndy Fiddaman 	struct qemu_loader_element *element;
90*32640292SAndy Fiddaman 
91*32640292SAndy Fiddaman 	if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
92*32640292SAndy Fiddaman 		return (EINVAL);
93*32640292SAndy Fiddaman 
94*32640292SAndy Fiddaman 	element = calloc(1, sizeof(struct qemu_loader_element));
95*32640292SAndy Fiddaman 	if (element == NULL) {
96*32640292SAndy Fiddaman 		warnx("%s: failed to allocate command", __func__);
97*32640292SAndy Fiddaman 		return (ENOMEM);
98*32640292SAndy Fiddaman 	}
99*32640292SAndy Fiddaman 
100*32640292SAndy Fiddaman 	element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ALLOC);
101*32640292SAndy Fiddaman 	strncpy(element->entry.alloc.name, name, QEMU_FWCFG_MAX_NAME);
102*32640292SAndy Fiddaman 	element->entry.alloc.alignment_le = htole32(alignment);
103*32640292SAndy Fiddaman 	element->entry.alloc.zone = zone;
104*32640292SAndy Fiddaman 
105*32640292SAndy Fiddaman 	/*
106*32640292SAndy Fiddaman 	 * The guest always works on copies of the fwcfg item, which where
107*32640292SAndy Fiddaman 	 * loaded into guest memory. Loading a fwcfg item is caused by ALLOC.
108*32640292SAndy Fiddaman 	 * For that reason, ALLOC should be scheduled in front of any other
109*32640292SAndy Fiddaman 	 * commands.
110*32640292SAndy Fiddaman 	 */
111*32640292SAndy Fiddaman 	STAILQ_INSERT_HEAD(&loader->list, element, chain);
112*32640292SAndy Fiddaman 
113*32640292SAndy Fiddaman 	return (0);
114*32640292SAndy Fiddaman }
115*32640292SAndy Fiddaman 
116*32640292SAndy Fiddaman int
qemu_loader_add_checksum(struct qemu_loader * const loader,const uint8_t * name,const uint32_t off,const uint32_t start,const uint32_t len)117*32640292SAndy Fiddaman qemu_loader_add_checksum(struct qemu_loader *const loader, const uint8_t *name,
118*32640292SAndy Fiddaman     const uint32_t off, const uint32_t start, const uint32_t len)
119*32640292SAndy Fiddaman {
120*32640292SAndy Fiddaman 	struct qemu_loader_element *element;
121*32640292SAndy Fiddaman 
122*32640292SAndy Fiddaman 	if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
123*32640292SAndy Fiddaman 		return (EINVAL);
124*32640292SAndy Fiddaman 
125*32640292SAndy Fiddaman 	element = calloc(1, sizeof(struct qemu_loader_element));
126*32640292SAndy Fiddaman 	if (element == NULL) {
127*32640292SAndy Fiddaman 		warnx("%s: failed to allocate command", __func__);
128*32640292SAndy Fiddaman 		return (ENOMEM);
129*32640292SAndy Fiddaman 	}
130*32640292SAndy Fiddaman 
131*32640292SAndy Fiddaman 	element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_CHECKSUM);
132*32640292SAndy Fiddaman 	strncpy(element->entry.add_checksum.name, name, QEMU_FWCFG_MAX_NAME);
133*32640292SAndy Fiddaman 	element->entry.add_checksum.off_le = htole32(off);
134*32640292SAndy Fiddaman 	element->entry.add_checksum.start_le = htole32(start);
135*32640292SAndy Fiddaman 	element->entry.add_checksum.len_le = htole32(len);
136*32640292SAndy Fiddaman 
137*32640292SAndy Fiddaman 	STAILQ_INSERT_TAIL(&loader->list, element, chain);
138*32640292SAndy Fiddaman 
139*32640292SAndy Fiddaman 	return (0);
140*32640292SAndy Fiddaman }
141*32640292SAndy Fiddaman 
142*32640292SAndy Fiddaman int
qemu_loader_add_pointer(struct qemu_loader * const loader,const uint8_t * dest_name,const uint8_t * src_name,const uint32_t off,const uint8_t size)143*32640292SAndy Fiddaman qemu_loader_add_pointer(struct qemu_loader *const loader,
144*32640292SAndy Fiddaman     const uint8_t *dest_name, const uint8_t *src_name, const uint32_t off,
145*32640292SAndy Fiddaman     const uint8_t size)
146*32640292SAndy Fiddaman {
147*32640292SAndy Fiddaman 	struct qemu_loader_element *element;
148*32640292SAndy Fiddaman 
149*32640292SAndy Fiddaman 	if (strlen(dest_name) >= QEMU_FWCFG_MAX_NAME ||
150*32640292SAndy Fiddaman 	    strlen(src_name) >= QEMU_FWCFG_MAX_NAME)
151*32640292SAndy Fiddaman 		return (EINVAL);
152*32640292SAndy Fiddaman 
153*32640292SAndy Fiddaman 	element = calloc(1, sizeof(struct qemu_loader_element));
154*32640292SAndy Fiddaman 	if (element == NULL) {
155*32640292SAndy Fiddaman 		warnx("%s: failed to allocate command", __func__);
156*32640292SAndy Fiddaman 		return (ENOMEM);
157*32640292SAndy Fiddaman 	}
158*32640292SAndy Fiddaman 
159*32640292SAndy Fiddaman 	element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_POINTER);
160*32640292SAndy Fiddaman 	strncpy(element->entry.add_pointer.dest_name, dest_name,
161*32640292SAndy Fiddaman 	    QEMU_FWCFG_MAX_NAME);
162*32640292SAndy Fiddaman 	strncpy(element->entry.add_pointer.src_name, src_name,
163*32640292SAndy Fiddaman 	    QEMU_FWCFG_MAX_NAME);
164*32640292SAndy Fiddaman 	element->entry.add_pointer.off_le = htole32(off);
165*32640292SAndy Fiddaman 	element->entry.add_pointer.size = size;
166*32640292SAndy Fiddaman 
167*32640292SAndy Fiddaman 	STAILQ_INSERT_TAIL(&loader->list, element, chain);
168*32640292SAndy Fiddaman 
169*32640292SAndy Fiddaman 	return (0);
170*32640292SAndy Fiddaman }
171*32640292SAndy Fiddaman 
172*32640292SAndy Fiddaman int
qemu_loader_create(struct qemu_loader ** const new_loader,const uint8_t * fwcfg_name)173*32640292SAndy Fiddaman qemu_loader_create(struct qemu_loader **const new_loader,
174*32640292SAndy Fiddaman     const uint8_t *fwcfg_name)
175*32640292SAndy Fiddaman {
176*32640292SAndy Fiddaman 	struct qemu_loader *loader;
177*32640292SAndy Fiddaman 
178*32640292SAndy Fiddaman 	if (new_loader == NULL || strlen(fwcfg_name) >= QEMU_FWCFG_MAX_NAME) {
179*32640292SAndy Fiddaman 		return (EINVAL);
180*32640292SAndy Fiddaman 	}
181*32640292SAndy Fiddaman 
182*32640292SAndy Fiddaman 	loader = calloc(1, sizeof(struct qemu_loader));
183*32640292SAndy Fiddaman 	if (loader == NULL) {
184*32640292SAndy Fiddaman 		warnx("%s: failed to allocate loader", __func__);
185*32640292SAndy Fiddaman 		return (ENOMEM);
186*32640292SAndy Fiddaman 	}
187*32640292SAndy Fiddaman 
188*32640292SAndy Fiddaman 	strncpy(loader->fwcfg_name, fwcfg_name, QEMU_FWCFG_MAX_NAME);
189*32640292SAndy Fiddaman 	STAILQ_INIT(&loader->list);
190*32640292SAndy Fiddaman 
191*32640292SAndy Fiddaman 	*new_loader = loader;
192*32640292SAndy Fiddaman 
193*32640292SAndy Fiddaman 	return (0);
194*32640292SAndy Fiddaman }
195*32640292SAndy Fiddaman 
196*32640292SAndy Fiddaman static const uint8_t *
qemu_loader_get_zone_name(const enum qemu_loader_zone zone)197*32640292SAndy Fiddaman qemu_loader_get_zone_name(const enum qemu_loader_zone zone)
198*32640292SAndy Fiddaman {
199*32640292SAndy Fiddaman #ifdef	__FreeBSD__
200*32640292SAndy Fiddaman 	switch (zone) {
201*32640292SAndy Fiddaman 	case QEMU_LOADER_ALLOC_HIGH:
202*32640292SAndy Fiddaman 		return ("HIGH");
203*32640292SAndy Fiddaman 	case QEMU_LOADER_ALLOC_FSEG:
204*32640292SAndy Fiddaman 		return ("FSEG");
205*32640292SAndy Fiddaman 	default:
206*32640292SAndy Fiddaman 		return ("Unknown");
207*32640292SAndy Fiddaman 	}
208*32640292SAndy Fiddaman #else
209*32640292SAndy Fiddaman 	switch (zone) {
210*32640292SAndy Fiddaman 	case QEMU_LOADER_ALLOC_HIGH:
211*32640292SAndy Fiddaman 		return ((uint8_t *)"HIGH");
212*32640292SAndy Fiddaman 	case QEMU_LOADER_ALLOC_FSEG:
213*32640292SAndy Fiddaman 		return ((uint8_t *)"FSEG");
214*32640292SAndy Fiddaman 	default:
215*32640292SAndy Fiddaman 		return ((uint8_t *)"Unknown");
216*32640292SAndy Fiddaman 	}
217*32640292SAndy Fiddaman #endif
218*32640292SAndy Fiddaman }
219*32640292SAndy Fiddaman 
220*32640292SAndy Fiddaman static void __unused
qemu_loader_dump_entry(const struct qemu_loader_entry * const entry)221*32640292SAndy Fiddaman qemu_loader_dump_entry(const struct qemu_loader_entry *const entry)
222*32640292SAndy Fiddaman {
223*32640292SAndy Fiddaman 	switch (le32toh(entry->cmd_le)) {
224*32640292SAndy Fiddaman 	case QEMU_LOADER_CMD_ALLOC:
225*32640292SAndy Fiddaman 		printf("CMD_ALLOC\n\r");
226*32640292SAndy Fiddaman 		printf("  name     : %s\n\r", entry->alloc.name);
227*32640292SAndy Fiddaman 		printf("  alignment: %8x\n\r",
228*32640292SAndy Fiddaman 		    le32toh(entry->alloc.alignment_le));
229*32640292SAndy Fiddaman 		printf("  zone     : %s\n\r",
230*32640292SAndy Fiddaman 		    qemu_loader_get_zone_name(entry->alloc.zone));
231*32640292SAndy Fiddaman 		break;
232*32640292SAndy Fiddaman 	case QEMU_LOADER_CMD_ADD_POINTER:
233*32640292SAndy Fiddaman 		printf("CMD_ADD_POINTER\n\r");
234*32640292SAndy Fiddaman 		printf("  dest_name: %s\n\r", entry->add_pointer.dest_name);
235*32640292SAndy Fiddaman 		printf("  src_name : %s\n\r", entry->add_pointer.src_name);
236*32640292SAndy Fiddaman 		printf("  off      : %8x\n\r",
237*32640292SAndy Fiddaman 		    le32toh(entry->add_pointer.off_le));
238*32640292SAndy Fiddaman 		printf("  size     : %8x\n\r", entry->add_pointer.size);
239*32640292SAndy Fiddaman 		break;
240*32640292SAndy Fiddaman 	case QEMU_LOADER_CMD_ADD_CHECKSUM:
241*32640292SAndy Fiddaman 		printf("CMD_ADD_CHECKSUM\n\r");
242*32640292SAndy Fiddaman 		printf("  name     : %s\n\r", entry->add_checksum.name);
243*32640292SAndy Fiddaman 		printf("  off      : %8x\n\r",
244*32640292SAndy Fiddaman 		    le32toh(entry->add_checksum.off_le));
245*32640292SAndy Fiddaman 		printf("  start    : %8x\n\r",
246*32640292SAndy Fiddaman 		    le32toh(entry->add_checksum.start_le));
247*32640292SAndy Fiddaman 		printf("  length   : %8x\n\r",
248*32640292SAndy Fiddaman 		    le32toh(entry->add_checksum.len_le));
249*32640292SAndy Fiddaman 		break;
250*32640292SAndy Fiddaman 	case QEMU_LOADER_CMD_WRITE_POINTER:
251*32640292SAndy Fiddaman 		printf("CMD_WRITE_POINTER\n\r");
252*32640292SAndy Fiddaman 		printf("  dest_name: %s\n\r", entry->write_pointer.dest_name);
253*32640292SAndy Fiddaman 		printf("  src_name : %s\n\r", entry->write_pointer.src_name);
254*32640292SAndy Fiddaman 		printf("  dest_off : %8x\n\r",
255*32640292SAndy Fiddaman 		    le32toh(entry->write_pointer.dest_off_le));
256*32640292SAndy Fiddaman 		printf("  src_off  : %8x\n\r",
257*32640292SAndy Fiddaman 		    le32toh(entry->write_pointer.src_off_le));
258*32640292SAndy Fiddaman 		printf("  size     : %8x\n\r", entry->write_pointer.size);
259*32640292SAndy Fiddaman 		break;
260*32640292SAndy Fiddaman 	default:
261*32640292SAndy Fiddaman 		printf("UNKNOWN\n\r");
262*32640292SAndy Fiddaman 		break;
263*32640292SAndy Fiddaman 	}
264*32640292SAndy Fiddaman }
265*32640292SAndy Fiddaman 
266*32640292SAndy Fiddaman int
qemu_loader_finish(struct qemu_loader * const loader)267*32640292SAndy Fiddaman qemu_loader_finish(struct qemu_loader *const loader)
268*32640292SAndy Fiddaman {
269*32640292SAndy Fiddaman 	struct qemu_loader_element *element;
270*32640292SAndy Fiddaman 	struct qemu_loader_entry *data;
271*32640292SAndy Fiddaman 	size_t len = 0;
272*32640292SAndy Fiddaman 
273*32640292SAndy Fiddaman 	STAILQ_FOREACH(element, &loader->list, chain) {
274*32640292SAndy Fiddaman 		len += sizeof(struct qemu_loader_entry);
275*32640292SAndy Fiddaman 	}
276*32640292SAndy Fiddaman 	if (len == 0) {
277*32640292SAndy Fiddaman 		warnx("%s: bios loader empty", __func__);
278*32640292SAndy Fiddaman 		return (EFAULT);
279*32640292SAndy Fiddaman 	}
280*32640292SAndy Fiddaman 
281*32640292SAndy Fiddaman 	data = calloc(1, len);
282*32640292SAndy Fiddaman 	if (data == NULL) {
283*32640292SAndy Fiddaman 		warnx("%s: failed to allocate fwcfg data", __func__);
284*32640292SAndy Fiddaman 		return (ENOMEM);
285*32640292SAndy Fiddaman 	}
286*32640292SAndy Fiddaman 
287*32640292SAndy Fiddaman 	int i = 0;
288*32640292SAndy Fiddaman 	STAILQ_FOREACH(element, &loader->list, chain) {
289*32640292SAndy Fiddaman 		memcpy(&data[i], &element->entry,
290*32640292SAndy Fiddaman 		    sizeof(struct qemu_loader_entry));
291*32640292SAndy Fiddaman 		++i;
292*32640292SAndy Fiddaman 	}
293*32640292SAndy Fiddaman 
294*32640292SAndy Fiddaman #ifdef	__FreeBSD__
295*32640292SAndy Fiddaman 	return (qemu_fwcfg_add_file(loader->fwcfg_name, len, data));
296*32640292SAndy Fiddaman #else
297*32640292SAndy Fiddaman 	return (qemu_fwcfg_add_file((const char *)loader->fwcfg_name,
298*32640292SAndy Fiddaman 	    len, data));
299*32640292SAndy Fiddaman #endif
300*32640292SAndy Fiddaman }
301