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