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, int vcpu, int in, int port, int bytes, 83 uint32_t *eax, void *arg) 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, vcpu, 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