xref: /freebsd/sys/amd64/vmm/vmm_ioport.c (revision 5a17f489d5848085307e1d5fa7ca142694b4946b)
1762fd208STycho Nightingale /*-
2ebc3c37cSMarcelo Araujo  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3ebc3c37cSMarcelo Araujo  *
4762fd208STycho Nightingale  * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5762fd208STycho Nightingale  * All rights reserved.
6762fd208STycho Nightingale  *
7762fd208STycho Nightingale  * Redistribution and use in source and binary forms, with or without
8762fd208STycho Nightingale  * modification, are permitted provided that the following conditions
9762fd208STycho Nightingale  * are met:
10762fd208STycho Nightingale  * 1. Redistributions of source code must retain the above copyright
11762fd208STycho Nightingale  *    notice, this list of conditions and the following disclaimer.
12762fd208STycho Nightingale  * 2. Redistributions in binary form must reproduce the above copyright
13762fd208STycho Nightingale  *    notice, this list of conditions and the following disclaimer in the
14762fd208STycho Nightingale  *    documentation and/or other materials provided with the distribution.
15762fd208STycho Nightingale  *
16762fd208STycho Nightingale  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
17762fd208STycho Nightingale  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18762fd208STycho Nightingale  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19762fd208STycho Nightingale  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20762fd208STycho Nightingale  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21762fd208STycho Nightingale  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22762fd208STycho Nightingale  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23762fd208STycho Nightingale  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24762fd208STycho Nightingale  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25762fd208STycho Nightingale  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26762fd208STycho Nightingale  * SUCH DAMAGE.
27762fd208STycho Nightingale  */
28762fd208STycho Nightingale 
29762fd208STycho Nightingale #include <sys/cdefs.h>
30762fd208STycho Nightingale __FBSDID("$FreeBSD$");
31762fd208STycho Nightingale 
32762fd208STycho Nightingale #include <sys/param.h>
33762fd208STycho Nightingale #include <sys/systm.h>
34762fd208STycho Nightingale 
35762fd208STycho Nightingale #include <machine/vmm.h>
36e813a873SNeel Natu #include <machine/vmm_instruction_emul.h>
37762fd208STycho Nightingale 
38762fd208STycho Nightingale #include "vatpic.h"
39e883c9bbSTycho Nightingale #include "vatpit.h"
40160ef77aSNeel Natu #include "vpmtmr.h"
410dafa5cdSNeel Natu #include "vrtc.h"
42762fd208STycho Nightingale #include "vmm_ioport.h"
43d17b5104SNeel Natu #include "vmm_ktr.h"
44762fd208STycho Nightingale 
45762fd208STycho Nightingale #define	MAX_IOPORTS		1280
46762fd208STycho Nightingale 
47762fd208STycho Nightingale ioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
48e883c9bbSTycho Nightingale 	[TIMER_MODE] = vatpit_handler,
49e883c9bbSTycho Nightingale 	[TIMER_CNTR0] = vatpit_handler,
50e883c9bbSTycho Nightingale 	[TIMER_CNTR1] = vatpit_handler,
51e883c9bbSTycho Nightingale 	[TIMER_CNTR2] = vatpit_handler,
5279d6ca33STycho Nightingale 	[NMISC_PORT] = vatpit_nmisc_handler,
53762fd208STycho Nightingale 	[IO_ICU1] = vatpic_master_handler,
54762fd208STycho Nightingale 	[IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler,
55762fd208STycho Nightingale 	[IO_ICU2] = vatpic_slave_handler,
56762fd208STycho Nightingale 	[IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler,
57762fd208STycho Nightingale 	[IO_ELCR1] = vatpic_elc_handler,
58762fd208STycho Nightingale 	[IO_ELCR2] = vatpic_elc_handler,
59160ef77aSNeel Natu 	[IO_PMTMR] = vpmtmr_handler,
600dafa5cdSNeel Natu 	[IO_RTC] = vrtc_addr_handler,
610dafa5cdSNeel Natu 	[IO_RTC + 1] = vrtc_data_handler,
62762fd208STycho Nightingale };
63762fd208STycho Nightingale 
64d17b5104SNeel Natu #ifdef KTR
65d17b5104SNeel Natu static const char *
66d17b5104SNeel Natu inout_instruction(struct vm_exit *vmexit)
67d17b5104SNeel Natu {
68d17b5104SNeel Natu 	int index;
69d17b5104SNeel Natu 
70d17b5104SNeel Natu 	static const char *iodesc[] = {
71d17b5104SNeel Natu 		"outb", "outw", "outl",
72d17b5104SNeel Natu 		"inb", "inw", "inl",
7307820b4bSNeel Natu 		"outsb", "outsw", "outsd",
74d17b5104SNeel Natu 		"insb", "insw", "insd",
75d17b5104SNeel Natu 	};
76d17b5104SNeel Natu 
77d17b5104SNeel Natu 	switch (vmexit->u.inout.bytes) {
78d17b5104SNeel Natu 	case 1:
79d17b5104SNeel Natu 		index = 0;
80d17b5104SNeel Natu 		break;
81d17b5104SNeel Natu 	case 2:
82d17b5104SNeel Natu 		index = 1;
83d17b5104SNeel Natu 		break;
84d17b5104SNeel Natu 	default:
85d17b5104SNeel Natu 		index = 2;
86d17b5104SNeel Natu 		break;
87d17b5104SNeel Natu 	}
88d17b5104SNeel Natu 
89d17b5104SNeel Natu 	if (vmexit->u.inout.in)
90d17b5104SNeel Natu 		index += 3;
91d17b5104SNeel Natu 
92d17b5104SNeel Natu 	if (vmexit->u.inout.string)
93d17b5104SNeel Natu 		index += 6;
94d17b5104SNeel Natu 
95d17b5104SNeel Natu 	KASSERT(index < nitems(iodesc), ("%s: invalid index %d",
96d17b5104SNeel Natu 	    __func__, index));
97d17b5104SNeel Natu 
98d17b5104SNeel Natu 	return (iodesc[index]);
99d17b5104SNeel Natu }
100d17b5104SNeel Natu #endif	/* KTR */
101d17b5104SNeel Natu 
102d17b5104SNeel Natu static int
103d17b5104SNeel Natu emulate_inout_port(struct vm *vm, int vcpuid, struct vm_exit *vmexit,
104d17b5104SNeel Natu     bool *retu)
105762fd208STycho Nightingale {
106762fd208STycho Nightingale 	ioport_handler_func_t handler;
107d6aa08c3STycho Nightingale 	uint32_t mask, val;
108d6aa08c3STycho Nightingale 	int error;
109762fd208STycho Nightingale 
11031b117beSNeel Natu 	/*
11131b117beSNeel Natu 	 * If there is no handler for the I/O port then punt to userspace.
11231b117beSNeel Natu 	 */
11331b117beSNeel Natu 	if (vmexit->u.inout.port >= MAX_IOPORTS ||
11431b117beSNeel Natu 	    (handler = ioport_handler[vmexit->u.inout.port]) == NULL) {
115d17b5104SNeel Natu 		*retu = true;
11631b117beSNeel Natu 		return (0);
11731b117beSNeel Natu 	}
118762fd208STycho Nightingale 
119d17b5104SNeel Natu 	mask = vie_size2mask(vmexit->u.inout.bytes);
12082c2c890STycho Nightingale 
12182c2c890STycho Nightingale 	if (!vmexit->u.inout.in) {
122d6aa08c3STycho Nightingale 		val = vmexit->u.inout.eax & mask;
123d6aa08c3STycho Nightingale 	}
124d6aa08c3STycho Nightingale 
125d6aa08c3STycho Nightingale 	error = (*handler)(vm, vcpuid, vmexit->u.inout.in,
126d6aa08c3STycho Nightingale 	    vmexit->u.inout.port, vmexit->u.inout.bytes, &val);
12731b117beSNeel Natu 	if (error) {
12831b117beSNeel Natu 		/*
12931b117beSNeel Natu 		 * The value returned by this function is also the return value
13031b117beSNeel Natu 		 * of vm_run(). This needs to be a positive number otherwise it
13131b117beSNeel Natu 		 * can be interpreted as a "pseudo-error" like ERESTART.
13231b117beSNeel Natu 		 *
13331b117beSNeel Natu 		 * Enforce this by mapping all errors to EIO.
13431b117beSNeel Natu 		 */
13531b117beSNeel Natu 		return (EIO);
13631b117beSNeel Natu 	}
137d6aa08c3STycho Nightingale 
138d17b5104SNeel Natu 	if (vmexit->u.inout.in) {
139d6aa08c3STycho Nightingale 		vmexit->u.inout.eax &= ~mask;
140d6aa08c3STycho Nightingale 		vmexit->u.inout.eax |= val & mask;
14131b117beSNeel Natu 		error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX,
14231b117beSNeel Natu 		    vmexit->u.inout.eax);
14331b117beSNeel Natu 		KASSERT(error == 0, ("emulate_ioport: error %d setting guest "
14431b117beSNeel Natu 		    "rax register", error));
145d6aa08c3STycho Nightingale 	}
14631b117beSNeel Natu 	*retu = false;
14731b117beSNeel Natu 	return (0);
148d17b5104SNeel Natu }
149d17b5104SNeel Natu 
150d17b5104SNeel Natu static int
151d17b5104SNeel Natu emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
152d17b5104SNeel Natu {
153d17b5104SNeel Natu 	*retu = true;
154d17b5104SNeel Natu 	return (0);	/* Return to userspace to finish emulation */
155d17b5104SNeel Natu }
156d17b5104SNeel Natu 
157d17b5104SNeel Natu int
158d17b5104SNeel Natu vm_handle_inout(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
159d17b5104SNeel Natu {
160*5a17f489SRobert Wing 	int bytes __diagused, error;
161d17b5104SNeel Natu 
162d17b5104SNeel Natu 	bytes = vmexit->u.inout.bytes;
163d17b5104SNeel Natu 	KASSERT(bytes == 1 || bytes == 2 || bytes == 4,
164d17b5104SNeel Natu 	    ("vm_handle_inout: invalid operand size %d", bytes));
165d17b5104SNeel Natu 
166d17b5104SNeel Natu 	if (vmexit->u.inout.string)
167d17b5104SNeel Natu 		error = emulate_inout_str(vm, vcpuid, vmexit, retu);
168d17b5104SNeel Natu 	else
169d17b5104SNeel Natu 		error = emulate_inout_port(vm, vcpuid, vmexit, retu);
170d17b5104SNeel Natu 
171d17b5104SNeel Natu 	VCPU_CTR4(vm, vcpuid, "%s%s 0x%04x: %s",
172d17b5104SNeel Natu 	    vmexit->u.inout.rep ? "rep " : "",
173d17b5104SNeel Natu 	    inout_instruction(vmexit),
174d17b5104SNeel Natu 	    vmexit->u.inout.port,
175d17b5104SNeel Natu 	    error ? "error" : (*retu ? "userspace" : "handled"));
176d6aa08c3STycho Nightingale 
177d6aa08c3STycho Nightingale 	return (error);
178762fd208STycho Nightingale }
179