1/*- 2 * Copyright (c) 2011 NetApp, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: releng/11.0/usr.sbin/bhyve/inout.c 282558 2015-05-06 16:25:20Z neel $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/inout.c 282558 2015-05-06 16:25:20Z neel $"); 31 32#include <sys/param.h> 33#include <sys/linker_set.h> 34#include <sys/_iovec.h> 35#include <sys/mman.h> 36 37#include <x86/psl.h> 38#include <x86/segments.h> 39 40#include <machine/vmm.h> 41#include <machine/vmm_instruction_emul.h> 42#include <vmmapi.h> 43 44#include <stdio.h> 45#include <string.h> 46#include <assert.h> 47 48#include "bhyverun.h" 49#include "inout.h" 50 51SET_DECLARE(inout_port_set, struct inout_port); 52 53#define MAX_IOPORTS (1 << 16) 54 55#define VERIFY_IOPORT(port, size) \ 56 assert((port) >= 0 && (size) > 0 && ((port) + (size)) <= MAX_IOPORTS) 57 58static struct { 59 const char *name; 60 int flags; 61 inout_func_t handler; 62 void *arg; 63} inout_handlers[MAX_IOPORTS]; 64 65static int 66default_inout(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 67 uint32_t *eax, void *arg) 68{ 69 if (in) { 70 switch (bytes) { 71 case 4: 72 *eax = 0xffffffff; 73 break; 74 case 2: 75 *eax = 0xffff; 76 break; 77 case 1: 78 *eax = 0xff; 79 break; 80 } 81 } 82 83 return (0); 84} 85 86static void 87register_default_iohandler(int start, int size) 88{ 89 struct inout_port iop; 90 91 VERIFY_IOPORT(start, size); 92 93 bzero(&iop, sizeof(iop)); 94 iop.name = "default"; 95 iop.port = start; 96 iop.size = size; 97 iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT; 98 iop.handler = default_inout; 99 100 register_inout(&iop); 101} 102 103int 104emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict) 105{ 106 int addrsize, bytes, flags, in, port, prot, rep; 107 uint32_t eax, val; 108 inout_func_t handler; 109 void *arg; 110 int error, fault, retval; 111 enum vm_reg_name idxreg; 112 uint64_t gla, index, iterations, count; 113 struct vm_inout_str *vis; 114 struct iovec iov[2]; 115 116 bytes = vmexit->u.inout.bytes; 117 in = vmexit->u.inout.in; 118 port = vmexit->u.inout.port; 119 120 assert(port < MAX_IOPORTS); 121 assert(bytes == 1 || bytes == 2 || bytes == 4); 122 123 handler = inout_handlers[port].handler; 124 125 if (strict && handler == default_inout) 126 return (-1); 127 128 flags = inout_handlers[port].flags; 129 arg = inout_handlers[port].arg; 130 131 if (in) { 132 if (!(flags & IOPORT_F_IN)) 133 return (-1); 134 } else { 135 if (!(flags & IOPORT_F_OUT)) 136 return (-1); 137 } 138 139 retval = 0; 140 if (vmexit->u.inout.string) { 141 vis = &vmexit->u.inout_str; 142 rep = vis->inout.rep; 143 addrsize = vis->addrsize; 144 prot = in ? PROT_WRITE : PROT_READ; 145 assert(addrsize == 2 || addrsize == 4 || addrsize == 8); 146 147 /* Index register */ 148 idxreg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI; 149 index = vis->index & vie_size2mask(addrsize); 150 151 /* Count register */ 152 count = vis->count & vie_size2mask(addrsize); 153 154 /* Limit number of back-to-back in/out emulations to 16 */ 155 iterations = MIN(count, 16); 156 while (iterations > 0) { 157 assert(retval == 0); 158 if (vie_calculate_gla(vis->paging.cpu_mode, 159 vis->seg_name, &vis->seg_desc, index, bytes, 160 addrsize, prot, &gla)) { 161 vm_inject_gp(ctx, vcpu); 162 break; 163 } 164 165 error = vm_copy_setup(ctx, vcpu, &vis->paging, gla, 166 bytes, prot, iov, nitems(iov), &fault); 167 if (error) { 168 retval = -1; /* Unrecoverable error */ 169 break; 170 } else if (fault) { 171 retval = 0; /* Resume guest to handle fault */ 172 break; 173 } 174 175 if (vie_alignment_check(vis->paging.cpl, bytes, 176 vis->cr0, vis->rflags, gla)) { 177 vm_inject_ac(ctx, vcpu, 0); 178 break; 179 } 180 181 val = 0; 182 if (!in) 183 vm_copyin(ctx, vcpu, iov, &val, bytes); 184 185 retval = handler(ctx, vcpu, in, port, bytes, &val, arg); 186 if (retval != 0) 187 break; 188 189 if (in) 190 vm_copyout(ctx, vcpu, &val, iov, bytes); 191 192 /* Update index */ 193 if (vis->rflags & PSL_D) 194 index -= bytes; 195 else 196 index += bytes; 197 198 count--; 199 iterations--; 200 } 201 202 /* Update index register */ 203 error = vie_update_register(ctx, vcpu, idxreg, index, addrsize); 204 assert(error == 0); 205 206 /* 207 * Update count register only if the instruction had a repeat 208 * prefix. 209 */ 210 if (rep) { 211 error = vie_update_register(ctx, vcpu, VM_REG_GUEST_RCX, 212 count, addrsize); 213 assert(error == 0); 214 } 215 216 /* Restart the instruction if more iterations remain */ 217 if (retval == 0 && count != 0) { 218 error = vm_restart_instruction(ctx, vcpu); 219 assert(error == 0); 220 } 221 } else { 222 eax = vmexit->u.inout.eax; 223 val = eax & vie_size2mask(bytes); 224 retval = handler(ctx, vcpu, in, port, bytes, &val, arg); 225 if (retval == 0 && in) { 226 eax &= ~vie_size2mask(bytes); 227 eax |= val & vie_size2mask(bytes); 228 error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX, 229 eax); 230 assert(error == 0); 231 } 232 } 233 return (retval); 234} 235 236void 237init_inout(void) 238{ 239 struct inout_port **iopp, *iop; 240 241 /* 242 * Set up the default handler for all ports 243 */ 244 register_default_iohandler(0, MAX_IOPORTS); 245 246 /* 247 * Overwrite with specified handlers 248 */ 249 SET_FOREACH(iopp, inout_port_set) { 250 iop = *iopp; 251 assert(iop->port < MAX_IOPORTS); 252 inout_handlers[iop->port].name = iop->name; 253 inout_handlers[iop->port].flags = iop->flags; 254 inout_handlers[iop->port].handler = iop->handler; 255 inout_handlers[iop->port].arg = NULL; 256 } 257} 258 259int 260register_inout(struct inout_port *iop) 261{ 262 int i; 263 264 VERIFY_IOPORT(iop->port, iop->size); 265 266 /* 267 * Verify that the new registration is not overwriting an already 268 * allocated i/o range. 269 */ 270 if ((iop->flags & IOPORT_F_DEFAULT) == 0) { 271 for (i = iop->port; i < iop->port + iop->size; i++) { 272 if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0) 273 return (-1); 274 } 275 } 276 277 for (i = iop->port; i < iop->port + iop->size; i++) { 278 inout_handlers[i].name = iop->name; 279 inout_handlers[i].flags = iop->flags; 280 inout_handlers[i].handler = iop->handler; 281 inout_handlers[i].arg = iop->arg; 282 } 283 284 return (0); 285} 286 287int 288unregister_inout(struct inout_port *iop) 289{ 290 291 VERIFY_IOPORT(iop->port, iop->size); 292 assert(inout_handlers[iop->port].name == iop->name); 293 294 register_default_iohandler(iop->port, iop->size); 295 296 return (0); 297} 298