xref: /illumos-gate/usr/src/cmd/bhyve/qemu_fwcfg.c (revision 32640292)
1*32640292SAndy Fiddaman /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3*32640292SAndy Fiddaman  *
4*32640292SAndy Fiddaman  * Copyright (c) 2021 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/param.h>
9*32640292SAndy Fiddaman #ifdef	__FreeBSD__
10*32640292SAndy Fiddaman #include <sys/endian.h>
11*32640292SAndy Fiddaman #else
12*32640292SAndy Fiddaman #include <endian.h>
13*32640292SAndy Fiddaman #endif
14*32640292SAndy Fiddaman #include <sys/queue.h>
15*32640292SAndy Fiddaman #include <sys/stat.h>
16*32640292SAndy Fiddaman 
17*32640292SAndy Fiddaman #include <machine/vmm.h>
18*32640292SAndy Fiddaman 
19*32640292SAndy Fiddaman #include <err.h>
20*32640292SAndy Fiddaman #include <errno.h>
21*32640292SAndy Fiddaman #include <fcntl.h>
22*32640292SAndy Fiddaman #include <stdio.h>
23*32640292SAndy Fiddaman #include <stdlib.h>
24*32640292SAndy Fiddaman #include <string.h>
25*32640292SAndy Fiddaman #include <unistd.h>
26*32640292SAndy Fiddaman 
27*32640292SAndy Fiddaman #include "acpi_device.h"
28*32640292SAndy Fiddaman #include "bhyverun.h"
29*32640292SAndy Fiddaman #include "inout.h"
30*32640292SAndy Fiddaman #include "pci_lpc.h"
31*32640292SAndy Fiddaman #include "qemu_fwcfg.h"
32*32640292SAndy Fiddaman 
33*32640292SAndy Fiddaman #define QEMU_FWCFG_ACPI_DEVICE_NAME "FWCF"
34*32640292SAndy Fiddaman #define QEMU_FWCFG_ACPI_HARDWARE_ID "QEMU0002"
35*32640292SAndy Fiddaman 
36*32640292SAndy Fiddaman #define QEMU_FWCFG_SELECTOR_PORT_NUMBER 0x510
37*32640292SAndy Fiddaman #define QEMU_FWCFG_SELECTOR_PORT_SIZE 1
38*32640292SAndy Fiddaman #define QEMU_FWCFG_SELECTOR_PORT_FLAGS IOPORT_F_INOUT
39*32640292SAndy Fiddaman #define QEMU_FWCFG_DATA_PORT_NUMBER 0x511
40*32640292SAndy Fiddaman #define QEMU_FWCFG_DATA_PORT_SIZE 1
41*32640292SAndy Fiddaman #define QEMU_FWCFG_DATA_PORT_FLAGS \
42*32640292SAndy Fiddaman 	IOPORT_F_INOUT /* QEMU v2.4+ ignores writes */
43*32640292SAndy Fiddaman 
44*32640292SAndy Fiddaman #define QEMU_FWCFG_ARCHITECTURE_MASK 0x0001
45*32640292SAndy Fiddaman #define QEMU_FWCFG_INDEX_MASK 0x3FFF
46*32640292SAndy Fiddaman 
47*32640292SAndy Fiddaman #define QEMU_FWCFG_SELECT_READ 0
48*32640292SAndy Fiddaman #define QEMU_FWCFG_SELECT_WRITE 1
49*32640292SAndy Fiddaman 
50*32640292SAndy Fiddaman #define QEMU_FWCFG_ARCHITECTURE_GENERIC 0
51*32640292SAndy Fiddaman #define QEMU_FWCFG_ARCHITECTURE_SPECIFIC 1
52*32640292SAndy Fiddaman 
53*32640292SAndy Fiddaman #define QEMU_FWCFG_INDEX_SIGNATURE 0x00
54*32640292SAndy Fiddaman #define QEMU_FWCFG_INDEX_ID 0x01
55*32640292SAndy Fiddaman #define QEMU_FWCFG_INDEX_NB_CPUS 0x05
56*32640292SAndy Fiddaman #define QEMU_FWCFG_INDEX_MAX_CPUS 0x0F
57*32640292SAndy Fiddaman #define QEMU_FWCFG_INDEX_FILE_DIR 0x19
58*32640292SAndy Fiddaman 
59*32640292SAndy Fiddaman #define QEMU_FWCFG_FIRST_FILE_INDEX 0x20
60*32640292SAndy Fiddaman 
61*32640292SAndy Fiddaman #define QEMU_FWCFG_MIN_FILES 10
62*32640292SAndy Fiddaman 
63*32640292SAndy Fiddaman #pragma pack(1)
64*32640292SAndy Fiddaman 
65*32640292SAndy Fiddaman union qemu_fwcfg_selector {
66*32640292SAndy Fiddaman 	struct {
67*32640292SAndy Fiddaman 		uint16_t index : 14;
68*32640292SAndy Fiddaman 		uint16_t writeable : 1;
69*32640292SAndy Fiddaman 		uint16_t architecture : 1;
70*32640292SAndy Fiddaman 	};
71*32640292SAndy Fiddaman 	uint16_t bits;
72*32640292SAndy Fiddaman };
73*32640292SAndy Fiddaman 
74*32640292SAndy Fiddaman struct qemu_fwcfg_signature {
75*32640292SAndy Fiddaman 	uint8_t signature[4];
76*32640292SAndy Fiddaman };
77*32640292SAndy Fiddaman 
78*32640292SAndy Fiddaman struct qemu_fwcfg_id {
79*32640292SAndy Fiddaman 	uint32_t interface : 1; /* always set */
80*32640292SAndy Fiddaman 	uint32_t DMA : 1;
81*32640292SAndy Fiddaman 	uint32_t reserved : 30;
82*32640292SAndy Fiddaman };
83*32640292SAndy Fiddaman 
84*32640292SAndy Fiddaman struct qemu_fwcfg_file {
85*32640292SAndy Fiddaman 	uint32_t be_size;
86*32640292SAndy Fiddaman 	uint16_t be_selector;
87*32640292SAndy Fiddaman 	uint16_t reserved;
88*32640292SAndy Fiddaman 	uint8_t name[QEMU_FWCFG_MAX_NAME];
89*32640292SAndy Fiddaman };
90*32640292SAndy Fiddaman 
91*32640292SAndy Fiddaman struct qemu_fwcfg_directory {
92*32640292SAndy Fiddaman 	uint32_t be_count;
93*32640292SAndy Fiddaman 	struct qemu_fwcfg_file files[0];
94*32640292SAndy Fiddaman };
95*32640292SAndy Fiddaman 
96*32640292SAndy Fiddaman #pragma pack()
97*32640292SAndy Fiddaman 
98*32640292SAndy Fiddaman struct qemu_fwcfg_softc {
99*32640292SAndy Fiddaman 	struct acpi_device *acpi_dev;
100*32640292SAndy Fiddaman 
101*32640292SAndy Fiddaman 	uint32_t data_offset;
102*32640292SAndy Fiddaman 	union qemu_fwcfg_selector selector;
103*32640292SAndy Fiddaman 	struct qemu_fwcfg_item items[QEMU_FWCFG_MAX_ARCHS]
104*32640292SAndy Fiddaman 				    [QEMU_FWCFG_MAX_ENTRIES];
105*32640292SAndy Fiddaman 	struct qemu_fwcfg_directory *directory;
106*32640292SAndy Fiddaman };
107*32640292SAndy Fiddaman 
108*32640292SAndy Fiddaman static struct qemu_fwcfg_softc fwcfg_sc;
109*32640292SAndy Fiddaman 
110*32640292SAndy Fiddaman struct qemu_fwcfg_user_file {
111*32640292SAndy Fiddaman 	STAILQ_ENTRY(qemu_fwcfg_user_file) chain;
112*32640292SAndy Fiddaman 	uint8_t name[QEMU_FWCFG_MAX_NAME];
113*32640292SAndy Fiddaman 	uint32_t size;
114*32640292SAndy Fiddaman 	void *data;
115*32640292SAndy Fiddaman };
116*32640292SAndy Fiddaman static STAILQ_HEAD(qemu_fwcfg_user_file_list,
117*32640292SAndy Fiddaman     qemu_fwcfg_user_file) user_files = STAILQ_HEAD_INITIALIZER(user_files);
118*32640292SAndy Fiddaman 
119*32640292SAndy Fiddaman static int
qemu_fwcfg_selector_port_handler(struct vmctx * const ctx __unused,const int in,const int port __unused,const int bytes,uint32_t * const eax,void * const arg __unused)120*32640292SAndy Fiddaman qemu_fwcfg_selector_port_handler(struct vmctx *const ctx __unused, const int in,
121*32640292SAndy Fiddaman     const int port __unused, const int bytes, uint32_t *const eax,
122*32640292SAndy Fiddaman     void *const arg __unused)
123*32640292SAndy Fiddaman {
124*32640292SAndy Fiddaman 	if (bytes != sizeof(uint16_t)) {
125*32640292SAndy Fiddaman 		warnx("%s: invalid size (%d) of IO port access", __func__,
126*32640292SAndy Fiddaman 		    bytes);
127*32640292SAndy Fiddaman 		return (-1);
128*32640292SAndy Fiddaman 	}
129*32640292SAndy Fiddaman 
130*32640292SAndy Fiddaman 	if (in) {
131*32640292SAndy Fiddaman 		*eax = htole16(fwcfg_sc.selector.bits);
132*32640292SAndy Fiddaman 		return (0);
133*32640292SAndy Fiddaman 	}
134*32640292SAndy Fiddaman 
135*32640292SAndy Fiddaman 	fwcfg_sc.data_offset = 0;
136*32640292SAndy Fiddaman 	fwcfg_sc.selector.bits = le16toh(*eax);
137*32640292SAndy Fiddaman 
138*32640292SAndy Fiddaman 	return (0);
139*32640292SAndy Fiddaman }
140*32640292SAndy Fiddaman 
141*32640292SAndy Fiddaman static int
qemu_fwcfg_data_port_handler(struct vmctx * const ctx __unused,const int in,const int port __unused,const int bytes,uint32_t * const eax,void * const arg __unused)142*32640292SAndy Fiddaman qemu_fwcfg_data_port_handler(struct vmctx *const ctx __unused, const int in,
143*32640292SAndy Fiddaman     const int port __unused, const int bytes, uint32_t *const eax,
144*32640292SAndy Fiddaman     void *const arg __unused)
145*32640292SAndy Fiddaman {
146*32640292SAndy Fiddaman 	if (bytes != sizeof(uint8_t)) {
147*32640292SAndy Fiddaman 		warnx("%s: invalid size (%d) of IO port access", __func__,
148*32640292SAndy Fiddaman 		    bytes);
149*32640292SAndy Fiddaman 		return (-1);
150*32640292SAndy Fiddaman 	}
151*32640292SAndy Fiddaman 
152*32640292SAndy Fiddaman 	if (!in) {
153*32640292SAndy Fiddaman 		warnx("%s: Writes to qemu fwcfg data port aren't allowed",
154*32640292SAndy Fiddaman 		    __func__);
155*32640292SAndy Fiddaman 		return (-1);
156*32640292SAndy Fiddaman 	}
157*32640292SAndy Fiddaman 
158*32640292SAndy Fiddaman 	/* get fwcfg item */
159*32640292SAndy Fiddaman 	struct qemu_fwcfg_item *const item =
160*32640292SAndy Fiddaman 	    &fwcfg_sc.items[fwcfg_sc.selector.architecture]
161*32640292SAndy Fiddaman 			   [fwcfg_sc.selector.index];
162*32640292SAndy Fiddaman 	if (item->data == NULL) {
163*32640292SAndy Fiddaman 		warnx(
164*32640292SAndy Fiddaman 		    "%s: qemu fwcfg item doesn't exist (architecture %s index 0x%x)",
165*32640292SAndy Fiddaman 		    __func__,
166*32640292SAndy Fiddaman 		    fwcfg_sc.selector.architecture ? "specific" : "generic",
167*32640292SAndy Fiddaman 		    fwcfg_sc.selector.index);
168*32640292SAndy Fiddaman 		*eax = 0x00;
169*32640292SAndy Fiddaman 		return (0);
170*32640292SAndy Fiddaman 	} else if (fwcfg_sc.data_offset >= item->size) {
171*32640292SAndy Fiddaman 		warnx(
172*32640292SAndy Fiddaman 		    "%s: qemu fwcfg item read exceeds size (architecture %s index 0x%x size 0x%x offset 0x%x)",
173*32640292SAndy Fiddaman 		    __func__,
174*32640292SAndy Fiddaman 		    fwcfg_sc.selector.architecture ? "specific" : "generic",
175*32640292SAndy Fiddaman 		    fwcfg_sc.selector.index, item->size, fwcfg_sc.data_offset);
176*32640292SAndy Fiddaman 		*eax = 0x00;
177*32640292SAndy Fiddaman 		return (0);
178*32640292SAndy Fiddaman 	}
179*32640292SAndy Fiddaman 
180*32640292SAndy Fiddaman 	/* return item data */
181*32640292SAndy Fiddaman 	*eax = item->data[fwcfg_sc.data_offset];
182*32640292SAndy Fiddaman 	fwcfg_sc.data_offset++;
183*32640292SAndy Fiddaman 
184*32640292SAndy Fiddaman 	return (0);
185*32640292SAndy Fiddaman }
186*32640292SAndy Fiddaman 
187*32640292SAndy Fiddaman static int
qemu_fwcfg_add_item(const uint16_t architecture,const uint16_t index,const uint32_t size,void * const data)188*32640292SAndy Fiddaman qemu_fwcfg_add_item(const uint16_t architecture, const uint16_t index,
189*32640292SAndy Fiddaman     const uint32_t size, void *const data)
190*32640292SAndy Fiddaman {
191*32640292SAndy Fiddaman 	/* truncate architecture and index to their desired size */
192*32640292SAndy Fiddaman 	const uint16_t arch = architecture & QEMU_FWCFG_ARCHITECTURE_MASK;
193*32640292SAndy Fiddaman 	const uint16_t idx = index & QEMU_FWCFG_INDEX_MASK;
194*32640292SAndy Fiddaman 
195*32640292SAndy Fiddaman 	/* get pointer to item specified by selector */
196*32640292SAndy Fiddaman 	struct qemu_fwcfg_item *const fwcfg_item = &fwcfg_sc.items[arch][idx];
197*32640292SAndy Fiddaman 
198*32640292SAndy Fiddaman 	/* check if item is already used */
199*32640292SAndy Fiddaman 	if (fwcfg_item->data != NULL) {
200*32640292SAndy Fiddaman 		warnx("%s: qemu fwcfg item exists (architecture %s index 0x%x)",
201*32640292SAndy Fiddaman 		    __func__, arch ? "specific" : "generic", idx);
202*32640292SAndy Fiddaman 		return (EEXIST);
203*32640292SAndy Fiddaman 	}
204*32640292SAndy Fiddaman 
205*32640292SAndy Fiddaman 	/* save data of the item */
206*32640292SAndy Fiddaman 	fwcfg_item->size = size;
207*32640292SAndy Fiddaman 	fwcfg_item->data = data;
208*32640292SAndy Fiddaman 
209*32640292SAndy Fiddaman 	return (0);
210*32640292SAndy Fiddaman }
211*32640292SAndy Fiddaman 
212*32640292SAndy Fiddaman static int
qemu_fwcfg_add_item_file_dir(void)213*32640292SAndy Fiddaman qemu_fwcfg_add_item_file_dir(void)
214*32640292SAndy Fiddaman {
215*32640292SAndy Fiddaman 	const size_t size = sizeof(struct qemu_fwcfg_directory) +
216*32640292SAndy Fiddaman 	    QEMU_FWCFG_MIN_FILES * sizeof(struct qemu_fwcfg_file);
217*32640292SAndy Fiddaman #ifdef	__FreeBSD__
218*32640292SAndy Fiddaman 	struct qemu_fwcfg_directory *const fwcfg_directory = calloc(1, size);
219*32640292SAndy Fiddaman #else
220*32640292SAndy Fiddaman 	// SMATCH: double check that we're allocating correct size: 4 vs 644
221*32640292SAndy Fiddaman 	void *ptr = calloc(1, size);
222*32640292SAndy Fiddaman 	struct qemu_fwcfg_directory *const fwcfg_directory =
223*32640292SAndy Fiddaman 	    (struct qemu_fwcfg_directory *)ptr;
224*32640292SAndy Fiddaman #endif
225*32640292SAndy Fiddaman 	if (fwcfg_directory == NULL) {
226*32640292SAndy Fiddaman 		return (ENOMEM);
227*32640292SAndy Fiddaman 	}
228*32640292SAndy Fiddaman 
229*32640292SAndy Fiddaman 	fwcfg_sc.directory = fwcfg_directory;
230*32640292SAndy Fiddaman 
231*32640292SAndy Fiddaman 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
232*32640292SAndy Fiddaman 	    QEMU_FWCFG_INDEX_FILE_DIR, sizeof(struct qemu_fwcfg_directory),
233*32640292SAndy Fiddaman 	    (uint8_t *)fwcfg_sc.directory));
234*32640292SAndy Fiddaman }
235*32640292SAndy Fiddaman 
236*32640292SAndy Fiddaman static int
qemu_fwcfg_add_item_id(void)237*32640292SAndy Fiddaman qemu_fwcfg_add_item_id(void)
238*32640292SAndy Fiddaman {
239*32640292SAndy Fiddaman 	struct qemu_fwcfg_id *const fwcfg_id = calloc(1,
240*32640292SAndy Fiddaman 	    sizeof(struct qemu_fwcfg_id));
241*32640292SAndy Fiddaman 	if (fwcfg_id == NULL) {
242*32640292SAndy Fiddaman 		return (ENOMEM);
243*32640292SAndy Fiddaman 	}
244*32640292SAndy Fiddaman 
245*32640292SAndy Fiddaman 	fwcfg_id->interface = 1;
246*32640292SAndy Fiddaman 	fwcfg_id->DMA = 0;
247*32640292SAndy Fiddaman 
248*32640292SAndy Fiddaman 	uint32_t *const le_fwcfg_id_ptr = (uint32_t *)fwcfg_id;
249*32640292SAndy Fiddaman 	*le_fwcfg_id_ptr = htole32(*le_fwcfg_id_ptr);
250*32640292SAndy Fiddaman 
251*32640292SAndy Fiddaman 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
252*32640292SAndy Fiddaman 	    QEMU_FWCFG_INDEX_ID, sizeof(struct qemu_fwcfg_id),
253*32640292SAndy Fiddaman 	    (uint8_t *)fwcfg_id));
254*32640292SAndy Fiddaman }
255*32640292SAndy Fiddaman 
256*32640292SAndy Fiddaman static int
qemu_fwcfg_add_item_max_cpus(void)257*32640292SAndy Fiddaman qemu_fwcfg_add_item_max_cpus(void)
258*32640292SAndy Fiddaman {
259*32640292SAndy Fiddaman 	uint16_t *fwcfg_max_cpus = calloc(1, sizeof(uint16_t));
260*32640292SAndy Fiddaman 	if (fwcfg_max_cpus == NULL) {
261*32640292SAndy Fiddaman 		return (ENOMEM);
262*32640292SAndy Fiddaman 	}
263*32640292SAndy Fiddaman 
264*32640292SAndy Fiddaman 	/*
265*32640292SAndy Fiddaman 	 * We don't support cpu hotplug yet. For that reason, use guest_ncpus instead
266*32640292SAndy Fiddaman 	 * of maxcpus.
267*32640292SAndy Fiddaman 	 */
268*32640292SAndy Fiddaman 	*fwcfg_max_cpus = htole16(guest_ncpus);
269*32640292SAndy Fiddaman 
270*32640292SAndy Fiddaman 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
271*32640292SAndy Fiddaman 	    QEMU_FWCFG_INDEX_MAX_CPUS, sizeof(uint16_t), fwcfg_max_cpus));
272*32640292SAndy Fiddaman }
273*32640292SAndy Fiddaman 
274*32640292SAndy Fiddaman static int
qemu_fwcfg_add_item_nb_cpus(void)275*32640292SAndy Fiddaman qemu_fwcfg_add_item_nb_cpus(void)
276*32640292SAndy Fiddaman {
277*32640292SAndy Fiddaman 	uint16_t *fwcfg_max_cpus = calloc(1, sizeof(uint16_t));
278*32640292SAndy Fiddaman 	if (fwcfg_max_cpus == NULL) {
279*32640292SAndy Fiddaman 		return (ENOMEM);
280*32640292SAndy Fiddaman 	}
281*32640292SAndy Fiddaman 
282*32640292SAndy Fiddaman 	*fwcfg_max_cpus = htole16(guest_ncpus);
283*32640292SAndy Fiddaman 
284*32640292SAndy Fiddaman 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
285*32640292SAndy Fiddaman 	    QEMU_FWCFG_INDEX_NB_CPUS, sizeof(uint16_t), fwcfg_max_cpus));
286*32640292SAndy Fiddaman }
287*32640292SAndy Fiddaman 
288*32640292SAndy Fiddaman static int
qemu_fwcfg_add_item_signature(void)289*32640292SAndy Fiddaman qemu_fwcfg_add_item_signature(void)
290*32640292SAndy Fiddaman {
291*32640292SAndy Fiddaman 	struct qemu_fwcfg_signature *const fwcfg_signature = calloc(1,
292*32640292SAndy Fiddaman 	    sizeof(struct qemu_fwcfg_signature));
293*32640292SAndy Fiddaman 	if (fwcfg_signature == NULL) {
294*32640292SAndy Fiddaman 		return (ENOMEM);
295*32640292SAndy Fiddaman 	}
296*32640292SAndy Fiddaman 
297*32640292SAndy Fiddaman 	fwcfg_signature->signature[0] = 'Q';
298*32640292SAndy Fiddaman 	fwcfg_signature->signature[1] = 'E';
299*32640292SAndy Fiddaman 	fwcfg_signature->signature[2] = 'M';
300*32640292SAndy Fiddaman 	fwcfg_signature->signature[3] = 'U';
301*32640292SAndy Fiddaman 
302*32640292SAndy Fiddaman 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
303*32640292SAndy Fiddaman 	    QEMU_FWCFG_INDEX_SIGNATURE, sizeof(struct qemu_fwcfg_signature),
304*32640292SAndy Fiddaman 	    (uint8_t *)fwcfg_signature));
305*32640292SAndy Fiddaman }
306*32640292SAndy Fiddaman 
307*32640292SAndy Fiddaman static int
qemu_fwcfg_register_port(const char * const name,const int port,const int size,const int flags,const inout_func_t handler)308*32640292SAndy Fiddaman qemu_fwcfg_register_port(const char *const name, const int port, const int size,
309*32640292SAndy Fiddaman     const int flags, const inout_func_t handler)
310*32640292SAndy Fiddaman {
311*32640292SAndy Fiddaman 	struct inout_port iop;
312*32640292SAndy Fiddaman 
313*32640292SAndy Fiddaman 	bzero(&iop, sizeof(iop));
314*32640292SAndy Fiddaman 	iop.name = name;
315*32640292SAndy Fiddaman 	iop.port = port;
316*32640292SAndy Fiddaman 	iop.size = size;
317*32640292SAndy Fiddaman 	iop.flags = flags;
318*32640292SAndy Fiddaman 	iop.handler = handler;
319*32640292SAndy Fiddaman 
320*32640292SAndy Fiddaman 	return (register_inout(&iop));
321*32640292SAndy Fiddaman }
322*32640292SAndy Fiddaman 
323*32640292SAndy Fiddaman int
qemu_fwcfg_add_file(const char * name,const uint32_t size,void * const data)324*32640292SAndy Fiddaman qemu_fwcfg_add_file(const char *name, const uint32_t size, void *const data)
325*32640292SAndy Fiddaman {
326*32640292SAndy Fiddaman 	if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
327*32640292SAndy Fiddaman 		return (EINVAL);
328*32640292SAndy Fiddaman 
329*32640292SAndy Fiddaman 	/*
330*32640292SAndy Fiddaman 	 * QEMU specifies count as big endian.
331*32640292SAndy Fiddaman 	 * Convert it to host endian to work with it.
332*32640292SAndy Fiddaman 	 */
333*32640292SAndy Fiddaman 	const uint32_t count = be32toh(fwcfg_sc.directory->be_count) + 1;
334*32640292SAndy Fiddaman 
335*32640292SAndy Fiddaman 	/* add file to items list */
336*32640292SAndy Fiddaman 	const uint32_t index = QEMU_FWCFG_FIRST_FILE_INDEX + count - 1;
337*32640292SAndy Fiddaman 	const int error = qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
338*32640292SAndy Fiddaman 	    index, size, data);
339*32640292SAndy Fiddaman 	if (error != 0) {
340*32640292SAndy Fiddaman 		return (error);
341*32640292SAndy Fiddaman 	}
342*32640292SAndy Fiddaman 
343*32640292SAndy Fiddaman 	/*
344*32640292SAndy Fiddaman 	 * files should be sorted alphabetical, get index for new file
345*32640292SAndy Fiddaman 	 */
346*32640292SAndy Fiddaman 	uint32_t file_index;
347*32640292SAndy Fiddaman 	for (file_index = 0; file_index < count - 1; ++file_index) {
348*32640292SAndy Fiddaman #ifdef	__FreeBSD__
349*32640292SAndy Fiddaman 		if (strcmp(name, fwcfg_sc.directory->files[file_index].name) <
350*32640292SAndy Fiddaman 		    0)
351*32640292SAndy Fiddaman 			break;
352*32640292SAndy Fiddaman #else
353*32640292SAndy Fiddaman 		if (strcmp((char *)name,
354*32640292SAndy Fiddaman 		    (char *)fwcfg_sc.directory->files[file_index].name) < 0) {
355*32640292SAndy Fiddaman 			break;
356*32640292SAndy Fiddaman 		}
357*32640292SAndy Fiddaman #endif
358*32640292SAndy Fiddaman 	}
359*32640292SAndy Fiddaman 
360*32640292SAndy Fiddaman 	if (count > QEMU_FWCFG_MIN_FILES) {
361*32640292SAndy Fiddaman 		/* alloc new file directory */
362*32640292SAndy Fiddaman 		const uint64_t new_size = sizeof(struct qemu_fwcfg_directory) +
363*32640292SAndy Fiddaman 		    count * sizeof(struct qemu_fwcfg_file);
364*32640292SAndy Fiddaman 		struct qemu_fwcfg_directory *const new_directory = calloc(1,
365*32640292SAndy Fiddaman 		    new_size);
366*32640292SAndy Fiddaman 		if (new_directory == NULL) {
367*32640292SAndy Fiddaman 			warnx(
368*32640292SAndy Fiddaman 			    "%s: Unable to allocate a new qemu fwcfg files directory (count %d)",
369*32640292SAndy Fiddaman 			    __func__, count);
370*32640292SAndy Fiddaman 			return (ENOMEM);
371*32640292SAndy Fiddaman 		}
372*32640292SAndy Fiddaman 
373*32640292SAndy Fiddaman 		/* copy files below file_index to new directory */
374*32640292SAndy Fiddaman 		memcpy(new_directory->files, fwcfg_sc.directory->files,
375*32640292SAndy Fiddaman 		    file_index * sizeof(struct qemu_fwcfg_file));
376*32640292SAndy Fiddaman 
377*32640292SAndy Fiddaman 		/* copy files above file_index to directory */
378*32640292SAndy Fiddaman 		memcpy(&new_directory->files[file_index + 1],
379*32640292SAndy Fiddaman 		    &fwcfg_sc.directory->files[file_index],
380*32640292SAndy Fiddaman 		    (count - file_index - 1) * sizeof(struct qemu_fwcfg_file));
381*32640292SAndy Fiddaman 
382*32640292SAndy Fiddaman 		/* free old directory */
383*32640292SAndy Fiddaman 		free(fwcfg_sc.directory);
384*32640292SAndy Fiddaman 
385*32640292SAndy Fiddaman 		/* set directory pointer to new directory */
386*32640292SAndy Fiddaman 		fwcfg_sc.directory = new_directory;
387*32640292SAndy Fiddaman 
388*32640292SAndy Fiddaman 		/* adjust directory pointer */
389*32640292SAndy Fiddaman 		fwcfg_sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].data =
390*32640292SAndy Fiddaman 		    (uint8_t *)fwcfg_sc.directory;
391*32640292SAndy Fiddaman 	} else {
392*32640292SAndy Fiddaman 		/* shift files behind file_index */
393*32640292SAndy Fiddaman 		for (uint32_t i = QEMU_FWCFG_MIN_FILES - 1; i > file_index;
394*32640292SAndy Fiddaman 		     --i) {
395*32640292SAndy Fiddaman 			memcpy(&fwcfg_sc.directory->files[i],
396*32640292SAndy Fiddaman 			    &fwcfg_sc.directory->files[i - 1],
397*32640292SAndy Fiddaman 			    sizeof(struct qemu_fwcfg_file));
398*32640292SAndy Fiddaman 		}
399*32640292SAndy Fiddaman 	}
400*32640292SAndy Fiddaman 
401*32640292SAndy Fiddaman 	/*
402*32640292SAndy Fiddaman 	 * QEMU specifies count, size and index as big endian.
403*32640292SAndy Fiddaman 	 * Save these values in big endian to simplify guest reads of these
404*32640292SAndy Fiddaman 	 * values.
405*32640292SAndy Fiddaman 	 */
406*32640292SAndy Fiddaman 	fwcfg_sc.directory->be_count = htobe32(count);
407*32640292SAndy Fiddaman 	fwcfg_sc.directory->files[file_index].be_size = htobe32(size);
408*32640292SAndy Fiddaman 	fwcfg_sc.directory->files[file_index].be_selector = htobe16(index);
409*32640292SAndy Fiddaman #ifdef	__FreeBSD__
410*32640292SAndy Fiddaman 	strcpy(fwcfg_sc.directory->files[file_index].name, name);
411*32640292SAndy Fiddaman #else
412*32640292SAndy Fiddaman 	strcpy((char *)fwcfg_sc.directory->files[file_index].name,
413*32640292SAndy Fiddaman 	    (char *)name);
414*32640292SAndy Fiddaman #endif
415*32640292SAndy Fiddaman 
416*32640292SAndy Fiddaman 	/* set new size for the fwcfg_file_directory */
417*32640292SAndy Fiddaman 	fwcfg_sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].size =
418*32640292SAndy Fiddaman 	    sizeof(struct qemu_fwcfg_directory) +
419*32640292SAndy Fiddaman 	    count * sizeof(struct qemu_fwcfg_file);
420*32640292SAndy Fiddaman 
421*32640292SAndy Fiddaman 	return (0);
422*32640292SAndy Fiddaman }
423*32640292SAndy Fiddaman 
424*32640292SAndy Fiddaman static int
qemu_fwcfg_add_user_files(void)425*32640292SAndy Fiddaman qemu_fwcfg_add_user_files(void)
426*32640292SAndy Fiddaman {
427*32640292SAndy Fiddaman 	const struct qemu_fwcfg_user_file *fwcfg_file;
428*32640292SAndy Fiddaman 	int error;
429*32640292SAndy Fiddaman 
430*32640292SAndy Fiddaman 	STAILQ_FOREACH(fwcfg_file, &user_files, chain) {
431*32640292SAndy Fiddaman #ifdef	__FreeBSD__
432*32640292SAndy Fiddaman 		error = qemu_fwcfg_add_file(fwcfg_file->name, fwcfg_file->size,
433*32640292SAndy Fiddaman 		    fwcfg_file->data);
434*32640292SAndy Fiddaman #else
435*32640292SAndy Fiddaman 		error = qemu_fwcfg_add_file((char *)fwcfg_file->name,
436*32640292SAndy Fiddaman 		    fwcfg_file->size, fwcfg_file->data);
437*32640292SAndy Fiddaman #endif
438*32640292SAndy Fiddaman 		if (error)
439*32640292SAndy Fiddaman 			return (error);
440*32640292SAndy Fiddaman 	}
441*32640292SAndy Fiddaman 
442*32640292SAndy Fiddaman 	return (0);
443*32640292SAndy Fiddaman }
444*32640292SAndy Fiddaman 
445*32640292SAndy Fiddaman static const struct acpi_device_emul qemu_fwcfg_acpi_device_emul = {
446*32640292SAndy Fiddaman 	.name = QEMU_FWCFG_ACPI_DEVICE_NAME,
447*32640292SAndy Fiddaman 	.hid = QEMU_FWCFG_ACPI_HARDWARE_ID,
448*32640292SAndy Fiddaman };
449*32640292SAndy Fiddaman 
450*32640292SAndy Fiddaman int
qemu_fwcfg_init(struct vmctx * const ctx)451*32640292SAndy Fiddaman qemu_fwcfg_init(struct vmctx *const ctx)
452*32640292SAndy Fiddaman {
453*32640292SAndy Fiddaman 	int error;
454*32640292SAndy Fiddaman 
455*32640292SAndy Fiddaman 	/*
456*32640292SAndy Fiddaman 	 * Bhyve supports fwctl (bhyve) and fwcfg (qemu) as firmware interfaces.
457*32640292SAndy Fiddaman 	 * Both are using the same ports. So, it's not possible to provide both
458*32640292SAndy Fiddaman 	 * interfaces at the same time to the guest. Therefore, only create acpi
459*32640292SAndy Fiddaman 	 * tables and register io ports for fwcfg, if it's used.
460*32640292SAndy Fiddaman 	 */
461*32640292SAndy Fiddaman 	if (strcmp(lpc_fwcfg(), "qemu") == 0) {
462*32640292SAndy Fiddaman 		error = acpi_device_create(&fwcfg_sc.acpi_dev, &fwcfg_sc, ctx,
463*32640292SAndy Fiddaman 		    &qemu_fwcfg_acpi_device_emul);
464*32640292SAndy Fiddaman 		if (error) {
465*32640292SAndy Fiddaman 			warnx("%s: failed to create ACPI device for QEMU FwCfg",
466*32640292SAndy Fiddaman 			    __func__);
467*32640292SAndy Fiddaman 			goto done;
468*32640292SAndy Fiddaman 		}
469*32640292SAndy Fiddaman 
470*32640292SAndy Fiddaman 		error = acpi_device_add_res_fixed_ioport(fwcfg_sc.acpi_dev,
471*32640292SAndy Fiddaman 		    QEMU_FWCFG_SELECTOR_PORT_NUMBER, 2);
472*32640292SAndy Fiddaman 		if (error) {
473*32640292SAndy Fiddaman 			warnx("%s: failed to add fixed IO port for QEMU FwCfg",
474*32640292SAndy Fiddaman 			    __func__);
475*32640292SAndy Fiddaman 			goto done;
476*32640292SAndy Fiddaman 		}
477*32640292SAndy Fiddaman 
478*32640292SAndy Fiddaman 		/* add handlers for fwcfg ports */
479*32640292SAndy Fiddaman 		if ((error = qemu_fwcfg_register_port("qemu_fwcfg_selector",
480*32640292SAndy Fiddaman 		    QEMU_FWCFG_SELECTOR_PORT_NUMBER,
481*32640292SAndy Fiddaman 		    QEMU_FWCFG_SELECTOR_PORT_SIZE,
482*32640292SAndy Fiddaman 		    QEMU_FWCFG_SELECTOR_PORT_FLAGS,
483*32640292SAndy Fiddaman 		    qemu_fwcfg_selector_port_handler)) != 0) {
484*32640292SAndy Fiddaman 			warnx(
485*32640292SAndy Fiddaman 			    "%s: Unable to register qemu fwcfg selector port 0x%x",
486*32640292SAndy Fiddaman 			    __func__, QEMU_FWCFG_SELECTOR_PORT_NUMBER);
487*32640292SAndy Fiddaman 			goto done;
488*32640292SAndy Fiddaman 		}
489*32640292SAndy Fiddaman 		if ((error = qemu_fwcfg_register_port("qemu_fwcfg_data",
490*32640292SAndy Fiddaman 		    QEMU_FWCFG_DATA_PORT_NUMBER, QEMU_FWCFG_DATA_PORT_SIZE,
491*32640292SAndy Fiddaman 		    QEMU_FWCFG_DATA_PORT_FLAGS,
492*32640292SAndy Fiddaman 		    qemu_fwcfg_data_port_handler)) != 0) {
493*32640292SAndy Fiddaman 			warnx(
494*32640292SAndy Fiddaman 			    "%s: Unable to register qemu fwcfg data port 0x%x",
495*32640292SAndy Fiddaman 			    __func__, QEMU_FWCFG_DATA_PORT_NUMBER);
496*32640292SAndy Fiddaman 			goto done;
497*32640292SAndy Fiddaman 		}
498*32640292SAndy Fiddaman 	}
499*32640292SAndy Fiddaman 
500*32640292SAndy Fiddaman 	/* add common fwcfg items */
501*32640292SAndy Fiddaman 	if ((error = qemu_fwcfg_add_item_signature()) != 0) {
502*32640292SAndy Fiddaman 		warnx("%s: Unable to add signature item", __func__);
503*32640292SAndy Fiddaman 		goto done;
504*32640292SAndy Fiddaman 	}
505*32640292SAndy Fiddaman 	if ((error = qemu_fwcfg_add_item_id()) != 0) {
506*32640292SAndy Fiddaman 		warnx("%s: Unable to add id item", __func__);
507*32640292SAndy Fiddaman 		goto done;
508*32640292SAndy Fiddaman 	}
509*32640292SAndy Fiddaman 	if ((error = qemu_fwcfg_add_item_nb_cpus()) != 0) {
510*32640292SAndy Fiddaman 		warnx("%s: Unable to add nb_cpus item", __func__);
511*32640292SAndy Fiddaman 		goto done;
512*32640292SAndy Fiddaman 	}
513*32640292SAndy Fiddaman 	if ((error = qemu_fwcfg_add_item_max_cpus()) != 0) {
514*32640292SAndy Fiddaman 		warnx("%s: Unable to add max_cpus item", __func__);
515*32640292SAndy Fiddaman 		goto done;
516*32640292SAndy Fiddaman 	}
517*32640292SAndy Fiddaman 	if ((error = qemu_fwcfg_add_item_file_dir()) != 0) {
518*32640292SAndy Fiddaman 		warnx("%s: Unable to add file_dir item", __func__);
519*32640292SAndy Fiddaman 		goto done;
520*32640292SAndy Fiddaman 	}
521*32640292SAndy Fiddaman 
522*32640292SAndy Fiddaman 	/* add user defined fwcfg files */
523*32640292SAndy Fiddaman 	if ((error = qemu_fwcfg_add_user_files()) != 0) {
524*32640292SAndy Fiddaman 		warnx("%s: Unable to add user files", __func__);
525*32640292SAndy Fiddaman 		goto done;
526*32640292SAndy Fiddaman 	}
527*32640292SAndy Fiddaman 
528*32640292SAndy Fiddaman done:
529*32640292SAndy Fiddaman 	if (error) {
530*32640292SAndy Fiddaman 		acpi_device_destroy(fwcfg_sc.acpi_dev);
531*32640292SAndy Fiddaman 	}
532*32640292SAndy Fiddaman 
533*32640292SAndy Fiddaman 	return (error);
534*32640292SAndy Fiddaman }
535*32640292SAndy Fiddaman 
536*32640292SAndy Fiddaman static void
qemu_fwcfg_usage(const char * opt)537*32640292SAndy Fiddaman qemu_fwcfg_usage(const char *opt)
538*32640292SAndy Fiddaman {
539*32640292SAndy Fiddaman 	warnx("Invalid fw_cfg option \"%s\"", opt);
540*32640292SAndy Fiddaman 	warnx("-f [name=]<name>,(string|file)=<value>");
541*32640292SAndy Fiddaman }
542*32640292SAndy Fiddaman 
543*32640292SAndy Fiddaman /*
544*32640292SAndy Fiddaman  * Parses the cmdline argument for user defined fw_cfg items. The cmdline
545*32640292SAndy Fiddaman  * argument has the format:
546*32640292SAndy Fiddaman  * "-f [name=]<name>,(string|file)=<value>"
547*32640292SAndy Fiddaman  *
548*32640292SAndy Fiddaman  * E.g.: "-f opt/com.page/example,string=Hello"
549*32640292SAndy Fiddaman  */
550*32640292SAndy Fiddaman int
qemu_fwcfg_parse_cmdline_arg(const char * opt)551*32640292SAndy Fiddaman qemu_fwcfg_parse_cmdline_arg(const char *opt)
552*32640292SAndy Fiddaman {
553*32640292SAndy Fiddaman 	struct qemu_fwcfg_user_file *fwcfg_file;
554*32640292SAndy Fiddaman 	struct stat sb;
555*32640292SAndy Fiddaman 	const char *opt_ptr, *opt_end;
556*32640292SAndy Fiddaman 	ssize_t bytes_read;
557*32640292SAndy Fiddaman 	int fd;
558*32640292SAndy Fiddaman 
559*32640292SAndy Fiddaman 	fwcfg_file = malloc(sizeof(*fwcfg_file));
560*32640292SAndy Fiddaman 	if (fwcfg_file == NULL) {
561*32640292SAndy Fiddaman 		warnx("Unable to allocate fw_cfg_user_file");
562*32640292SAndy Fiddaman 		return (ENOMEM);
563*32640292SAndy Fiddaman 	}
564*32640292SAndy Fiddaman 
565*32640292SAndy Fiddaman 	/* get pointer to <name> */
566*32640292SAndy Fiddaman 	opt_ptr = opt;
567*32640292SAndy Fiddaman 	/* If [name=] is specified, skip it */
568*32640292SAndy Fiddaman 	if (strncmp(opt_ptr, "name=", sizeof("name=") - 1) == 0) {
569*32640292SAndy Fiddaman 		opt_ptr += sizeof("name=") - 1;
570*32640292SAndy Fiddaman 	}
571*32640292SAndy Fiddaman 
572*32640292SAndy Fiddaman 	/* get the end of <name> */
573*32640292SAndy Fiddaman 	opt_end = strchr(opt_ptr, ',');
574*32640292SAndy Fiddaman 	if (opt_end == NULL) {
575*32640292SAndy Fiddaman 		qemu_fwcfg_usage(opt);
576*32640292SAndy Fiddaman #ifndef	__FreeBSD__
577*32640292SAndy Fiddaman 		free(fwcfg_file);
578*32640292SAndy Fiddaman #endif
579*32640292SAndy Fiddaman 		return (EINVAL);
580*32640292SAndy Fiddaman 	}
581*32640292SAndy Fiddaman 
582*32640292SAndy Fiddaman 	/* check if <name> is too long */
583*32640292SAndy Fiddaman 	if (opt_end - opt_ptr >= QEMU_FWCFG_MAX_NAME) {
584*32640292SAndy Fiddaman 		warnx("fw_cfg name too long: \"%s\"", opt);
585*32640292SAndy Fiddaman #ifndef	__FreeBSD__
586*32640292SAndy Fiddaman 		free(fwcfg_file);
587*32640292SAndy Fiddaman #endif
588*32640292SAndy Fiddaman 		return (EINVAL);
589*32640292SAndy Fiddaman 	}
590*32640292SAndy Fiddaman 
591*32640292SAndy Fiddaman 	/* save <name> */
592*32640292SAndy Fiddaman #ifdef	__FreeBSD__
593*32640292SAndy Fiddaman 	strncpy(fwcfg_file->name, opt_ptr, opt_end - opt_ptr);
594*32640292SAndy Fiddaman #else
595*32640292SAndy Fiddaman 	strncpy((char *)fwcfg_file->name, opt_ptr, opt_end - opt_ptr);
596*32640292SAndy Fiddaman #endif
597*32640292SAndy Fiddaman 	fwcfg_file->name[opt_end - opt_ptr] = '\0';
598*32640292SAndy Fiddaman 
599*32640292SAndy Fiddaman 	/* set opt_ptr and opt_end to <value> */
600*32640292SAndy Fiddaman 	opt_ptr = opt_end + 1;
601*32640292SAndy Fiddaman 	opt_end = opt_ptr + strlen(opt_ptr);
602*32640292SAndy Fiddaman 
603*32640292SAndy Fiddaman 	if (strncmp(opt_ptr, "string=", sizeof("string=") - 1) == 0) {
604*32640292SAndy Fiddaman 		opt_ptr += sizeof("string=") - 1;
605*32640292SAndy Fiddaman 		fwcfg_file->data = strdup(opt_ptr);
606*32640292SAndy Fiddaman 		if (fwcfg_file->data == NULL) {
607*32640292SAndy Fiddaman 			warnx("Can't duplicate fw_cfg_user_file string \"%s\"",
608*32640292SAndy Fiddaman 			    opt_ptr);
609*32640292SAndy Fiddaman 			return (ENOMEM);
610*32640292SAndy Fiddaman 		}
611*32640292SAndy Fiddaman 		fwcfg_file->size = strlen(opt_ptr) + 1;
612*32640292SAndy Fiddaman 	} else if (strncmp(opt_ptr, "file=", sizeof("file=") - 1) == 0) {
613*32640292SAndy Fiddaman 		opt_ptr += sizeof("file=") - 1;
614*32640292SAndy Fiddaman 
615*32640292SAndy Fiddaman 		fd = open(opt_ptr, O_RDONLY);
616*32640292SAndy Fiddaman 		if (fd < 0) {
617*32640292SAndy Fiddaman 			warn("Can't open fw_cfg_user_file file \"%s\"",
618*32640292SAndy Fiddaman 			    opt_ptr);
619*32640292SAndy Fiddaman 			return (EINVAL);
620*32640292SAndy Fiddaman 		}
621*32640292SAndy Fiddaman 
622*32640292SAndy Fiddaman 		if (fstat(fd, &sb) < 0) {
623*32640292SAndy Fiddaman 			warn("Unable to get size of file \"%s\"", opt_ptr);
624*32640292SAndy Fiddaman 			close(fd);
625*32640292SAndy Fiddaman 			return (-1);
626*32640292SAndy Fiddaman 		}
627*32640292SAndy Fiddaman 
628*32640292SAndy Fiddaman 		fwcfg_file->data = malloc(sb.st_size);
629*32640292SAndy Fiddaman 		if (fwcfg_file->data == NULL) {
630*32640292SAndy Fiddaman 			warnx(
631*32640292SAndy Fiddaman 			    "Can't allocate fw_cfg_user_file file \"%s\" (size: 0x%16lx)",
632*32640292SAndy Fiddaman 			    opt_ptr, sb.st_size);
633*32640292SAndy Fiddaman 			close(fd);
634*32640292SAndy Fiddaman 			return (ENOMEM);
635*32640292SAndy Fiddaman 		}
636*32640292SAndy Fiddaman 		bytes_read = read(fd, fwcfg_file->data, sb.st_size);
637*32640292SAndy Fiddaman 		if (bytes_read < 0 || bytes_read != sb.st_size) {
638*32640292SAndy Fiddaman 			warn("Unable to read file \"%s\"", opt_ptr);
639*32640292SAndy Fiddaman 			free(fwcfg_file->data);
640*32640292SAndy Fiddaman 			close(fd);
641*32640292SAndy Fiddaman 			return (-1);
642*32640292SAndy Fiddaman 		}
643*32640292SAndy Fiddaman 		fwcfg_file->size = bytes_read;
644*32640292SAndy Fiddaman 
645*32640292SAndy Fiddaman 		close(fd);
646*32640292SAndy Fiddaman 	} else {
647*32640292SAndy Fiddaman 		qemu_fwcfg_usage(opt);
648*32640292SAndy Fiddaman 		return (EINVAL);
649*32640292SAndy Fiddaman 	}
650*32640292SAndy Fiddaman 
651*32640292SAndy Fiddaman 	STAILQ_INSERT_TAIL(&user_files, fwcfg_file, chain);
652*32640292SAndy Fiddaman 
653*32640292SAndy Fiddaman 	return (0);
654*32640292SAndy Fiddaman }
655