vmm_lapic.c revision 267447
129088Smarkm/*- 229088Smarkm * Copyright (c) 2011 NetApp, Inc. 329088Smarkm * All rights reserved. 429088Smarkm * 529088Smarkm * Redistribution and use in source and binary forms, with or without 629088Smarkm * modification, are permitted provided that the following conditions 729088Smarkm * are met: 829088Smarkm * 1. Redistributions of source code must retain the above copyright 929088Smarkm * notice, this list of conditions and the following disclaimer. 1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1129088Smarkm * notice, this list of conditions and the following disclaimer in the 1229088Smarkm * documentation and/or other materials provided with the distribution. 1329088Smarkm * 1429088Smarkm * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 1529088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1629088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1729088Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 1829088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1929088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2029088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2129088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2229088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2329088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2429088Smarkm * SUCH DAMAGE. 2529088Smarkm * 2629088Smarkm * $FreeBSD: stable/10/sys/amd64/vmm/vmm_lapic.c 267447 2014-06-13 19:10:40Z jhb $ 2729088Smarkm */ 2829088Smarkm 2929088Smarkm#include <sys/cdefs.h> 3029088Smarkm__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/vmm_lapic.c 267447 2014-06-13 19:10:40Z jhb $"); 3129088Smarkm 3229088Smarkm#include <sys/param.h> 3329088Smarkm#include <sys/systm.h> 3484305Smarkm#include <sys/smp.h> 3587139Smarkm 3684305Smarkm#include <x86/specialreg.h> 3784305Smarkm#include <x86/apicreg.h> 3829088Smarkm 3929088Smarkm#include <machine/vmm.h> 4029088Smarkm#include "vmm_ipi.h" 4129088Smarkm#include "vmm_ktr.h" 4229088Smarkm#include "vmm_lapic.h" 4329088Smarkm#include "vlapic.h" 4429088Smarkm 4529088Smarkm/* 4629088Smarkm * Some MSI message definitions 4729088Smarkm */ 4829088Smarkm#define MSI_X86_ADDR_MASK 0xfff00000 4929088Smarkm#define MSI_X86_ADDR_BASE 0xfee00000 5029088Smarkm#define MSI_X86_ADDR_RH 0x00000008 /* Redirection Hint */ 5129088Smarkm#define MSI_X86_ADDR_LOG 0x00000004 /* Destination Mode */ 5229088Smarkm 5329088Smarkmint 5429088Smarkmlapic_set_intr(struct vm *vm, int cpu, int vector, bool level) 5529088Smarkm{ 5629088Smarkm struct vlapic *vlapic; 5729088Smarkm 5829088Smarkm if (cpu < 0 || cpu >= VM_MAXCPU) 5929088Smarkm return (EINVAL); 6029088Smarkm 6129088Smarkm if (vector < 32 || vector > 255) 6229088Smarkm return (EINVAL); 6329088Smarkm 6429088Smarkm vlapic = vm_lapic(vm, cpu); 6529088Smarkm if (vlapic_set_intr_ready(vlapic, vector, level)) 6629088Smarkm vcpu_notify_event(vm, cpu, true); 6729088Smarkm return (0); 6829088Smarkm} 6929088Smarkm 7029088Smarkmint 7129088Smarkmlapic_set_local_intr(struct vm *vm, int cpu, int vector) 7229088Smarkm{ 7329088Smarkm struct vlapic *vlapic; 7429088Smarkm cpuset_t dmask; 7529088Smarkm int error; 7629088Smarkm 7729088Smarkm if (cpu < -1 || cpu >= VM_MAXCPU) 7887139Smarkm return (EINVAL); 7929088Smarkm 8087139Smarkm if (cpu == -1) 8129088Smarkm dmask = vm_active_cpus(vm); 8229088Smarkm else 8329088Smarkm CPU_SETOF(cpu, &dmask); 8429088Smarkm error = 0; 8529088Smarkm while ((cpu = CPU_FFS(&dmask)) != 0) { 8629088Smarkm cpu--; 8729088Smarkm CPU_CLR(cpu, &dmask); 8829088Smarkm vlapic = vm_lapic(vm, cpu); 8929088Smarkm error = vlapic_trigger_lvt(vlapic, vector); 9087155Smarkm if (error) 9187155Smarkm break; 9229088Smarkm } 9329088Smarkm 9429088Smarkm return (error); 9529088Smarkm} 9629088Smarkm 9729088Smarkmint 9829088Smarkmlapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg) 9929088Smarkm{ 10029088Smarkm int delmode, vec; 10129088Smarkm uint32_t dest; 10229088Smarkm bool phys; 10329088Smarkm 10429088Smarkm VM_CTR2(vm, "lapic MSI addr: %#lx msg: %#lx", addr, msg); 10529088Smarkm 10629088Smarkm if ((addr & MSI_X86_ADDR_MASK) != MSI_X86_ADDR_BASE) { 10729088Smarkm VM_CTR1(vm, "lapic MSI invalid addr %#lx", addr); 10829088Smarkm return (-1); 10929088Smarkm } 11029088Smarkm 11129088Smarkm /* 11229088Smarkm * Extract the x86-specific fields from the MSI addr/msg 11329088Smarkm * params according to the Intel Arch spec, Vol3 Ch 10. 11429088Smarkm * 11529088Smarkm * The PCI specification does not support level triggered 11629088Smarkm * MSI/MSI-X so ignore trigger level in 'msg'. 11729088Smarkm * 11829088Smarkm * The 'dest' is interpreted as a logical APIC ID if both 11929088Smarkm * the Redirection Hint and Destination Mode are '1' and 12029088Smarkm * physical otherwise. 12129088Smarkm */ 12229088Smarkm dest = (addr >> 12) & 0xff; 12329088Smarkm phys = ((addr & (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG)) != 12429088Smarkm (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG)); 12529088Smarkm delmode = msg & APIC_DELMODE_MASK; 12629088Smarkm vec = msg & 0xff; 12729088Smarkm 12829088Smarkm VM_CTR3(vm, "lapic MSI %s dest %#x, vec %d", 12929088Smarkm phys ? "physical" : "logical", dest, vec); 13029088Smarkm 13129088Smarkm vlapic_deliver_intr(vm, LAPIC_TRIG_EDGE, dest, phys, delmode, vec); 13229088Smarkm return (0); 13329088Smarkm} 13429088Smarkm 13529088Smarkmstatic boolean_t 13629088Smarkmx2apic_msr(u_int msr) 13729088Smarkm{ 13829088Smarkm if (msr >= 0x800 && msr <= 0xBFF) 13929088Smarkm return (TRUE); 14029088Smarkm else 14129088Smarkm return (FALSE); 14229088Smarkm} 14329088Smarkm 14429088Smarkmstatic u_int 14529088Smarkmx2apic_msr_to_regoff(u_int msr) 14629088Smarkm{ 14729088Smarkm 14829088Smarkm return ((msr - 0x800) << 4); 14929088Smarkm} 15029088Smarkm 15129088Smarkmboolean_t 15229088Smarkmlapic_msr(u_int msr) 15329088Smarkm{ 15429088Smarkm 15529088Smarkm if (x2apic_msr(msr) || (msr == MSR_APICBASE)) 15629088Smarkm return (TRUE); 15729088Smarkm else 15829088Smarkm return (FALSE); 15929088Smarkm} 16029088Smarkm 16129088Smarkmint 16229088Smarkmlapic_rdmsr(struct vm *vm, int cpu, u_int msr, uint64_t *rval, bool *retu) 16329088Smarkm{ 16429088Smarkm int error; 16529088Smarkm u_int offset; 16629088Smarkm struct vlapic *vlapic; 16729088Smarkm 16829088Smarkm vlapic = vm_lapic(vm, cpu); 16929088Smarkm 17029088Smarkm if (msr == MSR_APICBASE) { 17129088Smarkm *rval = vlapic_get_apicbase(vlapic); 17229088Smarkm error = 0; 17329088Smarkm } else { 17429088Smarkm offset = x2apic_msr_to_regoff(msr); 17529088Smarkm error = vlapic_read(vlapic, 0, offset, rval, retu); 17629088Smarkm } 17729088Smarkm 17829088Smarkm return (error); 17929088Smarkm} 18029088Smarkm 18129088Smarkmint 18229088Smarkmlapic_wrmsr(struct vm *vm, int cpu, u_int msr, uint64_t val, bool *retu) 18329088Smarkm{ 18429088Smarkm int error; 18529088Smarkm u_int offset; 18629088Smarkm struct vlapic *vlapic; 18729088Smarkm 18829088Smarkm vlapic = vm_lapic(vm, cpu); 18929088Smarkm 19029088Smarkm if (msr == MSR_APICBASE) { 19129088Smarkm error = vlapic_set_apicbase(vlapic, val); 19229088Smarkm } else { 19329088Smarkm offset = x2apic_msr_to_regoff(msr); 19429088Smarkm error = vlapic_write(vlapic, 0, offset, val, retu); 19529088Smarkm } 19629088Smarkm 19729088Smarkm return (error); 19829088Smarkm} 19929088Smarkm 20029088Smarkmint 20129088Smarkmlapic_mmio_write(void *vm, int cpu, uint64_t gpa, uint64_t wval, int size, 20229088Smarkm void *arg) 20329088Smarkm{ 20429088Smarkm int error; 20529088Smarkm uint64_t off; 20629088Smarkm struct vlapic *vlapic; 20729088Smarkm 20829088Smarkm off = gpa - DEFAULT_APIC_BASE; 20929088Smarkm 21029088Smarkm /* 21129088Smarkm * Memory mapped local apic accesses must be 4 bytes wide and 21229088Smarkm * aligned on a 16-byte boundary. 21329088Smarkm */ 21429088Smarkm if (size != 4 || off & 0xf) 21529088Smarkm return (EINVAL); 21629088Smarkm 21729088Smarkm vlapic = vm_lapic(vm, cpu); 21829088Smarkm error = vlapic_write(vlapic, 1, off, wval, arg); 21929088Smarkm return (error); 22029088Smarkm} 22129088Smarkm 22229088Smarkmint 22329088Smarkmlapic_mmio_read(void *vm, int cpu, uint64_t gpa, uint64_t *rval, int size, 22429088Smarkm void *arg) 22529088Smarkm{ 22629088Smarkm int error; 22729088Smarkm uint64_t off; 22829088Smarkm struct vlapic *vlapic; 22929088Smarkm 23029088Smarkm off = gpa - DEFAULT_APIC_BASE; 23129088Smarkm 23229088Smarkm /* 23329088Smarkm * Memory mapped local apic accesses must be 4 bytes wide and 23429088Smarkm * aligned on a 16-byte boundary. 23529088Smarkm */ 23629088Smarkm if (size != 4 || off & 0xf) 23729088Smarkm return (EINVAL); 23829088Smarkm 23929088Smarkm vlapic = vm_lapic(vm, cpu); 24029088Smarkm error = vlapic_read(vlapic, 1, off, rval, arg); 24129088Smarkm return (error); 24229088Smarkm} 24329088Smarkm