xref: /freebsd/usr.sbin/bhyve/qemu_fwcfg.c (revision 4a381007)
19b99de77SCorvin Köhne /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
39b99de77SCorvin Köhne  *
49b99de77SCorvin Köhne  * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
59b99de77SCorvin Köhne  * Author: Corvin Köhne <c.koehne@beckhoff.com>
69b99de77SCorvin Köhne  */
79b99de77SCorvin Köhne 
89b99de77SCorvin Köhne #include <sys/param.h>
934f804e5SCorvin Köhne #include <sys/endian.h>
10ca14781cSCorvin Köhne #include <sys/queue.h>
11ca14781cSCorvin Köhne #include <sys/stat.h>
129b99de77SCorvin Köhne 
139b99de77SCorvin Köhne #include <machine/vmm.h>
149b99de77SCorvin Köhne 
159b99de77SCorvin Köhne #include <err.h>
169b99de77SCorvin Köhne #include <errno.h>
17ca14781cSCorvin Köhne #include <fcntl.h>
18ca14781cSCorvin Köhne #include <stdio.h>
199b99de77SCorvin Köhne #include <stdlib.h>
209b99de77SCorvin Köhne #include <string.h>
21ca14781cSCorvin Köhne #include <unistd.h>
229b99de77SCorvin Köhne 
239b99de77SCorvin Köhne #include "acpi_device.h"
2476fa62b5SCorvin Köhne #include "bhyverun.h"
2555c13f6eSMark Johnston #ifdef __amd64__
2631cf78c9SMark Johnston #include "amd64/inout.h"
2755c13f6eSMark Johnston #include "amd64/pci_lpc.h"
2855c13f6eSMark Johnston #endif
299b99de77SCorvin Köhne #include "qemu_fwcfg.h"
309b99de77SCorvin Köhne 
319b99de77SCorvin Köhne #define QEMU_FWCFG_ACPI_DEVICE_NAME "FWCF"
329b99de77SCorvin Köhne #define QEMU_FWCFG_ACPI_HARDWARE_ID "QEMU0002"
339b99de77SCorvin Köhne 
349b99de77SCorvin Köhne #define QEMU_FWCFG_SELECTOR_PORT_NUMBER 0x510
359b99de77SCorvin Köhne #define QEMU_FWCFG_SELECTOR_PORT_SIZE 1
369b99de77SCorvin Köhne #define QEMU_FWCFG_SELECTOR_PORT_FLAGS IOPORT_F_INOUT
379b99de77SCorvin Köhne #define QEMU_FWCFG_DATA_PORT_NUMBER 0x511
389b99de77SCorvin Köhne #define QEMU_FWCFG_DATA_PORT_SIZE 1
399b99de77SCorvin Köhne #define QEMU_FWCFG_DATA_PORT_FLAGS \
409b99de77SCorvin Köhne 	IOPORT_F_INOUT /* QEMU v2.4+ ignores writes */
419b99de77SCorvin Köhne 
42151d8131SCorvin Köhne #define QEMU_FWCFG_ARCHITECTURE_MASK 0x0001
43151d8131SCorvin Köhne #define QEMU_FWCFG_INDEX_MASK 0x3FFF
44151d8131SCorvin Köhne 
45151d8131SCorvin Köhne #define QEMU_FWCFG_SELECT_READ 0
46151d8131SCorvin Köhne #define QEMU_FWCFG_SELECT_WRITE 1
47151d8131SCorvin Köhne 
48151d8131SCorvin Köhne #define QEMU_FWCFG_ARCHITECTURE_GENERIC 0
49151d8131SCorvin Köhne #define QEMU_FWCFG_ARCHITECTURE_SPECIFIC 1
50151d8131SCorvin Köhne 
5134f804e5SCorvin Köhne #define QEMU_FWCFG_INDEX_SIGNATURE 0x00
5234f804e5SCorvin Köhne #define QEMU_FWCFG_INDEX_ID 0x01
53e46be58cSCorvin Köhne #define QEMU_FWCFG_INDEX_NB_CPUS 0x05
54305edaa4SCorvin Köhne #define QEMU_FWCFG_INDEX_MAX_CPUS 0x0F
5534f804e5SCorvin Köhne #define QEMU_FWCFG_INDEX_FILE_DIR 0x19
5634f804e5SCorvin Köhne 
576f9ebb3dSCorvin Köhne #define QEMU_FWCFG_FIRST_FILE_INDEX 0x20
586f9ebb3dSCorvin Köhne 
5934f804e5SCorvin Köhne #define QEMU_FWCFG_MIN_FILES 10
6034f804e5SCorvin Köhne 
61151d8131SCorvin Köhne #pragma pack(1)
62151d8131SCorvin Köhne 
63151d8131SCorvin Köhne union qemu_fwcfg_selector {
64151d8131SCorvin Köhne 	struct {
65151d8131SCorvin Köhne 		uint16_t index : 14;
66151d8131SCorvin Köhne 		uint16_t writeable : 1;
67151d8131SCorvin Köhne 		uint16_t architecture : 1;
68151d8131SCorvin Köhne 	};
69151d8131SCorvin Köhne 	uint16_t bits;
70151d8131SCorvin Köhne };
71151d8131SCorvin Köhne 
7234f804e5SCorvin Köhne struct qemu_fwcfg_signature {
7334f804e5SCorvin Köhne 	uint8_t signature[4];
7434f804e5SCorvin Köhne };
7534f804e5SCorvin Köhne 
7634f804e5SCorvin Köhne struct qemu_fwcfg_id {
7734f804e5SCorvin Köhne 	uint32_t interface : 1; /* always set */
7834f804e5SCorvin Köhne 	uint32_t DMA : 1;
7934f804e5SCorvin Köhne 	uint32_t reserved : 30;
8034f804e5SCorvin Köhne };
8134f804e5SCorvin Köhne 
8234f804e5SCorvin Köhne struct qemu_fwcfg_file {
8334f804e5SCorvin Köhne 	uint32_t be_size;
8434f804e5SCorvin Köhne 	uint16_t be_selector;
8534f804e5SCorvin Köhne 	uint16_t reserved;
8634f804e5SCorvin Köhne 	uint8_t name[QEMU_FWCFG_MAX_NAME];
8734f804e5SCorvin Köhne };
8834f804e5SCorvin Köhne 
8934f804e5SCorvin Köhne struct qemu_fwcfg_directory {
9034f804e5SCorvin Köhne 	uint32_t be_count;
9134f804e5SCorvin Köhne 	struct qemu_fwcfg_file files[0];
9234f804e5SCorvin Köhne };
9334f804e5SCorvin Köhne 
94151d8131SCorvin Köhne #pragma pack()
95151d8131SCorvin Köhne 
969b99de77SCorvin Köhne struct qemu_fwcfg_softc {
979b99de77SCorvin Köhne 	struct acpi_device *acpi_dev;
98151d8131SCorvin Köhne 
99151d8131SCorvin Köhne 	uint32_t data_offset;
100151d8131SCorvin Köhne 	union qemu_fwcfg_selector selector;
101b11081dcSCorvin Köhne 	struct qemu_fwcfg_item items[QEMU_FWCFG_MAX_ARCHS]
102b11081dcSCorvin Köhne 				    [QEMU_FWCFG_MAX_ENTRIES];
1036f9ebb3dSCorvin Köhne 	struct qemu_fwcfg_directory *directory;
1049b99de77SCorvin Köhne };
1059b99de77SCorvin Köhne 
1069b99de77SCorvin Köhne static struct qemu_fwcfg_softc fwcfg_sc;
1079b99de77SCorvin Köhne 
108ca14781cSCorvin Köhne struct qemu_fwcfg_user_file {
109ca14781cSCorvin Köhne 	STAILQ_ENTRY(qemu_fwcfg_user_file) chain;
110ca14781cSCorvin Köhne 	uint8_t name[QEMU_FWCFG_MAX_NAME];
111ca14781cSCorvin Köhne 	uint32_t size;
112ca14781cSCorvin Köhne 	void *data;
113ca14781cSCorvin Köhne };
114ca14781cSCorvin Köhne static STAILQ_HEAD(qemu_fwcfg_user_file_list,
115ca14781cSCorvin Köhne     qemu_fwcfg_user_file) user_files = STAILQ_HEAD_INITIALIZER(user_files);
116ca14781cSCorvin Köhne 
11731cf78c9SMark Johnston #ifdef __amd64__
1189b99de77SCorvin Köhne 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)1199b99de77SCorvin Köhne qemu_fwcfg_selector_port_handler(struct vmctx *const ctx __unused, const int in,
1209b99de77SCorvin Köhne     const int port __unused, const int bytes, uint32_t *const eax,
1219b99de77SCorvin Köhne     void *const arg __unused)
1229b99de77SCorvin Köhne {
123151d8131SCorvin Köhne 	if (bytes != sizeof(uint16_t)) {
124151d8131SCorvin Köhne 		warnx("%s: invalid size (%d) of IO port access", __func__,
125151d8131SCorvin Köhne 		    bytes);
126151d8131SCorvin Köhne 		return (-1);
127151d8131SCorvin Köhne 	}
128151d8131SCorvin Köhne 
129151d8131SCorvin Köhne 	if (in) {
130151d8131SCorvin Köhne 		*eax = htole16(fwcfg_sc.selector.bits);
131151d8131SCorvin Köhne 		return (0);
132151d8131SCorvin Köhne 	}
133151d8131SCorvin Köhne 
134151d8131SCorvin Köhne 	fwcfg_sc.data_offset = 0;
135151d8131SCorvin Köhne 	fwcfg_sc.selector.bits = le16toh(*eax);
136151d8131SCorvin Köhne 
1379b99de77SCorvin Köhne 	return (0);
1389b99de77SCorvin Köhne }
1399b99de77SCorvin Köhne 
1409b99de77SCorvin Köhne 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)1419b99de77SCorvin Köhne qemu_fwcfg_data_port_handler(struct vmctx *const ctx __unused, const int in,
1429b99de77SCorvin Köhne     const int port __unused, const int bytes, uint32_t *const eax,
1439b99de77SCorvin Köhne     void *const arg __unused)
1449b99de77SCorvin Köhne {
145b11081dcSCorvin Köhne 	if (bytes != sizeof(uint8_t)) {
146b11081dcSCorvin Köhne 		warnx("%s: invalid size (%d) of IO port access", __func__,
147b11081dcSCorvin Köhne 		    bytes);
148b11081dcSCorvin Köhne 		return (-1);
149b11081dcSCorvin Köhne 	}
150b11081dcSCorvin Köhne 
151b11081dcSCorvin Köhne 	if (!in) {
152b11081dcSCorvin Köhne 		warnx("%s: Writes to qemu fwcfg data port aren't allowed",
153b11081dcSCorvin Köhne 		    __func__);
154b11081dcSCorvin Köhne 		return (-1);
155b11081dcSCorvin Köhne 	}
156b11081dcSCorvin Köhne 
157b11081dcSCorvin Köhne 	/* get fwcfg item */
158b11081dcSCorvin Köhne 	struct qemu_fwcfg_item *const item =
159b11081dcSCorvin Köhne 	    &fwcfg_sc.items[fwcfg_sc.selector.architecture]
160b11081dcSCorvin Köhne 			   [fwcfg_sc.selector.index];
161b11081dcSCorvin Köhne 	if (item->data == NULL) {
162b11081dcSCorvin Köhne 		warnx(
163b11081dcSCorvin Köhne 		    "%s: qemu fwcfg item doesn't exist (architecture %s index 0x%x)",
164b11081dcSCorvin Köhne 		    __func__,
165b11081dcSCorvin Köhne 		    fwcfg_sc.selector.architecture ? "specific" : "generic",
166b11081dcSCorvin Köhne 		    fwcfg_sc.selector.index);
167b11081dcSCorvin Köhne 		*eax = 0x00;
168b11081dcSCorvin Köhne 		return (0);
169b11081dcSCorvin Köhne 	} else if (fwcfg_sc.data_offset >= item->size) {
170b11081dcSCorvin Köhne 		warnx(
171b11081dcSCorvin Köhne 		    "%s: qemu fwcfg item read exceeds size (architecture %s index 0x%x size 0x%x offset 0x%x)",
172b11081dcSCorvin Köhne 		    __func__,
173b11081dcSCorvin Köhne 		    fwcfg_sc.selector.architecture ? "specific" : "generic",
174b11081dcSCorvin Köhne 		    fwcfg_sc.selector.index, item->size, fwcfg_sc.data_offset);
175b11081dcSCorvin Köhne 		*eax = 0x00;
176b11081dcSCorvin Köhne 		return (0);
177b11081dcSCorvin Köhne 	}
178b11081dcSCorvin Köhne 
179b11081dcSCorvin Köhne 	/* return item data */
180b11081dcSCorvin Köhne 	*eax = item->data[fwcfg_sc.data_offset];
181b11081dcSCorvin Köhne 	fwcfg_sc.data_offset++;
182b11081dcSCorvin Köhne 
1839b99de77SCorvin Köhne 	return (0);
1849b99de77SCorvin Köhne }
18531cf78c9SMark Johnston #endif
1869b99de77SCorvin Köhne 
1879b99de77SCorvin Köhne static int
qemu_fwcfg_add_item(const uint16_t architecture,const uint16_t index,const uint32_t size,void * const data)1883ef46195SCorvin Köhne qemu_fwcfg_add_item(const uint16_t architecture, const uint16_t index,
1893ef46195SCorvin Köhne     const uint32_t size, void *const data)
1903ef46195SCorvin Köhne {
1913ef46195SCorvin Köhne 	/* truncate architecture and index to their desired size */
1923ef46195SCorvin Köhne 	const uint16_t arch = architecture & QEMU_FWCFG_ARCHITECTURE_MASK;
1933ef46195SCorvin Köhne 	const uint16_t idx = index & QEMU_FWCFG_INDEX_MASK;
1943ef46195SCorvin Köhne 
1953ef46195SCorvin Köhne 	/* get pointer to item specified by selector */
1963ef46195SCorvin Köhne 	struct qemu_fwcfg_item *const fwcfg_item = &fwcfg_sc.items[arch][idx];
1973ef46195SCorvin Köhne 
1983ef46195SCorvin Köhne 	/* check if item is already used */
1993ef46195SCorvin Köhne 	if (fwcfg_item->data != NULL) {
2003ef46195SCorvin Köhne 		warnx("%s: qemu fwcfg item exists (architecture %s index 0x%x)",
2013ef46195SCorvin Köhne 		    __func__, arch ? "specific" : "generic", idx);
202e37edc91SCorvin Köhne 		return (EEXIST);
2033ef46195SCorvin Köhne 	}
2043ef46195SCorvin Köhne 
2053ef46195SCorvin Köhne 	/* save data of the item */
2063ef46195SCorvin Köhne 	fwcfg_item->size = size;
2073ef46195SCorvin Köhne 	fwcfg_item->data = data;
2083ef46195SCorvin Köhne 
2093ef46195SCorvin Köhne 	return (0);
2103ef46195SCorvin Köhne }
2113ef46195SCorvin Köhne 
2123ef46195SCorvin Köhne static int
qemu_fwcfg_add_item_file_dir(void)21334f804e5SCorvin Köhne qemu_fwcfg_add_item_file_dir(void)
21434f804e5SCorvin Köhne {
21534f804e5SCorvin Köhne 	const size_t size = sizeof(struct qemu_fwcfg_directory) +
21634f804e5SCorvin Köhne 	    QEMU_FWCFG_MIN_FILES * sizeof(struct qemu_fwcfg_file);
21734f804e5SCorvin Köhne 	struct qemu_fwcfg_directory *const fwcfg_directory = calloc(1, size);
21834f804e5SCorvin Köhne 	if (fwcfg_directory == NULL) {
21934f804e5SCorvin Köhne 		return (ENOMEM);
22034f804e5SCorvin Köhne 	}
22134f804e5SCorvin Köhne 
22234f804e5SCorvin Köhne 	fwcfg_sc.directory = fwcfg_directory;
22334f804e5SCorvin Köhne 
22434f804e5SCorvin Köhne 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
22534f804e5SCorvin Köhne 	    QEMU_FWCFG_INDEX_FILE_DIR, sizeof(struct qemu_fwcfg_directory),
22634f804e5SCorvin Köhne 	    (uint8_t *)fwcfg_sc.directory));
22734f804e5SCorvin Köhne }
22834f804e5SCorvin Köhne 
22934f804e5SCorvin Köhne static int
qemu_fwcfg_add_item_id(void)23034f804e5SCorvin Köhne qemu_fwcfg_add_item_id(void)
23134f804e5SCorvin Köhne {
23234f804e5SCorvin Köhne 	struct qemu_fwcfg_id *const fwcfg_id = calloc(1,
23334f804e5SCorvin Köhne 	    sizeof(struct qemu_fwcfg_id));
23434f804e5SCorvin Köhne 	if (fwcfg_id == NULL) {
23534f804e5SCorvin Köhne 		return (ENOMEM);
23634f804e5SCorvin Köhne 	}
23734f804e5SCorvin Köhne 
23834f804e5SCorvin Köhne 	fwcfg_id->interface = 1;
23934f804e5SCorvin Köhne 	fwcfg_id->DMA = 0;
24034f804e5SCorvin Köhne 
24134f804e5SCorvin Köhne 	uint32_t *const le_fwcfg_id_ptr = (uint32_t *)fwcfg_id;
24234f804e5SCorvin Köhne 	*le_fwcfg_id_ptr = htole32(*le_fwcfg_id_ptr);
24334f804e5SCorvin Köhne 
24434f804e5SCorvin Köhne 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
24534f804e5SCorvin Köhne 	    QEMU_FWCFG_INDEX_ID, sizeof(struct qemu_fwcfg_id),
24634f804e5SCorvin Köhne 	    (uint8_t *)fwcfg_id));
24734f804e5SCorvin Köhne }
24834f804e5SCorvin Köhne 
24934f804e5SCorvin Köhne static int
qemu_fwcfg_add_item_max_cpus(void)250305edaa4SCorvin Köhne qemu_fwcfg_add_item_max_cpus(void)
251305edaa4SCorvin Köhne {
252305edaa4SCorvin Köhne 	uint16_t *fwcfg_max_cpus = calloc(1, sizeof(uint16_t));
253305edaa4SCorvin Köhne 	if (fwcfg_max_cpus == NULL) {
254305edaa4SCorvin Köhne 		return (ENOMEM);
255305edaa4SCorvin Köhne 	}
256305edaa4SCorvin Köhne 
257305edaa4SCorvin Köhne 	/*
258305edaa4SCorvin Köhne 	 * We don't support cpu hotplug yet. For that reason, use guest_ncpus instead
259305edaa4SCorvin Köhne 	 * of maxcpus.
260305edaa4SCorvin Köhne 	 */
261305edaa4SCorvin Köhne 	*fwcfg_max_cpus = htole16(guest_ncpus);
262305edaa4SCorvin Köhne 
263305edaa4SCorvin Köhne 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
264305edaa4SCorvin Köhne 	    QEMU_FWCFG_INDEX_MAX_CPUS, sizeof(uint16_t), fwcfg_max_cpus));
265305edaa4SCorvin Köhne }
266305edaa4SCorvin Köhne 
267305edaa4SCorvin Köhne static int
qemu_fwcfg_add_item_nb_cpus(void)268e46be58cSCorvin Köhne qemu_fwcfg_add_item_nb_cpus(void)
269e46be58cSCorvin Köhne {
270e46be58cSCorvin Köhne 	uint16_t *fwcfg_max_cpus = calloc(1, sizeof(uint16_t));
271e46be58cSCorvin Köhne 	if (fwcfg_max_cpus == NULL) {
272e46be58cSCorvin Köhne 		return (ENOMEM);
273e46be58cSCorvin Köhne 	}
274e46be58cSCorvin Köhne 
275e46be58cSCorvin Köhne 	*fwcfg_max_cpus = htole16(guest_ncpus);
276e46be58cSCorvin Köhne 
277e46be58cSCorvin Köhne 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
278e46be58cSCorvin Köhne 	    QEMU_FWCFG_INDEX_NB_CPUS, sizeof(uint16_t), fwcfg_max_cpus));
279e46be58cSCorvin Köhne }
280e46be58cSCorvin Köhne 
281e46be58cSCorvin Köhne static int
qemu_fwcfg_add_item_signature(void)28234f804e5SCorvin Köhne qemu_fwcfg_add_item_signature(void)
28334f804e5SCorvin Köhne {
28434f804e5SCorvin Köhne 	struct qemu_fwcfg_signature *const fwcfg_signature = calloc(1,
28534f804e5SCorvin Köhne 	    sizeof(struct qemu_fwcfg_signature));
28634f804e5SCorvin Köhne 	if (fwcfg_signature == NULL) {
28734f804e5SCorvin Köhne 		return (ENOMEM);
28834f804e5SCorvin Köhne 	}
28934f804e5SCorvin Köhne 
29034f804e5SCorvin Köhne 	fwcfg_signature->signature[0] = 'Q';
29134f804e5SCorvin Köhne 	fwcfg_signature->signature[1] = 'E';
29234f804e5SCorvin Köhne 	fwcfg_signature->signature[2] = 'M';
29334f804e5SCorvin Köhne 	fwcfg_signature->signature[3] = 'U';
29434f804e5SCorvin Köhne 
29534f804e5SCorvin Köhne 	return (qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
29634f804e5SCorvin Köhne 	    QEMU_FWCFG_INDEX_SIGNATURE, sizeof(struct qemu_fwcfg_signature),
29734f804e5SCorvin Köhne 	    (uint8_t *)fwcfg_signature));
29834f804e5SCorvin Köhne }
29934f804e5SCorvin Köhne 
30031cf78c9SMark Johnston #ifdef __amd64__
30134f804e5SCorvin Köhne static int
qemu_fwcfg_register_port(const char * const name,const int port,const int size,const int flags,const inout_func_t handler)3029b99de77SCorvin Köhne qemu_fwcfg_register_port(const char *const name, const int port, const int size,
3039b99de77SCorvin Köhne     const int flags, const inout_func_t handler)
3049b99de77SCorvin Köhne {
3059b99de77SCorvin Köhne 	struct inout_port iop;
3069b99de77SCorvin Köhne 
3079b99de77SCorvin Köhne 	bzero(&iop, sizeof(iop));
3089b99de77SCorvin Köhne 	iop.name = name;
3099b99de77SCorvin Köhne 	iop.port = port;
3109b99de77SCorvin Köhne 	iop.size = size;
3119b99de77SCorvin Köhne 	iop.flags = flags;
3129b99de77SCorvin Köhne 	iop.handler = handler;
3139b99de77SCorvin Köhne 
3149b99de77SCorvin Köhne 	return (register_inout(&iop));
3159b99de77SCorvin Köhne }
31631cf78c9SMark Johnston #endif
3179b99de77SCorvin Köhne 
3189b99de77SCorvin Köhne int
qemu_fwcfg_add_file(const char * name,const uint32_t size,void * const data)31961482760SJohn Baldwin qemu_fwcfg_add_file(const char *name, const uint32_t size, void *const data)
3206f9ebb3dSCorvin Köhne {
32161482760SJohn Baldwin 	if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
32261482760SJohn Baldwin 		return (EINVAL);
32361482760SJohn Baldwin 
3246f9ebb3dSCorvin Köhne 	/*
3256f9ebb3dSCorvin Köhne 	 * QEMU specifies count as big endian.
3266f9ebb3dSCorvin Köhne 	 * Convert it to host endian to work with it.
3276f9ebb3dSCorvin Köhne 	 */
3286f9ebb3dSCorvin Köhne 	const uint32_t count = be32toh(fwcfg_sc.directory->be_count) + 1;
3296f9ebb3dSCorvin Köhne 
3306f9ebb3dSCorvin Köhne 	/* add file to items list */
3316f9ebb3dSCorvin Köhne 	const uint32_t index = QEMU_FWCFG_FIRST_FILE_INDEX + count - 1;
3326f9ebb3dSCorvin Köhne 	const int error = qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
3336f9ebb3dSCorvin Köhne 	    index, size, data);
3346f9ebb3dSCorvin Köhne 	if (error != 0) {
3356f9ebb3dSCorvin Köhne 		return (error);
3366f9ebb3dSCorvin Köhne 	}
3376f9ebb3dSCorvin Köhne 
3386f9ebb3dSCorvin Köhne 	/*
3396f9ebb3dSCorvin Köhne 	 * files should be sorted alphabetical, get index for new file
3406f9ebb3dSCorvin Köhne 	 */
3416f9ebb3dSCorvin Köhne 	uint32_t file_index;
3426f9ebb3dSCorvin Köhne 	for (file_index = 0; file_index < count - 1; ++file_index) {
3436f9ebb3dSCorvin Köhne 		if (strcmp(name, fwcfg_sc.directory->files[file_index].name) <
3446f9ebb3dSCorvin Köhne 		    0)
3456f9ebb3dSCorvin Köhne 			break;
3466f9ebb3dSCorvin Köhne 	}
3476f9ebb3dSCorvin Köhne 
3486f9ebb3dSCorvin Köhne 	if (count > QEMU_FWCFG_MIN_FILES) {
3496f9ebb3dSCorvin Köhne 		/* alloc new file directory */
3506f9ebb3dSCorvin Köhne 		const uint64_t new_size = sizeof(struct qemu_fwcfg_directory) +
3516f9ebb3dSCorvin Köhne 		    count * sizeof(struct qemu_fwcfg_file);
3526f9ebb3dSCorvin Köhne 		struct qemu_fwcfg_directory *const new_directory = calloc(1,
3536f9ebb3dSCorvin Köhne 		    new_size);
3546f9ebb3dSCorvin Köhne 		if (new_directory == NULL) {
3556f9ebb3dSCorvin Köhne 			warnx(
3566f9ebb3dSCorvin Köhne 			    "%s: Unable to allocate a new qemu fwcfg files directory (count %d)",
3576f9ebb3dSCorvin Köhne 			    __func__, count);
3587bf44831SJohn Baldwin 			return (ENOMEM);
3596f9ebb3dSCorvin Köhne 		}
3606f9ebb3dSCorvin Köhne 
3616f9ebb3dSCorvin Köhne 		/* copy files below file_index to new directory */
3626f9ebb3dSCorvin Köhne 		memcpy(new_directory->files, fwcfg_sc.directory->files,
3636f9ebb3dSCorvin Köhne 		    file_index * sizeof(struct qemu_fwcfg_file));
3646f9ebb3dSCorvin Köhne 
3656f9ebb3dSCorvin Köhne 		/* copy files above file_index to directory */
3666f9ebb3dSCorvin Köhne 		memcpy(&new_directory->files[file_index + 1],
3676f9ebb3dSCorvin Köhne 		    &fwcfg_sc.directory->files[file_index],
3684a381007SCorvin Köhne 		    (count - file_index - 1) * sizeof(struct qemu_fwcfg_file));
3696f9ebb3dSCorvin Köhne 
3706f9ebb3dSCorvin Köhne 		/* free old directory */
3716f9ebb3dSCorvin Köhne 		free(fwcfg_sc.directory);
3726f9ebb3dSCorvin Köhne 
3736f9ebb3dSCorvin Köhne 		/* set directory pointer to new directory */
3746f9ebb3dSCorvin Köhne 		fwcfg_sc.directory = new_directory;
3756f9ebb3dSCorvin Köhne 
3766f9ebb3dSCorvin Köhne 		/* adjust directory pointer */
3776f9ebb3dSCorvin Köhne 		fwcfg_sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].data =
3786f9ebb3dSCorvin Köhne 		    (uint8_t *)fwcfg_sc.directory;
3796f9ebb3dSCorvin Köhne 	} else {
3806f9ebb3dSCorvin Köhne 		/* shift files behind file_index */
3816f9ebb3dSCorvin Köhne 		for (uint32_t i = QEMU_FWCFG_MIN_FILES - 1; i > file_index;
3826f9ebb3dSCorvin Köhne 		     --i) {
3836f9ebb3dSCorvin Köhne 			memcpy(&fwcfg_sc.directory->files[i],
3846f9ebb3dSCorvin Köhne 			    &fwcfg_sc.directory->files[i - 1],
3856f9ebb3dSCorvin Köhne 			    sizeof(struct qemu_fwcfg_file));
3866f9ebb3dSCorvin Köhne 		}
3876f9ebb3dSCorvin Köhne 	}
3886f9ebb3dSCorvin Köhne 
3896f9ebb3dSCorvin Köhne 	/*
3906f9ebb3dSCorvin Köhne 	 * QEMU specifies count, size and index as big endian.
3916f9ebb3dSCorvin Köhne 	 * Save these values in big endian to simplify guest reads of these
3926f9ebb3dSCorvin Köhne 	 * values.
3936f9ebb3dSCorvin Köhne 	 */
3946f9ebb3dSCorvin Köhne 	fwcfg_sc.directory->be_count = htobe32(count);
3956f9ebb3dSCorvin Köhne 	fwcfg_sc.directory->files[file_index].be_size = htobe32(size);
3966f9ebb3dSCorvin Köhne 	fwcfg_sc.directory->files[file_index].be_selector = htobe16(index);
3976f9ebb3dSCorvin Köhne 	strcpy(fwcfg_sc.directory->files[file_index].name, name);
3986f9ebb3dSCorvin Köhne 
3996f9ebb3dSCorvin Köhne 	/* set new size for the fwcfg_file_directory */
4006f9ebb3dSCorvin Köhne 	fwcfg_sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].size =
4016f9ebb3dSCorvin Köhne 	    sizeof(struct qemu_fwcfg_directory) +
4026f9ebb3dSCorvin Köhne 	    count * sizeof(struct qemu_fwcfg_file);
4036f9ebb3dSCorvin Köhne 
4046f9ebb3dSCorvin Köhne 	return (0);
4056f9ebb3dSCorvin Köhne }
4066f9ebb3dSCorvin Köhne 
407ca14781cSCorvin Köhne static int
qemu_fwcfg_add_user_files(void)408ca14781cSCorvin Köhne qemu_fwcfg_add_user_files(void)
409ca14781cSCorvin Köhne {
410ca14781cSCorvin Köhne 	const struct qemu_fwcfg_user_file *fwcfg_file;
411ca14781cSCorvin Köhne 	int error;
412ca14781cSCorvin Köhne 
413ca14781cSCorvin Köhne 	STAILQ_FOREACH(fwcfg_file, &user_files, chain) {
414ca14781cSCorvin Köhne 		error = qemu_fwcfg_add_file(fwcfg_file->name, fwcfg_file->size,
415ca14781cSCorvin Köhne 		    fwcfg_file->data);
416ca14781cSCorvin Köhne 		if (error)
417ca14781cSCorvin Köhne 			return (error);
418ca14781cSCorvin Köhne 	}
419ca14781cSCorvin Köhne 
420ca14781cSCorvin Köhne 	return (0);
421ca14781cSCorvin Köhne }
422ca14781cSCorvin Köhne 
423acd0088cSCorvin Köhne static const struct acpi_device_emul qemu_fwcfg_acpi_device_emul = {
424acd0088cSCorvin Köhne 	.name = QEMU_FWCFG_ACPI_DEVICE_NAME,
425acd0088cSCorvin Köhne 	.hid = QEMU_FWCFG_ACPI_HARDWARE_ID,
426acd0088cSCorvin Köhne };
427acd0088cSCorvin Köhne 
4286f9ebb3dSCorvin Köhne int
qemu_fwcfg_init(struct vmctx * const ctx)4299b99de77SCorvin Köhne qemu_fwcfg_init(struct vmctx *const ctx)
4309b99de77SCorvin Köhne {
4319b99de77SCorvin Köhne 	int error;
43255c13f6eSMark Johnston 	bool fwcfg_enabled;
43355c13f6eSMark Johnston 
43455c13f6eSMark Johnston 	/*
43555c13f6eSMark Johnston 	 * The fwcfg implementation currently only provides an I/O port
43655c13f6eSMark Johnston 	 * interface and thus is amd64-specific for now.  An MMIO interface is
43755c13f6eSMark Johnston 	 * required for other platforms.
43855c13f6eSMark Johnston 	 */
43955c13f6eSMark Johnston #ifdef __amd64__
44055c13f6eSMark Johnston 	fwcfg_enabled = strcmp(lpc_fwcfg(), "qemu") == 0;
44155c13f6eSMark Johnston #else
44255c13f6eSMark Johnston 	fwcfg_enabled = false;
44355c13f6eSMark Johnston #endif
4449b99de77SCorvin Köhne 
445d85147f3SCorvin Köhne 	/*
446d85147f3SCorvin Köhne 	 * Bhyve supports fwctl (bhyve) and fwcfg (qemu) as firmware interfaces.
447d85147f3SCorvin Köhne 	 * Both are using the same ports. So, it's not possible to provide both
448d85147f3SCorvin Köhne 	 * interfaces at the same time to the guest. Therefore, only create acpi
449d85147f3SCorvin Köhne 	 * tables and register io ports for fwcfg, if it's used.
450d85147f3SCorvin Köhne 	 */
45155c13f6eSMark Johnston 	if (fwcfg_enabled) {
452158adcedSCorvin Köhne 		error = acpi_device_create(&fwcfg_sc.acpi_dev, &fwcfg_sc, ctx,
453acd0088cSCorvin Köhne 		    &qemu_fwcfg_acpi_device_emul);
4549b99de77SCorvin Köhne 		if (error) {
4559b99de77SCorvin Köhne 			warnx("%s: failed to create ACPI device for QEMU FwCfg",
4569b99de77SCorvin Köhne 			    __func__);
4579b99de77SCorvin Köhne 			goto done;
4589b99de77SCorvin Köhne 		}
4599b99de77SCorvin Köhne 
4609b99de77SCorvin Köhne 		error = acpi_device_add_res_fixed_ioport(fwcfg_sc.acpi_dev,
4619b99de77SCorvin Köhne 		    QEMU_FWCFG_SELECTOR_PORT_NUMBER, 2);
4629b99de77SCorvin Köhne 		if (error) {
4639b99de77SCorvin Köhne 			warnx("%s: failed to add fixed IO port for QEMU FwCfg",
4649b99de77SCorvin Köhne 			    __func__);
4659b99de77SCorvin Köhne 			goto done;
4669b99de77SCorvin Köhne 		}
4679b99de77SCorvin Köhne 
46831cf78c9SMark Johnston #ifdef __amd64__
4699b99de77SCorvin Köhne 		if ((error = qemu_fwcfg_register_port("qemu_fwcfg_selector",
470d85147f3SCorvin Köhne 		    QEMU_FWCFG_SELECTOR_PORT_NUMBER,
471d85147f3SCorvin Köhne 		    QEMU_FWCFG_SELECTOR_PORT_SIZE,
4729b99de77SCorvin Köhne 		    QEMU_FWCFG_SELECTOR_PORT_FLAGS,
4739b99de77SCorvin Köhne 		    qemu_fwcfg_selector_port_handler)) != 0) {
474d85147f3SCorvin Köhne 			warnx(
475d85147f3SCorvin Köhne 			    "%s: Unable to register qemu fwcfg selector port 0x%x",
4769b99de77SCorvin Köhne 			    __func__, QEMU_FWCFG_SELECTOR_PORT_NUMBER);
4779b99de77SCorvin Köhne 			goto done;
4789b99de77SCorvin Köhne 		}
4799b99de77SCorvin Köhne 		if ((error = qemu_fwcfg_register_port("qemu_fwcfg_data",
4809b99de77SCorvin Köhne 		    QEMU_FWCFG_DATA_PORT_NUMBER, QEMU_FWCFG_DATA_PORT_SIZE,
481d85147f3SCorvin Köhne 		    QEMU_FWCFG_DATA_PORT_FLAGS,
482d85147f3SCorvin Köhne 		    qemu_fwcfg_data_port_handler)) != 0) {
483d85147f3SCorvin Köhne 			warnx(
484d85147f3SCorvin Köhne 			    "%s: Unable to register qemu fwcfg data port 0x%x",
4859b99de77SCorvin Köhne 			    __func__, QEMU_FWCFG_DATA_PORT_NUMBER);
4869b99de77SCorvin Köhne 			goto done;
4879b99de77SCorvin Köhne 		}
48831cf78c9SMark Johnston #endif
489d85147f3SCorvin Köhne 	}
4909b99de77SCorvin Köhne 
49134f804e5SCorvin Köhne 	/* add common fwcfg items */
49234f804e5SCorvin Köhne 	if ((error = qemu_fwcfg_add_item_signature()) != 0) {
49334f804e5SCorvin Köhne 		warnx("%s: Unable to add signature item", __func__);
49434f804e5SCorvin Köhne 		goto done;
49534f804e5SCorvin Köhne 	}
49634f804e5SCorvin Köhne 	if ((error = qemu_fwcfg_add_item_id()) != 0) {
49734f804e5SCorvin Köhne 		warnx("%s: Unable to add id item", __func__);
49834f804e5SCorvin Köhne 		goto done;
49934f804e5SCorvin Köhne 	}
500e46be58cSCorvin Köhne 	if ((error = qemu_fwcfg_add_item_nb_cpus()) != 0) {
501e46be58cSCorvin Köhne 		warnx("%s: Unable to add nb_cpus item", __func__);
502e46be58cSCorvin Köhne 		goto done;
503e46be58cSCorvin Köhne 	}
504305edaa4SCorvin Köhne 	if ((error = qemu_fwcfg_add_item_max_cpus()) != 0) {
505305edaa4SCorvin Köhne 		warnx("%s: Unable to add max_cpus item", __func__);
506305edaa4SCorvin Köhne 		goto done;
507305edaa4SCorvin Köhne 	}
50834f804e5SCorvin Köhne 	if ((error = qemu_fwcfg_add_item_file_dir()) != 0) {
50934f804e5SCorvin Köhne 		warnx("%s: Unable to add file_dir item", __func__);
510ca14781cSCorvin Köhne 	}
511ca14781cSCorvin Köhne 
512ca14781cSCorvin Köhne 	/* add user defined fwcfg files */
513ca14781cSCorvin Köhne 	if ((error = qemu_fwcfg_add_user_files()) != 0) {
514ca14781cSCorvin Köhne 		warnx("%s: Unable to add user files", __func__);
51534f804e5SCorvin Köhne 		goto done;
51634f804e5SCorvin Köhne 	}
51734f804e5SCorvin Köhne 
5189b99de77SCorvin Köhne done:
5199b99de77SCorvin Köhne 	if (error) {
5209b99de77SCorvin Köhne 		acpi_device_destroy(fwcfg_sc.acpi_dev);
5219b99de77SCorvin Köhne 	}
5229b99de77SCorvin Köhne 
5239b99de77SCorvin Köhne 	return (error);
5249b99de77SCorvin Köhne }
525ca14781cSCorvin Köhne 
526ca14781cSCorvin Köhne static void
qemu_fwcfg_usage(const char * opt)527ca14781cSCorvin Köhne qemu_fwcfg_usage(const char *opt)
528ca14781cSCorvin Köhne {
529ca14781cSCorvin Köhne 	warnx("Invalid fw_cfg option \"%s\"", opt);
530ca14781cSCorvin Köhne 	warnx("-f [name=]<name>,(string|file)=<value>");
531ca14781cSCorvin Köhne }
532ca14781cSCorvin Köhne 
533ca14781cSCorvin Köhne /*
534ca14781cSCorvin Köhne  * Parses the cmdline argument for user defined fw_cfg items. The cmdline
535ca14781cSCorvin Köhne  * argument has the format:
536ca14781cSCorvin Köhne  * "-f [name=]<name>,(string|file)=<value>"
537ca14781cSCorvin Köhne  *
538ca14781cSCorvin Köhne  * E.g.: "-f opt/com.page/example,string=Hello"
539ca14781cSCorvin Köhne  */
540ca14781cSCorvin Köhne int
qemu_fwcfg_parse_cmdline_arg(const char * opt)541ca14781cSCorvin Köhne qemu_fwcfg_parse_cmdline_arg(const char *opt)
542ca14781cSCorvin Köhne {
543ca14781cSCorvin Köhne 	struct qemu_fwcfg_user_file *fwcfg_file;
544ca14781cSCorvin Köhne 	struct stat sb;
545ca14781cSCorvin Köhne 	const char *opt_ptr, *opt_end;
54626d9f973SCorvin Köhne 	ssize_t bytes_read;
547ca14781cSCorvin Köhne 	int fd;
548ca14781cSCorvin Köhne 
549ca14781cSCorvin Köhne 	fwcfg_file = malloc(sizeof(*fwcfg_file));
550ca14781cSCorvin Köhne 	if (fwcfg_file == NULL) {
551ca14781cSCorvin Köhne 		warnx("Unable to allocate fw_cfg_user_file");
552ca14781cSCorvin Köhne 		return (ENOMEM);
553ca14781cSCorvin Köhne 	}
554ca14781cSCorvin Köhne 
555ca14781cSCorvin Köhne 	/* get pointer to <name> */
556ca14781cSCorvin Köhne 	opt_ptr = opt;
557ca14781cSCorvin Köhne 	/* If [name=] is specified, skip it */
558ca14781cSCorvin Köhne 	if (strncmp(opt_ptr, "name=", sizeof("name=") - 1) == 0) {
559ca14781cSCorvin Köhne 		opt_ptr += sizeof("name=") - 1;
560ca14781cSCorvin Köhne 	}
561ca14781cSCorvin Köhne 
562ca14781cSCorvin Köhne 	/* get the end of <name> */
563ca14781cSCorvin Köhne 	opt_end = strchr(opt_ptr, ',');
564ca14781cSCorvin Köhne 	if (opt_end == NULL) {
565ca14781cSCorvin Köhne 		qemu_fwcfg_usage(opt);
566ca14781cSCorvin Köhne 		return (EINVAL);
567ca14781cSCorvin Köhne 	}
568ca14781cSCorvin Köhne 
569ca14781cSCorvin Köhne 	/* check if <name> is too long */
570ca14781cSCorvin Köhne 	if (opt_end - opt_ptr >= QEMU_FWCFG_MAX_NAME) {
571ca14781cSCorvin Köhne 		warnx("fw_cfg name too long: \"%s\"", opt);
572ca14781cSCorvin Köhne 		return (EINVAL);
573ca14781cSCorvin Köhne 	}
574ca14781cSCorvin Köhne 
575ca14781cSCorvin Köhne 	/* save <name> */
576ca14781cSCorvin Köhne 	strncpy(fwcfg_file->name, opt_ptr, opt_end - opt_ptr);
577ca14781cSCorvin Köhne 	fwcfg_file->name[opt_end - opt_ptr] = '\0';
578ca14781cSCorvin Köhne 
579ca14781cSCorvin Köhne 	/* set opt_ptr and opt_end to <value> */
580ca14781cSCorvin Köhne 	opt_ptr = opt_end + 1;
581ca14781cSCorvin Köhne 	opt_end = opt_ptr + strlen(opt_ptr);
582ca14781cSCorvin Köhne 
583ca14781cSCorvin Köhne 	if (strncmp(opt_ptr, "string=", sizeof("string=") - 1) == 0) {
584ca14781cSCorvin Köhne 		opt_ptr += sizeof("string=") - 1;
585ca14781cSCorvin Köhne 		fwcfg_file->data = strdup(opt_ptr);
586ca14781cSCorvin Köhne 		if (fwcfg_file->data == NULL) {
587ca14781cSCorvin Köhne 			warnx("Can't duplicate fw_cfg_user_file string \"%s\"",
588ca14781cSCorvin Köhne 			    opt_ptr);
589ca14781cSCorvin Köhne 			return (ENOMEM);
590ca14781cSCorvin Köhne 		}
591ca14781cSCorvin Köhne 		fwcfg_file->size = strlen(opt_ptr) + 1;
592ca14781cSCorvin Köhne 	} else if (strncmp(opt_ptr, "file=", sizeof("file=") - 1) == 0) {
593ca14781cSCorvin Köhne 		opt_ptr += sizeof("file=") - 1;
594ca14781cSCorvin Köhne 
595ca14781cSCorvin Köhne 		fd = open(opt_ptr, O_RDONLY);
596ca14781cSCorvin Köhne 		if (fd < 0) {
597ca14781cSCorvin Köhne 			warn("Can't open fw_cfg_user_file file \"%s\"",
598ca14781cSCorvin Köhne 			    opt_ptr);
599ca14781cSCorvin Köhne 			return (EINVAL);
600ca14781cSCorvin Köhne 		}
601ca14781cSCorvin Köhne 
602ca14781cSCorvin Köhne 		if (fstat(fd, &sb) < 0) {
603ca14781cSCorvin Köhne 			warn("Unable to get size of file \"%s\"", opt_ptr);
604ca14781cSCorvin Köhne 			close(fd);
605ca14781cSCorvin Köhne 			return (-1);
606ca14781cSCorvin Köhne 		}
607ca14781cSCorvin Köhne 
608ca14781cSCorvin Köhne 		fwcfg_file->data = malloc(sb.st_size);
609ca14781cSCorvin Köhne 		if (fwcfg_file->data == NULL) {
610ca14781cSCorvin Köhne 			warnx(
611ca14781cSCorvin Köhne 			    "Can't allocate fw_cfg_user_file file \"%s\" (size: 0x%16lx)",
612ca14781cSCorvin Köhne 			    opt_ptr, sb.st_size);
613ca14781cSCorvin Köhne 			close(fd);
614ca14781cSCorvin Köhne 			return (ENOMEM);
615ca14781cSCorvin Köhne 		}
61626d9f973SCorvin Köhne 		bytes_read = read(fd, fwcfg_file->data, sb.st_size);
61726d9f973SCorvin Köhne 		if (bytes_read < 0 || bytes_read != sb.st_size) {
618ca14781cSCorvin Köhne 			warn("Unable to read file \"%s\"", opt_ptr);
619ca14781cSCorvin Köhne 			free(fwcfg_file->data);
620ca14781cSCorvin Köhne 			close(fd);
621ca14781cSCorvin Köhne 			return (-1);
622ca14781cSCorvin Köhne 		}
62326d9f973SCorvin Köhne 		fwcfg_file->size = bytes_read;
624ca14781cSCorvin Köhne 
625ca14781cSCorvin Köhne 		close(fd);
626ca14781cSCorvin Köhne 	} else {
627ca14781cSCorvin Köhne 		qemu_fwcfg_usage(opt);
628ca14781cSCorvin Köhne 		return (EINVAL);
629ca14781cSCorvin Köhne 	}
630ca14781cSCorvin Köhne 
631ca14781cSCorvin Köhne 	STAILQ_INSERT_TAIL(&user_files, fwcfg_file, chain);
632ca14781cSCorvin Köhne 
633ca14781cSCorvin Köhne 	return (0);
634ca14781cSCorvin Köhne }
635