xref: /freebsd/sys/amd64/vmm/vmm_ioport.c (revision 94693ec7c85363f9b0098247489cea3efdb183f9)
1762fd208STycho Nightingale /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
30762fd208STycho Nightingale #include <sys/systm.h>
31762fd208STycho Nightingale 
32762fd208STycho Nightingale #include <machine/vmm.h>
33e813a873SNeel Natu #include <machine/vmm_instruction_emul.h>
34762fd208STycho Nightingale 
353ccb0233SMark Johnston #include <dev/vmm/vmm_ktr.h>
363ccb0233SMark Johnston 
37762fd208STycho Nightingale #include "vatpic.h"
38e883c9bbSTycho Nightingale #include "vatpit.h"
39160ef77aSNeel Natu #include "vpmtmr.h"
400dafa5cdSNeel Natu #include "vrtc.h"
41762fd208STycho Nightingale #include "vmm_ioport.h"
42762fd208STycho Nightingale 
43762fd208STycho Nightingale #define	MAX_IOPORTS		1280
44762fd208STycho Nightingale 
45762fd208STycho Nightingale ioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
46e883c9bbSTycho Nightingale 	[TIMER_MODE] = vatpit_handler,
47e883c9bbSTycho Nightingale 	[TIMER_CNTR0] = vatpit_handler,
48e883c9bbSTycho Nightingale 	[TIMER_CNTR1] = vatpit_handler,
49e883c9bbSTycho Nightingale 	[TIMER_CNTR2] = vatpit_handler,
5079d6ca33STycho Nightingale 	[NMISC_PORT] = vatpit_nmisc_handler,
51762fd208STycho Nightingale 	[IO_ICU1] = vatpic_master_handler,
52762fd208STycho Nightingale 	[IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler,
53762fd208STycho Nightingale 	[IO_ICU2] = vatpic_slave_handler,
54762fd208STycho Nightingale 	[IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler,
55762fd208STycho Nightingale 	[IO_ELCR1] = vatpic_elc_handler,
56762fd208STycho Nightingale 	[IO_ELCR2] = vatpic_elc_handler,
57160ef77aSNeel Natu 	[IO_PMTMR] = vpmtmr_handler,
580dafa5cdSNeel Natu 	[IO_RTC] = vrtc_addr_handler,
590dafa5cdSNeel Natu 	[IO_RTC + 1] = vrtc_data_handler,
60762fd208STycho Nightingale };
61762fd208STycho Nightingale 
62d17b5104SNeel Natu #ifdef KTR
63d17b5104SNeel Natu static const char *
inout_instruction(struct vm_exit * vmexit)64d17b5104SNeel Natu inout_instruction(struct vm_exit *vmexit)
65d17b5104SNeel Natu {
66d17b5104SNeel Natu 	int index;
67d17b5104SNeel Natu 
68d17b5104SNeel Natu 	static const char *iodesc[] = {
69d17b5104SNeel Natu 		"outb", "outw", "outl",
70d17b5104SNeel Natu 		"inb", "inw", "inl",
7107820b4bSNeel Natu 		"outsb", "outsw", "outsd",
72d17b5104SNeel Natu 		"insb", "insw", "insd",
73d17b5104SNeel Natu 	};
74d17b5104SNeel Natu 
75d17b5104SNeel Natu 	switch (vmexit->u.inout.bytes) {
76d17b5104SNeel Natu 	case 1:
77d17b5104SNeel Natu 		index = 0;
78d17b5104SNeel Natu 		break;
79d17b5104SNeel Natu 	case 2:
80d17b5104SNeel Natu 		index = 1;
81d17b5104SNeel Natu 		break;
82d17b5104SNeel Natu 	default:
83d17b5104SNeel Natu 		index = 2;
84d17b5104SNeel Natu 		break;
85d17b5104SNeel Natu 	}
86d17b5104SNeel Natu 
87d17b5104SNeel Natu 	if (vmexit->u.inout.in)
88d17b5104SNeel Natu 		index += 3;
89d17b5104SNeel Natu 
90d17b5104SNeel Natu 	if (vmexit->u.inout.string)
91d17b5104SNeel Natu 		index += 6;
92d17b5104SNeel Natu 
93d17b5104SNeel Natu 	KASSERT(index < nitems(iodesc), ("%s: invalid index %d",
94d17b5104SNeel Natu 	    __func__, index));
95d17b5104SNeel Natu 
96d17b5104SNeel Natu 	return (iodesc[index]);
97d17b5104SNeel Natu }
98d17b5104SNeel Natu #endif	/* KTR */
99d17b5104SNeel Natu 
100d17b5104SNeel Natu static int
emulate_inout_port(struct vcpu * vcpu,struct vm_exit * vmexit,bool * retu)1013f0f4b15SJohn Baldwin emulate_inout_port(struct vcpu *vcpu, struct vm_exit *vmexit, bool *retu)
102762fd208STycho Nightingale {
103762fd208STycho Nightingale 	ioport_handler_func_t handler;
104*94693ec7SPierre Pronchery 	uint32_t mask, val = 0;
105d6aa08c3STycho Nightingale 	int error;
106762fd208STycho Nightingale 
10731b117beSNeel Natu 	/*
10831b117beSNeel Natu 	 * If there is no handler for the I/O port then punt to userspace.
10931b117beSNeel Natu 	 */
11031b117beSNeel Natu 	if (vmexit->u.inout.port >= MAX_IOPORTS ||
11131b117beSNeel Natu 	    (handler = ioport_handler[vmexit->u.inout.port]) == NULL) {
112d17b5104SNeel Natu 		*retu = true;
11331b117beSNeel Natu 		return (0);
11431b117beSNeel Natu 	}
115762fd208STycho Nightingale 
116d17b5104SNeel Natu 	mask = vie_size2mask(vmexit->u.inout.bytes);
11782c2c890STycho Nightingale 
11882c2c890STycho Nightingale 	if (!vmexit->u.inout.in) {
119d6aa08c3STycho Nightingale 		val = vmexit->u.inout.eax & mask;
120d6aa08c3STycho Nightingale 	}
121d6aa08c3STycho Nightingale 
1223f0f4b15SJohn Baldwin 	error = (*handler)(vcpu_vm(vcpu), vmexit->u.inout.in,
1233f0f4b15SJohn Baldwin 	    vmexit->u.inout.port, vmexit->u.inout.bytes, &val);
12431b117beSNeel Natu 	if (error) {
12531b117beSNeel Natu 		/*
12631b117beSNeel Natu 		 * The value returned by this function is also the return value
12731b117beSNeel Natu 		 * of vm_run(). This needs to be a positive number otherwise it
12831b117beSNeel Natu 		 * can be interpreted as a "pseudo-error" like ERESTART.
12931b117beSNeel Natu 		 *
13031b117beSNeel Natu 		 * Enforce this by mapping all errors to EIO.
13131b117beSNeel Natu 		 */
13231b117beSNeel Natu 		return (EIO);
13331b117beSNeel Natu 	}
134d6aa08c3STycho Nightingale 
135d17b5104SNeel Natu 	if (vmexit->u.inout.in) {
136d6aa08c3STycho Nightingale 		vmexit->u.inout.eax &= ~mask;
137d6aa08c3STycho Nightingale 		vmexit->u.inout.eax |= val & mask;
1383f0f4b15SJohn Baldwin 		error = vm_set_register(vcpu, VM_REG_GUEST_RAX,
13931b117beSNeel Natu 		    vmexit->u.inout.eax);
14031b117beSNeel Natu 		KASSERT(error == 0, ("emulate_ioport: error %d setting guest "
14131b117beSNeel Natu 		    "rax register", error));
142d6aa08c3STycho Nightingale 	}
14331b117beSNeel Natu 	*retu = false;
14431b117beSNeel Natu 	return (0);
145d17b5104SNeel Natu }
146d17b5104SNeel Natu 
147d17b5104SNeel Natu static int
emulate_inout_str(struct vcpu * vcpu,struct vm_exit * vmexit,bool * retu)1483f0f4b15SJohn Baldwin emulate_inout_str(struct vcpu *vcpu, struct vm_exit *vmexit, bool *retu)
149d17b5104SNeel Natu {
150d17b5104SNeel Natu 	*retu = true;
151d17b5104SNeel Natu 	return (0);	/* Return to userspace to finish emulation */
152d17b5104SNeel Natu }
153d17b5104SNeel Natu 
154d17b5104SNeel Natu int
vm_handle_inout(struct vcpu * vcpu,struct vm_exit * vmexit,bool * retu)1553f0f4b15SJohn Baldwin vm_handle_inout(struct vcpu *vcpu, struct vm_exit *vmexit, bool *retu)
156d17b5104SNeel Natu {
1575a17f489SRobert Wing 	int bytes __diagused, error;
158d17b5104SNeel Natu 
159d17b5104SNeel Natu 	bytes = vmexit->u.inout.bytes;
160d17b5104SNeel Natu 	KASSERT(bytes == 1 || bytes == 2 || bytes == 4,
161d17b5104SNeel Natu 	    ("vm_handle_inout: invalid operand size %d", bytes));
162d17b5104SNeel Natu 
163d17b5104SNeel Natu 	if (vmexit->u.inout.string)
1643f0f4b15SJohn Baldwin 		error = emulate_inout_str(vcpu, vmexit, retu);
165d17b5104SNeel Natu 	else
1663f0f4b15SJohn Baldwin 		error = emulate_inout_port(vcpu, vmexit, retu);
167d17b5104SNeel Natu 
1683f0f4b15SJohn Baldwin 	VCPU_CTR4(vcpu_vm(vcpu), vcpu_vcpuid(vcpu), "%s%s 0x%04x: %s",
169d17b5104SNeel Natu 	    vmexit->u.inout.rep ? "rep " : "",
170d17b5104SNeel Natu 	    inout_instruction(vmexit),
171d17b5104SNeel Natu 	    vmexit->u.inout.port,
172d17b5104SNeel Natu 	    error ? "error" : (*retu ? "userspace" : "handled"));
173d6aa08c3STycho Nightingale 
174d6aa08c3STycho Nightingale 	return (error);
175762fd208STycho Nightingale }
176