]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/qemu_fwcfg.c
MFV: zlib 1.2.13.
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / qemu_fwcfg.c
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 }