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