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