1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #include <sys/types.h> 34 #include <sys/mman.h> 35 #include <machine/vmm.h> 36 37 #include <assert.h> 38 #include <stdbool.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 43 #include <vmmapi.h> 44 45 #include "debug.h" 46 #include "inout.h" 47 #include "mem.h" 48 #include "pctestdev.h" 49 50 #define DEBUGEXIT_BASE 0xf4 51 #define DEBUGEXIT_LEN 4 52 #define DEBUGEXIT_NAME "isa-debug-exit" 53 54 #define IOMEM_BASE 0xff000000 55 #define IOMEM_LEN 0x10000 56 #define IOMEM_NAME "pc-testdev-iomem" 57 58 #define IOPORT_BASE 0xe0 59 #define IOPORT_LEN 4 60 #define IOPORT_NAME "pc-testdev-ioport" 61 62 #define IRQ_BASE 0x2000 63 #define IRQ_IOAPIC_PINCOUNT_MIN 24 64 #define IRQ_IOAPIC_PINCOUNT_MAX 32 65 #define IRQ_NAME "pc-testdev-irq-line" 66 67 #define PCTESTDEV_NAME "pc-testdev" 68 69 static bool pctestdev_inited; 70 static uint8_t pctestdev_iomem_buf[IOMEM_LEN]; 71 static uint32_t pctestdev_ioport_data; 72 73 static int pctestdev_debugexit_io(struct vmctx *ctx, int in, 74 int port, int bytes, uint32_t *eax, void *arg); 75 static int pctestdev_iomem_io(struct vcpu *vcpu, int dir, 76 uint64_t addr, int size, uint64_t *val, void *arg1, 77 long arg2); 78 static int pctestdev_ioport_io(struct vmctx *ctx, int in, 79 int port, int bytes, uint32_t *eax, void *arg); 80 static int pctestdev_irq_io(struct vmctx *ctx, int in, 81 int port, int bytes, uint32_t *eax, void *arg); 82 83 const char * 84 pctestdev_getname(void) 85 { 86 return (PCTESTDEV_NAME); 87 } 88 89 int 90 pctestdev_init(struct vmctx *ctx) 91 { 92 struct mem_range iomem; 93 struct inout_port debugexit, ioport, irq; 94 int err, pincount; 95 96 if (pctestdev_inited) { 97 EPRINTLN("Only one pc-testdev device is allowed."); 98 99 return (-1); 100 } 101 102 err = vm_ioapic_pincount(ctx, &pincount); 103 if (err != 0) { 104 EPRINTLN("pc-testdev: Failed to obtain IOAPIC pin count."); 105 106 return (-1); 107 } 108 if (pincount < IRQ_IOAPIC_PINCOUNT_MIN || 109 pincount > IRQ_IOAPIC_PINCOUNT_MAX) { 110 EPRINTLN("pc-testdev: Unsupported IOAPIC pin count: %d.", 111 pincount); 112 113 return (-1); 114 } 115 116 debugexit.name = DEBUGEXIT_NAME; 117 debugexit.port = DEBUGEXIT_BASE; 118 debugexit.size = DEBUGEXIT_LEN; 119 debugexit.flags = IOPORT_F_INOUT; 120 debugexit.handler = pctestdev_debugexit_io; 121 debugexit.arg = NULL; 122 123 iomem.name = IOMEM_NAME; 124 iomem.flags = MEM_F_RW | MEM_F_IMMUTABLE; 125 iomem.handler = pctestdev_iomem_io; 126 iomem.arg1 = NULL; 127 iomem.arg2 = 0; 128 iomem.base = IOMEM_BASE; 129 iomem.size = IOMEM_LEN; 130 131 ioport.name = IOPORT_NAME; 132 ioport.port = IOPORT_BASE; 133 ioport.size = IOPORT_LEN; 134 ioport.flags = IOPORT_F_INOUT; 135 ioport.handler = pctestdev_ioport_io; 136 ioport.arg = NULL; 137 138 irq.name = IRQ_NAME; 139 irq.port = IRQ_BASE; 140 irq.size = pincount; 141 irq.flags = IOPORT_F_INOUT; 142 irq.handler = pctestdev_irq_io; 143 irq.arg = NULL; 144 145 err = register_inout(&debugexit); 146 if (err != 0) 147 goto fail; 148 149 err = register_inout(&ioport); 150 if (err != 0) 151 goto fail_after_debugexit_reg; 152 153 err = register_inout(&irq); 154 if (err != 0) 155 goto fail_after_ioport_reg; 156 157 err = register_mem(&iomem); 158 if (err != 0) 159 goto fail_after_irq_reg; 160 161 pctestdev_inited = true; 162 163 return (0); 164 165 fail_after_irq_reg: 166 (void)unregister_inout(&irq); 167 168 fail_after_ioport_reg: 169 (void)unregister_inout(&ioport); 170 171 fail_after_debugexit_reg: 172 (void)unregister_inout(&debugexit); 173 174 fail: 175 return (err); 176 } 177 178 static int 179 pctestdev_debugexit_io(struct vmctx *ctx __unused, int in, 180 int port __unused, int bytes __unused, uint32_t *eax, void *arg __unused) 181 { 182 if (in) 183 *eax = 0; 184 else 185 exit((*eax << 1) | 1); 186 187 return (0); 188 } 189 190 static int 191 pctestdev_iomem_io(struct vcpu *vcpu __unused, int dir, 192 uint64_t addr, int size, uint64_t *val, void *arg1 __unused, 193 long arg2 __unused) 194 { 195 uint64_t offset; 196 197 if (addr + size > IOMEM_BASE + IOMEM_LEN) 198 return (-1); 199 200 offset = addr - IOMEM_BASE; 201 if (dir == MEM_F_READ) { 202 (void)memcpy(val, pctestdev_iomem_buf + offset, size); 203 } else { 204 assert(dir == MEM_F_WRITE); 205 (void)memcpy(pctestdev_iomem_buf + offset, val, size); 206 } 207 208 return (0); 209 } 210 211 static int 212 pctestdev_ioport_io(struct vmctx *ctx __unused, int in, 213 int port, int bytes, uint32_t *eax, void *arg __unused) 214 { 215 uint32_t mask; 216 int lsb; 217 218 if (port + bytes > IOPORT_BASE + IOPORT_LEN) 219 return (-1); 220 221 lsb = (port & 0x3) * 8; 222 mask = (-1UL >> (32 - (bytes * 8))) << lsb; 223 224 if (in) 225 *eax = (pctestdev_ioport_data & mask) >> lsb; 226 else { 227 pctestdev_ioport_data &= ~mask; 228 pctestdev_ioport_data |= *eax << lsb; 229 } 230 231 return (0); 232 } 233 234 static int 235 pctestdev_irq_io(struct vmctx *ctx, int in, int port, 236 int bytes, uint32_t *eax, void *arg __unused) 237 { 238 int irq; 239 240 if (bytes != 1) 241 return (-1); 242 243 if (in) { 244 *eax = 0; 245 return (0); 246 } else { 247 irq = port - IRQ_BASE; 248 if (irq < 16) { 249 if (*eax) 250 return (vm_isa_assert_irq(ctx, irq, irq)); 251 else 252 return (vm_isa_deassert_irq(ctx, irq, irq)); 253 } else { 254 if (*eax) 255 return (vm_ioapic_assert_irq(ctx, irq)); 256 else 257 return (vm_ioapic_deassert_irq(ctx, irq)); 258 } 259 } 260 } 261