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 #include <sys/cdefs.h> 30 #include <sys/param.h> 31 #include <sys/linker_set.h> 32 #include <sys/_iovec.h> 33 #include <sys/mman.h> 34 35 #include <x86/psl.h> 36 37 #include <machine/vmm.h> 38 #include <machine/vmm_instruction_emul.h> 39 #include <vmmapi.h> 40 41 #include <stdio.h> 42 #include <string.h> 43 #include <assert.h> 44 45 #include "bhyverun.h" 46 #include "config.h" 47 #include "inout.h" 48 49 SET_DECLARE(inout_port_set, struct inout_port); 50 51 #define MAX_IOPORTS (1 << 16) 52 53 #define VERIFY_IOPORT(port, size) \ 54 assert((port) >= 0 && (size) > 0 && ((port) + (size)) <= MAX_IOPORTS) 55 56 static struct { 57 const char *name; 58 int flags; 59 inout_func_t handler; 60 void *arg; 61 } inout_handlers[MAX_IOPORTS]; 62 63 static int 64 default_inout(struct vmctx *ctx __unused, int in, 65 int port __unused, int bytes, uint32_t *eax, void *arg __unused) 66 { 67 if (in) { 68 switch (bytes) { 69 case 4: 70 *eax = 0xffffffff; 71 break; 72 case 2: 73 *eax = 0xffff; 74 break; 75 case 1: 76 *eax = 0xff; 77 break; 78 } 79 } 80 81 return (0); 82 } 83 84 static void 85 register_default_iohandler(int start, int size) 86 { 87 struct inout_port iop; 88 89 VERIFY_IOPORT(start, size); 90 91 bzero(&iop, sizeof(iop)); 92 iop.name = "default"; 93 iop.port = start; 94 iop.size = size; 95 iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT; 96 iop.handler = default_inout; 97 98 register_inout(&iop); 99 } 100 101 int 102 emulate_inout(struct vmctx *ctx, struct vcpu *vcpu, struct vm_exit *vmexit) 103 { 104 int addrsize, bytes, flags, in, port, prot, rep; 105 uint32_t eax, val; 106 inout_func_t handler; 107 void *arg; 108 int error, fault, retval; 109 enum vm_reg_name idxreg; 110 uint64_t gla, index, iterations, count; 111 struct vm_inout_str *vis; 112 struct iovec iov[2]; 113 114 bytes = vmexit->u.inout.bytes; 115 in = vmexit->u.inout.in; 116 port = vmexit->u.inout.port; 117 118 assert(port < MAX_IOPORTS); 119 assert(bytes == 1 || bytes == 2 || bytes == 4); 120 121 handler = inout_handlers[port].handler; 122 123 if (handler == default_inout && 124 get_config_bool_default("x86.strictio", false)) 125 return (-1); 126 127 flags = inout_handlers[port].flags; 128 arg = inout_handlers[port].arg; 129 130 if (in) { 131 if (!(flags & IOPORT_F_IN)) 132 return (-1); 133 } else { 134 if (!(flags & IOPORT_F_OUT)) 135 return (-1); 136 } 137 138 retval = 0; 139 if (vmexit->u.inout.string) { 140 vis = &vmexit->u.inout_str; 141 rep = vis->inout.rep; 142 addrsize = vis->addrsize; 143 prot = in ? PROT_WRITE : PROT_READ; 144 assert(addrsize == 2 || addrsize == 4 || addrsize == 8); 145 146 /* Index register */ 147 idxreg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI; 148 index = vis->index & vie_size2mask(addrsize); 149 150 /* Count register */ 151 count = vis->count & vie_size2mask(addrsize); 152 153 /* Limit number of back-to-back in/out emulations to 16 */ 154 iterations = MIN(count, 16); 155 while (iterations > 0) { 156 assert(retval == 0); 157 if (vie_calculate_gla(vis->paging.cpu_mode, 158 vis->seg_name, &vis->seg_desc, index, bytes, 159 addrsize, prot, &gla)) { 160 vm_inject_gp(vcpu); 161 break; 162 } 163 164 error = vm_copy_setup(vcpu, &vis->paging, gla, 165 bytes, prot, iov, nitems(iov), &fault); 166 if (error) { 167 retval = -1; /* Unrecoverable error */ 168 break; 169 } else if (fault) { 170 retval = 0; /* Resume guest to handle fault */ 171 break; 172 } 173 174 if (vie_alignment_check(vis->paging.cpl, bytes, 175 vis->cr0, vis->rflags, gla)) { 176 vm_inject_ac(vcpu, 0); 177 break; 178 } 179 180 val = 0; 181 if (!in) 182 vm_copyin(iov, &val, bytes); 183 184 retval = handler(ctx, in, port, bytes, &val, arg); 185 if (retval != 0) 186 break; 187 188 if (in) 189 vm_copyout(&val, iov, bytes); 190 191 /* Update index */ 192 if (vis->rflags & PSL_D) 193 index -= bytes; 194 else 195 index += bytes; 196 197 count--; 198 iterations--; 199 } 200 201 /* Update index register */ 202 error = vie_update_register(vcpu, idxreg, index, addrsize); 203 assert(error == 0); 204 205 /* 206 * Update count register only if the instruction had a repeat 207 * prefix. 208 */ 209 if (rep) { 210 error = vie_update_register(vcpu, VM_REG_GUEST_RCX, 211 count, addrsize); 212 assert(error == 0); 213 } 214 215 /* Restart the instruction if more iterations remain */ 216 if (retval == 0 && count != 0) { 217 error = vm_restart_instruction(vcpu); 218 assert(error == 0); 219 } 220 } else { 221 eax = vmexit->u.inout.eax; 222 val = eax & vie_size2mask(bytes); 223 retval = handler(ctx, in, port, bytes, &val, arg); 224 if (retval == 0 && in) { 225 eax &= ~vie_size2mask(bytes); 226 eax |= val & vie_size2mask(bytes); 227 error = vm_set_register(vcpu, VM_REG_GUEST_RAX, 228 eax); 229 assert(error == 0); 230 } 231 } 232 return (retval); 233 } 234 235 void 236 init_inout(void) 237 { 238 struct inout_port **iopp, *iop; 239 240 /* 241 * Set up the default handler for all ports 242 */ 243 register_default_iohandler(0, MAX_IOPORTS); 244 245 /* 246 * Overwrite with specified handlers 247 */ 248 SET_FOREACH(iopp, inout_port_set) { 249 iop = *iopp; 250 assert(iop->port < MAX_IOPORTS); 251 inout_handlers[iop->port].name = iop->name; 252 inout_handlers[iop->port].flags = iop->flags; 253 inout_handlers[iop->port].handler = iop->handler; 254 inout_handlers[iop->port].arg = NULL; 255 } 256 } 257 258 int 259 register_inout(struct inout_port *iop) 260 { 261 int i; 262 263 VERIFY_IOPORT(iop->port, iop->size); 264 265 /* 266 * Verify that the new registration is not overwriting an already 267 * allocated i/o range. 268 */ 269 if ((iop->flags & IOPORT_F_DEFAULT) == 0) { 270 for (i = iop->port; i < iop->port + iop->size; i++) { 271 if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0) 272 return (-1); 273 } 274 } 275 276 for (i = iop->port; i < iop->port + iop->size; i++) { 277 inout_handlers[i].name = iop->name; 278 inout_handlers[i].flags = iop->flags; 279 inout_handlers[i].handler = iop->handler; 280 inout_handlers[i].arg = iop->arg; 281 } 282 283 return (0); 284 } 285 286 int 287 unregister_inout(struct inout_port *iop) 288 { 289 290 VERIFY_IOPORT(iop->port, iop->size); 291 assert(inout_handlers[iop->port].name == iop->name); 292 293 register_default_iohandler(iop->port, iop->size); 294 295 return (0); 296 } 297