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