inout.c revision 330449
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2011 NetApp, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: stable/11/usr.sbin/bhyve/inout.c 330449 2018-03-05 07:26:05Z eadler $
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/11/usr.sbin/bhyve/inout.c 330449 2018-03-05 07:26:05Z eadler $");
33
34#include <sys/param.h>
35#include <sys/linker_set.h>
36#include <sys/_iovec.h>
37#include <sys/mman.h>
38
39#include <x86/psl.h>
40#include <x86/segments.h>
41
42#include <machine/vmm.h>
43#include <machine/vmm_instruction_emul.h>
44#include <vmmapi.h>
45
46#include <stdio.h>
47#include <string.h>
48#include <assert.h>
49
50#include "bhyverun.h"
51#include "inout.h"
52
53SET_DECLARE(inout_port_set, struct inout_port);
54
55#define	MAX_IOPORTS	(1 << 16)
56
57#define	VERIFY_IOPORT(port, size) \
58	assert((port) >= 0 && (size) > 0 && ((port) + (size)) <= MAX_IOPORTS)
59
60static struct {
61	const char	*name;
62	int		flags;
63	inout_func_t	handler;
64	void		*arg;
65} inout_handlers[MAX_IOPORTS];
66
67static int
68default_inout(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
69              uint32_t *eax, void *arg)
70{
71        if (in) {
72                switch (bytes) {
73                case 4:
74                        *eax = 0xffffffff;
75                        break;
76                case 2:
77                        *eax = 0xffff;
78                        break;
79                case 1:
80                        *eax = 0xff;
81                        break;
82                }
83        }
84
85        return (0);
86}
87
88static void
89register_default_iohandler(int start, int size)
90{
91	struct inout_port iop;
92
93	VERIFY_IOPORT(start, size);
94
95	bzero(&iop, sizeof(iop));
96	iop.name = "default";
97	iop.port = start;
98	iop.size = size;
99	iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT;
100	iop.handler = default_inout;
101
102	register_inout(&iop);
103}
104
105int
106emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
107{
108	int addrsize, bytes, flags, in, port, prot, rep;
109	uint32_t eax, val;
110	inout_func_t handler;
111	void *arg;
112	int error, fault, retval;
113	enum vm_reg_name idxreg;
114	uint64_t gla, index, iterations, count;
115	struct vm_inout_str *vis;
116	struct iovec iov[2];
117
118	bytes = vmexit->u.inout.bytes;
119	in = vmexit->u.inout.in;
120	port = vmexit->u.inout.port;
121
122	assert(port < MAX_IOPORTS);
123	assert(bytes == 1 || bytes == 2 || bytes == 4);
124
125	handler = inout_handlers[port].handler;
126
127	if (strict && handler == default_inout)
128		return (-1);
129
130	flags = inout_handlers[port].flags;
131	arg = inout_handlers[port].arg;
132
133	if (in) {
134		if (!(flags & IOPORT_F_IN))
135			return (-1);
136	} else {
137		if (!(flags & IOPORT_F_OUT))
138			return (-1);
139	}
140
141	retval = 0;
142	if (vmexit->u.inout.string) {
143		vis = &vmexit->u.inout_str;
144		rep = vis->inout.rep;
145		addrsize = vis->addrsize;
146		prot = in ? PROT_WRITE : PROT_READ;
147		assert(addrsize == 2 || addrsize == 4 || addrsize == 8);
148
149		/* Index register */
150		idxreg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI;
151		index = vis->index & vie_size2mask(addrsize);
152
153		/* Count register */
154		count = vis->count & vie_size2mask(addrsize);
155
156		/* Limit number of back-to-back in/out emulations to 16 */
157		iterations = MIN(count, 16);
158		while (iterations > 0) {
159			assert(retval == 0);
160			if (vie_calculate_gla(vis->paging.cpu_mode,
161			    vis->seg_name, &vis->seg_desc, index, bytes,
162			    addrsize, prot, &gla)) {
163				vm_inject_gp(ctx, vcpu);
164				break;
165			}
166
167			error = vm_copy_setup(ctx, vcpu, &vis->paging, gla,
168			    bytes, prot, iov, nitems(iov), &fault);
169			if (error) {
170				retval = -1;  /* Unrecoverable error */
171				break;
172			} else if (fault) {
173				retval = 0;  /* Resume guest to handle fault */
174				break;
175			}
176
177			if (vie_alignment_check(vis->paging.cpl, bytes,
178			    vis->cr0, vis->rflags, gla)) {
179				vm_inject_ac(ctx, vcpu, 0);
180				break;
181			}
182
183			val = 0;
184			if (!in)
185				vm_copyin(ctx, vcpu, iov, &val, bytes);
186
187			retval = handler(ctx, vcpu, in, port, bytes, &val, arg);
188			if (retval != 0)
189				break;
190
191			if (in)
192				vm_copyout(ctx, vcpu, &val, iov, bytes);
193
194			/* Update index */
195			if (vis->rflags & PSL_D)
196				index -= bytes;
197			else
198				index += bytes;
199
200			count--;
201			iterations--;
202		}
203
204		/* Update index register */
205		error = vie_update_register(ctx, vcpu, idxreg, index, addrsize);
206		assert(error == 0);
207
208		/*
209		 * Update count register only if the instruction had a repeat
210		 * prefix.
211		 */
212		if (rep) {
213			error = vie_update_register(ctx, vcpu, VM_REG_GUEST_RCX,
214			    count, addrsize);
215			assert(error == 0);
216		}
217
218		/* Restart the instruction if more iterations remain */
219		if (retval == 0 && count != 0) {
220			error = vm_restart_instruction(ctx, vcpu);
221			assert(error == 0);
222		}
223	} else {
224		eax = vmexit->u.inout.eax;
225		val = eax & vie_size2mask(bytes);
226		retval = handler(ctx, vcpu, in, port, bytes, &val, arg);
227		if (retval == 0 && in) {
228			eax &= ~vie_size2mask(bytes);
229			eax |= val & vie_size2mask(bytes);
230			error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX,
231			    eax);
232			assert(error == 0);
233		}
234	}
235	return (retval);
236}
237
238void
239init_inout(void)
240{
241	struct inout_port **iopp, *iop;
242
243	/*
244	 * Set up the default handler for all ports
245	 */
246	register_default_iohandler(0, MAX_IOPORTS);
247
248	/*
249	 * Overwrite with specified handlers
250	 */
251	SET_FOREACH(iopp, inout_port_set) {
252		iop = *iopp;
253		assert(iop->port < MAX_IOPORTS);
254		inout_handlers[iop->port].name = iop->name;
255		inout_handlers[iop->port].flags = iop->flags;
256		inout_handlers[iop->port].handler = iop->handler;
257		inout_handlers[iop->port].arg = NULL;
258	}
259}
260
261int
262register_inout(struct inout_port *iop)
263{
264	int i;
265
266	VERIFY_IOPORT(iop->port, iop->size);
267
268	/*
269	 * Verify that the new registration is not overwriting an already
270	 * allocated i/o range.
271	 */
272	if ((iop->flags & IOPORT_F_DEFAULT) == 0) {
273		for (i = iop->port; i < iop->port + iop->size; i++) {
274			if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0)
275				return (-1);
276		}
277	}
278
279	for (i = iop->port; i < iop->port + iop->size; i++) {
280		inout_handlers[i].name = iop->name;
281		inout_handlers[i].flags = iop->flags;
282		inout_handlers[i].handler = iop->handler;
283		inout_handlers[i].arg = iop->arg;
284	}
285
286	return (0);
287}
288
289int
290unregister_inout(struct inout_port *iop)
291{
292
293	VERIFY_IOPORT(iop->port, iop->size);
294	assert(inout_handlers[iop->port].name == iop->name);
295
296	register_default_iohandler(iop->port, iop->size);
297
298	return (0);
299}
300