inout.c revision 268976
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 268976 2014-07-22 04:39:16Z jhb $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/inout.c 268976 2014-07-22 04:39:16Z jhb $"); 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 if (vie_calculate_gla(vis->paging.cpu_mode, 158 vis->seg_name, &vis->seg_desc, index, bytes, 159 addrsize, prot, &gla)) { 160 error = vm_inject_exception2(ctx, vcpu, 161 IDT_GP, 0); 162 assert(error == 0); 163 retval = INOUT_RESTART; 164 break; 165 } 166 167 error = vm_gla2gpa(ctx, vcpu, &vis->paging, gla, bytes, 168 prot, iov, nitems(iov)); 169 assert(error == 0 || error == 1 || error == -1); 170 if (error) { 171 retval = (error == 1) ? INOUT_RESTART : 172 INOUT_ERROR; 173 break; 174 } 175 176 if (vie_alignment_check(vis->paging.cpl, bytes, 177 vis->cr0, vis->rflags, gla)) { 178 error = vm_inject_exception2(ctx, vcpu, 179 IDT_AC, 0); 180 assert(error == 0); 181 return (INOUT_RESTART); 182 } 183 184 val = 0; 185 if (!in) 186 vm_copyin(ctx, vcpu, iov, &val, bytes); 187 188 retval = handler(ctx, vcpu, in, port, bytes, &val, arg); 189 if (retval != 0) 190 break; 191 192 if (in) 193 vm_copyout(ctx, vcpu, &val, iov, bytes); 194 195 /* Update index */ 196 if (vis->rflags & PSL_D) 197 index -= bytes; 198 else 199 index += bytes; 200 201 count--; 202 iterations--; 203 } 204 205 /* Update index register */ 206 error = vie_update_register(ctx, vcpu, idxreg, index, addrsize); 207 assert(error == 0); 208 209 /* 210 * Update count register only if the instruction had a repeat 211 * prefix. 212 */ 213 if (rep) { 214 error = vie_update_register(ctx, vcpu, VM_REG_GUEST_RCX, 215 count, addrsize); 216 assert(error == 0); 217 } 218 219 /* Restart the instruction if more iterations remain */ 220 if (retval == INOUT_OK && count != 0) 221 retval = INOUT_RESTART; 222 } else { 223 if (!in) { 224 val = vmexit->u.inout.eax & vie_size2mask(bytes); 225 } 226 retval = handler(ctx, vcpu, in, port, bytes, &val, arg); 227 if (retval == 0 && in) { 228 vmexit->u.inout.eax &= ~vie_size2mask(bytes); 229 vmexit->u.inout.eax |= val & vie_size2mask(bytes); 230 } 231 } 232 return (retval); 233} 234 235void 236init_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 258int 259register_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 286int 287unregister_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