xref: /freebsd/usr.sbin/bhyve/qemu_fwcfg.c (revision dad64f0e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
5  * Author: Corvin Köhne <c.koehne@beckhoff.com>
6  */
7 
8 #include <sys/param.h>
9 
10 #include <machine/vmm.h>
11 
12 #include <err.h>
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include "acpi_device.h"
18 #include "inout.h"
19 #include "qemu_fwcfg.h"
20 
21 #define QEMU_FWCFG_ACPI_DEVICE_NAME "FWCF"
22 #define QEMU_FWCFG_ACPI_HARDWARE_ID "QEMU0002"
23 
24 #define QEMU_FWCFG_SELECTOR_PORT_NUMBER 0x510
25 #define QEMU_FWCFG_SELECTOR_PORT_SIZE 1
26 #define QEMU_FWCFG_SELECTOR_PORT_FLAGS IOPORT_F_INOUT
27 #define QEMU_FWCFG_DATA_PORT_NUMBER 0x511
28 #define QEMU_FWCFG_DATA_PORT_SIZE 1
29 #define QEMU_FWCFG_DATA_PORT_FLAGS \
30 	IOPORT_F_INOUT /* QEMU v2.4+ ignores writes */
31 
32 #define QEMU_FWCFG_ARCHITECTURE_MASK 0x0001
33 #define QEMU_FWCFG_INDEX_MASK 0x3FFF
34 
35 #define QEMU_FWCFG_SELECT_READ 0
36 #define QEMU_FWCFG_SELECT_WRITE 1
37 
38 #define QEMU_FWCFG_ARCHITECTURE_GENERIC 0
39 #define QEMU_FWCFG_ARCHITECTURE_SPECIFIC 1
40 
41 #pragma pack(1)
42 
43 union qemu_fwcfg_selector {
44 	struct {
45 		uint16_t index : 14;
46 		uint16_t writeable : 1;
47 		uint16_t architecture : 1;
48 	};
49 	uint16_t bits;
50 };
51 
52 #pragma pack()
53 
54 struct qemu_fwcfg_softc {
55 	struct acpi_device *acpi_dev;
56 
57 	uint32_t data_offset;
58 	union qemu_fwcfg_selector selector;
59 	struct qemu_fwcfg_item items[QEMU_FWCFG_MAX_ARCHS]
60 				    [QEMU_FWCFG_MAX_ENTRIES];
61 };
62 
63 static struct qemu_fwcfg_softc fwcfg_sc;
64 
65 static int
66 qemu_fwcfg_selector_port_handler(struct vmctx *const ctx __unused, const int in,
67     const int port __unused, const int bytes, uint32_t *const eax,
68     void *const arg __unused)
69 {
70 	if (bytes != sizeof(uint16_t)) {
71 		warnx("%s: invalid size (%d) of IO port access", __func__,
72 		    bytes);
73 		return (-1);
74 	}
75 
76 	if (in) {
77 		*eax = htole16(fwcfg_sc.selector.bits);
78 		return (0);
79 	}
80 
81 	fwcfg_sc.data_offset = 0;
82 	fwcfg_sc.selector.bits = le16toh(*eax);
83 
84 	return (0);
85 }
86 
87 static int
88 qemu_fwcfg_data_port_handler(struct vmctx *const ctx __unused, const int in,
89     const int port __unused, const int bytes, uint32_t *const eax,
90     void *const arg __unused)
91 {
92 	if (bytes != sizeof(uint8_t)) {
93 		warnx("%s: invalid size (%d) of IO port access", __func__,
94 		    bytes);
95 		return (-1);
96 	}
97 
98 	if (!in) {
99 		warnx("%s: Writes to qemu fwcfg data port aren't allowed",
100 		    __func__);
101 		return (-1);
102 	}
103 
104 	/* get fwcfg item */
105 	struct qemu_fwcfg_item *const item =
106 	    &fwcfg_sc.items[fwcfg_sc.selector.architecture]
107 			   [fwcfg_sc.selector.index];
108 	if (item->data == NULL) {
109 		warnx(
110 		    "%s: qemu fwcfg item doesn't exist (architecture %s index 0x%x)",
111 		    __func__,
112 		    fwcfg_sc.selector.architecture ? "specific" : "generic",
113 		    fwcfg_sc.selector.index);
114 		*eax = 0x00;
115 		return (0);
116 	} else if (fwcfg_sc.data_offset >= item->size) {
117 		warnx(
118 		    "%s: qemu fwcfg item read exceeds size (architecture %s index 0x%x size 0x%x offset 0x%x)",
119 		    __func__,
120 		    fwcfg_sc.selector.architecture ? "specific" : "generic",
121 		    fwcfg_sc.selector.index, item->size, fwcfg_sc.data_offset);
122 		*eax = 0x00;
123 		return (0);
124 	}
125 
126 	/* return item data */
127 	*eax = item->data[fwcfg_sc.data_offset];
128 	fwcfg_sc.data_offset++;
129 
130 	return (0);
131 }
132 
133 static int
134 qemu_fwcfg_register_port(const char *const name, const int port, const int size,
135     const int flags, const inout_func_t handler)
136 {
137 	struct inout_port iop;
138 
139 	bzero(&iop, sizeof(iop));
140 	iop.name = name;
141 	iop.port = port;
142 	iop.size = size;
143 	iop.flags = flags;
144 	iop.handler = handler;
145 
146 	return (register_inout(&iop));
147 }
148 
149 int
150 qemu_fwcfg_init(struct vmctx *const ctx)
151 {
152 	int error;
153 
154 	error = acpi_device_create(&fwcfg_sc.acpi_dev, ctx,
155 	    QEMU_FWCFG_ACPI_DEVICE_NAME, QEMU_FWCFG_ACPI_HARDWARE_ID);
156 	if (error) {
157 		warnx("%s: failed to create ACPI device for QEMU FwCfg",
158 		    __func__);
159 		goto done;
160 	}
161 
162 	error = acpi_device_add_res_fixed_ioport(fwcfg_sc.acpi_dev,
163 	    QEMU_FWCFG_SELECTOR_PORT_NUMBER, 2);
164 	if (error) {
165 		warnx("%s: failed to add fixed IO port for QEMU FwCfg",
166 		    __func__);
167 		goto done;
168 	}
169 
170 	/* add handlers for fwcfg ports */
171 	if ((error = qemu_fwcfg_register_port("qemu_fwcfg_selector",
172 	    QEMU_FWCFG_SELECTOR_PORT_NUMBER, QEMU_FWCFG_SELECTOR_PORT_SIZE,
173 	    QEMU_FWCFG_SELECTOR_PORT_FLAGS,
174 	    qemu_fwcfg_selector_port_handler)) != 0) {
175 		warnx("%s: Unable to register qemu fwcfg selector port 0x%x",
176 		    __func__, QEMU_FWCFG_SELECTOR_PORT_NUMBER);
177 		goto done;
178 	}
179 	if ((error = qemu_fwcfg_register_port("qemu_fwcfg_data",
180 	    QEMU_FWCFG_DATA_PORT_NUMBER, QEMU_FWCFG_DATA_PORT_SIZE,
181 	    QEMU_FWCFG_DATA_PORT_FLAGS, qemu_fwcfg_data_port_handler)) != 0) {
182 		warnx("%s: Unable to register qemu fwcfg data port 0x%x",
183 		    __func__, QEMU_FWCFG_DATA_PORT_NUMBER);
184 		goto done;
185 	}
186 
187 done:
188 	if (error) {
189 		acpi_device_destroy(fwcfg_sc.acpi_dev);
190 	}
191 
192 	return (error);
193 }
194