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