1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD$ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD$"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34268976Sjhb#include <sys/_iovec.h> 35268976Sjhb#include <sys/mman.h> 36221828Sgrehan 37268976Sjhb#include <x86/psl.h> 38268976Sjhb#include <x86/segments.h> 39268976Sjhb 40268976Sjhb#include <machine/vmm.h> 41268976Sjhb#include <machine/vmm_instruction_emul.h> 42268976Sjhb#include <vmmapi.h> 43268976Sjhb 44221828Sgrehan#include <stdio.h> 45249321Sneel#include <string.h> 46221828Sgrehan#include <assert.h> 47221828Sgrehan 48268976Sjhb#include "bhyverun.h" 49221828Sgrehan#include "inout.h" 50221828Sgrehan 51221828SgrehanSET_DECLARE(inout_port_set, struct inout_port); 52221828Sgrehan 53221828Sgrehan#define MAX_IOPORTS (1 << 16) 54221828Sgrehan 55249321Sneel#define VERIFY_IOPORT(port, size) \ 56249321Sneel assert((port) >= 0 && (size) > 0 && ((port) + (size)) <= MAX_IOPORTS) 57249321Sneel 58221828Sgrehanstatic struct { 59221828Sgrehan const char *name; 60221828Sgrehan int flags; 61221828Sgrehan inout_func_t handler; 62221828Sgrehan void *arg; 63221828Sgrehan} inout_handlers[MAX_IOPORTS]; 64221828Sgrehan 65222105Sgrehanstatic int 66222105Sgrehandefault_inout(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 67222105Sgrehan uint32_t *eax, void *arg) 68222105Sgrehan{ 69222105Sgrehan if (in) { 70222105Sgrehan switch (bytes) { 71222105Sgrehan case 4: 72222105Sgrehan *eax = 0xffffffff; 73222105Sgrehan break; 74222105Sgrehan case 2: 75222105Sgrehan *eax = 0xffff; 76222105Sgrehan break; 77222105Sgrehan case 1: 78222105Sgrehan *eax = 0xff; 79222105Sgrehan break; 80222105Sgrehan } 81222105Sgrehan } 82222105Sgrehan 83222105Sgrehan return (0); 84222105Sgrehan} 85222105Sgrehan 86249321Sneelstatic void 87249321Sneelregister_default_iohandler(int start, int size) 88249321Sneel{ 89249321Sneel struct inout_port iop; 90249321Sneel 91249321Sneel VERIFY_IOPORT(start, size); 92249321Sneel 93249321Sneel bzero(&iop, sizeof(iop)); 94249321Sneel iop.name = "default"; 95249321Sneel iop.port = start; 96249321Sneel iop.size = size; 97257396Sneel iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT; 98249321Sneel iop.handler = default_inout; 99249321Sneel 100249321Sneel register_inout(&iop); 101249321Sneel} 102249321Sneel 103221828Sgrehanint 104268976Sjhbemulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict) 105221828Sgrehan{ 106268976Sjhb int addrsize, bytes, flags, in, port, prot, rep; 107268976Sjhb uint32_t val; 108221828Sgrehan inout_func_t handler; 109221828Sgrehan void *arg; 110268976Sjhb int error, retval; 111268976Sjhb enum vm_reg_name idxreg; 112268976Sjhb uint64_t gla, index, iterations, count; 113268976Sjhb struct vm_inout_str *vis; 114268976Sjhb struct iovec iov[2]; 115221828Sgrehan 116268976Sjhb bytes = vmexit->u.inout.bytes; 117268976Sjhb in = vmexit->u.inout.in; 118268976Sjhb port = vmexit->u.inout.port; 119268976Sjhb 120221828Sgrehan assert(port < MAX_IOPORTS); 121268976Sjhb assert(bytes == 1 || bytes == 2 || bytes == 4); 122221828Sgrehan 123222105Sgrehan handler = inout_handlers[port].handler; 124222105Sgrehan 125222105Sgrehan if (strict && handler == default_inout) 126221828Sgrehan return (-1); 127221828Sgrehan 128268976Sjhb flags = inout_handlers[port].flags; 129268976Sjhb arg = inout_handlers[port].arg; 130268953Sjhb 131268976Sjhb if (in) { 132268976Sjhb if (!(flags & IOPORT_F_IN)) 133268976Sjhb return (-1); 134268976Sjhb } else { 135268976Sjhb if (!(flags & IOPORT_F_OUT)) 136268976Sjhb return (-1); 137243349Sneel } 138243349Sneel 139268976Sjhb retval = 0; 140268976Sjhb if (vmexit->u.inout.string) { 141268976Sjhb vis = &vmexit->u.inout_str; 142268976Sjhb rep = vis->inout.rep; 143268976Sjhb addrsize = vis->addrsize; 144268976Sjhb prot = in ? PROT_WRITE : PROT_READ; 145268976Sjhb assert(addrsize == 2 || addrsize == 4 || addrsize == 8); 146221828Sgrehan 147268976Sjhb /* Index register */ 148268976Sjhb idxreg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI; 149268976Sjhb index = vis->index & vie_size2mask(addrsize); 150268891Sjhb 151268976Sjhb /* Count register */ 152268976Sjhb count = vis->count & vie_size2mask(addrsize); 153268976Sjhb 154268976Sjhb /* Limit number of back-to-back in/out emulations to 16 */ 155268976Sjhb iterations = MIN(count, 16); 156268976Sjhb while (iterations > 0) { 157270159Sgrehan assert(retval == 0); 158268976Sjhb if (vie_calculate_gla(vis->paging.cpu_mode, 159268976Sjhb vis->seg_name, &vis->seg_desc, index, bytes, 160268976Sjhb addrsize, prot, &gla)) { 161270159Sgrehan vm_inject_gp(ctx, vcpu); 162268976Sjhb break; 163268976Sjhb } 164268976Sjhb 165270159Sgrehan error = vm_copy_setup(ctx, vcpu, &vis->paging, gla, 166270159Sgrehan bytes, prot, iov, nitems(iov)); 167270159Sgrehan if (error == -1) { 168270159Sgrehan retval = -1; /* Unrecoverable error */ 169268976Sjhb break; 170270159Sgrehan } else if (error == 1) { 171270159Sgrehan retval = 0; /* Resume guest to handle fault */ 172270159Sgrehan break; 173268976Sjhb } 174268976Sjhb 175268976Sjhb if (vie_alignment_check(vis->paging.cpl, bytes, 176268976Sjhb vis->cr0, vis->rflags, gla)) { 177270159Sgrehan vm_inject_ac(ctx, vcpu, 0); 178270159Sgrehan break; 179268976Sjhb } 180268976Sjhb 181268976Sjhb val = 0; 182268976Sjhb if (!in) 183268976Sjhb vm_copyin(ctx, vcpu, iov, &val, bytes); 184268976Sjhb 185268976Sjhb retval = handler(ctx, vcpu, in, port, bytes, &val, arg); 186268976Sjhb if (retval != 0) 187268976Sjhb break; 188268976Sjhb 189268976Sjhb if (in) 190268976Sjhb vm_copyout(ctx, vcpu, &val, iov, bytes); 191268976Sjhb 192268976Sjhb /* Update index */ 193268976Sjhb if (vis->rflags & PSL_D) 194268976Sjhb index -= bytes; 195268976Sjhb else 196268976Sjhb index += bytes; 197268976Sjhb 198268976Sjhb count--; 199268976Sjhb iterations--; 200268976Sjhb } 201268976Sjhb 202268976Sjhb /* Update index register */ 203268976Sjhb error = vie_update_register(ctx, vcpu, idxreg, index, addrsize); 204268976Sjhb assert(error == 0); 205268976Sjhb 206268976Sjhb /* 207268976Sjhb * Update count register only if the instruction had a repeat 208268976Sjhb * prefix. 209268976Sjhb */ 210268976Sjhb if (rep) { 211268976Sjhb error = vie_update_register(ctx, vcpu, VM_REG_GUEST_RCX, 212268976Sjhb count, addrsize); 213268976Sjhb assert(error == 0); 214268976Sjhb } 215268976Sjhb 216268976Sjhb /* Restart the instruction if more iterations remain */ 217270159Sgrehan if (retval == 0 && count != 0) 218270159Sgrehan vmexit->inst_length = 0; 219268976Sjhb } else { 220268976Sjhb if (!in) { 221268976Sjhb val = vmexit->u.inout.eax & vie_size2mask(bytes); 222268976Sjhb } 223268976Sjhb retval = handler(ctx, vcpu, in, port, bytes, &val, arg); 224268976Sjhb if (retval == 0 && in) { 225268976Sjhb vmexit->u.inout.eax &= ~vie_size2mask(bytes); 226268976Sjhb vmexit->u.inout.eax |= val & vie_size2mask(bytes); 227268976Sjhb } 228268891Sjhb } 229268976Sjhb return (retval); 230221828Sgrehan} 231221828Sgrehan 232221828Sgrehanvoid 233221828Sgrehaninit_inout(void) 234221828Sgrehan{ 235221828Sgrehan struct inout_port **iopp, *iop; 236221828Sgrehan 237222105Sgrehan /* 238222105Sgrehan * Set up the default handler for all ports 239222105Sgrehan */ 240249321Sneel register_default_iohandler(0, MAX_IOPORTS); 241222105Sgrehan 242222105Sgrehan /* 243222105Sgrehan * Overwrite with specified handlers 244222105Sgrehan */ 245221828Sgrehan SET_FOREACH(iopp, inout_port_set) { 246221828Sgrehan iop = *iopp; 247221828Sgrehan assert(iop->port < MAX_IOPORTS); 248221828Sgrehan inout_handlers[iop->port].name = iop->name; 249221828Sgrehan inout_handlers[iop->port].flags = iop->flags; 250221828Sgrehan inout_handlers[iop->port].handler = iop->handler; 251221828Sgrehan inout_handlers[iop->port].arg = NULL; 252221828Sgrehan } 253221828Sgrehan} 254221828Sgrehan 255221828Sgrehanint 256221828Sgrehanregister_inout(struct inout_port *iop) 257221828Sgrehan{ 258249321Sneel int i; 259221828Sgrehan 260249321Sneel VERIFY_IOPORT(iop->port, iop->size); 261257396Sneel 262257396Sneel /* 263257396Sneel * Verify that the new registration is not overwriting an already 264257396Sneel * allocated i/o range. 265257396Sneel */ 266257396Sneel if ((iop->flags & IOPORT_F_DEFAULT) == 0) { 267257396Sneel for (i = iop->port; i < iop->port + iop->size; i++) { 268257396Sneel if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0) 269257396Sneel return (-1); 270257396Sneel } 271257396Sneel } 272257396Sneel 273249321Sneel for (i = iop->port; i < iop->port + iop->size; i++) { 274249321Sneel inout_handlers[i].name = iop->name; 275249321Sneel inout_handlers[i].flags = iop->flags; 276249321Sneel inout_handlers[i].handler = iop->handler; 277249321Sneel inout_handlers[i].arg = iop->arg; 278249321Sneel } 279249321Sneel 280221828Sgrehan return (0); 281221828Sgrehan} 282249321Sneel 283249321Sneelint 284249321Sneelunregister_inout(struct inout_port *iop) 285249321Sneel{ 286249321Sneel 287249321Sneel VERIFY_IOPORT(iop->port, iop->size); 288249321Sneel assert(inout_handlers[iop->port].name == iop->name); 289249321Sneel 290249321Sneel register_default_iohandler(iop->port, iop->size); 291249321Sneel 292249321Sneel return (0); 293249321Sneel} 294