16960cd89SAndy Fiddaman /*-
2*32640292SAndy Fiddaman * SPDX-License-Identifier: BSD-2-Clause
36960cd89SAndy Fiddaman *
46960cd89SAndy Fiddaman * Copyright (c) 2020 Adam Fenn <adam@fenn.io>
56960cd89SAndy Fiddaman *
66960cd89SAndy Fiddaman * Redistribution and use in source and binary forms, with or without
76960cd89SAndy Fiddaman * modification, are permitted provided that the following conditions
86960cd89SAndy Fiddaman * are met:
96960cd89SAndy Fiddaman * 1. Redistributions of source code must retain the above copyright
106960cd89SAndy Fiddaman * notice, this list of conditions and the following disclaimer.
116960cd89SAndy Fiddaman * 2. Redistributions in binary form must reproduce the above copyright
126960cd89SAndy Fiddaman * notice, this list of conditions and the following disclaimer in the
136960cd89SAndy Fiddaman * documentation and/or other materials provided with the distribution.
146960cd89SAndy Fiddaman *
156960cd89SAndy Fiddaman * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
166960cd89SAndy Fiddaman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
176960cd89SAndy Fiddaman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
186960cd89SAndy Fiddaman * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
196960cd89SAndy Fiddaman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
206960cd89SAndy Fiddaman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
216960cd89SAndy Fiddaman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
226960cd89SAndy Fiddaman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
236960cd89SAndy Fiddaman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
246960cd89SAndy Fiddaman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
256960cd89SAndy Fiddaman * SUCH DAMAGE.
266960cd89SAndy Fiddaman */
276960cd89SAndy Fiddaman
286960cd89SAndy Fiddaman /*
296960cd89SAndy Fiddaman * Emulation of selected legacy test/debug interfaces expected by KVM-unit-tests
306960cd89SAndy Fiddaman */
316960cd89SAndy Fiddaman
326960cd89SAndy Fiddaman #include <sys/cdefs.h>
336960cd89SAndy Fiddaman
346960cd89SAndy Fiddaman #include <sys/types.h>
356960cd89SAndy Fiddaman #include <sys/mman.h>
366960cd89SAndy Fiddaman #include <machine/vmm.h>
376960cd89SAndy Fiddaman
386960cd89SAndy Fiddaman #include <assert.h>
396960cd89SAndy Fiddaman #include <stdbool.h>
406960cd89SAndy Fiddaman #include <stdio.h>
416960cd89SAndy Fiddaman #include <stdlib.h>
426960cd89SAndy Fiddaman #include <string.h>
436960cd89SAndy Fiddaman
446960cd89SAndy Fiddaman #include <vmmapi.h>
456960cd89SAndy Fiddaman
466960cd89SAndy Fiddaman #include "debug.h"
476960cd89SAndy Fiddaman #include "inout.h"
486960cd89SAndy Fiddaman #include "mem.h"
496960cd89SAndy Fiddaman #include "pctestdev.h"
506960cd89SAndy Fiddaman
516960cd89SAndy Fiddaman #define DEBUGEXIT_BASE 0xf4
526960cd89SAndy Fiddaman #define DEBUGEXIT_LEN 4
536960cd89SAndy Fiddaman #define DEBUGEXIT_NAME "isa-debug-exit"
546960cd89SAndy Fiddaman
556960cd89SAndy Fiddaman #define IOMEM_BASE 0xff000000
566960cd89SAndy Fiddaman #define IOMEM_LEN 0x10000
576960cd89SAndy Fiddaman #define IOMEM_NAME "pc-testdev-iomem"
586960cd89SAndy Fiddaman
596960cd89SAndy Fiddaman #define IOPORT_BASE 0xe0
606960cd89SAndy Fiddaman #define IOPORT_LEN 4
616960cd89SAndy Fiddaman #define IOPORT_NAME "pc-testdev-ioport"
626960cd89SAndy Fiddaman
636960cd89SAndy Fiddaman #define IRQ_BASE 0x2000
646960cd89SAndy Fiddaman #define IRQ_IOAPIC_PINCOUNT_MIN 24
656960cd89SAndy Fiddaman #define IRQ_IOAPIC_PINCOUNT_MAX 32
666960cd89SAndy Fiddaman #define IRQ_NAME "pc-testdev-irq-line"
676960cd89SAndy Fiddaman
686960cd89SAndy Fiddaman #define PCTESTDEV_NAME "pc-testdev"
696960cd89SAndy Fiddaman
706960cd89SAndy Fiddaman static bool pctestdev_inited;
716960cd89SAndy Fiddaman static uint8_t pctestdev_iomem_buf[IOMEM_LEN];
726960cd89SAndy Fiddaman static uint32_t pctestdev_ioport_data;
736960cd89SAndy Fiddaman
7459d65d31SAndy Fiddaman static int pctestdev_debugexit_io(struct vmctx *ctx, int in,
756960cd89SAndy Fiddaman int port, int bytes, uint32_t *eax, void *arg);
76*32640292SAndy Fiddaman static int pctestdev_iomem_io(struct vcpu *vcpu, int dir,
776960cd89SAndy Fiddaman uint64_t addr, int size, uint64_t *val, void *arg1,
786960cd89SAndy Fiddaman long arg2);
7959d65d31SAndy Fiddaman static int pctestdev_ioport_io(struct vmctx *ctx, int in,
806960cd89SAndy Fiddaman int port, int bytes, uint32_t *eax, void *arg);
8159d65d31SAndy Fiddaman static int pctestdev_irq_io(struct vmctx *ctx, int in,
826960cd89SAndy Fiddaman int port, int bytes, uint32_t *eax, void *arg);
836960cd89SAndy Fiddaman
846960cd89SAndy Fiddaman const char *
pctestdev_getname(void)856960cd89SAndy Fiddaman pctestdev_getname(void)
866960cd89SAndy Fiddaman {
876960cd89SAndy Fiddaman return (PCTESTDEV_NAME);
886960cd89SAndy Fiddaman }
896960cd89SAndy Fiddaman
906960cd89SAndy Fiddaman int
pctestdev_init(struct vmctx * ctx)916960cd89SAndy Fiddaman pctestdev_init(struct vmctx *ctx)
926960cd89SAndy Fiddaman {
936960cd89SAndy Fiddaman struct mem_range iomem;
946960cd89SAndy Fiddaman struct inout_port debugexit, ioport, irq;
956960cd89SAndy Fiddaman int err, pincount;
966960cd89SAndy Fiddaman
976960cd89SAndy Fiddaman if (pctestdev_inited) {
986960cd89SAndy Fiddaman EPRINTLN("Only one pc-testdev device is allowed.");
996960cd89SAndy Fiddaman
1006960cd89SAndy Fiddaman return (-1);
1016960cd89SAndy Fiddaman }
1026960cd89SAndy Fiddaman
1036960cd89SAndy Fiddaman err = vm_ioapic_pincount(ctx, &pincount);
1046960cd89SAndy Fiddaman if (err != 0) {
1056960cd89SAndy Fiddaman EPRINTLN("pc-testdev: Failed to obtain IOAPIC pin count.");
1066960cd89SAndy Fiddaman
1076960cd89SAndy Fiddaman return (-1);
1086960cd89SAndy Fiddaman }
1096960cd89SAndy Fiddaman if (pincount < IRQ_IOAPIC_PINCOUNT_MIN ||
1106960cd89SAndy Fiddaman pincount > IRQ_IOAPIC_PINCOUNT_MAX) {
1116960cd89SAndy Fiddaman EPRINTLN("pc-testdev: Unsupported IOAPIC pin count: %d.",
1126960cd89SAndy Fiddaman pincount);
1136960cd89SAndy Fiddaman
1146960cd89SAndy Fiddaman return (-1);
1156960cd89SAndy Fiddaman }
1166960cd89SAndy Fiddaman
1176960cd89SAndy Fiddaman debugexit.name = DEBUGEXIT_NAME;
1186960cd89SAndy Fiddaman debugexit.port = DEBUGEXIT_BASE;
1196960cd89SAndy Fiddaman debugexit.size = DEBUGEXIT_LEN;
1206960cd89SAndy Fiddaman debugexit.flags = IOPORT_F_INOUT;
1216960cd89SAndy Fiddaman debugexit.handler = pctestdev_debugexit_io;
1226960cd89SAndy Fiddaman debugexit.arg = NULL;
1236960cd89SAndy Fiddaman
1246960cd89SAndy Fiddaman iomem.name = IOMEM_NAME;
1256960cd89SAndy Fiddaman iomem.flags = MEM_F_RW | MEM_F_IMMUTABLE;
1266960cd89SAndy Fiddaman iomem.handler = pctestdev_iomem_io;
1276960cd89SAndy Fiddaman iomem.arg1 = NULL;
1286960cd89SAndy Fiddaman iomem.arg2 = 0;
1296960cd89SAndy Fiddaman iomem.base = IOMEM_BASE;
1306960cd89SAndy Fiddaman iomem.size = IOMEM_LEN;
1316960cd89SAndy Fiddaman
1326960cd89SAndy Fiddaman ioport.name = IOPORT_NAME;
1336960cd89SAndy Fiddaman ioport.port = IOPORT_BASE;
1346960cd89SAndy Fiddaman ioport.size = IOPORT_LEN;
1356960cd89SAndy Fiddaman ioport.flags = IOPORT_F_INOUT;
1366960cd89SAndy Fiddaman ioport.handler = pctestdev_ioport_io;
1376960cd89SAndy Fiddaman ioport.arg = NULL;
1386960cd89SAndy Fiddaman
1396960cd89SAndy Fiddaman irq.name = IRQ_NAME;
1406960cd89SAndy Fiddaman irq.port = IRQ_BASE;
1416960cd89SAndy Fiddaman irq.size = pincount;
1426960cd89SAndy Fiddaman irq.flags = IOPORT_F_INOUT;
1436960cd89SAndy Fiddaman irq.handler = pctestdev_irq_io;
1446960cd89SAndy Fiddaman irq.arg = NULL;
1456960cd89SAndy Fiddaman
1466960cd89SAndy Fiddaman err = register_inout(&debugexit);
1476960cd89SAndy Fiddaman if (err != 0)
1486960cd89SAndy Fiddaman goto fail;
1496960cd89SAndy Fiddaman
1506960cd89SAndy Fiddaman err = register_inout(&ioport);
1516960cd89SAndy Fiddaman if (err != 0)
1526960cd89SAndy Fiddaman goto fail_after_debugexit_reg;
1536960cd89SAndy Fiddaman
1546960cd89SAndy Fiddaman err = register_inout(&irq);
1556960cd89SAndy Fiddaman if (err != 0)
1566960cd89SAndy Fiddaman goto fail_after_ioport_reg;
1576960cd89SAndy Fiddaman
1586960cd89SAndy Fiddaman err = register_mem(&iomem);
1596960cd89SAndy Fiddaman if (err != 0)
1606960cd89SAndy Fiddaman goto fail_after_irq_reg;
1616960cd89SAndy Fiddaman
1626960cd89SAndy Fiddaman pctestdev_inited = true;
1636960cd89SAndy Fiddaman
1646960cd89SAndy Fiddaman return (0);
1656960cd89SAndy Fiddaman
1666960cd89SAndy Fiddaman fail_after_irq_reg:
1676960cd89SAndy Fiddaman (void)unregister_inout(&irq);
1686960cd89SAndy Fiddaman
1696960cd89SAndy Fiddaman fail_after_ioport_reg:
1706960cd89SAndy Fiddaman (void)unregister_inout(&ioport);
1716960cd89SAndy Fiddaman
1726960cd89SAndy Fiddaman fail_after_debugexit_reg:
1736960cd89SAndy Fiddaman (void)unregister_inout(&debugexit);
1746960cd89SAndy Fiddaman
1756960cd89SAndy Fiddaman fail:
1766960cd89SAndy Fiddaman return (err);
1776960cd89SAndy Fiddaman }
1786960cd89SAndy Fiddaman
1796960cd89SAndy Fiddaman static int
pctestdev_debugexit_io(struct vmctx * ctx __unused,int in,int port __unused,int bytes __unused,uint32_t * eax,void * arg __unused)18059d65d31SAndy Fiddaman pctestdev_debugexit_io(struct vmctx *ctx __unused, int in,
18159d65d31SAndy Fiddaman int port __unused, int bytes __unused, uint32_t *eax, void *arg __unused)
1826960cd89SAndy Fiddaman {
1836960cd89SAndy Fiddaman if (in)
1846960cd89SAndy Fiddaman *eax = 0;
1856960cd89SAndy Fiddaman else
1866960cd89SAndy Fiddaman exit((*eax << 1) | 1);
1876960cd89SAndy Fiddaman
1886960cd89SAndy Fiddaman return (0);
1896960cd89SAndy Fiddaman }
1906960cd89SAndy Fiddaman
1916960cd89SAndy Fiddaman 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)192*32640292SAndy Fiddaman pctestdev_iomem_io(struct vcpu *vcpu __unused, int dir,
19359d65d31SAndy Fiddaman uint64_t addr, int size, uint64_t *val, void *arg1 __unused,
19459d65d31SAndy Fiddaman long arg2 __unused)
1956960cd89SAndy Fiddaman {
1966960cd89SAndy Fiddaman uint64_t offset;
1976960cd89SAndy Fiddaman
1986960cd89SAndy Fiddaman if (addr + size > IOMEM_BASE + IOMEM_LEN)
1996960cd89SAndy Fiddaman return (-1);
2006960cd89SAndy Fiddaman
2016960cd89SAndy Fiddaman offset = addr - IOMEM_BASE;
2026960cd89SAndy Fiddaman if (dir == MEM_F_READ) {
2036960cd89SAndy Fiddaman (void)memcpy(val, pctestdev_iomem_buf + offset, size);
2046960cd89SAndy Fiddaman } else {
2056960cd89SAndy Fiddaman assert(dir == MEM_F_WRITE);
2066960cd89SAndy Fiddaman (void)memcpy(pctestdev_iomem_buf + offset, val, size);
2076960cd89SAndy Fiddaman }
2086960cd89SAndy Fiddaman
2096960cd89SAndy Fiddaman return (0);
2106960cd89SAndy Fiddaman }
2116960cd89SAndy Fiddaman
2126960cd89SAndy Fiddaman static int
pctestdev_ioport_io(struct vmctx * ctx __unused,int in,int port,int bytes,uint32_t * eax,void * arg __unused)21359d65d31SAndy Fiddaman pctestdev_ioport_io(struct vmctx *ctx __unused, int in,
21459d65d31SAndy Fiddaman int port, int bytes, uint32_t *eax, void *arg __unused)
2156960cd89SAndy Fiddaman {
2166960cd89SAndy Fiddaman uint32_t mask;
2176960cd89SAndy Fiddaman int lsb;
2186960cd89SAndy Fiddaman
2196960cd89SAndy Fiddaman if (port + bytes > IOPORT_BASE + IOPORT_LEN)
2206960cd89SAndy Fiddaman return (-1);
2216960cd89SAndy Fiddaman
2226960cd89SAndy Fiddaman lsb = (port & 0x3) * 8;
2236960cd89SAndy Fiddaman mask = (-1UL >> (32 - (bytes * 8))) << lsb;
2246960cd89SAndy Fiddaman
2256960cd89SAndy Fiddaman if (in)
2266960cd89SAndy Fiddaman *eax = (pctestdev_ioport_data & mask) >> lsb;
2276960cd89SAndy Fiddaman else {
2286960cd89SAndy Fiddaman pctestdev_ioport_data &= ~mask;
2296960cd89SAndy Fiddaman pctestdev_ioport_data |= *eax << lsb;
2306960cd89SAndy Fiddaman }
2316960cd89SAndy Fiddaman
2326960cd89SAndy Fiddaman return (0);
2336960cd89SAndy Fiddaman }
2346960cd89SAndy Fiddaman
2356960cd89SAndy Fiddaman static int
pctestdev_irq_io(struct vmctx * ctx,int in,int port,int bytes,uint32_t * eax,void * arg __unused)23659d65d31SAndy Fiddaman pctestdev_irq_io(struct vmctx *ctx, int in, int port,
23759d65d31SAndy Fiddaman int bytes, uint32_t *eax, void *arg __unused)
2386960cd89SAndy Fiddaman {
2396960cd89SAndy Fiddaman int irq;
2406960cd89SAndy Fiddaman
2416960cd89SAndy Fiddaman if (bytes != 1)
2426960cd89SAndy Fiddaman return (-1);
2436960cd89SAndy Fiddaman
2446960cd89SAndy Fiddaman if (in) {
2456960cd89SAndy Fiddaman *eax = 0;
2466960cd89SAndy Fiddaman return (0);
2476960cd89SAndy Fiddaman } else {
2486960cd89SAndy Fiddaman irq = port - IRQ_BASE;
2496960cd89SAndy Fiddaman if (irq < 16) {
2506960cd89SAndy Fiddaman if (*eax)
2516960cd89SAndy Fiddaman return (vm_isa_assert_irq(ctx, irq, irq));
2526960cd89SAndy Fiddaman else
2536960cd89SAndy Fiddaman return (vm_isa_deassert_irq(ctx, irq, irq));
2546960cd89SAndy Fiddaman } else {
2556960cd89SAndy Fiddaman if (*eax)
2566960cd89SAndy Fiddaman return (vm_ioapic_assert_irq(ctx, irq));
2576960cd89SAndy Fiddaman else
2586960cd89SAndy Fiddaman return (vm_ioapic_deassert_irq(ctx, irq));
2596960cd89SAndy Fiddaman }
2606960cd89SAndy Fiddaman }
2616960cd89SAndy Fiddaman }
262