xref: /illumos-gate/usr/src/cmd/bhyve/inout.c (revision e0c0d44e)
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 "inout.h"
63 
64 SET_DECLARE(inout_port_set, struct inout_port);
65 
66 #define	MAX_IOPORTS	(1 << 16)
67 
68 #define	VERIFY_IOPORT(port, size) \
69 	assert((port) >= 0 && (size) > 0 && ((port) + (size)) <= MAX_IOPORTS)
70 
71 struct inout_handler {
72 	const char	*name;
73 	int		flags;
74 	inout_func_t	handler;
75 	void		*arg;
76 };
77 
78 static struct inout_handler inout_handlers[MAX_IOPORTS];
79 
80 static int
default_inout(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)81 default_inout(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
82               uint32_t *eax, void *arg)
83 {
84 	if (in) {
85 		switch (bytes) {
86 		case 4:
87 			*eax = 0xffffffff;
88 			break;
89 		case 2:
90 			*eax = 0xffff;
91 			break;
92 		case 1:
93 			*eax = 0xff;
94 			break;
95 		}
96 	}
97 
98 	return (0);
99 }
100 
101 static void
register_default_iohandler(int start,int size)102 register_default_iohandler(int start, int size)
103 {
104 	struct inout_port iop;
105 
106 	VERIFY_IOPORT(start, size);
107 
108 	bzero(&iop, sizeof(iop));
109 	iop.name = "default";
110 	iop.port = start;
111 	iop.size = size;
112 	iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT;
113 	iop.handler = default_inout;
114 
115 	register_inout(&iop);
116 }
117 
118 int
emulate_inout(struct vmctx * ctx,int vcpu,struct vm_inout * inout,bool strict)119 emulate_inout(struct vmctx *ctx, int vcpu, struct vm_inout *inout, bool strict)
120 {
121 	struct inout_handler handler;
122 	inout_func_t hfunc;
123 	void *harg;
124 	int error;
125 	uint8_t bytes;
126 	bool in;
127 
128 	bytes = inout->bytes;
129 	in = (inout->flags & INOUT_IN) != 0;
130 
131 	assert(bytes == 1 || bytes == 2 || bytes == 4);
132 
133 	handler = inout_handlers[inout->port];
134 	hfunc = handler.handler;
135 	harg = handler.arg;
136 
137 	if (strict && hfunc == default_inout)
138 		return (-1);
139 
140 	if (in) {
141 		if (!(handler.flags & IOPORT_F_IN))
142 			return (-1);
143 	} else {
144 		if (!(handler.flags & IOPORT_F_OUT))
145 			return (-1);
146 	}
147 
148 	error = hfunc(ctx, vcpu, in, inout->port, bytes, &inout->eax, harg);
149 	return (error);
150 }
151 
152 void
init_inout(void)153 init_inout(void)
154 {
155 	struct inout_port **iopp, *iop;
156 
157 	/*
158 	 * Set up the default handler for all ports
159 	 */
160 	register_default_iohandler(0, MAX_IOPORTS);
161 
162 	/*
163 	 * Overwrite with specified handlers
164 	 */
165 	SET_FOREACH(iopp, inout_port_set) {
166 		iop = *iopp;
167 		assert(iop->port < MAX_IOPORTS);
168 		inout_handlers[iop->port].name = iop->name;
169 		inout_handlers[iop->port].flags = iop->flags;
170 		inout_handlers[iop->port].handler = iop->handler;
171 		inout_handlers[iop->port].arg = NULL;
172 	}
173 }
174 
175 int
register_inout(struct inout_port * iop)176 register_inout(struct inout_port *iop)
177 {
178 	int i;
179 
180 	VERIFY_IOPORT(iop->port, iop->size);
181 
182 	/*
183 	 * Verify that the new registration is not overwriting an already
184 	 * allocated i/o range.
185 	 */
186 	if ((iop->flags & IOPORT_F_DEFAULT) == 0) {
187 		for (i = iop->port; i < iop->port + iop->size; i++) {
188 			if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0)
189 				return (-1);
190 		}
191 	}
192 
193 	for (i = iop->port; i < iop->port + iop->size; i++) {
194 		inout_handlers[i].name = iop->name;
195 		inout_handlers[i].flags = iop->flags;
196 		inout_handlers[i].handler = iop->handler;
197 		inout_handlers[i].arg = iop->arg;
198 	}
199 
200 	return (0);
201 }
202 
203 int
unregister_inout(struct inout_port * iop)204 unregister_inout(struct inout_port *iop)
205 {
206 
207 	VERIFY_IOPORT(iop->port, iop->size);
208 	assert(inout_handlers[iop->port].name == iop->name);
209 
210 	register_default_iohandler(iop->port, iop->size);
211 
212 	return (0);
213 }
214