xref: /freebsd/usr.sbin/bhyve/pctestdev.c (revision 7d9ef309bd09c061e9cad8ace6f7bb4c60f087e6)
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