xref: /freebsd/usr.sbin/bhyve/pctestdev.c (revision 4d65a7c6)
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"
4531cf78c9SMark Johnston #ifdef __amd64__
4631cf78c9SMark Johnston #include "amd64/inout.h"
4731cf78c9SMark 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