1263035Stychon/*- 2263035Stychon * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 3263035Stychon * All rights reserved. 4263035Stychon * 5263035Stychon * Redistribution and use in source and binary forms, with or without 6263035Stychon * modification, are permitted provided that the following conditions 7263035Stychon * are met: 8263035Stychon * 1. Redistributions of source code must retain the above copyright 9263035Stychon * notice, this list of conditions and the following disclaimer. 10263035Stychon * 2. Redistributions in binary form must reproduce the above copyright 11263035Stychon * notice, this list of conditions and the following disclaimer in the 12263035Stychon * documentation and/or other materials provided with the distribution. 13263035Stychon * 14263035Stychon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15263035Stychon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16263035Stychon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17263035Stychon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18263035Stychon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19263035Stychon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20263035Stychon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21263035Stychon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22263035Stychon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23263035Stychon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24263035Stychon * SUCH DAMAGE. 25263035Stychon */ 26263035Stychon 27263035Stychon#include <sys/cdefs.h> 28263035Stychon__FBSDID("$FreeBSD$"); 29263035Stychon 30263035Stychon#include <sys/param.h> 31263035Stychon#include <sys/systm.h> 32263035Stychon 33263035Stychon#include <machine/vmm.h> 34268976Sjhb#include <machine/vmm_instruction_emul.h> 35263035Stychon 36263035Stychon#include "vatpic.h" 37268891Sjhb#include "vatpit.h" 38276429Sneel#include "vpmtmr.h" 39284894Sneel#include "vrtc.h" 40263035Stychon#include "vmm_ioport.h" 41268976Sjhb#include "vmm_ktr.h" 42263035Stychon 43263035Stychon#define MAX_IOPORTS 1280 44263035Stychon 45263035Stychonioport_handler_func_t ioport_handler[MAX_IOPORTS] = { 46268891Sjhb [TIMER_MODE] = vatpit_handler, 47268891Sjhb [TIMER_CNTR0] = vatpit_handler, 48268891Sjhb [TIMER_CNTR1] = vatpit_handler, 49268891Sjhb [TIMER_CNTR2] = vatpit_handler, 50268891Sjhb [NMISC_PORT] = vatpit_nmisc_handler, 51263035Stychon [IO_ICU1] = vatpic_master_handler, 52263035Stychon [IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler, 53263035Stychon [IO_ICU2] = vatpic_slave_handler, 54263035Stychon [IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler, 55263035Stychon [IO_ELCR1] = vatpic_elc_handler, 56263035Stychon [IO_ELCR2] = vatpic_elc_handler, 57276429Sneel [IO_PMTMR] = vpmtmr_handler, 58284894Sneel [IO_RTC] = vrtc_addr_handler, 59284894Sneel [IO_RTC + 1] = vrtc_data_handler, 60263035Stychon}; 61263035Stychon 62268976Sjhb#ifdef KTR 63268976Sjhbstatic const char * 64268976Sjhbinout_instruction(struct vm_exit *vmexit) 65263035Stychon{ 66268976Sjhb int index; 67263035Stychon 68268976Sjhb static const char *iodesc[] = { 69268976Sjhb "outb", "outw", "outl", 70268976Sjhb "inb", "inw", "inl", 71284894Sneel "outsb", "outsw", "outsd", 72268976Sjhb "insb", "insw", "insd", 73268976Sjhb }; 74263035Stychon 75268953Sjhb switch (vmexit->u.inout.bytes) { 76268953Sjhb case 1: 77268976Sjhb index = 0; 78268953Sjhb break; 79268953Sjhb case 2: 80268976Sjhb index = 1; 81268953Sjhb break; 82268953Sjhb default: 83268976Sjhb index = 2; 84268953Sjhb break; 85268953Sjhb } 86268953Sjhb 87268976Sjhb if (vmexit->u.inout.in) 88268976Sjhb index += 3; 89268976Sjhb 90268976Sjhb if (vmexit->u.inout.string) 91268976Sjhb index += 6; 92268976Sjhb 93268976Sjhb KASSERT(index < nitems(iodesc), ("%s: invalid index %d", 94268976Sjhb __func__, index)); 95268976Sjhb 96268976Sjhb return (iodesc[index]); 97268976Sjhb} 98268976Sjhb#endif /* KTR */ 99268976Sjhb 100268976Sjhbstatic int 101268976Sjhbemulate_inout_port(struct vm *vm, int vcpuid, struct vm_exit *vmexit, 102268976Sjhb bool *retu) 103268976Sjhb{ 104268976Sjhb ioport_handler_func_t handler; 105268976Sjhb uint32_t mask, val; 106268976Sjhb int error; 107268976Sjhb 108273807Sneel /* 109273807Sneel * If there is no handler for the I/O port then punt to userspace. 110273807Sneel */ 111273807Sneel if (vmexit->u.inout.port >= MAX_IOPORTS || 112273807Sneel (handler = ioport_handler[vmexit->u.inout.port]) == NULL) { 113273807Sneel *retu = true; 114273807Sneel return (0); 115273807Sneel } 116268976Sjhb 117268976Sjhb mask = vie_size2mask(vmexit->u.inout.bytes); 118268976Sjhb 119268891Sjhb if (!vmexit->u.inout.in) { 120268891Sjhb val = vmexit->u.inout.eax & mask; 121268891Sjhb } 122268891Sjhb 123268891Sjhb error = (*handler)(vm, vcpuid, vmexit->u.inout.in, 124268891Sjhb vmexit->u.inout.port, vmexit->u.inout.bytes, &val); 125273807Sneel if (error) { 126273807Sneel /* 127273807Sneel * The value returned by this function is also the return value 128273807Sneel * of vm_run(). This needs to be a positive number otherwise it 129273807Sneel * can be interpreted as a "pseudo-error" like ERESTART. 130273807Sneel * 131273807Sneel * Enforce this by mapping all errors to EIO. 132273807Sneel */ 133273807Sneel return (EIO); 134273807Sneel } 135268891Sjhb 136273807Sneel if (vmexit->u.inout.in) { 137273807Sneel vmexit->u.inout.eax &= ~mask; 138273807Sneel vmexit->u.inout.eax |= val & mask; 139273807Sneel error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, 140273807Sneel vmexit->u.inout.eax); 141273807Sneel KASSERT(error == 0, ("emulate_ioport: error %d setting guest " 142273807Sneel "rax register", error)); 143268891Sjhb } 144273807Sneel *retu = false; 145273807Sneel return (0); 146268976Sjhb} 147268891Sjhb 148268976Sjhbstatic int 149268976Sjhbemulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) 150268976Sjhb{ 151268976Sjhb *retu = true; 152268976Sjhb return (0); /* Return to userspace to finish emulation */ 153268976Sjhb} 154268976Sjhb 155268976Sjhbint 156268976Sjhbvm_handle_inout(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) 157268976Sjhb{ 158268976Sjhb int bytes, error; 159268976Sjhb 160268976Sjhb bytes = vmexit->u.inout.bytes; 161268976Sjhb KASSERT(bytes == 1 || bytes == 2 || bytes == 4, 162268976Sjhb ("vm_handle_inout: invalid operand size %d", bytes)); 163268976Sjhb 164268976Sjhb if (vmexit->u.inout.string) 165268976Sjhb error = emulate_inout_str(vm, vcpuid, vmexit, retu); 166268976Sjhb else 167268976Sjhb error = emulate_inout_port(vm, vcpuid, vmexit, retu); 168268976Sjhb 169268976Sjhb VCPU_CTR4(vm, vcpuid, "%s%s 0x%04x: %s", 170268976Sjhb vmexit->u.inout.rep ? "rep " : "", 171268976Sjhb inout_instruction(vmexit), 172268976Sjhb vmexit->u.inout.port, 173268976Sjhb error ? "error" : (*retu ? "userspace" : "handled")); 174268976Sjhb 175268891Sjhb return (error); 176263035Stychon} 177