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