12f40fc6fSPeter Grehan /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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/types.h>
332f40fc6fSPeter Grehan #include <sys/mman.h>
342f40fc6fSPeter Grehan #include <machine/vmm.h>
352f40fc6fSPeter Grehan
362f40fc6fSPeter Grehan #include <assert.h>
372f40fc6fSPeter Grehan #include <stdbool.h>
382f40fc6fSPeter Grehan #include <stdio.h>
392f40fc6fSPeter Grehan #include <stdlib.h>
402f40fc6fSPeter Grehan #include <string.h>
412f40fc6fSPeter Grehan
422f40fc6fSPeter Grehan #include <vmmapi.h>
432f40fc6fSPeter Grehan
442f40fc6fSPeter Grehan #include "debug.h"
45*31cf78c9SMark Johnston #ifdef __amd64__
46*31cf78c9SMark Johnston #include "amd64/inout.h"
47*31cf78c9SMark Johnston #endif
482f40fc6fSPeter Grehan #include "mem.h"
492f40fc6fSPeter Grehan #include "pctestdev.h"
502f40fc6fSPeter Grehan
512f40fc6fSPeter Grehan #define DEBUGEXIT_BASE 0xf4
522f40fc6fSPeter Grehan #define DEBUGEXIT_LEN 4
532f40fc6fSPeter Grehan #define DEBUGEXIT_NAME "isa-debug-exit"
542f40fc6fSPeter Grehan
552f40fc6fSPeter Grehan #define IOMEM_BASE 0xff000000
562f40fc6fSPeter Grehan #define IOMEM_LEN 0x10000
572f40fc6fSPeter Grehan #define IOMEM_NAME "pc-testdev-iomem"
582f40fc6fSPeter Grehan
592f40fc6fSPeter Grehan #define IOPORT_BASE 0xe0
602f40fc6fSPeter Grehan #define IOPORT_LEN 4
612f40fc6fSPeter Grehan #define IOPORT_NAME "pc-testdev-ioport"
622f40fc6fSPeter Grehan
632f40fc6fSPeter Grehan #define IRQ_BASE 0x2000
642f40fc6fSPeter Grehan #define IRQ_IOAPIC_PINCOUNT_MIN 24
652f40fc6fSPeter Grehan #define IRQ_IOAPIC_PINCOUNT_MAX 32
662f40fc6fSPeter Grehan #define IRQ_NAME "pc-testdev-irq-line"
672f40fc6fSPeter Grehan
682f40fc6fSPeter Grehan #define PCTESTDEV_NAME "pc-testdev"
692f40fc6fSPeter Grehan
702f40fc6fSPeter Grehan static bool pctestdev_inited;
712f40fc6fSPeter Grehan static uint8_t pctestdev_iomem_buf[IOMEM_LEN];
722f40fc6fSPeter Grehan static uint32_t pctestdev_ioport_data;
732f40fc6fSPeter Grehan
7408b05de1SJohn Baldwin static int pctestdev_debugexit_io(struct vmctx *ctx, int in,
752f40fc6fSPeter Grehan int port, int bytes, uint32_t *eax, void *arg);
767d9ef309SJohn Baldwin static int pctestdev_iomem_io(struct vcpu *vcpu, int dir,
772f40fc6fSPeter Grehan uint64_t addr, int size, uint64_t *val, void *arg1,
782f40fc6fSPeter Grehan long arg2);
7908b05de1SJohn Baldwin static int pctestdev_ioport_io(struct vmctx *ctx, int in,
802f40fc6fSPeter Grehan int port, int bytes, uint32_t *eax, void *arg);
8108b05de1SJohn Baldwin static int pctestdev_irq_io(struct vmctx *ctx, int in,
822f40fc6fSPeter Grehan int port, int bytes, uint32_t *eax, void *arg);
832f40fc6fSPeter Grehan
842f40fc6fSPeter Grehan const char *
pctestdev_getname(void)852f40fc6fSPeter Grehan pctestdev_getname(void)
862f40fc6fSPeter Grehan {
872f40fc6fSPeter Grehan return (PCTESTDEV_NAME);
882f40fc6fSPeter Grehan }
892f40fc6fSPeter Grehan
902f40fc6fSPeter Grehan int
pctestdev_init(struct vmctx * ctx)912f40fc6fSPeter Grehan pctestdev_init(struct vmctx *ctx)
922f40fc6fSPeter Grehan {
932f40fc6fSPeter Grehan struct mem_range iomem;
942f40fc6fSPeter Grehan struct inout_port debugexit, ioport, irq;
952f40fc6fSPeter Grehan int err, pincount;
962f40fc6fSPeter Grehan
972f40fc6fSPeter Grehan if (pctestdev_inited) {
982f40fc6fSPeter Grehan EPRINTLN("Only one pc-testdev device is allowed.");
992f40fc6fSPeter Grehan
1002f40fc6fSPeter Grehan return (-1);
1012f40fc6fSPeter Grehan }
1022f40fc6fSPeter Grehan
1032f40fc6fSPeter Grehan err = vm_ioapic_pincount(ctx, &pincount);
1042f40fc6fSPeter Grehan if (err != 0) {
1052f40fc6fSPeter Grehan EPRINTLN("pc-testdev: Failed to obtain IOAPIC pin count.");
1062f40fc6fSPeter Grehan
1072f40fc6fSPeter Grehan return (-1);
1082f40fc6fSPeter Grehan }
1092f40fc6fSPeter Grehan if (pincount < IRQ_IOAPIC_PINCOUNT_MIN ||
1102f40fc6fSPeter Grehan pincount > IRQ_IOAPIC_PINCOUNT_MAX) {
1112f40fc6fSPeter Grehan EPRINTLN("pc-testdev: Unsupported IOAPIC pin count: %d.",
1122f40fc6fSPeter Grehan pincount);
1132f40fc6fSPeter Grehan
1142f40fc6fSPeter Grehan return (-1);
1152f40fc6fSPeter Grehan }
1162f40fc6fSPeter Grehan
1172f40fc6fSPeter Grehan debugexit.name = DEBUGEXIT_NAME;
1182f40fc6fSPeter Grehan debugexit.port = DEBUGEXIT_BASE;
1192f40fc6fSPeter Grehan debugexit.size = DEBUGEXIT_LEN;
1202f40fc6fSPeter Grehan debugexit.flags = IOPORT_F_INOUT;
1212f40fc6fSPeter Grehan debugexit.handler = pctestdev_debugexit_io;
1222f40fc6fSPeter Grehan debugexit.arg = NULL;
1232f40fc6fSPeter Grehan
1242f40fc6fSPeter Grehan iomem.name = IOMEM_NAME;
1252f40fc6fSPeter Grehan iomem.flags = MEM_F_RW | MEM_F_IMMUTABLE;
1262f40fc6fSPeter Grehan iomem.handler = pctestdev_iomem_io;
1272f40fc6fSPeter Grehan iomem.arg1 = NULL;
1282f40fc6fSPeter Grehan iomem.arg2 = 0;
1292f40fc6fSPeter Grehan iomem.base = IOMEM_BASE;
1302f40fc6fSPeter Grehan iomem.size = IOMEM_LEN;
1312f40fc6fSPeter Grehan
1322f40fc6fSPeter Grehan ioport.name = IOPORT_NAME;
1332f40fc6fSPeter Grehan ioport.port = IOPORT_BASE;
1342f40fc6fSPeter Grehan ioport.size = IOPORT_LEN;
1352f40fc6fSPeter Grehan ioport.flags = IOPORT_F_INOUT;
1362f40fc6fSPeter Grehan ioport.handler = pctestdev_ioport_io;
1372f40fc6fSPeter Grehan ioport.arg = NULL;
1382f40fc6fSPeter Grehan
1392f40fc6fSPeter Grehan irq.name = IRQ_NAME;
1402f40fc6fSPeter Grehan irq.port = IRQ_BASE;
1412f40fc6fSPeter Grehan irq.size = pincount;
1422f40fc6fSPeter Grehan irq.flags = IOPORT_F_INOUT;
1432f40fc6fSPeter Grehan irq.handler = pctestdev_irq_io;
1442f40fc6fSPeter Grehan irq.arg = NULL;
1452f40fc6fSPeter Grehan
1462f40fc6fSPeter Grehan err = register_inout(&debugexit);
1472f40fc6fSPeter Grehan if (err != 0)
1482f40fc6fSPeter Grehan goto fail;
1492f40fc6fSPeter Grehan
1502f40fc6fSPeter Grehan err = register_inout(&ioport);
1512f40fc6fSPeter Grehan if (err != 0)
1522f40fc6fSPeter Grehan goto fail_after_debugexit_reg;
1532f40fc6fSPeter Grehan
1542f40fc6fSPeter Grehan err = register_inout(&irq);
1552f40fc6fSPeter Grehan if (err != 0)
1562f40fc6fSPeter Grehan goto fail_after_ioport_reg;
1572f40fc6fSPeter Grehan
1582f40fc6fSPeter Grehan err = register_mem(&iomem);
1592f40fc6fSPeter Grehan if (err != 0)
1602f40fc6fSPeter Grehan goto fail_after_irq_reg;
1612f40fc6fSPeter Grehan
1622f40fc6fSPeter Grehan pctestdev_inited = true;
1632f40fc6fSPeter Grehan
1642f40fc6fSPeter Grehan return (0);
1652f40fc6fSPeter Grehan
1662f40fc6fSPeter Grehan fail_after_irq_reg:
1672f40fc6fSPeter Grehan (void)unregister_inout(&irq);
1682f40fc6fSPeter Grehan
1692f40fc6fSPeter Grehan fail_after_ioport_reg:
1702f40fc6fSPeter Grehan (void)unregister_inout(&ioport);
1712f40fc6fSPeter Grehan
1722f40fc6fSPeter Grehan fail_after_debugexit_reg:
1732f40fc6fSPeter Grehan (void)unregister_inout(&debugexit);
1742f40fc6fSPeter Grehan
1752f40fc6fSPeter Grehan fail:
1762f40fc6fSPeter Grehan return (err);
1772f40fc6fSPeter Grehan }
1782f40fc6fSPeter Grehan
1792f40fc6fSPeter Grehan static int
pctestdev_debugexit_io(struct vmctx * ctx __unused,int in,int port __unused,int bytes __unused,uint32_t * eax,void * arg __unused)18008b05de1SJohn Baldwin pctestdev_debugexit_io(struct vmctx *ctx __unused, int in,
18198d920d9SMark Johnston int port __unused, int bytes __unused, uint32_t *eax, void *arg __unused)
1822f40fc6fSPeter Grehan {
1832f40fc6fSPeter Grehan if (in)
1842f40fc6fSPeter Grehan *eax = 0;
1852f40fc6fSPeter Grehan else
1862f40fc6fSPeter Grehan exit((*eax << 1) | 1);
1872f40fc6fSPeter Grehan
1882f40fc6fSPeter Grehan return (0);
1892f40fc6fSPeter Grehan }
1902f40fc6fSPeter Grehan
1912f40fc6fSPeter Grehan static int
pctestdev_iomem_io(struct vcpu * vcpu __unused,int dir,uint64_t addr,int size,uint64_t * val,void * arg1 __unused,long arg2 __unused)1927d9ef309SJohn Baldwin pctestdev_iomem_io(struct vcpu *vcpu __unused, int dir,
19398d920d9SMark Johnston uint64_t addr, int size, uint64_t *val, void *arg1 __unused,
19498d920d9SMark Johnston long arg2 __unused)
1952f40fc6fSPeter Grehan {
1962f40fc6fSPeter Grehan uint64_t offset;
1972f40fc6fSPeter Grehan
1982f40fc6fSPeter Grehan if (addr + size > IOMEM_BASE + IOMEM_LEN)
1992f40fc6fSPeter Grehan return (-1);
2002f40fc6fSPeter Grehan
2012f40fc6fSPeter Grehan offset = addr - IOMEM_BASE;
2022f40fc6fSPeter Grehan if (dir == MEM_F_READ) {
2032f40fc6fSPeter Grehan (void)memcpy(val, pctestdev_iomem_buf + offset, size);
2042f40fc6fSPeter Grehan } else {
2052f40fc6fSPeter Grehan assert(dir == MEM_F_WRITE);
2062f40fc6fSPeter Grehan (void)memcpy(pctestdev_iomem_buf + offset, val, size);
2072f40fc6fSPeter Grehan }
2082f40fc6fSPeter Grehan
2092f40fc6fSPeter Grehan return (0);
2102f40fc6fSPeter Grehan }
2112f40fc6fSPeter Grehan
2122f40fc6fSPeter Grehan static int
pctestdev_ioport_io(struct vmctx * ctx __unused,int in,int port,int bytes,uint32_t * eax,void * arg __unused)21308b05de1SJohn Baldwin pctestdev_ioport_io(struct vmctx *ctx __unused, int in,
21498d920d9SMark Johnston int port, int bytes, uint32_t *eax, void *arg __unused)
2152f40fc6fSPeter Grehan {
2162f40fc6fSPeter Grehan uint32_t mask;
2172f40fc6fSPeter Grehan int lsb;
2182f40fc6fSPeter Grehan
2192f40fc6fSPeter Grehan if (port + bytes > IOPORT_BASE + IOPORT_LEN)
2202f40fc6fSPeter Grehan return (-1);
2212f40fc6fSPeter Grehan
2222f40fc6fSPeter Grehan lsb = (port & 0x3) * 8;
2232f40fc6fSPeter Grehan mask = (-1UL >> (32 - (bytes * 8))) << lsb;
2242f40fc6fSPeter Grehan
2252f40fc6fSPeter Grehan if (in)
2262f40fc6fSPeter Grehan *eax = (pctestdev_ioport_data & mask) >> lsb;
2272f40fc6fSPeter Grehan else {
2282f40fc6fSPeter Grehan pctestdev_ioport_data &= ~mask;
2292f40fc6fSPeter Grehan pctestdev_ioport_data |= *eax << lsb;
2302f40fc6fSPeter Grehan }
2312f40fc6fSPeter Grehan
2322f40fc6fSPeter Grehan return (0);
2332f40fc6fSPeter Grehan }
2342f40fc6fSPeter Grehan
2352f40fc6fSPeter Grehan static int
pctestdev_irq_io(struct vmctx * ctx,int in,int port,int bytes,uint32_t * eax,void * arg __unused)23608b05de1SJohn Baldwin pctestdev_irq_io(struct vmctx *ctx, int in, int port,
23798d920d9SMark Johnston int bytes, uint32_t *eax, void *arg __unused)
2382f40fc6fSPeter Grehan {
2392f40fc6fSPeter Grehan int irq;
2402f40fc6fSPeter Grehan
2412f40fc6fSPeter Grehan if (bytes != 1)
2422f40fc6fSPeter Grehan return (-1);
2432f40fc6fSPeter Grehan
2442f40fc6fSPeter Grehan if (in) {
2452f40fc6fSPeter Grehan *eax = 0;
2462f40fc6fSPeter Grehan return (0);
2472f40fc6fSPeter Grehan } else {
2482f40fc6fSPeter Grehan irq = port - IRQ_BASE;
2492f40fc6fSPeter Grehan if (irq < 16) {
2502f40fc6fSPeter Grehan if (*eax)
2512f40fc6fSPeter Grehan return (vm_isa_assert_irq(ctx, irq, irq));
2522f40fc6fSPeter Grehan else
2532f40fc6fSPeter Grehan return (vm_isa_deassert_irq(ctx, irq, irq));
2542f40fc6fSPeter Grehan } else {
2552f40fc6fSPeter Grehan if (*eax)
2562f40fc6fSPeter Grehan return (vm_ioapic_assert_irq(ctx, irq));
2572f40fc6fSPeter Grehan else
2582f40fc6fSPeter Grehan return (vm_ioapic_deassert_irq(ctx, irq));
2592f40fc6fSPeter Grehan }
2602f40fc6fSPeter Grehan }
2612f40fc6fSPeter Grehan }
262