xref: /illumos-gate/usr/src/cmd/bhyve/pctestdev.c (revision 32640292)
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