12f40fc6fSPeter Grehan /*- 22f40fc6fSPeter Grehan * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 32f40fc6fSPeter Grehan * 42f40fc6fSPeter Grehan * Copyright (c) 2020 Adam Fenn <adam@fenn.io> 52f40fc6fSPeter Grehan * 62f40fc6fSPeter Grehan * Redistribution and use in source and binary forms, with or without 72f40fc6fSPeter Grehan * modification, are permitted provided that the following conditions 82f40fc6fSPeter Grehan * are met: 92f40fc6fSPeter Grehan * 1. Redistributions of source code must retain the above copyright 102f40fc6fSPeter Grehan * notice, this list of conditions and the following disclaimer. 112f40fc6fSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 122f40fc6fSPeter Grehan * notice, this list of conditions and the following disclaimer in the 132f40fc6fSPeter Grehan * documentation and/or other materials provided with the distribution. 142f40fc6fSPeter Grehan * 152f40fc6fSPeter Grehan * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 162f40fc6fSPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 172f40fc6fSPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 182f40fc6fSPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 192f40fc6fSPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 202f40fc6fSPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 212f40fc6fSPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 222f40fc6fSPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 232f40fc6fSPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 242f40fc6fSPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 252f40fc6fSPeter Grehan * SUCH DAMAGE. 262f40fc6fSPeter Grehan */ 272f40fc6fSPeter Grehan 282f40fc6fSPeter Grehan /* 292f40fc6fSPeter Grehan * Emulation of selected legacy test/debug interfaces expected by KVM-unit-tests 302f40fc6fSPeter Grehan */ 312f40fc6fSPeter Grehan 322f40fc6fSPeter Grehan #include <sys/cdefs.h> 332f40fc6fSPeter Grehan __FBSDID("$FreeBSD$"); 342f40fc6fSPeter Grehan 352f40fc6fSPeter Grehan #include <sys/types.h> 362f40fc6fSPeter Grehan #include <sys/mman.h> 372f40fc6fSPeter Grehan #include <machine/vmm.h> 382f40fc6fSPeter Grehan 392f40fc6fSPeter Grehan #include <assert.h> 402f40fc6fSPeter Grehan #include <stdbool.h> 412f40fc6fSPeter Grehan #include <stdio.h> 422f40fc6fSPeter Grehan #include <stdlib.h> 432f40fc6fSPeter Grehan #include <string.h> 442f40fc6fSPeter Grehan 452f40fc6fSPeter Grehan #include <vmmapi.h> 462f40fc6fSPeter Grehan 472f40fc6fSPeter Grehan #include "debug.h" 482f40fc6fSPeter Grehan #include "inout.h" 492f40fc6fSPeter Grehan #include "mem.h" 502f40fc6fSPeter Grehan #include "pctestdev.h" 512f40fc6fSPeter Grehan 522f40fc6fSPeter Grehan #define DEBUGEXIT_BASE 0xf4 532f40fc6fSPeter Grehan #define DEBUGEXIT_LEN 4 542f40fc6fSPeter Grehan #define DEBUGEXIT_NAME "isa-debug-exit" 552f40fc6fSPeter Grehan 562f40fc6fSPeter Grehan #define IOMEM_BASE 0xff000000 572f40fc6fSPeter Grehan #define IOMEM_LEN 0x10000 582f40fc6fSPeter Grehan #define IOMEM_NAME "pc-testdev-iomem" 592f40fc6fSPeter Grehan 602f40fc6fSPeter Grehan #define IOPORT_BASE 0xe0 612f40fc6fSPeter Grehan #define IOPORT_LEN 4 622f40fc6fSPeter Grehan #define IOPORT_NAME "pc-testdev-ioport" 632f40fc6fSPeter Grehan 642f40fc6fSPeter Grehan #define IRQ_BASE 0x2000 652f40fc6fSPeter Grehan #define IRQ_IOAPIC_PINCOUNT_MIN 24 662f40fc6fSPeter Grehan #define IRQ_IOAPIC_PINCOUNT_MAX 32 672f40fc6fSPeter Grehan #define IRQ_NAME "pc-testdev-irq-line" 682f40fc6fSPeter Grehan 692f40fc6fSPeter Grehan #define PCTESTDEV_NAME "pc-testdev" 702f40fc6fSPeter Grehan 712f40fc6fSPeter Grehan static bool pctestdev_inited; 722f40fc6fSPeter Grehan static uint8_t pctestdev_iomem_buf[IOMEM_LEN]; 732f40fc6fSPeter Grehan static uint32_t pctestdev_ioport_data; 742f40fc6fSPeter Grehan 7508b05de1SJohn Baldwin static int pctestdev_debugexit_io(struct vmctx *ctx, int in, 762f40fc6fSPeter Grehan int port, int bytes, uint32_t *eax, void *arg); 77*7d9ef309SJohn Baldwin static int pctestdev_iomem_io(struct vcpu *vcpu, int dir, 782f40fc6fSPeter Grehan uint64_t addr, int size, uint64_t *val, void *arg1, 792f40fc6fSPeter Grehan long arg2); 8008b05de1SJohn Baldwin static int pctestdev_ioport_io(struct vmctx *ctx, int in, 812f40fc6fSPeter Grehan int port, int bytes, uint32_t *eax, void *arg); 8208b05de1SJohn Baldwin static int pctestdev_irq_io(struct vmctx *ctx, int in, 832f40fc6fSPeter Grehan int port, int bytes, uint32_t *eax, void *arg); 842f40fc6fSPeter Grehan 852f40fc6fSPeter Grehan const char * 862f40fc6fSPeter Grehan pctestdev_getname(void) 872f40fc6fSPeter Grehan { 882f40fc6fSPeter Grehan return (PCTESTDEV_NAME); 892f40fc6fSPeter Grehan } 902f40fc6fSPeter Grehan 912f40fc6fSPeter Grehan int 922f40fc6fSPeter Grehan pctestdev_init(struct vmctx *ctx) 932f40fc6fSPeter Grehan { 942f40fc6fSPeter Grehan struct mem_range iomem; 952f40fc6fSPeter Grehan struct inout_port debugexit, ioport, irq; 962f40fc6fSPeter Grehan int err, pincount; 972f40fc6fSPeter Grehan 982f40fc6fSPeter Grehan if (pctestdev_inited) { 992f40fc6fSPeter Grehan EPRINTLN("Only one pc-testdev device is allowed."); 1002f40fc6fSPeter Grehan 1012f40fc6fSPeter Grehan return (-1); 1022f40fc6fSPeter Grehan } 1032f40fc6fSPeter Grehan 1042f40fc6fSPeter Grehan err = vm_ioapic_pincount(ctx, &pincount); 1052f40fc6fSPeter Grehan if (err != 0) { 1062f40fc6fSPeter Grehan EPRINTLN("pc-testdev: Failed to obtain IOAPIC pin count."); 1072f40fc6fSPeter Grehan 1082f40fc6fSPeter Grehan return (-1); 1092f40fc6fSPeter Grehan } 1102f40fc6fSPeter Grehan if (pincount < IRQ_IOAPIC_PINCOUNT_MIN || 1112f40fc6fSPeter Grehan pincount > IRQ_IOAPIC_PINCOUNT_MAX) { 1122f40fc6fSPeter Grehan EPRINTLN("pc-testdev: Unsupported IOAPIC pin count: %d.", 1132f40fc6fSPeter Grehan pincount); 1142f40fc6fSPeter Grehan 1152f40fc6fSPeter Grehan return (-1); 1162f40fc6fSPeter Grehan } 1172f40fc6fSPeter Grehan 1182f40fc6fSPeter Grehan debugexit.name = DEBUGEXIT_NAME; 1192f40fc6fSPeter Grehan debugexit.port = DEBUGEXIT_BASE; 1202f40fc6fSPeter Grehan debugexit.size = DEBUGEXIT_LEN; 1212f40fc6fSPeter Grehan debugexit.flags = IOPORT_F_INOUT; 1222f40fc6fSPeter Grehan debugexit.handler = pctestdev_debugexit_io; 1232f40fc6fSPeter Grehan debugexit.arg = NULL; 1242f40fc6fSPeter Grehan 1252f40fc6fSPeter Grehan iomem.name = IOMEM_NAME; 1262f40fc6fSPeter Grehan iomem.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1272f40fc6fSPeter Grehan iomem.handler = pctestdev_iomem_io; 1282f40fc6fSPeter Grehan iomem.arg1 = NULL; 1292f40fc6fSPeter Grehan iomem.arg2 = 0; 1302f40fc6fSPeter Grehan iomem.base = IOMEM_BASE; 1312f40fc6fSPeter Grehan iomem.size = IOMEM_LEN; 1322f40fc6fSPeter Grehan 1332f40fc6fSPeter Grehan ioport.name = IOPORT_NAME; 1342f40fc6fSPeter Grehan ioport.port = IOPORT_BASE; 1352f40fc6fSPeter Grehan ioport.size = IOPORT_LEN; 1362f40fc6fSPeter Grehan ioport.flags = IOPORT_F_INOUT; 1372f40fc6fSPeter Grehan ioport.handler = pctestdev_ioport_io; 1382f40fc6fSPeter Grehan ioport.arg = NULL; 1392f40fc6fSPeter Grehan 1402f40fc6fSPeter Grehan irq.name = IRQ_NAME; 1412f40fc6fSPeter Grehan irq.port = IRQ_BASE; 1422f40fc6fSPeter Grehan irq.size = pincount; 1432f40fc6fSPeter Grehan irq.flags = IOPORT_F_INOUT; 1442f40fc6fSPeter Grehan irq.handler = pctestdev_irq_io; 1452f40fc6fSPeter Grehan irq.arg = NULL; 1462f40fc6fSPeter Grehan 1472f40fc6fSPeter Grehan err = register_inout(&debugexit); 1482f40fc6fSPeter Grehan if (err != 0) 1492f40fc6fSPeter Grehan goto fail; 1502f40fc6fSPeter Grehan 1512f40fc6fSPeter Grehan err = register_inout(&ioport); 1522f40fc6fSPeter Grehan if (err != 0) 1532f40fc6fSPeter Grehan goto fail_after_debugexit_reg; 1542f40fc6fSPeter Grehan 1552f40fc6fSPeter Grehan err = register_inout(&irq); 1562f40fc6fSPeter Grehan if (err != 0) 1572f40fc6fSPeter Grehan goto fail_after_ioport_reg; 1582f40fc6fSPeter Grehan 1592f40fc6fSPeter Grehan err = register_mem(&iomem); 1602f40fc6fSPeter Grehan if (err != 0) 1612f40fc6fSPeter Grehan goto fail_after_irq_reg; 1622f40fc6fSPeter Grehan 1632f40fc6fSPeter Grehan pctestdev_inited = true; 1642f40fc6fSPeter Grehan 1652f40fc6fSPeter Grehan return (0); 1662f40fc6fSPeter Grehan 1672f40fc6fSPeter Grehan fail_after_irq_reg: 1682f40fc6fSPeter Grehan (void)unregister_inout(&irq); 1692f40fc6fSPeter Grehan 1702f40fc6fSPeter Grehan fail_after_ioport_reg: 1712f40fc6fSPeter Grehan (void)unregister_inout(&ioport); 1722f40fc6fSPeter Grehan 1732f40fc6fSPeter Grehan fail_after_debugexit_reg: 1742f40fc6fSPeter Grehan (void)unregister_inout(&debugexit); 1752f40fc6fSPeter Grehan 1762f40fc6fSPeter Grehan fail: 1772f40fc6fSPeter Grehan return (err); 1782f40fc6fSPeter Grehan } 1792f40fc6fSPeter Grehan 1802f40fc6fSPeter Grehan static int 18108b05de1SJohn Baldwin pctestdev_debugexit_io(struct vmctx *ctx __unused, int in, 18298d920d9SMark Johnston int port __unused, int bytes __unused, uint32_t *eax, void *arg __unused) 1832f40fc6fSPeter Grehan { 1842f40fc6fSPeter Grehan if (in) 1852f40fc6fSPeter Grehan *eax = 0; 1862f40fc6fSPeter Grehan else 1872f40fc6fSPeter Grehan exit((*eax << 1) | 1); 1882f40fc6fSPeter Grehan 1892f40fc6fSPeter Grehan return (0); 1902f40fc6fSPeter Grehan } 1912f40fc6fSPeter Grehan 1922f40fc6fSPeter Grehan static int 193*7d9ef309SJohn Baldwin pctestdev_iomem_io(struct vcpu *vcpu __unused, int dir, 19498d920d9SMark Johnston uint64_t addr, int size, uint64_t *val, void *arg1 __unused, 19598d920d9SMark Johnston long arg2 __unused) 1962f40fc6fSPeter Grehan { 1972f40fc6fSPeter Grehan uint64_t offset; 1982f40fc6fSPeter Grehan 1992f40fc6fSPeter Grehan if (addr + size > IOMEM_BASE + IOMEM_LEN) 2002f40fc6fSPeter Grehan return (-1); 2012f40fc6fSPeter Grehan 2022f40fc6fSPeter Grehan offset = addr - IOMEM_BASE; 2032f40fc6fSPeter Grehan if (dir == MEM_F_READ) { 2042f40fc6fSPeter Grehan (void)memcpy(val, pctestdev_iomem_buf + offset, size); 2052f40fc6fSPeter Grehan } else { 2062f40fc6fSPeter Grehan assert(dir == MEM_F_WRITE); 2072f40fc6fSPeter Grehan (void)memcpy(pctestdev_iomem_buf + offset, val, size); 2082f40fc6fSPeter Grehan } 2092f40fc6fSPeter Grehan 2102f40fc6fSPeter Grehan return (0); 2112f40fc6fSPeter Grehan } 2122f40fc6fSPeter Grehan 2132f40fc6fSPeter Grehan static int 21408b05de1SJohn Baldwin pctestdev_ioport_io(struct vmctx *ctx __unused, int in, 21598d920d9SMark Johnston int port, int bytes, uint32_t *eax, void *arg __unused) 2162f40fc6fSPeter Grehan { 2172f40fc6fSPeter Grehan uint32_t mask; 2182f40fc6fSPeter Grehan int lsb; 2192f40fc6fSPeter Grehan 2202f40fc6fSPeter Grehan if (port + bytes > IOPORT_BASE + IOPORT_LEN) 2212f40fc6fSPeter Grehan return (-1); 2222f40fc6fSPeter Grehan 2232f40fc6fSPeter Grehan lsb = (port & 0x3) * 8; 2242f40fc6fSPeter Grehan mask = (-1UL >> (32 - (bytes * 8))) << lsb; 2252f40fc6fSPeter Grehan 2262f40fc6fSPeter Grehan if (in) 2272f40fc6fSPeter Grehan *eax = (pctestdev_ioport_data & mask) >> lsb; 2282f40fc6fSPeter Grehan else { 2292f40fc6fSPeter Grehan pctestdev_ioport_data &= ~mask; 2302f40fc6fSPeter Grehan pctestdev_ioport_data |= *eax << lsb; 2312f40fc6fSPeter Grehan } 2322f40fc6fSPeter Grehan 2332f40fc6fSPeter Grehan return (0); 2342f40fc6fSPeter Grehan } 2352f40fc6fSPeter Grehan 2362f40fc6fSPeter Grehan static int 23708b05de1SJohn Baldwin pctestdev_irq_io(struct vmctx *ctx, int in, int port, 23898d920d9SMark Johnston int bytes, uint32_t *eax, void *arg __unused) 2392f40fc6fSPeter Grehan { 2402f40fc6fSPeter Grehan int irq; 2412f40fc6fSPeter Grehan 2422f40fc6fSPeter Grehan if (bytes != 1) 2432f40fc6fSPeter Grehan return (-1); 2442f40fc6fSPeter Grehan 2452f40fc6fSPeter Grehan if (in) { 2462f40fc6fSPeter Grehan *eax = 0; 2472f40fc6fSPeter Grehan return (0); 2482f40fc6fSPeter Grehan } else { 2492f40fc6fSPeter Grehan irq = port - IRQ_BASE; 2502f40fc6fSPeter Grehan if (irq < 16) { 2512f40fc6fSPeter Grehan if (*eax) 2522f40fc6fSPeter Grehan return (vm_isa_assert_irq(ctx, irq, irq)); 2532f40fc6fSPeter Grehan else 2542f40fc6fSPeter Grehan return (vm_isa_deassert_irq(ctx, irq, irq)); 2552f40fc6fSPeter Grehan } else { 2562f40fc6fSPeter Grehan if (*eax) 2572f40fc6fSPeter Grehan return (vm_ioapic_assert_irq(ctx, irq)); 2582f40fc6fSPeter Grehan else 2592f40fc6fSPeter Grehan return (vm_ioapic_deassert_irq(ctx, irq)); 2602f40fc6fSPeter Grehan } 2612f40fc6fSPeter Grehan } 2622f40fc6fSPeter Grehan } 263