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 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 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 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 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 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 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