xref: /illumos-gate/usr/src/cmd/bhyve/inout.c (revision 5d9d9091f564c198a760790b0bfa72c44e17912b)
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$
29  */
30 /*
31  * This file and its contents are supplied under the terms of the
32  * Common Development and Distribution License ("CDDL"), version 1.0.
33  * You may only use this file in accordance with the terms of version
34  * 1.0 of the CDDL.
35  *
36  * A full copy of the text of the CDDL should have accompanied this
37  * source.  A copy of the CDDL is also available via the Internet at
38  * http://www.illumos.org/license/CDDL.
39  *
40  * Copyright 2020 Oxide Computer Company
41  */
42 
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 #include <sys/param.h>
47 #include <sys/linker_set.h>
48 #include <sys/_iovec.h>
49 #include <sys/mman.h>
50 
51 #include <x86/psl.h>
52 #include <x86/segments.h>
53 
54 #include <machine/vmm.h>
55 #include <vmmapi.h>
56 
57 #include <stdio.h>
58 #include <string.h>
59 #include <assert.h>
60 
61 #include "bhyverun.h"
62 #include "config.h"
63 #include "inout.h"
64 
65 SET_DECLARE(inout_port_set, struct inout_port);
66 
67 #define	MAX_IOPORTS	(1 << 16)
68 
69 #define	VERIFY_IOPORT(port, size) \
70 	assert((port) >= 0 && (size) > 0 && ((port) + (size)) <= MAX_IOPORTS)
71 
72 struct inout_handler {
73 	const char	*name;
74 	int		flags;
75 	inout_func_t	handler;
76 	void		*arg;
77 };
78 
79 static struct inout_handler inout_handlers[MAX_IOPORTS];
80 
81 static int
82 default_inout(struct vmctx *ctx __unused, int in,
83     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
84 {
85 	if (in) {
86 		switch (bytes) {
87 		case 4:
88 			*eax = 0xffffffff;
89 			break;
90 		case 2:
91 			*eax = 0xffff;
92 			break;
93 		case 1:
94 			*eax = 0xff;
95 			break;
96 		}
97 	}
98 
99 	return (0);
100 }
101 
102 static void
103 register_default_iohandler(int start, int size)
104 {
105 	struct inout_port iop;
106 
107 	VERIFY_IOPORT(start, size);
108 
109 	bzero(&iop, sizeof(iop));
110 	iop.name = "default";
111 	iop.port = start;
112 	iop.size = size;
113 	iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT;
114 	iop.handler = default_inout;
115 
116 	register_inout(&iop);
117 }
118 
119 int
120 emulate_inout(struct vmctx *ctx, int vcpu, struct vm_inout *inout)
121 {
122 	struct inout_handler handler;
123 	inout_func_t hfunc;
124 	void *harg;
125 	int error;
126 	uint8_t bytes;
127 	bool in;
128 
129 	bytes = inout->bytes;
130 	in = (inout->flags & INOUT_IN) != 0;
131 
132 	assert(bytes == 1 || bytes == 2 || bytes == 4);
133 
134 	handler = inout_handlers[inout->port];
135 	hfunc = handler.handler;
136 	harg = handler.arg;
137 
138 	if (hfunc == default_inout &&
139 	    get_config_bool_default("x86.strictio", false))
140 		return (-1);
141 
142 	if (in) {
143 		if (!(handler.flags & IOPORT_F_IN))
144 			return (-1);
145 	} else {
146 		if (!(handler.flags & IOPORT_F_OUT))
147 			return (-1);
148 	}
149 
150 	error = hfunc(ctx, in, inout->port, bytes, &inout->eax, harg);
151 	return (error);
152 }
153 
154 void
155 init_inout(void)
156 {
157 	struct inout_port **iopp, *iop;
158 
159 	/*
160 	 * Set up the default handler for all ports
161 	 */
162 	register_default_iohandler(0, MAX_IOPORTS);
163 
164 	/*
165 	 * Overwrite with specified handlers
166 	 */
167 	SET_FOREACH(iopp, inout_port_set) {
168 		iop = *iopp;
169 		assert(iop->port < MAX_IOPORTS);
170 		inout_handlers[iop->port].name = iop->name;
171 		inout_handlers[iop->port].flags = iop->flags;
172 		inout_handlers[iop->port].handler = iop->handler;
173 		inout_handlers[iop->port].arg = NULL;
174 	}
175 }
176 
177 int
178 register_inout(struct inout_port *iop)
179 {
180 	int i;
181 
182 	VERIFY_IOPORT(iop->port, iop->size);
183 
184 	/*
185 	 * Verify that the new registration is not overwriting an already
186 	 * allocated i/o range.
187 	 */
188 	if ((iop->flags & IOPORT_F_DEFAULT) == 0) {
189 		for (i = iop->port; i < iop->port + iop->size; i++) {
190 			if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0)
191 				return (-1);
192 		}
193 	}
194 
195 	for (i = iop->port; i < iop->port + iop->size; i++) {
196 		inout_handlers[i].name = iop->name;
197 		inout_handlers[i].flags = iop->flags;
198 		inout_handlers[i].handler = iop->handler;
199 		inout_handlers[i].arg = iop->arg;
200 	}
201 
202 	return (0);
203 }
204 
205 int
206 unregister_inout(struct inout_port *iop)
207 {
208 
209 	VERIFY_IOPORT(iop->port, iop->size);
210 	assert(inout_handlers[iop->port].name == iop->name);
211 
212 	register_default_iohandler(iop->port, iop->size);
213 
214 	return (0);
215 }
216