1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2020 Adam Fenn <adam@fenn.io> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * Emulation of selected legacy test/debug interfaces expected by KVM-unit-tests 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/types.h> 36 #include <sys/mman.h> 37 #include <machine/vmm.h> 38 39 #include <assert.h> 40 #include <stdbool.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 45 #include <vmmapi.h> 46 47 #include "debug.h" 48 #include "inout.h" 49 #include "mem.h" 50 #include "pctestdev.h" 51 52 #define DEBUGEXIT_BASE 0xf4 53 #define DEBUGEXIT_LEN 4 54 #define DEBUGEXIT_NAME "isa-debug-exit" 55 56 #define IOMEM_BASE 0xff000000 57 #define IOMEM_LEN 0x10000 58 #define IOMEM_NAME "pc-testdev-iomem" 59 60 #define IOPORT_BASE 0xe0 61 #define IOPORT_LEN 4 62 #define IOPORT_NAME "pc-testdev-ioport" 63 64 #define IRQ_BASE 0x2000 65 #define IRQ_IOAPIC_PINCOUNT_MIN 24 66 #define IRQ_IOAPIC_PINCOUNT_MAX 32 67 #define IRQ_NAME "pc-testdev-irq-line" 68 69 #define PCTESTDEV_NAME "pc-testdev" 70 71 static bool pctestdev_inited; 72 static uint8_t pctestdev_iomem_buf[IOMEM_LEN]; 73 static uint32_t pctestdev_ioport_data; 74 75 static int pctestdev_debugexit_io(struct vmctx *ctx, int vcpu, int in, 76 int port, int bytes, uint32_t *eax, void *arg); 77 static int pctestdev_iomem_io(struct vmctx *ctx, int vcpu, int dir, 78 uint64_t addr, int size, uint64_t *val, void *arg1, 79 long arg2); 80 static int pctestdev_ioport_io(struct vmctx *ctx, int vcpu, int in, 81 int port, int bytes, uint32_t *eax, void *arg); 82 static int pctestdev_irq_io(struct vmctx *ctx, int vcpu, int in, 83 int port, int bytes, uint32_t *eax, void *arg); 84 85 const char * 86 pctestdev_getname(void) 87 { 88 return (PCTESTDEV_NAME); 89 } 90 91 int 92 pctestdev_parse(const char *opts) 93 { 94 if (opts != NULL && *opts != '\0') 95 return (-1); 96 97 return (0); 98 } 99 100 int 101 pctestdev_init(struct vmctx *ctx) 102 { 103 struct mem_range iomem; 104 struct inout_port debugexit, ioport, irq; 105 int err, pincount; 106 107 if (pctestdev_inited) { 108 EPRINTLN("Only one pc-testdev device is allowed."); 109 110 return (-1); 111 } 112 113 err = vm_ioapic_pincount(ctx, &pincount); 114 if (err != 0) { 115 EPRINTLN("pc-testdev: Failed to obtain IOAPIC pin count."); 116 117 return (-1); 118 } 119 if (pincount < IRQ_IOAPIC_PINCOUNT_MIN || 120 pincount > IRQ_IOAPIC_PINCOUNT_MAX) { 121 EPRINTLN("pc-testdev: Unsupported IOAPIC pin count: %d.", 122 pincount); 123 124 return (-1); 125 } 126 127 debugexit.name = DEBUGEXIT_NAME; 128 debugexit.port = DEBUGEXIT_BASE; 129 debugexit.size = DEBUGEXIT_LEN; 130 debugexit.flags = IOPORT_F_INOUT; 131 debugexit.handler = pctestdev_debugexit_io; 132 debugexit.arg = NULL; 133 134 iomem.name = IOMEM_NAME; 135 iomem.flags = MEM_F_RW | MEM_F_IMMUTABLE; 136 iomem.handler = pctestdev_iomem_io; 137 iomem.arg1 = NULL; 138 iomem.arg2 = 0; 139 iomem.base = IOMEM_BASE; 140 iomem.size = IOMEM_LEN; 141 142 ioport.name = IOPORT_NAME; 143 ioport.port = IOPORT_BASE; 144 ioport.size = IOPORT_LEN; 145 ioport.flags = IOPORT_F_INOUT; 146 ioport.handler = pctestdev_ioport_io; 147 ioport.arg = NULL; 148 149 irq.name = IRQ_NAME; 150 irq.port = IRQ_BASE; 151 irq.size = pincount; 152 irq.flags = IOPORT_F_INOUT; 153 irq.handler = pctestdev_irq_io; 154 irq.arg = NULL; 155 156 err = register_inout(&debugexit); 157 if (err != 0) 158 goto fail; 159 160 err = register_inout(&ioport); 161 if (err != 0) 162 goto fail_after_debugexit_reg; 163 164 err = register_inout(&irq); 165 if (err != 0) 166 goto fail_after_ioport_reg; 167 168 err = register_mem(&iomem); 169 if (err != 0) 170 goto fail_after_irq_reg; 171 172 pctestdev_inited = true; 173 174 return (0); 175 176 fail_after_irq_reg: 177 (void)unregister_inout(&irq); 178 179 fail_after_ioport_reg: 180 (void)unregister_inout(&ioport); 181 182 fail_after_debugexit_reg: 183 (void)unregister_inout(&debugexit); 184 185 fail: 186 return (err); 187 } 188 189 static int 190 pctestdev_debugexit_io(struct vmctx *ctx, int vcpu, int in, int port, 191 int bytes, uint32_t *eax, void *arg) 192 { 193 if (in) 194 *eax = 0; 195 else 196 exit((*eax << 1) | 1); 197 198 return (0); 199 } 200 201 static int 202 pctestdev_iomem_io(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 203 int size, uint64_t *val, void *arg1, long arg2) 204 { 205 uint64_t offset; 206 207 if (addr + size > IOMEM_BASE + IOMEM_LEN) 208 return (-1); 209 210 offset = addr - IOMEM_BASE; 211 if (dir == MEM_F_READ) { 212 (void)memcpy(val, pctestdev_iomem_buf + offset, size); 213 } else { 214 assert(dir == MEM_F_WRITE); 215 (void)memcpy(pctestdev_iomem_buf + offset, val, size); 216 } 217 218 return (0); 219 } 220 221 static int 222 pctestdev_ioport_io(struct vmctx *ctx, int vcpu, int in, int port, 223 int bytes, uint32_t *eax, void *arg) 224 { 225 uint32_t mask; 226 int lsb; 227 228 if (port + bytes > IOPORT_BASE + IOPORT_LEN) 229 return (-1); 230 231 lsb = (port & 0x3) * 8; 232 mask = (-1UL >> (32 - (bytes * 8))) << lsb; 233 234 if (in) 235 *eax = (pctestdev_ioport_data & mask) >> lsb; 236 else { 237 pctestdev_ioport_data &= ~mask; 238 pctestdev_ioport_data |= *eax << lsb; 239 } 240 241 return (0); 242 } 243 244 static int 245 pctestdev_irq_io(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 246 uint32_t *eax, void *arg) 247 { 248 int irq; 249 250 if (bytes != 1) 251 return (-1); 252 253 if (in) { 254 *eax = 0; 255 return (0); 256 } else { 257 irq = port - IRQ_BASE; 258 if (irq < 16) { 259 if (*eax) 260 return (vm_isa_assert_irq(ctx, irq, irq)); 261 else 262 return (vm_isa_deassert_irq(ctx, irq, irq)); 263 } else { 264 if (*eax) 265 return (vm_ioapic_assert_irq(ctx, irq)); 266 else 267 return (vm_ioapic_deassert_irq(ctx, irq)); 268 } 269 } 270 } 271