1221828Sgrehan/*-
2221828Sgrehan * Copyright (c) 2011 NetApp, Inc.
3221828Sgrehan * All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24221828Sgrehan * SUCH DAMAGE.
25221828Sgrehan *
26221828Sgrehan * $FreeBSD$
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD$");
31221828Sgrehan
32221828Sgrehan#include <sys/param.h>
33221828Sgrehan#include <sys/linker_set.h>
34268976Sjhb#include <sys/_iovec.h>
35268976Sjhb#include <sys/mman.h>
36221828Sgrehan
37268976Sjhb#include <x86/psl.h>
38268976Sjhb#include <x86/segments.h>
39268976Sjhb
40268976Sjhb#include <machine/vmm.h>
41268976Sjhb#include <machine/vmm_instruction_emul.h>
42268976Sjhb#include <vmmapi.h>
43268976Sjhb
44221828Sgrehan#include <stdio.h>
45249321Sneel#include <string.h>
46221828Sgrehan#include <assert.h>
47221828Sgrehan
48268976Sjhb#include "bhyverun.h"
49221828Sgrehan#include "inout.h"
50221828Sgrehan
51221828SgrehanSET_DECLARE(inout_port_set, struct inout_port);
52221828Sgrehan
53221828Sgrehan#define	MAX_IOPORTS	(1 << 16)
54221828Sgrehan
55249321Sneel#define	VERIFY_IOPORT(port, size) \
56249321Sneel	assert((port) >= 0 && (size) > 0 && ((port) + (size)) <= MAX_IOPORTS)
57249321Sneel
58221828Sgrehanstatic struct {
59221828Sgrehan	const char	*name;
60221828Sgrehan	int		flags;
61221828Sgrehan	inout_func_t	handler;
62221828Sgrehan	void		*arg;
63221828Sgrehan} inout_handlers[MAX_IOPORTS];
64221828Sgrehan
65222105Sgrehanstatic int
66222105Sgrehandefault_inout(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
67222105Sgrehan              uint32_t *eax, void *arg)
68222105Sgrehan{
69222105Sgrehan        if (in) {
70222105Sgrehan                switch (bytes) {
71222105Sgrehan                case 4:
72222105Sgrehan                        *eax = 0xffffffff;
73222105Sgrehan                        break;
74222105Sgrehan                case 2:
75222105Sgrehan                        *eax = 0xffff;
76222105Sgrehan                        break;
77222105Sgrehan                case 1:
78222105Sgrehan                        *eax = 0xff;
79222105Sgrehan                        break;
80222105Sgrehan                }
81222105Sgrehan        }
82222105Sgrehan
83222105Sgrehan        return (0);
84222105Sgrehan}
85222105Sgrehan
86249321Sneelstatic void
87249321Sneelregister_default_iohandler(int start, int size)
88249321Sneel{
89249321Sneel	struct inout_port iop;
90249321Sneel
91249321Sneel	VERIFY_IOPORT(start, size);
92249321Sneel
93249321Sneel	bzero(&iop, sizeof(iop));
94249321Sneel	iop.name = "default";
95249321Sneel	iop.port = start;
96249321Sneel	iop.size = size;
97257396Sneel	iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT;
98249321Sneel	iop.handler = default_inout;
99249321Sneel
100249321Sneel	register_inout(&iop);
101249321Sneel}
102249321Sneel
103221828Sgrehanint
104268976Sjhbemulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
105221828Sgrehan{
106268976Sjhb	int addrsize, bytes, flags, in, port, prot, rep;
107268976Sjhb	uint32_t val;
108221828Sgrehan	inout_func_t handler;
109221828Sgrehan	void *arg;
110268976Sjhb	int error, retval;
111268976Sjhb	enum vm_reg_name idxreg;
112268976Sjhb	uint64_t gla, index, iterations, count;
113268976Sjhb	struct vm_inout_str *vis;
114268976Sjhb	struct iovec iov[2];
115221828Sgrehan
116268976Sjhb	bytes = vmexit->u.inout.bytes;
117268976Sjhb	in = vmexit->u.inout.in;
118268976Sjhb	port = vmexit->u.inout.port;
119268976Sjhb
120221828Sgrehan	assert(port < MAX_IOPORTS);
121268976Sjhb	assert(bytes == 1 || bytes == 2 || bytes == 4);
122221828Sgrehan
123222105Sgrehan	handler = inout_handlers[port].handler;
124222105Sgrehan
125222105Sgrehan	if (strict && handler == default_inout)
126221828Sgrehan		return (-1);
127221828Sgrehan
128268976Sjhb	flags = inout_handlers[port].flags;
129268976Sjhb	arg = inout_handlers[port].arg;
130268953Sjhb
131268976Sjhb	if (in) {
132268976Sjhb		if (!(flags & IOPORT_F_IN))
133268976Sjhb			return (-1);
134268976Sjhb	} else {
135268976Sjhb		if (!(flags & IOPORT_F_OUT))
136268976Sjhb			return (-1);
137243349Sneel	}
138243349Sneel
139268976Sjhb	retval = 0;
140268976Sjhb	if (vmexit->u.inout.string) {
141268976Sjhb		vis = &vmexit->u.inout_str;
142268976Sjhb		rep = vis->inout.rep;
143268976Sjhb		addrsize = vis->addrsize;
144268976Sjhb		prot = in ? PROT_WRITE : PROT_READ;
145268976Sjhb		assert(addrsize == 2 || addrsize == 4 || addrsize == 8);
146221828Sgrehan
147268976Sjhb		/* Index register */
148268976Sjhb		idxreg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI;
149268976Sjhb		index = vis->index & vie_size2mask(addrsize);
150268891Sjhb
151268976Sjhb		/* Count register */
152268976Sjhb		count = vis->count & vie_size2mask(addrsize);
153268976Sjhb
154268976Sjhb		/* Limit number of back-to-back in/out emulations to 16 */
155268976Sjhb		iterations = MIN(count, 16);
156268976Sjhb		while (iterations > 0) {
157270159Sgrehan			assert(retval == 0);
158268976Sjhb			if (vie_calculate_gla(vis->paging.cpu_mode,
159268976Sjhb			    vis->seg_name, &vis->seg_desc, index, bytes,
160268976Sjhb			    addrsize, prot, &gla)) {
161270159Sgrehan				vm_inject_gp(ctx, vcpu);
162268976Sjhb				break;
163268976Sjhb			}
164268976Sjhb
165270159Sgrehan			error = vm_copy_setup(ctx, vcpu, &vis->paging, gla,
166270159Sgrehan			    bytes, prot, iov, nitems(iov));
167270159Sgrehan			if (error == -1) {
168270159Sgrehan				retval = -1;  /* Unrecoverable error */
169268976Sjhb				break;
170270159Sgrehan			} else if (error == 1) {
171270159Sgrehan				retval = 0;  /* Resume guest to handle fault */
172270159Sgrehan				break;
173268976Sjhb			}
174268976Sjhb
175268976Sjhb			if (vie_alignment_check(vis->paging.cpl, bytes,
176268976Sjhb			    vis->cr0, vis->rflags, gla)) {
177270159Sgrehan				vm_inject_ac(ctx, vcpu, 0);
178270159Sgrehan				break;
179268976Sjhb			}
180268976Sjhb
181268976Sjhb			val = 0;
182268976Sjhb			if (!in)
183268976Sjhb				vm_copyin(ctx, vcpu, iov, &val, bytes);
184268976Sjhb
185268976Sjhb			retval = handler(ctx, vcpu, in, port, bytes, &val, arg);
186268976Sjhb			if (retval != 0)
187268976Sjhb				break;
188268976Sjhb
189268976Sjhb			if (in)
190268976Sjhb				vm_copyout(ctx, vcpu, &val, iov, bytes);
191268976Sjhb
192268976Sjhb			/* Update index */
193268976Sjhb			if (vis->rflags & PSL_D)
194268976Sjhb				index -= bytes;
195268976Sjhb			else
196268976Sjhb				index += bytes;
197268976Sjhb
198268976Sjhb			count--;
199268976Sjhb			iterations--;
200268976Sjhb		}
201268976Sjhb
202268976Sjhb		/* Update index register */
203268976Sjhb		error = vie_update_register(ctx, vcpu, idxreg, index, addrsize);
204268976Sjhb		assert(error == 0);
205268976Sjhb
206268976Sjhb		/*
207268976Sjhb		 * Update count register only if the instruction had a repeat
208268976Sjhb		 * prefix.
209268976Sjhb		 */
210268976Sjhb		if (rep) {
211268976Sjhb			error = vie_update_register(ctx, vcpu, VM_REG_GUEST_RCX,
212268976Sjhb			    count, addrsize);
213268976Sjhb			assert(error == 0);
214268976Sjhb		}
215268976Sjhb
216268976Sjhb		/* Restart the instruction if more iterations remain */
217270159Sgrehan		if (retval == 0 && count != 0)
218270159Sgrehan			vmexit->inst_length = 0;
219268976Sjhb	} else {
220268976Sjhb		if (!in) {
221268976Sjhb			val = vmexit->u.inout.eax & vie_size2mask(bytes);
222268976Sjhb		}
223268976Sjhb		retval = handler(ctx, vcpu, in, port, bytes, &val, arg);
224268976Sjhb		if (retval == 0 && in) {
225268976Sjhb			vmexit->u.inout.eax &= ~vie_size2mask(bytes);
226268976Sjhb			vmexit->u.inout.eax |= val & vie_size2mask(bytes);
227268976Sjhb		}
228268891Sjhb	}
229268976Sjhb	return (retval);
230221828Sgrehan}
231221828Sgrehan
232221828Sgrehanvoid
233221828Sgrehaninit_inout(void)
234221828Sgrehan{
235221828Sgrehan	struct inout_port **iopp, *iop;
236221828Sgrehan
237222105Sgrehan	/*
238222105Sgrehan	 * Set up the default handler for all ports
239222105Sgrehan	 */
240249321Sneel	register_default_iohandler(0, MAX_IOPORTS);
241222105Sgrehan
242222105Sgrehan	/*
243222105Sgrehan	 * Overwrite with specified handlers
244222105Sgrehan	 */
245221828Sgrehan	SET_FOREACH(iopp, inout_port_set) {
246221828Sgrehan		iop = *iopp;
247221828Sgrehan		assert(iop->port < MAX_IOPORTS);
248221828Sgrehan		inout_handlers[iop->port].name = iop->name;
249221828Sgrehan		inout_handlers[iop->port].flags = iop->flags;
250221828Sgrehan		inout_handlers[iop->port].handler = iop->handler;
251221828Sgrehan		inout_handlers[iop->port].arg = NULL;
252221828Sgrehan	}
253221828Sgrehan}
254221828Sgrehan
255221828Sgrehanint
256221828Sgrehanregister_inout(struct inout_port *iop)
257221828Sgrehan{
258249321Sneel	int i;
259221828Sgrehan
260249321Sneel	VERIFY_IOPORT(iop->port, iop->size);
261257396Sneel
262257396Sneel	/*
263257396Sneel	 * Verify that the new registration is not overwriting an already
264257396Sneel	 * allocated i/o range.
265257396Sneel	 */
266257396Sneel	if ((iop->flags & IOPORT_F_DEFAULT) == 0) {
267257396Sneel		for (i = iop->port; i < iop->port + iop->size; i++) {
268257396Sneel			if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0)
269257396Sneel				return (-1);
270257396Sneel		}
271257396Sneel	}
272257396Sneel
273249321Sneel	for (i = iop->port; i < iop->port + iop->size; i++) {
274249321Sneel		inout_handlers[i].name = iop->name;
275249321Sneel		inout_handlers[i].flags = iop->flags;
276249321Sneel		inout_handlers[i].handler = iop->handler;
277249321Sneel		inout_handlers[i].arg = iop->arg;
278249321Sneel	}
279249321Sneel
280221828Sgrehan	return (0);
281221828Sgrehan}
282249321Sneel
283249321Sneelint
284249321Sneelunregister_inout(struct inout_port *iop)
285249321Sneel{
286249321Sneel
287249321Sneel	VERIFY_IOPORT(iop->port, iop->size);
288249321Sneel	assert(inout_handlers[iop->port].name == iop->name);
289249321Sneel
290249321Sneel	register_default_iohandler(iop->port, iop->size);
291249321Sneel
292249321Sneel	return (0);
293249321Sneel}
294