1263035Stychon/*-
2263035Stychon * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3263035Stychon * All rights reserved.
4263035Stychon *
5263035Stychon * Redistribution and use in source and binary forms, with or without
6263035Stychon * modification, are permitted provided that the following conditions
7263035Stychon * are met:
8263035Stychon * 1. Redistributions of source code must retain the above copyright
9263035Stychon *    notice, this list of conditions and the following disclaimer.
10263035Stychon * 2. Redistributions in binary form must reproduce the above copyright
11263035Stychon *    notice, this list of conditions and the following disclaimer in the
12263035Stychon *    documentation and/or other materials provided with the distribution.
13263035Stychon *
14263035Stychon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15263035Stychon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16263035Stychon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17263035Stychon * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18263035Stychon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19263035Stychon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20263035Stychon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21263035Stychon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22263035Stychon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23263035Stychon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24263035Stychon * SUCH DAMAGE.
25263035Stychon */
26263035Stychon
27263035Stychon#include <sys/cdefs.h>
28263035Stychon__FBSDID("$FreeBSD$");
29263035Stychon
30263035Stychon#include <sys/param.h>
31263035Stychon#include <sys/systm.h>
32263035Stychon
33263035Stychon#include <machine/vmm.h>
34268976Sjhb#include <machine/vmm_instruction_emul.h>
35263035Stychon
36263035Stychon#include "vatpic.h"
37268891Sjhb#include "vatpit.h"
38276429Sneel#include "vpmtmr.h"
39284894Sneel#include "vrtc.h"
40263035Stychon#include "vmm_ioport.h"
41268976Sjhb#include "vmm_ktr.h"
42263035Stychon
43263035Stychon#define	MAX_IOPORTS		1280
44263035Stychon
45263035Stychonioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
46268891Sjhb	[TIMER_MODE] = vatpit_handler,
47268891Sjhb	[TIMER_CNTR0] = vatpit_handler,
48268891Sjhb	[TIMER_CNTR1] = vatpit_handler,
49268891Sjhb	[TIMER_CNTR2] = vatpit_handler,
50268891Sjhb	[NMISC_PORT] = vatpit_nmisc_handler,
51263035Stychon	[IO_ICU1] = vatpic_master_handler,
52263035Stychon	[IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler,
53263035Stychon	[IO_ICU2] = vatpic_slave_handler,
54263035Stychon	[IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler,
55263035Stychon	[IO_ELCR1] = vatpic_elc_handler,
56263035Stychon	[IO_ELCR2] = vatpic_elc_handler,
57276429Sneel	[IO_PMTMR] = vpmtmr_handler,
58284894Sneel	[IO_RTC] = vrtc_addr_handler,
59284894Sneel	[IO_RTC + 1] = vrtc_data_handler,
60263035Stychon};
61263035Stychon
62268976Sjhb#ifdef KTR
63268976Sjhbstatic const char *
64268976Sjhbinout_instruction(struct vm_exit *vmexit)
65263035Stychon{
66268976Sjhb	int index;
67263035Stychon
68268976Sjhb	static const char *iodesc[] = {
69268976Sjhb		"outb", "outw", "outl",
70268976Sjhb		"inb", "inw", "inl",
71284894Sneel		"outsb", "outsw", "outsd",
72268976Sjhb		"insb", "insw", "insd",
73268976Sjhb	};
74263035Stychon
75268953Sjhb	switch (vmexit->u.inout.bytes) {
76268953Sjhb	case 1:
77268976Sjhb		index = 0;
78268953Sjhb		break;
79268953Sjhb	case 2:
80268976Sjhb		index = 1;
81268953Sjhb		break;
82268953Sjhb	default:
83268976Sjhb		index = 2;
84268953Sjhb		break;
85268953Sjhb	}
86268953Sjhb
87268976Sjhb	if (vmexit->u.inout.in)
88268976Sjhb		index += 3;
89268976Sjhb
90268976Sjhb	if (vmexit->u.inout.string)
91268976Sjhb		index += 6;
92268976Sjhb
93268976Sjhb	KASSERT(index < nitems(iodesc), ("%s: invalid index %d",
94268976Sjhb	    __func__, index));
95268976Sjhb
96268976Sjhb	return (iodesc[index]);
97268976Sjhb}
98268976Sjhb#endif	/* KTR */
99268976Sjhb
100268976Sjhbstatic int
101268976Sjhbemulate_inout_port(struct vm *vm, int vcpuid, struct vm_exit *vmexit,
102268976Sjhb    bool *retu)
103268976Sjhb{
104268976Sjhb	ioport_handler_func_t handler;
105268976Sjhb	uint32_t mask, val;
106268976Sjhb	int error;
107268976Sjhb
108273807Sneel	/*
109273807Sneel	 * If there is no handler for the I/O port then punt to userspace.
110273807Sneel	 */
111273807Sneel	if (vmexit->u.inout.port >= MAX_IOPORTS ||
112273807Sneel	    (handler = ioport_handler[vmexit->u.inout.port]) == NULL) {
113273807Sneel		*retu = true;
114273807Sneel		return (0);
115273807Sneel	}
116268976Sjhb
117268976Sjhb	mask = vie_size2mask(vmexit->u.inout.bytes);
118268976Sjhb
119268891Sjhb	if (!vmexit->u.inout.in) {
120268891Sjhb		val = vmexit->u.inout.eax & mask;
121268891Sjhb	}
122268891Sjhb
123268891Sjhb	error = (*handler)(vm, vcpuid, vmexit->u.inout.in,
124268891Sjhb	    vmexit->u.inout.port, vmexit->u.inout.bytes, &val);
125273807Sneel	if (error) {
126273807Sneel		/*
127273807Sneel		 * The value returned by this function is also the return value
128273807Sneel		 * of vm_run(). This needs to be a positive number otherwise it
129273807Sneel		 * can be interpreted as a "pseudo-error" like ERESTART.
130273807Sneel		 *
131273807Sneel		 * Enforce this by mapping all errors to EIO.
132273807Sneel		 */
133273807Sneel		return (EIO);
134273807Sneel	}
135268891Sjhb
136273807Sneel	if (vmexit->u.inout.in) {
137273807Sneel		vmexit->u.inout.eax &= ~mask;
138273807Sneel		vmexit->u.inout.eax |= val & mask;
139273807Sneel		error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX,
140273807Sneel		    vmexit->u.inout.eax);
141273807Sneel		KASSERT(error == 0, ("emulate_ioport: error %d setting guest "
142273807Sneel		    "rax register", error));
143268891Sjhb	}
144273807Sneel	*retu = false;
145273807Sneel	return (0);
146268976Sjhb}
147268891Sjhb
148268976Sjhbstatic int
149268976Sjhbemulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
150268976Sjhb{
151268976Sjhb	*retu = true;
152268976Sjhb	return (0);	/* Return to userspace to finish emulation */
153268976Sjhb}
154268976Sjhb
155268976Sjhbint
156268976Sjhbvm_handle_inout(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
157268976Sjhb{
158268976Sjhb	int bytes, error;
159268976Sjhb
160268976Sjhb	bytes = vmexit->u.inout.bytes;
161268976Sjhb	KASSERT(bytes == 1 || bytes == 2 || bytes == 4,
162268976Sjhb	    ("vm_handle_inout: invalid operand size %d", bytes));
163268976Sjhb
164268976Sjhb	if (vmexit->u.inout.string)
165268976Sjhb		error = emulate_inout_str(vm, vcpuid, vmexit, retu);
166268976Sjhb	else
167268976Sjhb		error = emulate_inout_port(vm, vcpuid, vmexit, retu);
168268976Sjhb
169268976Sjhb	VCPU_CTR4(vm, vcpuid, "%s%s 0x%04x: %s",
170268976Sjhb	    vmexit->u.inout.rep ? "rep " : "",
171268976Sjhb	    inout_instruction(vmexit),
172268976Sjhb	    vmexit->u.inout.port,
173268976Sjhb	    error ? "error" : (*retu ? "userspace" : "handled"));
174268976Sjhb
175268891Sjhb	return (error);
176263035Stychon}
177