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