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