1263035Stychon/*- 2336190Saraujo * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3336190Saraujo * 4263035Stychon * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 5263035Stychon * All rights reserved. 6263035Stychon * 7263035Stychon * Redistribution and use in source and binary forms, with or without 8263035Stychon * modification, are permitted provided that the following conditions 9263035Stychon * are met: 10263035Stychon * 1. Redistributions of source code must retain the above copyright 11263035Stychon * notice, this list of conditions and the following disclaimer. 12263035Stychon * 2. Redistributions in binary form must reproduce the above copyright 13263035Stychon * notice, this list of conditions and the following disclaimer in the 14263035Stychon * documentation and/or other materials provided with the distribution. 15263035Stychon * 16263035Stychon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 17263035Stychon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18263035Stychon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19263035Stychon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20263035Stychon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21263035Stychon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22263035Stychon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23263035Stychon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24263035Stychon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25263035Stychon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26263035Stychon * SUCH DAMAGE. 27263035Stychon */ 28263035Stychon 29263035Stychon#include <sys/cdefs.h> 30263035Stychon__FBSDID("$FreeBSD: stable/11/sys/amd64/vmm/vmm_ioport.c 336190 2018-07-11 07:19:42Z araujo $"); 31263035Stychon 32263035Stychon#include <sys/param.h> 33263035Stychon#include <sys/systm.h> 34263035Stychon 35263035Stychon#include <machine/vmm.h> 36266627Sneel#include <machine/vmm_instruction_emul.h> 37263035Stychon 38263035Stychon#include "vatpic.h" 39263744Stychon#include "vatpit.h" 40273683Sneel#include "vpmtmr.h" 41276428Sneel#include "vrtc.h" 42263035Stychon#include "vmm_ioport.h" 43266573Sneel#include "vmm_ktr.h" 44263035Stychon 45263035Stychon#define MAX_IOPORTS 1280 46263035Stychon 47263035Stychonioport_handler_func_t ioport_handler[MAX_IOPORTS] = { 48263744Stychon [TIMER_MODE] = vatpit_handler, 49263744Stychon [TIMER_CNTR0] = vatpit_handler, 50263744Stychon [TIMER_CNTR1] = vatpit_handler, 51263744Stychon [TIMER_CNTR2] = vatpit_handler, 52264631Stychon [NMISC_PORT] = vatpit_nmisc_handler, 53263035Stychon [IO_ICU1] = vatpic_master_handler, 54263035Stychon [IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler, 55263035Stychon [IO_ICU2] = vatpic_slave_handler, 56263035Stychon [IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler, 57263035Stychon [IO_ELCR1] = vatpic_elc_handler, 58263035Stychon [IO_ELCR2] = vatpic_elc_handler, 59273683Sneel [IO_PMTMR] = vpmtmr_handler, 60276428Sneel [IO_RTC] = vrtc_addr_handler, 61276428Sneel [IO_RTC + 1] = vrtc_data_handler, 62263035Stychon}; 63263035Stychon 64266573Sneel#ifdef KTR 65266573Sneelstatic const char * 66266573Sneelinout_instruction(struct vm_exit *vmexit) 67263035Stychon{ 68266573Sneel int index; 69263035Stychon 70266573Sneel static const char *iodesc[] = { 71266573Sneel "outb", "outw", "outl", 72266573Sneel "inb", "inw", "inl", 73277168Sneel "outsb", "outsw", "outsd", 74266573Sneel "insb", "insw", "insd", 75266573Sneel }; 76263035Stychon 77264768Stychon switch (vmexit->u.inout.bytes) { 78264768Stychon case 1: 79266573Sneel index = 0; 80264768Stychon break; 81264768Stychon case 2: 82266573Sneel index = 1; 83264768Stychon break; 84264768Stychon default: 85266573Sneel index = 2; 86264768Stychon break; 87264768Stychon } 88264768Stychon 89266573Sneel if (vmexit->u.inout.in) 90266573Sneel index += 3; 91266573Sneel 92266573Sneel if (vmexit->u.inout.string) 93266573Sneel index += 6; 94266573Sneel 95266573Sneel KASSERT(index < nitems(iodesc), ("%s: invalid index %d", 96266573Sneel __func__, index)); 97266573Sneel 98266573Sneel return (iodesc[index]); 99266573Sneel} 100266573Sneel#endif /* KTR */ 101266573Sneel 102266573Sneelstatic int 103266573Sneelemulate_inout_port(struct vm *vm, int vcpuid, struct vm_exit *vmexit, 104266573Sneel bool *retu) 105266573Sneel{ 106266573Sneel ioport_handler_func_t handler; 107266573Sneel uint32_t mask, val; 108266573Sneel int error; 109266573Sneel 110273666Sneel /* 111273666Sneel * If there is no handler for the I/O port then punt to userspace. 112273666Sneel */ 113273666Sneel if (vmexit->u.inout.port >= MAX_IOPORTS || 114273666Sneel (handler = ioport_handler[vmexit->u.inout.port]) == NULL) { 115273666Sneel *retu = true; 116273666Sneel return (0); 117273666Sneel } 118266573Sneel 119266573Sneel mask = vie_size2mask(vmexit->u.inout.bytes); 120266573Sneel 121264648Stychon if (!vmexit->u.inout.in) { 122264648Stychon val = vmexit->u.inout.eax & mask; 123264648Stychon } 124264648Stychon 125264648Stychon error = (*handler)(vm, vcpuid, vmexit->u.inout.in, 126264648Stychon vmexit->u.inout.port, vmexit->u.inout.bytes, &val); 127273666Sneel if (error) { 128273666Sneel /* 129273666Sneel * The value returned by this function is also the return value 130273666Sneel * of vm_run(). This needs to be a positive number otherwise it 131273666Sneel * can be interpreted as a "pseudo-error" like ERESTART. 132273666Sneel * 133273666Sneel * Enforce this by mapping all errors to EIO. 134273666Sneel */ 135273666Sneel return (EIO); 136273666Sneel } 137264648Stychon 138273666Sneel if (vmexit->u.inout.in) { 139273666Sneel vmexit->u.inout.eax &= ~mask; 140273666Sneel vmexit->u.inout.eax |= val & mask; 141273666Sneel error = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, 142273666Sneel vmexit->u.inout.eax); 143273666Sneel KASSERT(error == 0, ("emulate_ioport: error %d setting guest " 144273666Sneel "rax register", error)); 145264648Stychon } 146273666Sneel *retu = false; 147273666Sneel return (0); 148266573Sneel} 149264648Stychon 150266573Sneelstatic int 151266573Sneelemulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) 152266573Sneel{ 153266633Sneel *retu = true; 154266633Sneel return (0); /* Return to userspace to finish emulation */ 155266573Sneel} 156266573Sneel 157266573Sneelint 158266573Sneelvm_handle_inout(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) 159266573Sneel{ 160266573Sneel int bytes, error; 161266573Sneel 162266573Sneel bytes = vmexit->u.inout.bytes; 163266573Sneel KASSERT(bytes == 1 || bytes == 2 || bytes == 4, 164266573Sneel ("vm_handle_inout: invalid operand size %d", bytes)); 165266573Sneel 166266573Sneel if (vmexit->u.inout.string) 167266573Sneel error = emulate_inout_str(vm, vcpuid, vmexit, retu); 168266573Sneel else 169266573Sneel error = emulate_inout_port(vm, vcpuid, vmexit, retu); 170266573Sneel 171266573Sneel VCPU_CTR4(vm, vcpuid, "%s%s 0x%04x: %s", 172266573Sneel vmexit->u.inout.rep ? "rep " : "", 173266573Sneel inout_instruction(vmexit), 174266573Sneel vmexit->u.inout.port, 175266573Sneel error ? "error" : (*retu ? "userspace" : "handled")); 176266573Sneel 177264648Stychon return (error); 178263035Stychon} 179