inout.c revision 270159
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: stable/10/usr.sbin/bhyve/inout.c 270159 2014-08-19 01:20:24Z grehan $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/inout.c 270159 2014-08-19 01:20:24Z grehan $"); 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 val; 108 inout_func_t handler; 109 void *arg; 110 int error, 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)); 167 if (error == -1) { 168 retval = -1; /* Unrecoverable error */ 169 break; 170 } else if (error == 1) { 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 vmexit->inst_length = 0; 219 } else { 220 if (!in) { 221 val = vmexit->u.inout.eax & vie_size2mask(bytes); 222 } 223 retval = handler(ctx, vcpu, in, port, bytes, &val, arg); 224 if (retval == 0 && in) { 225 vmexit->u.inout.eax &= ~vie_size2mask(bytes); 226 vmexit->u.inout.eax |= val & vie_size2mask(bytes); 227 } 228 } 229 return (retval); 230} 231 232void 233init_inout(void) 234{ 235 struct inout_port **iopp, *iop; 236 237 /* 238 * Set up the default handler for all ports 239 */ 240 register_default_iohandler(0, MAX_IOPORTS); 241 242 /* 243 * Overwrite with specified handlers 244 */ 245 SET_FOREACH(iopp, inout_port_set) { 246 iop = *iopp; 247 assert(iop->port < MAX_IOPORTS); 248 inout_handlers[iop->port].name = iop->name; 249 inout_handlers[iop->port].flags = iop->flags; 250 inout_handlers[iop->port].handler = iop->handler; 251 inout_handlers[iop->port].arg = NULL; 252 } 253} 254 255int 256register_inout(struct inout_port *iop) 257{ 258 int i; 259 260 VERIFY_IOPORT(iop->port, iop->size); 261 262 /* 263 * Verify that the new registration is not overwriting an already 264 * allocated i/o range. 265 */ 266 if ((iop->flags & IOPORT_F_DEFAULT) == 0) { 267 for (i = iop->port; i < iop->port + iop->size; i++) { 268 if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0) 269 return (-1); 270 } 271 } 272 273 for (i = iop->port; i < iop->port + iop->size; i++) { 274 inout_handlers[i].name = iop->name; 275 inout_handlers[i].flags = iop->flags; 276 inout_handlers[i].handler = iop->handler; 277 inout_handlers[i].arg = iop->arg; 278 } 279 280 return (0); 281} 282 283int 284unregister_inout(struct inout_port *iop) 285{ 286 287 VERIFY_IOPORT(iop->port, iop->size); 288 assert(inout_handlers[iop->port].name == iop->name); 289 290 register_default_iohandler(iop->port, iop->size); 291 292 return (0); 293} 294