Merge branch 'kvm-updates/2.6.28' of git://git.kernel.org/pub/scm/linux/kernel/git/avi/kvm
* 'kvm-updates/2.6.28' of git://git.kernel.org/pub/scm/linux/kernel/git/avi/kvm: (134 commits) KVM: ia64: Add intel iommu support for guests. KVM: ia64: add directed mmio range support for kvm guests KVM: ia64: Make pmt table be able to hold physical mmio entries. KVM: Move irqchip_in_kernel() from ioapic.h to irq.h KVM: Separate irq ack notification out of arch/x86/kvm/irq.c KVM: Change is_mmio_pfn to kvm_is_mmio_pfn, and make it common for all archs KVM: Move device assignment logic to common code KVM: Device Assignment: Move vtd.c from arch/x86/kvm/ to virt/kvm/ KVM: VMX: enable invlpg exiting if EPT is disabled KVM: x86: Silence various LAPIC-related host kernel messages KVM: Device Assignment: Map mmio pages into VT-d page table KVM: PIC: enhance IPI avoidance KVM: MMU: add "oos_shadow" parameter to disable oos KVM: MMU: speed up mmu_unsync_walk KVM: MMU: out of sync shadow core KVM: MMU: mmu_convert_notrap helper KVM: MMU: awareness of new kvm_mmu_zap_page behaviour KVM: MMU: mmu_parent_walk KVM: x86: trap invlpg KVM: MMU: sync roots on mmu reload ...
This commit is contained in:
commit
08d19f51f0
|
@ -2448,7 +2448,14 @@ S: Supported
|
||||||
|
|
||||||
KERNEL VIRTUAL MACHINE (KVM)
|
KERNEL VIRTUAL MACHINE (KVM)
|
||||||
P: Avi Kivity
|
P: Avi Kivity
|
||||||
M: avi@qumranet.com
|
M: avi@redhat.com
|
||||||
|
L: kvm@vger.kernel.org
|
||||||
|
W: http://kvm.qumranet.com
|
||||||
|
S: Supported
|
||||||
|
|
||||||
|
KERNEL VIRTUAL MACHINE (KVM) FOR AMD-V
|
||||||
|
P: Joerg Roedel
|
||||||
|
M: joerg.roedel@amd.com
|
||||||
L: kvm@vger.kernel.org
|
L: kvm@vger.kernel.org
|
||||||
W: http://kvm.qumranet.com
|
W: http://kvm.qumranet.com
|
||||||
S: Supported
|
S: Supported
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
#define GPFN_IOSAPIC (4UL << 60) /* IOSAPIC base */
|
#define GPFN_IOSAPIC (4UL << 60) /* IOSAPIC base */
|
||||||
#define GPFN_LEGACY_IO (5UL << 60) /* Legacy I/O base */
|
#define GPFN_LEGACY_IO (5UL << 60) /* Legacy I/O base */
|
||||||
#define GPFN_GFW (6UL << 60) /* Guest Firmware */
|
#define GPFN_GFW (6UL << 60) /* Guest Firmware */
|
||||||
#define GPFN_HIGH_MMIO (7UL << 60) /* High MMIO range */
|
#define GPFN_PHYS_MMIO (7UL << 60) /* Directed MMIO Range */
|
||||||
|
|
||||||
#define GPFN_IO_MASK (7UL << 60) /* Guest pfn is I/O type */
|
#define GPFN_IO_MASK (7UL << 60) /* Guest pfn is I/O type */
|
||||||
#define GPFN_INV_MASK (1UL << 63) /* Guest pfn is invalid */
|
#define GPFN_INV_MASK (1UL << 63) /* Guest pfn is invalid */
|
||||||
|
@ -413,6 +413,10 @@ struct kvm_arch {
|
||||||
struct kvm_ioapic *vioapic;
|
struct kvm_ioapic *vioapic;
|
||||||
struct kvm_vm_stat stat;
|
struct kvm_vm_stat stat;
|
||||||
struct kvm_sal_data rdv_sal_data;
|
struct kvm_sal_data rdv_sal_data;
|
||||||
|
|
||||||
|
struct list_head assigned_dev_head;
|
||||||
|
struct dmar_domain *intel_iommu_domain;
|
||||||
|
struct hlist_head irq_ack_notifier_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
union cpuid3_t {
|
union cpuid3_t {
|
||||||
|
|
|
@ -46,4 +46,6 @@ config KVM_INTEL
|
||||||
config KVM_TRACE
|
config KVM_TRACE
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
source drivers/virtio/Kconfig
|
||||||
|
|
||||||
endif # VIRTUALIZATION
|
endif # VIRTUALIZATION
|
||||||
|
|
|
@ -44,7 +44,11 @@ EXTRA_CFLAGS += -Ivirt/kvm -Iarch/ia64/kvm/
|
||||||
EXTRA_AFLAGS += -Ivirt/kvm -Iarch/ia64/kvm/
|
EXTRA_AFLAGS += -Ivirt/kvm -Iarch/ia64/kvm/
|
||||||
|
|
||||||
common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
|
common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
|
||||||
coalesced_mmio.o)
|
coalesced_mmio.o irq_comm.o)
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_DMAR),y)
|
||||||
|
common-objs += $(addprefix ../../../virt/kvm/, vtd.o)
|
||||||
|
endif
|
||||||
|
|
||||||
kvm-objs := $(common-objs) kvm-ia64.o kvm_fw.o
|
kvm-objs := $(common-objs) kvm-ia64.o kvm_fw.o
|
||||||
obj-$(CONFIG_KVM) += kvm.o
|
obj-$(CONFIG_KVM) += kvm.o
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* irq.h: In-kernel interrupt controller related definitions
|
||||||
|
* Copyright (c) 2008, Intel Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||||
|
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Xiantao Zhang <xiantao.zhang@intel.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __IRQ_H
|
||||||
|
#define __IRQ_H
|
||||||
|
|
||||||
|
static inline int irqchip_in_kernel(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -31,6 +31,7 @@
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/hrtimer.h>
|
#include <linux/hrtimer.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/intel-iommu.h>
|
||||||
|
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
#include <asm/gcc_intrin.h>
|
#include <asm/gcc_intrin.h>
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
#include "iodev.h"
|
#include "iodev.h"
|
||||||
#include "ioapic.h"
|
#include "ioapic.h"
|
||||||
#include "lapic.h"
|
#include "lapic.h"
|
||||||
|
#include "irq.h"
|
||||||
|
|
||||||
static unsigned long kvm_vmm_base;
|
static unsigned long kvm_vmm_base;
|
||||||
static unsigned long kvm_vsa_base;
|
static unsigned long kvm_vsa_base;
|
||||||
|
@ -179,12 +181,16 @@ int kvm_dev_ioctl_check_extension(long ext)
|
||||||
switch (ext) {
|
switch (ext) {
|
||||||
case KVM_CAP_IRQCHIP:
|
case KVM_CAP_IRQCHIP:
|
||||||
case KVM_CAP_USER_MEMORY:
|
case KVM_CAP_USER_MEMORY:
|
||||||
|
case KVM_CAP_MP_STATE:
|
||||||
|
|
||||||
r = 1;
|
r = 1;
|
||||||
break;
|
break;
|
||||||
case KVM_CAP_COALESCED_MMIO:
|
case KVM_CAP_COALESCED_MMIO:
|
||||||
r = KVM_COALESCED_MMIO_PAGE_OFFSET;
|
r = KVM_COALESCED_MMIO_PAGE_OFFSET;
|
||||||
break;
|
break;
|
||||||
|
case KVM_CAP_IOMMU:
|
||||||
|
r = intel_iommu_found();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
r = 0;
|
r = 0;
|
||||||
}
|
}
|
||||||
|
@ -771,6 +777,7 @@ static void kvm_init_vm(struct kvm *kvm)
|
||||||
*/
|
*/
|
||||||
kvm_build_io_pmt(kvm);
|
kvm_build_io_pmt(kvm);
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct kvm *kvm_arch_create_vm(void)
|
struct kvm *kvm_arch_create_vm(void)
|
||||||
|
@ -1334,6 +1341,10 @@ static void kvm_release_vm_pages(struct kvm *kvm)
|
||||||
|
|
||||||
void kvm_arch_destroy_vm(struct kvm *kvm)
|
void kvm_arch_destroy_vm(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
|
kvm_iommu_unmap_guest(kvm);
|
||||||
|
#ifdef KVM_CAP_DEVICE_ASSIGNMENT
|
||||||
|
kvm_free_all_assigned_devices(kvm);
|
||||||
|
#endif
|
||||||
kfree(kvm->arch.vioapic);
|
kfree(kvm->arch.vioapic);
|
||||||
kvm_release_vm_pages(kvm);
|
kvm_release_vm_pages(kvm);
|
||||||
kvm_free_physmem(kvm);
|
kvm_free_physmem(kvm);
|
||||||
|
@ -1435,17 +1446,24 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
|
||||||
int user_alloc)
|
int user_alloc)
|
||||||
{
|
{
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
struct page *page;
|
unsigned long pfn;
|
||||||
int npages = mem->memory_size >> PAGE_SHIFT;
|
int npages = mem->memory_size >> PAGE_SHIFT;
|
||||||
struct kvm_memory_slot *memslot = &kvm->memslots[mem->slot];
|
struct kvm_memory_slot *memslot = &kvm->memslots[mem->slot];
|
||||||
unsigned long base_gfn = memslot->base_gfn;
|
unsigned long base_gfn = memslot->base_gfn;
|
||||||
|
|
||||||
for (i = 0; i < npages; i++) {
|
for (i = 0; i < npages; i++) {
|
||||||
page = gfn_to_page(kvm, base_gfn + i);
|
pfn = gfn_to_pfn(kvm, base_gfn + i);
|
||||||
kvm_set_pmt_entry(kvm, base_gfn + i,
|
if (!kvm_is_mmio_pfn(pfn)) {
|
||||||
page_to_pfn(page) << PAGE_SHIFT,
|
kvm_set_pmt_entry(kvm, base_gfn + i,
|
||||||
_PAGE_AR_RWX|_PAGE_MA_WB);
|
pfn << PAGE_SHIFT,
|
||||||
memslot->rmap[i] = (unsigned long)page;
|
_PAGE_AR_RWX | _PAGE_MA_WB);
|
||||||
|
memslot->rmap[i] = (unsigned long)pfn_to_page(pfn);
|
||||||
|
} else {
|
||||||
|
kvm_set_pmt_entry(kvm, base_gfn + i,
|
||||||
|
GPFN_PHYS_MMIO | (pfn << PAGE_SHIFT),
|
||||||
|
_PAGE_MA_UC);
|
||||||
|
memslot->rmap[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1789,11 +1807,43 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
|
||||||
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
|
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_mp_state *mp_state)
|
struct kvm_mp_state *mp_state)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
vcpu_load(vcpu);
|
||||||
|
mp_state->mp_state = vcpu->arch.mp_state;
|
||||||
|
vcpu_put(vcpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vcpu_reset(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
long psr;
|
||||||
|
local_irq_save(psr);
|
||||||
|
r = kvm_insert_vmm_mapping(vcpu);
|
||||||
|
if (r)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
vcpu->arch.launched = 0;
|
||||||
|
kvm_arch_vcpu_uninit(vcpu);
|
||||||
|
r = kvm_arch_vcpu_init(vcpu);
|
||||||
|
if (r)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
kvm_purge_vmm_mapping(vcpu);
|
||||||
|
r = 0;
|
||||||
|
fail:
|
||||||
|
local_irq_restore(psr);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
|
int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_mp_state *mp_state)
|
struct kvm_mp_state *mp_state)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
int r = 0;
|
||||||
|
|
||||||
|
vcpu_load(vcpu);
|
||||||
|
vcpu->arch.mp_state = mp_state->mp_state;
|
||||||
|
if (vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)
|
||||||
|
r = vcpu_reset(vcpu);
|
||||||
|
vcpu_put(vcpu);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,27 +50,18 @@
|
||||||
|
|
||||||
#define PAL_VSA_SYNC_READ \
|
#define PAL_VSA_SYNC_READ \
|
||||||
/* begin to call pal vps sync_read */ \
|
/* begin to call pal vps sync_read */ \
|
||||||
|
{.mii; \
|
||||||
add r25 = VMM_VPD_BASE_OFFSET, r21; \
|
add r25 = VMM_VPD_BASE_OFFSET, r21; \
|
||||||
adds r20 = VMM_VCPU_VSA_BASE_OFFSET, r21; /* entry point */ \
|
|
||||||
;; \
|
|
||||||
ld8 r25 = [r25]; /* read vpd base */ \
|
|
||||||
ld8 r20 = [r20]; \
|
|
||||||
;; \
|
|
||||||
add r20 = PAL_VPS_SYNC_READ,r20; \
|
|
||||||
;; \
|
|
||||||
{ .mii; \
|
|
||||||
nop 0x0; \
|
nop 0x0; \
|
||||||
mov r24 = ip; \
|
mov r24=ip; \
|
||||||
mov b0 = r20; \
|
;; \
|
||||||
|
} \
|
||||||
|
{.mmb \
|
||||||
|
add r24=0x20, r24; \
|
||||||
|
ld8 r25 = [r25]; /* read vpd base */ \
|
||||||
|
br.cond.sptk kvm_vps_sync_read; /*call the service*/ \
|
||||||
;; \
|
;; \
|
||||||
}; \
|
}; \
|
||||||
{ .mmb; \
|
|
||||||
add r24 = 0x20, r24; \
|
|
||||||
nop 0x0; \
|
|
||||||
br.cond.sptk b0; /* call the service */ \
|
|
||||||
;; \
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define KVM_MINSTATE_GET_CURRENT(reg) mov reg=r21
|
#define KVM_MINSTATE_GET_CURRENT(reg) mov reg=r21
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
/*
|
/*
|
||||||
* arch/ia64/vmx/optvfault.S
|
* arch/ia64/kvm/optvfault.S
|
||||||
* optimize virtualization fault handler
|
* optimize virtualization fault handler
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006 Intel Co
|
* Copyright (C) 2006 Intel Co
|
||||||
* Xuefei Xu (Anthony Xu) <anthony.xu@intel.com>
|
* Xuefei Xu (Anthony Xu) <anthony.xu@intel.com>
|
||||||
|
* Copyright (C) 2008 Intel Co
|
||||||
|
* Add the support for Tukwila processors.
|
||||||
|
* Xiantao Zhang <xiantao.zhang@intel.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <asm/asmmacro.h>
|
#include <asm/asmmacro.h>
|
||||||
|
@ -20,6 +23,98 @@
|
||||||
#define ACCE_MOV_TO_PSR
|
#define ACCE_MOV_TO_PSR
|
||||||
#define ACCE_THASH
|
#define ACCE_THASH
|
||||||
|
|
||||||
|
#define VMX_VPS_SYNC_READ \
|
||||||
|
add r16=VMM_VPD_BASE_OFFSET,r21; \
|
||||||
|
mov r17 = b0; \
|
||||||
|
mov r18 = r24; \
|
||||||
|
mov r19 = r25; \
|
||||||
|
mov r20 = r31; \
|
||||||
|
;; \
|
||||||
|
{.mii; \
|
||||||
|
ld8 r16 = [r16]; \
|
||||||
|
nop 0x0; \
|
||||||
|
mov r24 = ip; \
|
||||||
|
;; \
|
||||||
|
}; \
|
||||||
|
{.mmb; \
|
||||||
|
add r24=0x20, r24; \
|
||||||
|
mov r25 =r16; \
|
||||||
|
br.sptk.many kvm_vps_sync_read; \
|
||||||
|
}; \
|
||||||
|
mov b0 = r17; \
|
||||||
|
mov r24 = r18; \
|
||||||
|
mov r25 = r19; \
|
||||||
|
mov r31 = r20
|
||||||
|
|
||||||
|
ENTRY(kvm_vps_entry)
|
||||||
|
adds r29 = VMM_VCPU_VSA_BASE_OFFSET,r21
|
||||||
|
;;
|
||||||
|
ld8 r29 = [r29]
|
||||||
|
;;
|
||||||
|
add r29 = r29, r30
|
||||||
|
;;
|
||||||
|
mov b0 = r29
|
||||||
|
br.sptk.many b0
|
||||||
|
END(kvm_vps_entry)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inputs:
|
||||||
|
* r24 : return address
|
||||||
|
* r25 : vpd
|
||||||
|
* r29 : scratch
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
GLOBAL_ENTRY(kvm_vps_sync_read)
|
||||||
|
movl r30 = PAL_VPS_SYNC_READ
|
||||||
|
;;
|
||||||
|
br.sptk.many kvm_vps_entry
|
||||||
|
END(kvm_vps_sync_read)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inputs:
|
||||||
|
* r24 : return address
|
||||||
|
* r25 : vpd
|
||||||
|
* r29 : scratch
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
GLOBAL_ENTRY(kvm_vps_sync_write)
|
||||||
|
movl r30 = PAL_VPS_SYNC_WRITE
|
||||||
|
;;
|
||||||
|
br.sptk.many kvm_vps_entry
|
||||||
|
END(kvm_vps_sync_write)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inputs:
|
||||||
|
* r23 : pr
|
||||||
|
* r24 : guest b0
|
||||||
|
* r25 : vpd
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
GLOBAL_ENTRY(kvm_vps_resume_normal)
|
||||||
|
movl r30 = PAL_VPS_RESUME_NORMAL
|
||||||
|
;;
|
||||||
|
mov pr=r23,-2
|
||||||
|
br.sptk.many kvm_vps_entry
|
||||||
|
END(kvm_vps_resume_normal)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inputs:
|
||||||
|
* r23 : pr
|
||||||
|
* r24 : guest b0
|
||||||
|
* r25 : vpd
|
||||||
|
* r17 : isr
|
||||||
|
*/
|
||||||
|
GLOBAL_ENTRY(kvm_vps_resume_handler)
|
||||||
|
movl r30 = PAL_VPS_RESUME_HANDLER
|
||||||
|
;;
|
||||||
|
ld8 r27=[r25]
|
||||||
|
shr r17=r17,IA64_ISR_IR_BIT
|
||||||
|
;;
|
||||||
|
dep r27=r17,r27,63,1 // bit 63 of r27 indicate whether enable CFLE
|
||||||
|
mov pr=r23,-2
|
||||||
|
br.sptk.many kvm_vps_entry
|
||||||
|
END(kvm_vps_resume_handler)
|
||||||
|
|
||||||
//mov r1=ar3
|
//mov r1=ar3
|
||||||
GLOBAL_ENTRY(kvm_asm_mov_from_ar)
|
GLOBAL_ENTRY(kvm_asm_mov_from_ar)
|
||||||
#ifndef ACCE_MOV_FROM_AR
|
#ifndef ACCE_MOV_FROM_AR
|
||||||
|
@ -157,11 +252,11 @@ GLOBAL_ENTRY(kvm_asm_rsm)
|
||||||
#ifndef ACCE_RSM
|
#ifndef ACCE_RSM
|
||||||
br.many kvm_virtualization_fault_back
|
br.many kvm_virtualization_fault_back
|
||||||
#endif
|
#endif
|
||||||
add r16=VMM_VPD_BASE_OFFSET,r21
|
VMX_VPS_SYNC_READ
|
||||||
|
;;
|
||||||
extr.u r26=r25,6,21
|
extr.u r26=r25,6,21
|
||||||
extr.u r27=r25,31,2
|
extr.u r27=r25,31,2
|
||||||
;;
|
;;
|
||||||
ld8 r16=[r16]
|
|
||||||
extr.u r28=r25,36,1
|
extr.u r28=r25,36,1
|
||||||
dep r26=r27,r26,21,2
|
dep r26=r27,r26,21,2
|
||||||
;;
|
;;
|
||||||
|
@ -196,7 +291,7 @@ GLOBAL_ENTRY(kvm_asm_rsm)
|
||||||
tbit.nz p6,p0=r23,0
|
tbit.nz p6,p0=r23,0
|
||||||
;;
|
;;
|
||||||
tbit.z.or p6,p0=r26,IA64_PSR_DT_BIT
|
tbit.z.or p6,p0=r26,IA64_PSR_DT_BIT
|
||||||
(p6) br.dptk kvm_resume_to_guest
|
(p6) br.dptk kvm_resume_to_guest_with_sync
|
||||||
;;
|
;;
|
||||||
add r26=VMM_VCPU_META_RR0_OFFSET,r21
|
add r26=VMM_VCPU_META_RR0_OFFSET,r21
|
||||||
add r27=VMM_VCPU_META_RR0_OFFSET+8,r21
|
add r27=VMM_VCPU_META_RR0_OFFSET+8,r21
|
||||||
|
@ -212,7 +307,7 @@ GLOBAL_ENTRY(kvm_asm_rsm)
|
||||||
mov rr[r28]=r27
|
mov rr[r28]=r27
|
||||||
;;
|
;;
|
||||||
srlz.d
|
srlz.d
|
||||||
br.many kvm_resume_to_guest
|
br.many kvm_resume_to_guest_with_sync
|
||||||
END(kvm_asm_rsm)
|
END(kvm_asm_rsm)
|
||||||
|
|
||||||
|
|
||||||
|
@ -221,11 +316,11 @@ GLOBAL_ENTRY(kvm_asm_ssm)
|
||||||
#ifndef ACCE_SSM
|
#ifndef ACCE_SSM
|
||||||
br.many kvm_virtualization_fault_back
|
br.many kvm_virtualization_fault_back
|
||||||
#endif
|
#endif
|
||||||
add r16=VMM_VPD_BASE_OFFSET,r21
|
VMX_VPS_SYNC_READ
|
||||||
|
;;
|
||||||
extr.u r26=r25,6,21
|
extr.u r26=r25,6,21
|
||||||
extr.u r27=r25,31,2
|
extr.u r27=r25,31,2
|
||||||
;;
|
;;
|
||||||
ld8 r16=[r16]
|
|
||||||
extr.u r28=r25,36,1
|
extr.u r28=r25,36,1
|
||||||
dep r26=r27,r26,21,2
|
dep r26=r27,r26,21,2
|
||||||
;; //r26 is imm24
|
;; //r26 is imm24
|
||||||
|
@ -271,7 +366,7 @@ kvm_asm_ssm_1:
|
||||||
tbit.nz p6,p0=r29,IA64_PSR_I_BIT
|
tbit.nz p6,p0=r29,IA64_PSR_I_BIT
|
||||||
;;
|
;;
|
||||||
tbit.z.or p6,p0=r19,IA64_PSR_I_BIT
|
tbit.z.or p6,p0=r19,IA64_PSR_I_BIT
|
||||||
(p6) br.dptk kvm_resume_to_guest
|
(p6) br.dptk kvm_resume_to_guest_with_sync
|
||||||
;;
|
;;
|
||||||
add r29=VPD_VTPR_START_OFFSET,r16
|
add r29=VPD_VTPR_START_OFFSET,r16
|
||||||
add r30=VPD_VHPI_START_OFFSET,r16
|
add r30=VPD_VHPI_START_OFFSET,r16
|
||||||
|
@ -286,7 +381,7 @@ kvm_asm_ssm_1:
|
||||||
;;
|
;;
|
||||||
cmp.gt p6,p0=r30,r17
|
cmp.gt p6,p0=r30,r17
|
||||||
(p6) br.dpnt.few kvm_asm_dispatch_vexirq
|
(p6) br.dpnt.few kvm_asm_dispatch_vexirq
|
||||||
br.many kvm_resume_to_guest
|
br.many kvm_resume_to_guest_with_sync
|
||||||
END(kvm_asm_ssm)
|
END(kvm_asm_ssm)
|
||||||
|
|
||||||
|
|
||||||
|
@ -295,10 +390,9 @@ GLOBAL_ENTRY(kvm_asm_mov_to_psr)
|
||||||
#ifndef ACCE_MOV_TO_PSR
|
#ifndef ACCE_MOV_TO_PSR
|
||||||
br.many kvm_virtualization_fault_back
|
br.many kvm_virtualization_fault_back
|
||||||
#endif
|
#endif
|
||||||
add r16=VMM_VPD_BASE_OFFSET,r21
|
VMX_VPS_SYNC_READ
|
||||||
extr.u r26=r25,13,7 //r2
|
|
||||||
;;
|
;;
|
||||||
ld8 r16=[r16]
|
extr.u r26=r25,13,7 //r2
|
||||||
addl r20=@gprel(asm_mov_from_reg),gp
|
addl r20=@gprel(asm_mov_from_reg),gp
|
||||||
;;
|
;;
|
||||||
adds r30=kvm_asm_mov_to_psr_back-asm_mov_from_reg,r20
|
adds r30=kvm_asm_mov_to_psr_back-asm_mov_from_reg,r20
|
||||||
|
@ -374,7 +468,7 @@ kvm_asm_mov_to_psr_1:
|
||||||
;;
|
;;
|
||||||
tbit.nz.or p6,p0=r17,IA64_PSR_I_BIT
|
tbit.nz.or p6,p0=r17,IA64_PSR_I_BIT
|
||||||
tbit.z.or p6,p0=r30,IA64_PSR_I_BIT
|
tbit.z.or p6,p0=r30,IA64_PSR_I_BIT
|
||||||
(p6) br.dpnt.few kvm_resume_to_guest
|
(p6) br.dpnt.few kvm_resume_to_guest_with_sync
|
||||||
;;
|
;;
|
||||||
add r29=VPD_VTPR_START_OFFSET,r16
|
add r29=VPD_VTPR_START_OFFSET,r16
|
||||||
add r30=VPD_VHPI_START_OFFSET,r16
|
add r30=VPD_VHPI_START_OFFSET,r16
|
||||||
|
@ -389,13 +483,29 @@ kvm_asm_mov_to_psr_1:
|
||||||
;;
|
;;
|
||||||
cmp.gt p6,p0=r30,r17
|
cmp.gt p6,p0=r30,r17
|
||||||
(p6) br.dpnt.few kvm_asm_dispatch_vexirq
|
(p6) br.dpnt.few kvm_asm_dispatch_vexirq
|
||||||
br.many kvm_resume_to_guest
|
br.many kvm_resume_to_guest_with_sync
|
||||||
END(kvm_asm_mov_to_psr)
|
END(kvm_asm_mov_to_psr)
|
||||||
|
|
||||||
|
|
||||||
ENTRY(kvm_asm_dispatch_vexirq)
|
ENTRY(kvm_asm_dispatch_vexirq)
|
||||||
//increment iip
|
//increment iip
|
||||||
|
mov r17 = b0
|
||||||
|
mov r18 = r31
|
||||||
|
{.mii
|
||||||
|
add r25=VMM_VPD_BASE_OFFSET,r21
|
||||||
|
nop 0x0
|
||||||
|
mov r24 = ip
|
||||||
|
;;
|
||||||
|
}
|
||||||
|
{.mmb
|
||||||
|
add r24 = 0x20, r24
|
||||||
|
ld8 r25 = [r25]
|
||||||
|
br.sptk.many kvm_vps_sync_write
|
||||||
|
}
|
||||||
|
mov b0 =r17
|
||||||
mov r16=cr.ipsr
|
mov r16=cr.ipsr
|
||||||
|
mov r31 = r18
|
||||||
|
mov r19 = 37
|
||||||
;;
|
;;
|
||||||
extr.u r17=r16,IA64_PSR_RI_BIT,2
|
extr.u r17=r16,IA64_PSR_RI_BIT,2
|
||||||
tbit.nz p6,p7=r16,IA64_PSR_RI_BIT+1
|
tbit.nz p6,p7=r16,IA64_PSR_RI_BIT+1
|
||||||
|
@ -435,25 +545,31 @@ GLOBAL_ENTRY(kvm_asm_thash)
|
||||||
;;
|
;;
|
||||||
kvm_asm_thash_back1:
|
kvm_asm_thash_back1:
|
||||||
shr.u r23=r19,61 // get RR number
|
shr.u r23=r19,61 // get RR number
|
||||||
adds r25=VMM_VCPU_VRR0_OFFSET,r21 // get vcpu->arch.vrr[0]'s addr
|
adds r28=VMM_VCPU_VRR0_OFFSET,r21 // get vcpu->arch.vrr[0]'s addr
|
||||||
adds r16=VMM_VPD_VPTA_OFFSET,r16 // get vpta
|
adds r16=VMM_VPD_VPTA_OFFSET,r16 // get vpta
|
||||||
;;
|
;;
|
||||||
shladd r27=r23,3,r25 // get vcpu->arch.vrr[r23]'s addr
|
shladd r27=r23,3,r28 // get vcpu->arch.vrr[r23]'s addr
|
||||||
ld8 r17=[r16] // get PTA
|
ld8 r17=[r16] // get PTA
|
||||||
mov r26=1
|
mov r26=1
|
||||||
;;
|
;;
|
||||||
extr.u r29=r17,2,6 // get pta.size
|
extr.u r29=r17,2,6 // get pta.size
|
||||||
ld8 r25=[r27] // get vcpu->arch.vrr[r23]'s value
|
ld8 r28=[r27] // get vcpu->arch.vrr[r23]'s value
|
||||||
;;
|
;;
|
||||||
extr.u r25=r25,2,6 // get rr.ps
|
mov b0=r24
|
||||||
|
//Fallback to C if pta.vf is set
|
||||||
|
tbit.nz p6,p0=r17, 8
|
||||||
|
;;
|
||||||
|
(p6) mov r24=EVENT_THASH
|
||||||
|
(p6) br.cond.dpnt.many kvm_virtualization_fault_back
|
||||||
|
extr.u r28=r28,2,6 // get rr.ps
|
||||||
shl r22=r26,r29 // 1UL << pta.size
|
shl r22=r26,r29 // 1UL << pta.size
|
||||||
;;
|
;;
|
||||||
shr.u r23=r19,r25 // vaddr >> rr.ps
|
shr.u r23=r19,r28 // vaddr >> rr.ps
|
||||||
adds r26=3,r29 // pta.size + 3
|
adds r26=3,r29 // pta.size + 3
|
||||||
shl r27=r17,3 // pta << 3
|
shl r27=r17,3 // pta << 3
|
||||||
;;
|
;;
|
||||||
shl r23=r23,3 // (vaddr >> rr.ps) << 3
|
shl r23=r23,3 // (vaddr >> rr.ps) << 3
|
||||||
shr.u r27=r27,r26 // (pta << 3) >> (pta.size+3)
|
shr.u r27=r27,r26 // (pta << 3) >> (pta.size+3)
|
||||||
movl r16=7<<61
|
movl r16=7<<61
|
||||||
;;
|
;;
|
||||||
adds r22=-1,r22 // (1UL << pta.size) - 1
|
adds r22=-1,r22 // (1UL << pta.size) - 1
|
||||||
|
@ -724,6 +840,29 @@ END(asm_mov_from_reg)
|
||||||
* r31: pr
|
* r31: pr
|
||||||
* r24: b0
|
* r24: b0
|
||||||
*/
|
*/
|
||||||
|
ENTRY(kvm_resume_to_guest_with_sync)
|
||||||
|
adds r19=VMM_VPD_BASE_OFFSET,r21
|
||||||
|
mov r16 = r31
|
||||||
|
mov r17 = r24
|
||||||
|
;;
|
||||||
|
{.mii
|
||||||
|
ld8 r25 =[r19]
|
||||||
|
nop 0x0
|
||||||
|
mov r24 = ip
|
||||||
|
;;
|
||||||
|
}
|
||||||
|
{.mmb
|
||||||
|
add r24 =0x20, r24
|
||||||
|
nop 0x0
|
||||||
|
br.sptk.many kvm_vps_sync_write
|
||||||
|
}
|
||||||
|
|
||||||
|
mov r31 = r16
|
||||||
|
mov r24 =r17
|
||||||
|
;;
|
||||||
|
br.sptk.many kvm_resume_to_guest
|
||||||
|
END(kvm_resume_to_guest_with_sync)
|
||||||
|
|
||||||
ENTRY(kvm_resume_to_guest)
|
ENTRY(kvm_resume_to_guest)
|
||||||
adds r16 = VMM_VCPU_SAVED_GP_OFFSET,r21
|
adds r16 = VMM_VCPU_SAVED_GP_OFFSET,r21
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -962,9 +962,9 @@ static void kvm_do_resume_op(struct kvm_vcpu *vcpu)
|
||||||
void vmm_transition(struct kvm_vcpu *vcpu)
|
void vmm_transition(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
ia64_call_vsa(PAL_VPS_SAVE, (unsigned long)vcpu->arch.vpd,
|
ia64_call_vsa(PAL_VPS_SAVE, (unsigned long)vcpu->arch.vpd,
|
||||||
0, 0, 0, 0, 0, 0);
|
1, 0, 0, 0, 0, 0);
|
||||||
vmm_trampoline(&vcpu->arch.guest, &vcpu->arch.host);
|
vmm_trampoline(&vcpu->arch.guest, &vcpu->arch.host);
|
||||||
ia64_call_vsa(PAL_VPS_RESTORE, (unsigned long)vcpu->arch.vpd,
|
ia64_call_vsa(PAL_VPS_RESTORE, (unsigned long)vcpu->arch.vpd,
|
||||||
0, 0, 0, 0, 0, 0);
|
1, 0, 0, 0, 0, 0);
|
||||||
kvm_do_resume_op(vcpu);
|
kvm_do_resume_op(vcpu);
|
||||||
}
|
}
|
||||||
|
|
|
@ -313,21 +313,21 @@ static inline void vcpu_set_tr(struct thash_data *trp, u64 pte, u64 itir,
|
||||||
trp->rid = rid;
|
trp->rid = rid;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern u64 kvm_lookup_mpa(u64 gpfn);
|
extern u64 kvm_get_mpt_entry(u64 gpfn);
|
||||||
extern u64 kvm_gpa_to_mpa(u64 gpa);
|
|
||||||
|
|
||||||
/* Return I/O type if trye */
|
|
||||||
#define __gpfn_is_io(gpfn) \
|
|
||||||
({ \
|
|
||||||
u64 pte, ret = 0; \
|
|
||||||
pte = kvm_lookup_mpa(gpfn); \
|
|
||||||
if (!(pte & GPFN_INV_MASK)) \
|
|
||||||
ret = pte & GPFN_IO_MASK; \
|
|
||||||
ret; \
|
|
||||||
})
|
|
||||||
|
|
||||||
|
/* Return I/ */
|
||||||
|
static inline u64 __gpfn_is_io(u64 gpfn)
|
||||||
|
{
|
||||||
|
u64 pte;
|
||||||
|
pte = kvm_get_mpt_entry(gpfn);
|
||||||
|
if (!(pte & GPFN_INV_MASK)) {
|
||||||
|
pte = pte & GPFN_IO_MASK;
|
||||||
|
if (pte != GPFN_PHYS_MMIO)
|
||||||
|
return pte;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define IA64_NO_FAULT 0
|
#define IA64_NO_FAULT 0
|
||||||
#define IA64_FAULT 1
|
#define IA64_FAULT 1
|
||||||
|
|
||||||
|
|
|
@ -1261,11 +1261,6 @@ kvm_rse_clear_invalid:
|
||||||
adds r19=VMM_VPD_VPSR_OFFSET,r18
|
adds r19=VMM_VPD_VPSR_OFFSET,r18
|
||||||
;;
|
;;
|
||||||
ld8 r19=[r19] //vpsr
|
ld8 r19=[r19] //vpsr
|
||||||
adds r20=VMM_VCPU_VSA_BASE_OFFSET,r21
|
|
||||||
;;
|
|
||||||
ld8 r20=[r20]
|
|
||||||
;;
|
|
||||||
//vsa_sync_write_start
|
|
||||||
mov r25=r18
|
mov r25=r18
|
||||||
adds r16= VMM_VCPU_GP_OFFSET,r21
|
adds r16= VMM_VCPU_GP_OFFSET,r21
|
||||||
;;
|
;;
|
||||||
|
@ -1274,10 +1269,7 @@ kvm_rse_clear_invalid:
|
||||||
;;
|
;;
|
||||||
add r24=r24,r16
|
add r24=r24,r16
|
||||||
;;
|
;;
|
||||||
add r16=PAL_VPS_SYNC_WRITE,r20
|
br.sptk.many kvm_vps_sync_write // call the service
|
||||||
;;
|
|
||||||
mov b0=r16
|
|
||||||
br.cond.sptk b0 // call the service
|
|
||||||
;;
|
;;
|
||||||
END(ia64_leave_hypervisor)
|
END(ia64_leave_hypervisor)
|
||||||
// fall through
|
// fall through
|
||||||
|
@ -1288,28 +1280,15 @@ GLOBAL_ENTRY(ia64_vmm_entry)
|
||||||
* r17:cr.isr
|
* r17:cr.isr
|
||||||
* r18:vpd
|
* r18:vpd
|
||||||
* r19:vpsr
|
* r19:vpsr
|
||||||
* r20:__vsa_base
|
|
||||||
* r22:b0
|
* r22:b0
|
||||||
* r23:predicate
|
* r23:predicate
|
||||||
*/
|
*/
|
||||||
mov r24=r22
|
mov r24=r22
|
||||||
mov r25=r18
|
mov r25=r18
|
||||||
tbit.nz p1,p2 = r19,IA64_PSR_IC_BIT // p1=vpsr.ic
|
tbit.nz p1,p2 = r19,IA64_PSR_IC_BIT // p1=vpsr.ic
|
||||||
|
(p1) br.cond.sptk.few kvm_vps_resume_normal
|
||||||
|
(p2) br.cond.sptk.many kvm_vps_resume_handler
|
||||||
;;
|
;;
|
||||||
(p1) add r29=PAL_VPS_RESUME_NORMAL,r20
|
|
||||||
(p1) br.sptk.many ia64_vmm_entry_out
|
|
||||||
;;
|
|
||||||
tbit.nz p1,p2 = r17,IA64_ISR_IR_BIT //p1=cr.isr.ir
|
|
||||||
;;
|
|
||||||
(p1) add r29=PAL_VPS_RESUME_NORMAL,r20
|
|
||||||
(p2) add r29=PAL_VPS_RESUME_HANDLER,r20
|
|
||||||
(p2) ld8 r26=[r25]
|
|
||||||
;;
|
|
||||||
ia64_vmm_entry_out:
|
|
||||||
mov pr=r23,-2
|
|
||||||
mov b0=r29
|
|
||||||
;;
|
|
||||||
br.cond.sptk b0 // call pal service
|
|
||||||
END(ia64_vmm_entry)
|
END(ia64_vmm_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1376,6 +1355,9 @@ GLOBAL_ENTRY(vmm_reset_entry)
|
||||||
//set up ipsr, iip, vpd.vpsr, dcr
|
//set up ipsr, iip, vpd.vpsr, dcr
|
||||||
// For IPSR: it/dt/rt=1, i/ic=1, si=1, vm/bn=1
|
// For IPSR: it/dt/rt=1, i/ic=1, si=1, vm/bn=1
|
||||||
// For DCR: all bits 0
|
// For DCR: all bits 0
|
||||||
|
bsw.0
|
||||||
|
;;
|
||||||
|
mov r21 =r13
|
||||||
adds r14=-VMM_PT_REGS_SIZE, r12
|
adds r14=-VMM_PT_REGS_SIZE, r12
|
||||||
;;
|
;;
|
||||||
movl r6=0x501008826000 // IPSR dt/rt/it:1;i/ic:1, si:1, vm/bn:1
|
movl r6=0x501008826000 // IPSR dt/rt/it:1;i/ic:1, si:1, vm/bn:1
|
||||||
|
@ -1387,12 +1369,6 @@ GLOBAL_ENTRY(vmm_reset_entry)
|
||||||
;;
|
;;
|
||||||
srlz.i
|
srlz.i
|
||||||
;;
|
;;
|
||||||
bsw.0
|
|
||||||
;;
|
|
||||||
mov r21 =r13
|
|
||||||
;;
|
|
||||||
bsw.1
|
|
||||||
;;
|
|
||||||
mov ar.rsc = 0
|
mov ar.rsc = 0
|
||||||
;;
|
;;
|
||||||
flushrs
|
flushrs
|
||||||
|
@ -1406,12 +1382,9 @@ GLOBAL_ENTRY(vmm_reset_entry)
|
||||||
ld8 r1 = [r20]
|
ld8 r1 = [r20]
|
||||||
;;
|
;;
|
||||||
mov cr.iip=r4
|
mov cr.iip=r4
|
||||||
;;
|
|
||||||
adds r16=VMM_VPD_BASE_OFFSET,r13
|
adds r16=VMM_VPD_BASE_OFFSET,r13
|
||||||
adds r20=VMM_VCPU_VSA_BASE_OFFSET,r13
|
|
||||||
;;
|
;;
|
||||||
ld8 r18=[r16]
|
ld8 r18=[r16]
|
||||||
ld8 r20=[r20]
|
|
||||||
;;
|
;;
|
||||||
adds r19=VMM_VPD_VPSR_OFFSET,r18
|
adds r19=VMM_VPD_VPSR_OFFSET,r18
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -390,7 +390,7 @@ void thash_purge_entries_remote(struct kvm_vcpu *v, u64 va, u64 ps)
|
||||||
|
|
||||||
u64 translate_phy_pte(u64 *pte, u64 itir, u64 va)
|
u64 translate_phy_pte(u64 *pte, u64 itir, u64 va)
|
||||||
{
|
{
|
||||||
u64 ps, ps_mask, paddr, maddr;
|
u64 ps, ps_mask, paddr, maddr, io_mask;
|
||||||
union pte_flags phy_pte;
|
union pte_flags phy_pte;
|
||||||
|
|
||||||
ps = itir_ps(itir);
|
ps = itir_ps(itir);
|
||||||
|
@ -398,8 +398,9 @@ u64 translate_phy_pte(u64 *pte, u64 itir, u64 va)
|
||||||
phy_pte.val = *pte;
|
phy_pte.val = *pte;
|
||||||
paddr = *pte;
|
paddr = *pte;
|
||||||
paddr = ((paddr & _PAGE_PPN_MASK) & ps_mask) | (va & ~ps_mask);
|
paddr = ((paddr & _PAGE_PPN_MASK) & ps_mask) | (va & ~ps_mask);
|
||||||
maddr = kvm_lookup_mpa(paddr >> PAGE_SHIFT);
|
maddr = kvm_get_mpt_entry(paddr >> PAGE_SHIFT);
|
||||||
if (maddr & GPFN_IO_MASK) {
|
io_mask = maddr & GPFN_IO_MASK;
|
||||||
|
if (io_mask && (io_mask != GPFN_PHYS_MMIO)) {
|
||||||
*pte |= VTLB_PTE_IO;
|
*pte |= VTLB_PTE_IO;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -418,7 +419,7 @@ int thash_purge_and_insert(struct kvm_vcpu *v, u64 pte, u64 itir,
|
||||||
u64 ifa, int type)
|
u64 ifa, int type)
|
||||||
{
|
{
|
||||||
u64 ps;
|
u64 ps;
|
||||||
u64 phy_pte;
|
u64 phy_pte, io_mask, index;
|
||||||
union ia64_rr vrr, mrr;
|
union ia64_rr vrr, mrr;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -426,13 +427,16 @@ int thash_purge_and_insert(struct kvm_vcpu *v, u64 pte, u64 itir,
|
||||||
vrr.val = vcpu_get_rr(v, ifa);
|
vrr.val = vcpu_get_rr(v, ifa);
|
||||||
mrr.val = ia64_get_rr(ifa);
|
mrr.val = ia64_get_rr(ifa);
|
||||||
|
|
||||||
|
index = (pte & _PAGE_PPN_MASK) >> PAGE_SHIFT;
|
||||||
|
io_mask = kvm_get_mpt_entry(index) & GPFN_IO_MASK;
|
||||||
phy_pte = translate_phy_pte(&pte, itir, ifa);
|
phy_pte = translate_phy_pte(&pte, itir, ifa);
|
||||||
|
|
||||||
/* Ensure WB attribute if pte is related to a normal mem page,
|
/* Ensure WB attribute if pte is related to a normal mem page,
|
||||||
* which is required by vga acceleration since qemu maps shared
|
* which is required by vga acceleration since qemu maps shared
|
||||||
* vram buffer with WB.
|
* vram buffer with WB.
|
||||||
*/
|
*/
|
||||||
if (!(pte & VTLB_PTE_IO) && ((pte & _PAGE_MA_MASK) != _PAGE_MA_NAT)) {
|
if (!(pte & VTLB_PTE_IO) && ((pte & _PAGE_MA_MASK) != _PAGE_MA_NAT) &&
|
||||||
|
io_mask != GPFN_PHYS_MMIO) {
|
||||||
pte &= ~_PAGE_MA_MASK;
|
pte &= ~_PAGE_MA_MASK;
|
||||||
phy_pte &= ~_PAGE_MA_MASK;
|
phy_pte &= ~_PAGE_MA_MASK;
|
||||||
}
|
}
|
||||||
|
@ -566,12 +570,19 @@ void thash_init(struct thash_cb *hcb, u64 sz)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 kvm_lookup_mpa(u64 gpfn)
|
u64 kvm_get_mpt_entry(u64 gpfn)
|
||||||
{
|
{
|
||||||
u64 *base = (u64 *) KVM_P2M_BASE;
|
u64 *base = (u64 *) KVM_P2M_BASE;
|
||||||
return *(base + gpfn);
|
return *(base + gpfn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 kvm_lookup_mpa(u64 gpfn)
|
||||||
|
{
|
||||||
|
u64 maddr;
|
||||||
|
maddr = kvm_get_mpt_entry(gpfn);
|
||||||
|
return maddr&_PAGE_PPN_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
u64 kvm_gpa_to_mpa(u64 gpa)
|
u64 kvm_gpa_to_mpa(u64 gpa)
|
||||||
{
|
{
|
||||||
u64 pte = kvm_lookup_mpa(gpa >> PAGE_SHIFT);
|
u64 pte = kvm_lookup_mpa(gpa >> PAGE_SHIFT);
|
||||||
|
|
|
@ -81,11 +81,17 @@ struct kvm_vcpu_arch {
|
||||||
struct tlbe shadow_tlb[PPC44x_TLB_SIZE];
|
struct tlbe shadow_tlb[PPC44x_TLB_SIZE];
|
||||||
/* Pages which are referenced in the shadow TLB. */
|
/* Pages which are referenced in the shadow TLB. */
|
||||||
struct page *shadow_pages[PPC44x_TLB_SIZE];
|
struct page *shadow_pages[PPC44x_TLB_SIZE];
|
||||||
/* Copy of the host's TLB. */
|
|
||||||
struct tlbe host_tlb[PPC44x_TLB_SIZE];
|
/* Track which TLB entries we've modified in the current exit. */
|
||||||
|
u8 shadow_tlb_mod[PPC44x_TLB_SIZE];
|
||||||
|
|
||||||
u32 host_stack;
|
u32 host_stack;
|
||||||
u32 host_pid;
|
u32 host_pid;
|
||||||
|
u32 host_dbcr0;
|
||||||
|
u32 host_dbcr1;
|
||||||
|
u32 host_dbcr2;
|
||||||
|
u32 host_iac[4];
|
||||||
|
u32 host_msr;
|
||||||
|
|
||||||
u64 fpr[32];
|
u64 fpr[32];
|
||||||
u32 gpr[32];
|
u32 gpr[32];
|
||||||
|
@ -123,7 +129,11 @@ struct kvm_vcpu_arch {
|
||||||
u32 ivor[16];
|
u32 ivor[16];
|
||||||
u32 ivpr;
|
u32 ivpr;
|
||||||
u32 pir;
|
u32 pir;
|
||||||
|
|
||||||
|
u32 shadow_pid;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
|
u32 swap_pid;
|
||||||
|
|
||||||
u32 pvr;
|
u32 pvr;
|
||||||
u32 ccr0;
|
u32 ccr0;
|
||||||
u32 ccr1;
|
u32 ccr1;
|
||||||
|
|
|
@ -64,6 +64,10 @@ extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn,
|
||||||
extern void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr,
|
extern void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr,
|
||||||
gva_t eend, u32 asid);
|
gva_t eend, u32 asid);
|
||||||
extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode);
|
extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode);
|
||||||
|
extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid);
|
||||||
|
|
||||||
|
/* XXX Book E specific */
|
||||||
|
extern void kvmppc_tlbe_set_modified(struct kvm_vcpu *vcpu, unsigned int i);
|
||||||
|
|
||||||
extern void kvmppc_check_and_deliver_interrupts(struct kvm_vcpu *vcpu);
|
extern void kvmppc_check_and_deliver_interrupts(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
|
@ -92,4 +96,12 @@ static inline void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
|
||||||
kvm_vcpu_block(vcpu);
|
kvm_vcpu_block(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 new_pid)
|
||||||
|
{
|
||||||
|
if (vcpu->arch.pid != new_pid) {
|
||||||
|
vcpu->arch.pid = new_pid;
|
||||||
|
vcpu->arch.swap_pid = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __POWERPC_KVM_PPC_H__ */
|
#endif /* __POWERPC_KVM_PPC_H__ */
|
||||||
|
|
|
@ -359,8 +359,8 @@ int main(void)
|
||||||
|
|
||||||
DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack));
|
DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack));
|
||||||
DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid));
|
DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid));
|
||||||
DEFINE(VCPU_HOST_TLB, offsetof(struct kvm_vcpu, arch.host_tlb));
|
|
||||||
DEFINE(VCPU_SHADOW_TLB, offsetof(struct kvm_vcpu, arch.shadow_tlb));
|
DEFINE(VCPU_SHADOW_TLB, offsetof(struct kvm_vcpu, arch.shadow_tlb));
|
||||||
|
DEFINE(VCPU_SHADOW_MOD, offsetof(struct kvm_vcpu, arch.shadow_tlb_mod));
|
||||||
DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr));
|
DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr));
|
||||||
DEFINE(VCPU_LR, offsetof(struct kvm_vcpu, arch.lr));
|
DEFINE(VCPU_LR, offsetof(struct kvm_vcpu, arch.lr));
|
||||||
DEFINE(VCPU_CR, offsetof(struct kvm_vcpu, arch.cr));
|
DEFINE(VCPU_CR, offsetof(struct kvm_vcpu, arch.cr));
|
||||||
|
@ -372,7 +372,7 @@ int main(void)
|
||||||
DEFINE(VCPU_SPRG5, offsetof(struct kvm_vcpu, arch.sprg5));
|
DEFINE(VCPU_SPRG5, offsetof(struct kvm_vcpu, arch.sprg5));
|
||||||
DEFINE(VCPU_SPRG6, offsetof(struct kvm_vcpu, arch.sprg6));
|
DEFINE(VCPU_SPRG6, offsetof(struct kvm_vcpu, arch.sprg6));
|
||||||
DEFINE(VCPU_SPRG7, offsetof(struct kvm_vcpu, arch.sprg7));
|
DEFINE(VCPU_SPRG7, offsetof(struct kvm_vcpu, arch.sprg7));
|
||||||
DEFINE(VCPU_PID, offsetof(struct kvm_vcpu, arch.pid));
|
DEFINE(VCPU_SHADOW_PID, offsetof(struct kvm_vcpu, arch.shadow_pid));
|
||||||
|
|
||||||
DEFINE(VCPU_LAST_INST, offsetof(struct kvm_vcpu, arch.last_inst));
|
DEFINE(VCPU_LAST_INST, offsetof(struct kvm_vcpu, arch.last_inst));
|
||||||
DEFINE(VCPU_FAULT_DEAR, offsetof(struct kvm_vcpu, arch.fault_dear));
|
DEFINE(VCPU_FAULT_DEAR, offsetof(struct kvm_vcpu, arch.fault_dear));
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
#include <linux/kvm.h>
|
||||||
#include <linux/kvm_host.h>
|
#include <linux/kvm_host.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <asm/mmu-44x.h>
|
#include <asm/mmu-44x.h>
|
||||||
|
@ -109,7 +110,6 @@ static int kvmppc_44x_tlbe_is_writable(struct tlbe *tlbe)
|
||||||
return tlbe->word2 & (PPC44x_TLB_SW|PPC44x_TLB_UW);
|
return tlbe->word2 & (PPC44x_TLB_SW|PPC44x_TLB_UW);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be called with mmap_sem locked for writing. */
|
|
||||||
static void kvmppc_44x_shadow_release(struct kvm_vcpu *vcpu,
|
static void kvmppc_44x_shadow_release(struct kvm_vcpu *vcpu,
|
||||||
unsigned int index)
|
unsigned int index)
|
||||||
{
|
{
|
||||||
|
@ -124,6 +124,11 @@ static void kvmppc_44x_shadow_release(struct kvm_vcpu *vcpu,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvmppc_tlbe_set_modified(struct kvm_vcpu *vcpu, unsigned int i)
|
||||||
|
{
|
||||||
|
vcpu->arch.shadow_tlb_mod[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Caller must ensure that the specified guest TLB entry is safe to insert into
|
/* Caller must ensure that the specified guest TLB entry is safe to insert into
|
||||||
* the shadow TLB. */
|
* the shadow TLB. */
|
||||||
void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid,
|
void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid,
|
||||||
|
@ -142,19 +147,16 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid,
|
||||||
stlbe = &vcpu->arch.shadow_tlb[victim];
|
stlbe = &vcpu->arch.shadow_tlb[victim];
|
||||||
|
|
||||||
/* Get reference to new page. */
|
/* Get reference to new page. */
|
||||||
down_read(¤t->mm->mmap_sem);
|
|
||||||
new_page = gfn_to_page(vcpu->kvm, gfn);
|
new_page = gfn_to_page(vcpu->kvm, gfn);
|
||||||
if (is_error_page(new_page)) {
|
if (is_error_page(new_page)) {
|
||||||
printk(KERN_ERR "Couldn't get guest page for gfn %lx!\n", gfn);
|
printk(KERN_ERR "Couldn't get guest page for gfn %lx!\n", gfn);
|
||||||
kvm_release_page_clean(new_page);
|
kvm_release_page_clean(new_page);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hpaddr = page_to_phys(new_page);
|
hpaddr = page_to_phys(new_page);
|
||||||
|
|
||||||
/* Drop reference to old page. */
|
/* Drop reference to old page. */
|
||||||
kvmppc_44x_shadow_release(vcpu, victim);
|
kvmppc_44x_shadow_release(vcpu, victim);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
|
|
||||||
vcpu->arch.shadow_pages[victim] = new_page;
|
vcpu->arch.shadow_pages[victim] = new_page;
|
||||||
|
|
||||||
|
@ -164,27 +166,30 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid,
|
||||||
|
|
||||||
/* XXX what about AS? */
|
/* XXX what about AS? */
|
||||||
|
|
||||||
stlbe->tid = asid & 0xff;
|
stlbe->tid = !(asid & 0xff);
|
||||||
|
|
||||||
/* Force TS=1 for all guest mappings. */
|
/* Force TS=1 for all guest mappings. */
|
||||||
/* For now we hardcode 4KB mappings, but it will be important to
|
/* For now we hardcode 4KB mappings, but it will be important to
|
||||||
* use host large pages in the future. */
|
* use host large pages in the future. */
|
||||||
stlbe->word0 = (gvaddr & PAGE_MASK) | PPC44x_TLB_VALID | PPC44x_TLB_TS
|
stlbe->word0 = (gvaddr & PAGE_MASK) | PPC44x_TLB_VALID | PPC44x_TLB_TS
|
||||||
| PPC44x_TLB_4K;
|
| PPC44x_TLB_4K;
|
||||||
|
|
||||||
stlbe->word1 = (hpaddr & 0xfffffc00) | ((hpaddr >> 32) & 0xf);
|
stlbe->word1 = (hpaddr & 0xfffffc00) | ((hpaddr >> 32) & 0xf);
|
||||||
stlbe->word2 = kvmppc_44x_tlb_shadow_attrib(flags,
|
stlbe->word2 = kvmppc_44x_tlb_shadow_attrib(flags,
|
||||||
vcpu->arch.msr & MSR_PR);
|
vcpu->arch.msr & MSR_PR);
|
||||||
|
kvmppc_tlbe_set_modified(vcpu, victim);
|
||||||
|
|
||||||
|
KVMTRACE_5D(STLB_WRITE, vcpu, victim,
|
||||||
|
stlbe->tid, stlbe->word0, stlbe->word1, stlbe->word2,
|
||||||
|
handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr,
|
void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr,
|
||||||
gva_t eend, u32 asid)
|
gva_t eend, u32 asid)
|
||||||
{
|
{
|
||||||
unsigned int pid = asid & 0xff;
|
unsigned int pid = !(asid & 0xff);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* XXX Replace loop with fancy data structures. */
|
/* XXX Replace loop with fancy data structures. */
|
||||||
down_write(¤t->mm->mmap_sem);
|
|
||||||
for (i = 0; i <= tlb_44x_hwater; i++) {
|
for (i = 0; i <= tlb_44x_hwater; i++) {
|
||||||
struct tlbe *stlbe = &vcpu->arch.shadow_tlb[i];
|
struct tlbe *stlbe = &vcpu->arch.shadow_tlb[i];
|
||||||
unsigned int tid;
|
unsigned int tid;
|
||||||
|
@ -204,21 +209,35 @@ void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr,
|
||||||
|
|
||||||
kvmppc_44x_shadow_release(vcpu, i);
|
kvmppc_44x_shadow_release(vcpu, i);
|
||||||
stlbe->word0 = 0;
|
stlbe->word0 = 0;
|
||||||
|
kvmppc_tlbe_set_modified(vcpu, i);
|
||||||
|
KVMTRACE_5D(STLB_INVAL, vcpu, i,
|
||||||
|
stlbe->tid, stlbe->word0, stlbe->word1,
|
||||||
|
stlbe->word2, handler);
|
||||||
}
|
}
|
||||||
up_write(¤t->mm->mmap_sem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Invalidate all mappings, so that when they fault back in they will get the
|
/* Invalidate all mappings on the privilege switch after PID has been changed.
|
||||||
* proper permission bits. */
|
* The guest always runs with PID=1, so we must clear the entire TLB when
|
||||||
|
* switching address spaces. */
|
||||||
void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode)
|
void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* XXX Replace loop with fancy data structures. */
|
if (vcpu->arch.swap_pid) {
|
||||||
down_write(¤t->mm->mmap_sem);
|
/* XXX Replace loop with fancy data structures. */
|
||||||
for (i = 0; i <= tlb_44x_hwater; i++) {
|
for (i = 0; i <= tlb_44x_hwater; i++) {
|
||||||
kvmppc_44x_shadow_release(vcpu, i);
|
struct tlbe *stlbe = &vcpu->arch.shadow_tlb[i];
|
||||||
vcpu->arch.shadow_tlb[i].word0 = 0;
|
|
||||||
|
/* Future optimization: clear only userspace mappings. */
|
||||||
|
kvmppc_44x_shadow_release(vcpu, i);
|
||||||
|
stlbe->word0 = 0;
|
||||||
|
kvmppc_tlbe_set_modified(vcpu, i);
|
||||||
|
KVMTRACE_5D(STLB_INVAL, vcpu, i,
|
||||||
|
stlbe->tid, stlbe->word0, stlbe->word1,
|
||||||
|
stlbe->word2, handler);
|
||||||
|
}
|
||||||
|
vcpu->arch.swap_pid = 0;
|
||||||
}
|
}
|
||||||
up_write(¤t->mm->mmap_sem);
|
|
||||||
|
vcpu->arch.shadow_pid = !usermode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,17 @@ config KVM_BOOKE_HOST
|
||||||
Provides host support for KVM on Book E PowerPC processors. Currently
|
Provides host support for KVM on Book E PowerPC processors. Currently
|
||||||
this works on 440 processors only.
|
this works on 440 processors only.
|
||||||
|
|
||||||
|
config KVM_TRACE
|
||||||
|
bool "KVM trace support"
|
||||||
|
depends on KVM && MARKERS && SYSFS
|
||||||
|
select RELAY
|
||||||
|
select DEBUG_FS
|
||||||
|
default n
|
||||||
|
---help---
|
||||||
|
This option allows reading a trace of kvm-related events through
|
||||||
|
relayfs. Note the ABI is not considered stable and will be
|
||||||
|
modified in future updates.
|
||||||
|
|
||||||
source drivers/virtio/Kconfig
|
source drivers/virtio/Kconfig
|
||||||
|
|
||||||
endif # VIRTUALIZATION
|
endif # VIRTUALIZATION
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/powerpc/kvm
|
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/powerpc/kvm
|
||||||
|
|
||||||
common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
|
common-objs-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
|
||||||
|
|
||||||
kvm-objs := $(common-objs) powerpc.o emulate.o booke_guest.o
|
common-objs-$(CONFIG_KVM_TRACE) += $(addprefix ../../../virt/kvm/, kvm_trace.o)
|
||||||
|
|
||||||
|
kvm-objs := $(common-objs-y) powerpc.o emulate.o booke_guest.o
|
||||||
obj-$(CONFIG_KVM) += kvm.o
|
obj-$(CONFIG_KVM) += kvm.o
|
||||||
|
|
||||||
AFLAGS_booke_interrupts.o := -I$(obj)
|
AFLAGS_booke_interrupts.o := -I$(obj)
|
||||||
|
|
|
@ -410,6 +410,21 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BOOKE_INTERRUPT_DEBUG: {
|
||||||
|
u32 dbsr;
|
||||||
|
|
||||||
|
vcpu->arch.pc = mfspr(SPRN_CSRR0);
|
||||||
|
|
||||||
|
/* clear IAC events in DBSR register */
|
||||||
|
dbsr = mfspr(SPRN_DBSR);
|
||||||
|
dbsr &= DBSR_IAC1 | DBSR_IAC2 | DBSR_IAC3 | DBSR_IAC4;
|
||||||
|
mtspr(SPRN_DBSR, dbsr);
|
||||||
|
|
||||||
|
run->exit_reason = KVM_EXIT_DEBUG;
|
||||||
|
r = RESUME_HOST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printk(KERN_EMERG "exit_nr %d\n", exit_nr);
|
printk(KERN_EMERG "exit_nr %d\n", exit_nr);
|
||||||
BUG();
|
BUG();
|
||||||
|
@ -471,6 +486,8 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
|
||||||
vcpu->arch.msr = 0;
|
vcpu->arch.msr = 0;
|
||||||
vcpu->arch.gpr[1] = (16<<20) - 8; /* -8 for the callee-save LR slot */
|
vcpu->arch.gpr[1] = (16<<20) - 8; /* -8 for the callee-save LR slot */
|
||||||
|
|
||||||
|
vcpu->arch.shadow_pid = 1;
|
||||||
|
|
||||||
/* Eye-catching number so we know if the guest takes an interrupt
|
/* Eye-catching number so we know if the guest takes an interrupt
|
||||||
* before it's programmed its own IVPR. */
|
* before it's programmed its own IVPR. */
|
||||||
vcpu->arch.ivpr = 0x55550000;
|
vcpu->arch.ivpr = 0x55550000;
|
||||||
|
|
|
@ -42,7 +42,8 @@
|
||||||
#define HOST_STACK_LR (HOST_STACK_SIZE + 4) /* In caller stack frame. */
|
#define HOST_STACK_LR (HOST_STACK_SIZE + 4) /* In caller stack frame. */
|
||||||
|
|
||||||
#define NEED_INST_MASK ((1<<BOOKE_INTERRUPT_PROGRAM) | \
|
#define NEED_INST_MASK ((1<<BOOKE_INTERRUPT_PROGRAM) | \
|
||||||
(1<<BOOKE_INTERRUPT_DTLB_MISS))
|
(1<<BOOKE_INTERRUPT_DTLB_MISS) | \
|
||||||
|
(1<<BOOKE_INTERRUPT_DEBUG))
|
||||||
|
|
||||||
#define NEED_DEAR_MASK ((1<<BOOKE_INTERRUPT_DATA_STORAGE) | \
|
#define NEED_DEAR_MASK ((1<<BOOKE_INTERRUPT_DATA_STORAGE) | \
|
||||||
(1<<BOOKE_INTERRUPT_DTLB_MISS))
|
(1<<BOOKE_INTERRUPT_DTLB_MISS))
|
||||||
|
@ -331,50 +332,56 @@ lightweight_exit:
|
||||||
|
|
||||||
mfspr r3, SPRN_PID
|
mfspr r3, SPRN_PID
|
||||||
stw r3, VCPU_HOST_PID(r4)
|
stw r3, VCPU_HOST_PID(r4)
|
||||||
lwz r3, VCPU_PID(r4)
|
lwz r3, VCPU_SHADOW_PID(r4)
|
||||||
mtspr SPRN_PID, r3
|
mtspr SPRN_PID, r3
|
||||||
|
|
||||||
/* Prevent all TLB updates. */
|
/* Prevent all asynchronous TLB updates. */
|
||||||
mfmsr r5
|
mfmsr r5
|
||||||
lis r6, (MSR_EE|MSR_CE|MSR_ME|MSR_DE)@h
|
lis r6, (MSR_EE|MSR_CE|MSR_ME|MSR_DE)@h
|
||||||
ori r6, r6, (MSR_EE|MSR_CE|MSR_ME|MSR_DE)@l
|
ori r6, r6, (MSR_EE|MSR_CE|MSR_ME|MSR_DE)@l
|
||||||
andc r6, r5, r6
|
andc r6, r5, r6
|
||||||
mtmsr r6
|
mtmsr r6
|
||||||
|
|
||||||
/* Save the host's non-pinned TLB mappings, and load the guest mappings
|
/* Load the guest mappings, leaving the host's "pinned" kernel mappings
|
||||||
* over them. Leave the host's "pinned" kernel mappings in place. */
|
* in place. */
|
||||||
/* XXX optimization: use generation count to avoid swapping unmodified
|
|
||||||
* entries. */
|
|
||||||
mfspr r10, SPRN_MMUCR /* Save host MMUCR. */
|
mfspr r10, SPRN_MMUCR /* Save host MMUCR. */
|
||||||
lis r8, tlb_44x_hwater@ha
|
li r5, PPC44x_TLB_SIZE
|
||||||
lwz r8, tlb_44x_hwater@l(r8)
|
lis r5, tlb_44x_hwater@ha
|
||||||
addi r3, r4, VCPU_HOST_TLB - 4
|
lwz r5, tlb_44x_hwater@l(r5)
|
||||||
addi r9, r4, VCPU_SHADOW_TLB - 4
|
mtctr r5
|
||||||
|
addi r9, r4, VCPU_SHADOW_TLB
|
||||||
|
addi r5, r4, VCPU_SHADOW_MOD
|
||||||
|
li r3, 0
|
||||||
|
1:
|
||||||
|
lbzx r7, r3, r5
|
||||||
|
cmpwi r7, 0
|
||||||
|
beq 3f
|
||||||
|
|
||||||
|
/* Load guest entry. */
|
||||||
|
mulli r11, r3, TLBE_BYTES
|
||||||
|
add r11, r11, r9
|
||||||
|
lwz r7, 0(r11)
|
||||||
|
mtspr SPRN_MMUCR, r7
|
||||||
|
lwz r7, 4(r11)
|
||||||
|
tlbwe r7, r3, PPC44x_TLB_PAGEID
|
||||||
|
lwz r7, 8(r11)
|
||||||
|
tlbwe r7, r3, PPC44x_TLB_XLAT
|
||||||
|
lwz r7, 12(r11)
|
||||||
|
tlbwe r7, r3, PPC44x_TLB_ATTRIB
|
||||||
|
3:
|
||||||
|
addi r3, r3, 1 /* Increment index. */
|
||||||
|
bdnz 1b
|
||||||
|
|
||||||
|
mtspr SPRN_MMUCR, r10 /* Restore host MMUCR. */
|
||||||
|
|
||||||
|
/* Clear bitmap of modified TLB entries */
|
||||||
|
li r5, PPC44x_TLB_SIZE>>2
|
||||||
|
mtctr r5
|
||||||
|
addi r5, r4, VCPU_SHADOW_MOD - 4
|
||||||
li r6, 0
|
li r6, 0
|
||||||
1:
|
1:
|
||||||
/* Save host entry. */
|
stwu r6, 4(r5)
|
||||||
tlbre r7, r6, PPC44x_TLB_PAGEID
|
bdnz 1b
|
||||||
mfspr r5, SPRN_MMUCR
|
|
||||||
stwu r5, 4(r3)
|
|
||||||
stwu r7, 4(r3)
|
|
||||||
tlbre r7, r6, PPC44x_TLB_XLAT
|
|
||||||
stwu r7, 4(r3)
|
|
||||||
tlbre r7, r6, PPC44x_TLB_ATTRIB
|
|
||||||
stwu r7, 4(r3)
|
|
||||||
/* Load guest entry. */
|
|
||||||
lwzu r7, 4(r9)
|
|
||||||
mtspr SPRN_MMUCR, r7
|
|
||||||
lwzu r7, 4(r9)
|
|
||||||
tlbwe r7, r6, PPC44x_TLB_PAGEID
|
|
||||||
lwzu r7, 4(r9)
|
|
||||||
tlbwe r7, r6, PPC44x_TLB_XLAT
|
|
||||||
lwzu r7, 4(r9)
|
|
||||||
tlbwe r7, r6, PPC44x_TLB_ATTRIB
|
|
||||||
/* Increment index. */
|
|
||||||
addi r6, r6, 1
|
|
||||||
cmpw r6, r8
|
|
||||||
blt 1b
|
|
||||||
mtspr SPRN_MMUCR, r10 /* Restore host MMUCR. */
|
|
||||||
|
|
||||||
iccci 0, 0 /* XXX hack */
|
iccci 0, 0 /* XXX hack */
|
||||||
|
|
||||||
|
@ -431,6 +438,14 @@ lightweight_exit:
|
||||||
oris r3, r3, KVMPPC_MSR_MASK@h
|
oris r3, r3, KVMPPC_MSR_MASK@h
|
||||||
ori r3, r3, KVMPPC_MSR_MASK@l
|
ori r3, r3, KVMPPC_MSR_MASK@l
|
||||||
mtsrr1 r3
|
mtsrr1 r3
|
||||||
|
|
||||||
|
/* Clear any debug events which occurred since we disabled MSR[DE].
|
||||||
|
* XXX This gives us a 3-instruction window in which a breakpoint
|
||||||
|
* intended for guest context could fire in the host instead. */
|
||||||
|
lis r3, 0xffff
|
||||||
|
ori r3, r3, 0xffff
|
||||||
|
mtspr SPRN_DBSR, r3
|
||||||
|
|
||||||
lwz r3, VCPU_GPR(r3)(r4)
|
lwz r3, VCPU_GPR(r3)(r4)
|
||||||
lwz r4, VCPU_GPR(r4)(r4)
|
lwz r4, VCPU_GPR(r4)(r4)
|
||||||
rfi
|
rfi
|
||||||
|
|
|
@ -170,6 +170,10 @@ static int kvmppc_emul_tlbwe(struct kvm_vcpu *vcpu, u32 inst)
|
||||||
kvmppc_mmu_map(vcpu, eaddr, raddr >> PAGE_SHIFT, asid, flags);
|
kvmppc_mmu_map(vcpu, eaddr, raddr >> PAGE_SHIFT, asid, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KVMTRACE_5D(GTLB_WRITE, vcpu, index,
|
||||||
|
tlbe->tid, tlbe->word0, tlbe->word1, tlbe->word2,
|
||||||
|
handler);
|
||||||
|
|
||||||
return EMULATE_DONE;
|
return EMULATE_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,7 +508,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
|
||||||
case SPRN_MMUCR:
|
case SPRN_MMUCR:
|
||||||
vcpu->arch.mmucr = vcpu->arch.gpr[rs]; break;
|
vcpu->arch.mmucr = vcpu->arch.gpr[rs]; break;
|
||||||
case SPRN_PID:
|
case SPRN_PID:
|
||||||
vcpu->arch.pid = vcpu->arch.gpr[rs]; break;
|
kvmppc_set_pid(vcpu, vcpu->arch.gpr[rs]); break;
|
||||||
case SPRN_CCR0:
|
case SPRN_CCR0:
|
||||||
vcpu->arch.ccr0 = vcpu->arch.gpr[rs]; break;
|
vcpu->arch.ccr0 = vcpu->arch.gpr[rs]; break;
|
||||||
case SPRN_CCR1:
|
case SPRN_CCR1:
|
||||||
|
@ -765,6 +769,8 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KVMTRACE_3D(PPC_INSTR, vcpu, inst, vcpu->arch.pc, emulated, entryexit);
|
||||||
|
|
||||||
if (advance)
|
if (advance)
|
||||||
vcpu->arch.pc += 4; /* Advance past emulated instruction. */
|
vcpu->arch.pc += 4; /* Advance past emulated instruction. */
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <asm/cputable.h>
|
#include <asm/cputable.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/kvm_ppc.h>
|
#include <asm/kvm_ppc.h>
|
||||||
|
#include <asm/tlbflush.h>
|
||||||
|
|
||||||
|
|
||||||
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
|
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
|
||||||
|
@ -239,18 +240,114 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Note: clearing MSR[DE] just means that the debug interrupt will not be
|
||||||
|
* delivered *immediately*. Instead, it simply sets the appropriate DBSR bits.
|
||||||
|
* If those DBSR bits are still set when MSR[DE] is re-enabled, the interrupt
|
||||||
|
* will be delivered as an "imprecise debug event" (which is indicated by
|
||||||
|
* DBSR[IDE].
|
||||||
|
*/
|
||||||
|
static void kvmppc_disable_debug_interrupts(void)
|
||||||
|
{
|
||||||
|
mtmsr(mfmsr() & ~MSR_DE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvmppc_restore_host_debug_state(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
kvmppc_disable_debug_interrupts();
|
||||||
|
|
||||||
|
mtspr(SPRN_IAC1, vcpu->arch.host_iac[0]);
|
||||||
|
mtspr(SPRN_IAC2, vcpu->arch.host_iac[1]);
|
||||||
|
mtspr(SPRN_IAC3, vcpu->arch.host_iac[2]);
|
||||||
|
mtspr(SPRN_IAC4, vcpu->arch.host_iac[3]);
|
||||||
|
mtspr(SPRN_DBCR1, vcpu->arch.host_dbcr1);
|
||||||
|
mtspr(SPRN_DBCR2, vcpu->arch.host_dbcr2);
|
||||||
|
mtspr(SPRN_DBCR0, vcpu->arch.host_dbcr0);
|
||||||
|
mtmsr(vcpu->arch.host_msr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvmppc_load_guest_debug_registers(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm_guest_debug *dbg = &vcpu->guest_debug;
|
||||||
|
u32 dbcr0 = 0;
|
||||||
|
|
||||||
|
vcpu->arch.host_msr = mfmsr();
|
||||||
|
kvmppc_disable_debug_interrupts();
|
||||||
|
|
||||||
|
/* Save host debug register state. */
|
||||||
|
vcpu->arch.host_iac[0] = mfspr(SPRN_IAC1);
|
||||||
|
vcpu->arch.host_iac[1] = mfspr(SPRN_IAC2);
|
||||||
|
vcpu->arch.host_iac[2] = mfspr(SPRN_IAC3);
|
||||||
|
vcpu->arch.host_iac[3] = mfspr(SPRN_IAC4);
|
||||||
|
vcpu->arch.host_dbcr0 = mfspr(SPRN_DBCR0);
|
||||||
|
vcpu->arch.host_dbcr1 = mfspr(SPRN_DBCR1);
|
||||||
|
vcpu->arch.host_dbcr2 = mfspr(SPRN_DBCR2);
|
||||||
|
|
||||||
|
/* set registers up for guest */
|
||||||
|
|
||||||
|
if (dbg->bp[0]) {
|
||||||
|
mtspr(SPRN_IAC1, dbg->bp[0]);
|
||||||
|
dbcr0 |= DBCR0_IAC1 | DBCR0_IDM;
|
||||||
|
}
|
||||||
|
if (dbg->bp[1]) {
|
||||||
|
mtspr(SPRN_IAC2, dbg->bp[1]);
|
||||||
|
dbcr0 |= DBCR0_IAC2 | DBCR0_IDM;
|
||||||
|
}
|
||||||
|
if (dbg->bp[2]) {
|
||||||
|
mtspr(SPRN_IAC3, dbg->bp[2]);
|
||||||
|
dbcr0 |= DBCR0_IAC3 | DBCR0_IDM;
|
||||||
|
}
|
||||||
|
if (dbg->bp[3]) {
|
||||||
|
mtspr(SPRN_IAC4, dbg->bp[3]);
|
||||||
|
dbcr0 |= DBCR0_IAC4 | DBCR0_IDM;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtspr(SPRN_DBCR0, dbcr0);
|
||||||
|
mtspr(SPRN_DBCR1, 0);
|
||||||
|
mtspr(SPRN_DBCR2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (vcpu->guest_debug.enabled)
|
||||||
|
kvmppc_load_guest_debug_registers(vcpu);
|
||||||
|
|
||||||
|
/* Mark every guest entry in the shadow TLB entry modified, so that they
|
||||||
|
* will all be reloaded on the next vcpu run (instead of being
|
||||||
|
* demand-faulted). */
|
||||||
|
for (i = 0; i <= tlb_44x_hwater; i++)
|
||||||
|
kvmppc_tlbe_set_modified(vcpu, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
|
if (vcpu->guest_debug.enabled)
|
||||||
|
kvmppc_restore_host_debug_state(vcpu);
|
||||||
|
|
||||||
|
/* Don't leave guest TLB entries resident when being de-scheduled. */
|
||||||
|
/* XXX It would be nice to differentiate between heavyweight exit and
|
||||||
|
* sched_out here, since we could avoid the TLB flush for heavyweight
|
||||||
|
* exits. */
|
||||||
|
_tlbia();
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
|
int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_debug_guest *dbg)
|
struct kvm_debug_guest *dbg)
|
||||||
{
|
{
|
||||||
return -ENOTSUPP;
|
int i;
|
||||||
|
|
||||||
|
vcpu->guest_debug.enabled = dbg->enabled;
|
||||||
|
if (vcpu->guest_debug.enabled) {
|
||||||
|
for (i=0; i < ARRAY_SIZE(vcpu->guest_debug.bp); i++) {
|
||||||
|
if (dbg->breakpoints[i].enabled)
|
||||||
|
vcpu->guest_debug.bp[i] = dbg->breakpoints[i].address;
|
||||||
|
else
|
||||||
|
vcpu->guest_debug.bp[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu,
|
static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu,
|
||||||
|
|
|
@ -565,13 +565,16 @@ config ZFCPDUMP
|
||||||
Refer to <file:Documentation/s390/zfcpdump.txt> for more details on this.
|
Refer to <file:Documentation/s390/zfcpdump.txt> for more details on this.
|
||||||
|
|
||||||
config S390_GUEST
|
config S390_GUEST
|
||||||
bool "s390 guest support (EXPERIMENTAL)"
|
bool "s390 guest support for KVM (EXPERIMENTAL)"
|
||||||
depends on 64BIT && EXPERIMENTAL
|
depends on 64BIT && EXPERIMENTAL
|
||||||
select VIRTIO
|
select VIRTIO
|
||||||
select VIRTIO_RING
|
select VIRTIO_RING
|
||||||
select VIRTIO_CONSOLE
|
select VIRTIO_CONSOLE
|
||||||
help
|
help
|
||||||
Select this option if you want to run the kernel under s390 linux
|
Select this option if you want to run the kernel as a guest under
|
||||||
|
the KVM hypervisor. This will add detection for KVM as well as a
|
||||||
|
virtio transport. If KVM is detected, the virtio console will be
|
||||||
|
the default console.
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
source "net/Kconfig"
|
source "net/Kconfig"
|
||||||
|
|
|
@ -157,8 +157,8 @@ static int handle_stfl(struct kvm_vcpu *vcpu)
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
vcpu->stat.instruction_stfl++;
|
vcpu->stat.instruction_stfl++;
|
||||||
facility_list &= ~(1UL<<24); /* no stfle */
|
/* only pass the facility bits, which we can handle */
|
||||||
facility_list &= ~(1UL<<23); /* no large pages */
|
facility_list &= 0xfe00fff3;
|
||||||
|
|
||||||
rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list),
|
rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list),
|
||||||
&facility_list, sizeof(facility_list));
|
&facility_list, sizeof(facility_list));
|
||||||
|
|
|
@ -78,6 +78,34 @@ static cycle_t kvm_clock_read(void)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we don't do that, there is the possibility that the guest
|
||||||
|
* will calibrate under heavy load - thus, getting a lower lpj -
|
||||||
|
* and execute the delays themselves without load. This is wrong,
|
||||||
|
* because no delay loop can finish beforehand.
|
||||||
|
* Any heuristics is subject to fail, because ultimately, a large
|
||||||
|
* poll of guests can be running and trouble each other. So we preset
|
||||||
|
* lpj here
|
||||||
|
*/
|
||||||
|
static unsigned long kvm_get_tsc_khz(void)
|
||||||
|
{
|
||||||
|
return preset_lpj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_get_preset_lpj(void)
|
||||||
|
{
|
||||||
|
struct pvclock_vcpu_time_info *src;
|
||||||
|
unsigned long khz;
|
||||||
|
u64 lpj;
|
||||||
|
|
||||||
|
src = &per_cpu(hv_clock, 0);
|
||||||
|
khz = pvclock_tsc_khz(src);
|
||||||
|
|
||||||
|
lpj = ((u64)khz * 1000);
|
||||||
|
do_div(lpj, HZ);
|
||||||
|
preset_lpj = lpj;
|
||||||
|
}
|
||||||
|
|
||||||
static struct clocksource kvm_clock = {
|
static struct clocksource kvm_clock = {
|
||||||
.name = "kvm-clock",
|
.name = "kvm-clock",
|
||||||
.read = kvm_clock_read,
|
.read = kvm_clock_read,
|
||||||
|
@ -153,6 +181,7 @@ void __init kvmclock_init(void)
|
||||||
pv_time_ops.get_wallclock = kvm_get_wallclock;
|
pv_time_ops.get_wallclock = kvm_get_wallclock;
|
||||||
pv_time_ops.set_wallclock = kvm_set_wallclock;
|
pv_time_ops.set_wallclock = kvm_set_wallclock;
|
||||||
pv_time_ops.sched_clock = kvm_clock_read;
|
pv_time_ops.sched_clock = kvm_clock_read;
|
||||||
|
pv_time_ops.get_tsc_khz = kvm_get_tsc_khz;
|
||||||
#ifdef CONFIG_X86_LOCAL_APIC
|
#ifdef CONFIG_X86_LOCAL_APIC
|
||||||
pv_apic_ops.setup_secondary_clock = kvm_setup_secondary_clock;
|
pv_apic_ops.setup_secondary_clock = kvm_setup_secondary_clock;
|
||||||
#endif
|
#endif
|
||||||
|
@ -163,6 +192,7 @@ void __init kvmclock_init(void)
|
||||||
#ifdef CONFIG_KEXEC
|
#ifdef CONFIG_KEXEC
|
||||||
machine_ops.crash_shutdown = kvm_crash_shutdown;
|
machine_ops.crash_shutdown = kvm_crash_shutdown;
|
||||||
#endif
|
#endif
|
||||||
|
kvm_get_preset_lpj();
|
||||||
clocksource_register(&kvm_clock);
|
clocksource_register(&kvm_clock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,18 @@ static unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst,
|
||||||
return dst->version;
|
return dst->version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)
|
||||||
|
{
|
||||||
|
u64 pv_tsc_khz = 1000000ULL << 32;
|
||||||
|
|
||||||
|
do_div(pv_tsc_khz, src->tsc_to_system_mul);
|
||||||
|
if (src->tsc_shift < 0)
|
||||||
|
pv_tsc_khz <<= -src->tsc_shift;
|
||||||
|
else
|
||||||
|
pv_tsc_khz >>= src->tsc_shift;
|
||||||
|
return pv_tsc_khz;
|
||||||
|
}
|
||||||
|
|
||||||
cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
|
cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
|
||||||
{
|
{
|
||||||
struct pvclock_shadow_time shadow;
|
struct pvclock_shadow_time shadow;
|
||||||
|
|
|
@ -3,10 +3,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
|
common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
|
||||||
coalesced_mmio.o)
|
coalesced_mmio.o irq_comm.o)
|
||||||
ifeq ($(CONFIG_KVM_TRACE),y)
|
ifeq ($(CONFIG_KVM_TRACE),y)
|
||||||
common-objs += $(addprefix ../../../virt/kvm/, kvm_trace.o)
|
common-objs += $(addprefix ../../../virt/kvm/, kvm_trace.o)
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(CONFIG_DMAR),y)
|
||||||
|
common-objs += $(addprefix ../../../virt/kvm/, vtd.o)
|
||||||
|
endif
|
||||||
|
|
||||||
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/x86/kvm
|
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/x86/kvm
|
||||||
|
|
||||||
|
|
|
@ -200,13 +200,14 @@ static int __pit_timer_fn(struct kvm_kpit_state *ps)
|
||||||
|
|
||||||
if (!atomic_inc_and_test(&pt->pending))
|
if (!atomic_inc_and_test(&pt->pending))
|
||||||
set_bit(KVM_REQ_PENDING_TIMER, &vcpu0->requests);
|
set_bit(KVM_REQ_PENDING_TIMER, &vcpu0->requests);
|
||||||
if (vcpu0 && waitqueue_active(&vcpu0->wq)) {
|
|
||||||
vcpu0->arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
if (vcpu0 && waitqueue_active(&vcpu0->wq))
|
||||||
wake_up_interruptible(&vcpu0->wq);
|
wake_up_interruptible(&vcpu0->wq);
|
||||||
}
|
|
||||||
|
|
||||||
pt->timer.expires = ktime_add_ns(pt->timer.expires, pt->period);
|
pt->timer.expires = ktime_add_ns(pt->timer.expires, pt->period);
|
||||||
pt->scheduled = ktime_to_ns(pt->timer.expires);
|
pt->scheduled = ktime_to_ns(pt->timer.expires);
|
||||||
|
if (pt->period)
|
||||||
|
ps->channels[0].count_load_time = pt->timer.expires;
|
||||||
|
|
||||||
return (pt->period == 0 ? 0 : 1);
|
return (pt->period == 0 ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
@ -215,12 +216,22 @@ int pit_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct kvm_pit *pit = vcpu->kvm->arch.vpit;
|
struct kvm_pit *pit = vcpu->kvm->arch.vpit;
|
||||||
|
|
||||||
if (pit && vcpu->vcpu_id == 0 && pit->pit_state.inject_pending)
|
if (pit && vcpu->vcpu_id == 0 && pit->pit_state.irq_ack)
|
||||||
return atomic_read(&pit->pit_state.pit_timer.pending);
|
return atomic_read(&pit->pit_state.pit_timer.pending);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kvm_pit_ack_irq(struct kvm_irq_ack_notifier *kian)
|
||||||
|
{
|
||||||
|
struct kvm_kpit_state *ps = container_of(kian, struct kvm_kpit_state,
|
||||||
|
irq_ack_notifier);
|
||||||
|
spin_lock(&ps->inject_lock);
|
||||||
|
if (atomic_dec_return(&ps->pit_timer.pending) < 0)
|
||||||
|
atomic_inc(&ps->pit_timer.pending);
|
||||||
|
ps->irq_ack = 1;
|
||||||
|
spin_unlock(&ps->inject_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
|
static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
|
||||||
{
|
{
|
||||||
struct kvm_kpit_state *ps;
|
struct kvm_kpit_state *ps;
|
||||||
|
@ -255,8 +266,9 @@ static void destroy_pit_timer(struct kvm_kpit_timer *pt)
|
||||||
hrtimer_cancel(&pt->timer);
|
hrtimer_cancel(&pt->timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_pit_timer(struct kvm_kpit_timer *pt, u32 val, int is_period)
|
static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
|
||||||
{
|
{
|
||||||
|
struct kvm_kpit_timer *pt = &ps->pit_timer;
|
||||||
s64 interval;
|
s64 interval;
|
||||||
|
|
||||||
interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
|
interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
|
||||||
|
@ -268,6 +280,7 @@ static void create_pit_timer(struct kvm_kpit_timer *pt, u32 val, int is_period)
|
||||||
pt->period = (is_period == 0) ? 0 : interval;
|
pt->period = (is_period == 0) ? 0 : interval;
|
||||||
pt->timer.function = pit_timer_fn;
|
pt->timer.function = pit_timer_fn;
|
||||||
atomic_set(&pt->pending, 0);
|
atomic_set(&pt->pending, 0);
|
||||||
|
ps->irq_ack = 1;
|
||||||
|
|
||||||
hrtimer_start(&pt->timer, ktime_add_ns(ktime_get(), interval),
|
hrtimer_start(&pt->timer, ktime_add_ns(ktime_get(), interval),
|
||||||
HRTIMER_MODE_ABS);
|
HRTIMER_MODE_ABS);
|
||||||
|
@ -302,11 +315,11 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val)
|
||||||
case 1:
|
case 1:
|
||||||
/* FIXME: enhance mode 4 precision */
|
/* FIXME: enhance mode 4 precision */
|
||||||
case 4:
|
case 4:
|
||||||
create_pit_timer(&ps->pit_timer, val, 0);
|
create_pit_timer(ps, val, 0);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
create_pit_timer(&ps->pit_timer, val, 1);
|
create_pit_timer(ps, val, 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
destroy_pit_timer(&ps->pit_timer);
|
destroy_pit_timer(&ps->pit_timer);
|
||||||
|
@ -520,7 +533,7 @@ void kvm_pit_reset(struct kvm_pit *pit)
|
||||||
mutex_unlock(&pit->pit_state.lock);
|
mutex_unlock(&pit->pit_state.lock);
|
||||||
|
|
||||||
atomic_set(&pit->pit_state.pit_timer.pending, 0);
|
atomic_set(&pit->pit_state.pit_timer.pending, 0);
|
||||||
pit->pit_state.inject_pending = 1;
|
pit->pit_state.irq_ack = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct kvm_pit *kvm_create_pit(struct kvm *kvm)
|
struct kvm_pit *kvm_create_pit(struct kvm *kvm)
|
||||||
|
@ -534,6 +547,7 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm)
|
||||||
|
|
||||||
mutex_init(&pit->pit_state.lock);
|
mutex_init(&pit->pit_state.lock);
|
||||||
mutex_lock(&pit->pit_state.lock);
|
mutex_lock(&pit->pit_state.lock);
|
||||||
|
spin_lock_init(&pit->pit_state.inject_lock);
|
||||||
|
|
||||||
/* Initialize PIO device */
|
/* Initialize PIO device */
|
||||||
pit->dev.read = pit_ioport_read;
|
pit->dev.read = pit_ioport_read;
|
||||||
|
@ -555,6 +569,9 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm)
|
||||||
pit_state->pit = pit;
|
pit_state->pit = pit;
|
||||||
hrtimer_init(&pit_state->pit_timer.timer,
|
hrtimer_init(&pit_state->pit_timer.timer,
|
||||||
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||||
|
pit_state->irq_ack_notifier.gsi = 0;
|
||||||
|
pit_state->irq_ack_notifier.irq_acked = kvm_pit_ack_irq;
|
||||||
|
kvm_register_irq_ack_notifier(kvm, &pit_state->irq_ack_notifier);
|
||||||
mutex_unlock(&pit->pit_state.lock);
|
mutex_unlock(&pit->pit_state.lock);
|
||||||
|
|
||||||
kvm_pit_reset(pit);
|
kvm_pit_reset(pit);
|
||||||
|
@ -578,10 +595,8 @@ void kvm_free_pit(struct kvm *kvm)
|
||||||
static void __inject_pit_timer_intr(struct kvm *kvm)
|
static void __inject_pit_timer_intr(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
mutex_lock(&kvm->lock);
|
mutex_lock(&kvm->lock);
|
||||||
kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 1);
|
kvm_set_irq(kvm, 0, 1);
|
||||||
kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 0);
|
kvm_set_irq(kvm, 0, 0);
|
||||||
kvm_pic_set_irq(pic_irqchip(kvm), 0, 1);
|
|
||||||
kvm_pic_set_irq(pic_irqchip(kvm), 0, 0);
|
|
||||||
mutex_unlock(&kvm->lock);
|
mutex_unlock(&kvm->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,37 +607,19 @@ void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
|
||||||
struct kvm_kpit_state *ps;
|
struct kvm_kpit_state *ps;
|
||||||
|
|
||||||
if (vcpu && pit) {
|
if (vcpu && pit) {
|
||||||
|
int inject = 0;
|
||||||
ps = &pit->pit_state;
|
ps = &pit->pit_state;
|
||||||
|
|
||||||
/* Try to inject pending interrupts when:
|
/* Try to inject pending interrupts when
|
||||||
* 1. Pending exists
|
* last one has been acked.
|
||||||
* 2. Last interrupt was accepted or waited for too long time*/
|
*/
|
||||||
if (atomic_read(&ps->pit_timer.pending) &&
|
spin_lock(&ps->inject_lock);
|
||||||
(ps->inject_pending ||
|
if (atomic_read(&ps->pit_timer.pending) && ps->irq_ack) {
|
||||||
(jiffies - ps->last_injected_time
|
ps->irq_ack = 0;
|
||||||
>= KVM_MAX_PIT_INTR_INTERVAL))) {
|
inject = 1;
|
||||||
ps->inject_pending = 0;
|
}
|
||||||
|
spin_unlock(&ps->inject_lock);
|
||||||
|
if (inject)
|
||||||
__inject_pit_timer_intr(kvm);
|
__inject_pit_timer_intr(kvm);
|
||||||
ps->last_injected_time = jiffies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec)
|
|
||||||
{
|
|
||||||
struct kvm_arch *arch = &vcpu->kvm->arch;
|
|
||||||
struct kvm_kpit_state *ps;
|
|
||||||
|
|
||||||
if (vcpu && arch->vpit) {
|
|
||||||
ps = &arch->vpit->pit_state;
|
|
||||||
if (atomic_read(&ps->pit_timer.pending) &&
|
|
||||||
(((arch->vpic->pics[0].imr & 1) == 0 &&
|
|
||||||
arch->vpic->pics[0].irq_base == vec) ||
|
|
||||||
(arch->vioapic->redirtbl[0].fields.vector == vec &&
|
|
||||||
arch->vioapic->redirtbl[0].fields.mask != 1))) {
|
|
||||||
ps->inject_pending = 1;
|
|
||||||
atomic_dec(&ps->pit_timer.pending);
|
|
||||||
ps->channels[0].count_load_time = ktime_get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ struct kvm_kpit_timer {
|
||||||
int irq;
|
int irq;
|
||||||
s64 period; /* unit: ns */
|
s64 period; /* unit: ns */
|
||||||
s64 scheduled;
|
s64 scheduled;
|
||||||
ktime_t last_update;
|
|
||||||
atomic_t pending;
|
atomic_t pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,8 +33,9 @@ struct kvm_kpit_state {
|
||||||
u32 speaker_data_on;
|
u32 speaker_data_on;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
struct kvm_pit *pit;
|
struct kvm_pit *pit;
|
||||||
bool inject_pending; /* if inject pending interrupts */
|
spinlock_t inject_lock;
|
||||||
unsigned long last_injected_time;
|
unsigned long irq_ack;
|
||||||
|
struct kvm_irq_ack_notifier irq_ack_notifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_pit {
|
struct kvm_pit {
|
||||||
|
@ -54,7 +54,6 @@ struct kvm_pit {
|
||||||
#define KVM_PIT_CHANNEL_MASK 0x3
|
#define KVM_PIT_CHANNEL_MASK 0x3
|
||||||
|
|
||||||
void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu);
|
void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu);
|
||||||
void kvm_pit_timer_intr_post(struct kvm_vcpu *vcpu, int vec);
|
|
||||||
void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val);
|
void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val);
|
||||||
struct kvm_pit *kvm_create_pit(struct kvm *kvm);
|
struct kvm_pit *kvm_create_pit(struct kvm *kvm);
|
||||||
void kvm_free_pit(struct kvm *kvm);
|
void kvm_free_pit(struct kvm *kvm);
|
||||||
|
|
|
@ -30,6 +30,19 @@
|
||||||
|
|
||||||
#include <linux/kvm_host.h>
|
#include <linux/kvm_host.h>
|
||||||
|
|
||||||
|
static void pic_clear_isr(struct kvm_kpic_state *s, int irq)
|
||||||
|
{
|
||||||
|
s->isr &= ~(1 << irq);
|
||||||
|
s->isr_ack |= (1 << irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_pic_clear_isr_ack(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
struct kvm_pic *s = pic_irqchip(kvm);
|
||||||
|
s->pics[0].isr_ack = 0xff;
|
||||||
|
s->pics[1].isr_ack = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set irq level. If an edge is detected, then the IRR is set to 1
|
* set irq level. If an edge is detected, then the IRR is set to 1
|
||||||
*/
|
*/
|
||||||
|
@ -141,11 +154,12 @@ void kvm_pic_set_irq(void *opaque, int irq, int level)
|
||||||
*/
|
*/
|
||||||
static inline void pic_intack(struct kvm_kpic_state *s, int irq)
|
static inline void pic_intack(struct kvm_kpic_state *s, int irq)
|
||||||
{
|
{
|
||||||
|
s->isr |= 1 << irq;
|
||||||
if (s->auto_eoi) {
|
if (s->auto_eoi) {
|
||||||
if (s->rotate_on_auto_eoi)
|
if (s->rotate_on_auto_eoi)
|
||||||
s->priority_add = (irq + 1) & 7;
|
s->priority_add = (irq + 1) & 7;
|
||||||
} else
|
pic_clear_isr(s, irq);
|
||||||
s->isr |= (1 << irq);
|
}
|
||||||
/*
|
/*
|
||||||
* We don't clear a level sensitive interrupt here
|
* We don't clear a level sensitive interrupt here
|
||||||
*/
|
*/
|
||||||
|
@ -153,9 +167,10 @@ static inline void pic_intack(struct kvm_kpic_state *s, int irq)
|
||||||
s->irr &= ~(1 << irq);
|
s->irr &= ~(1 << irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_pic_read_irq(struct kvm_pic *s)
|
int kvm_pic_read_irq(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
int irq, irq2, intno;
|
int irq, irq2, intno;
|
||||||
|
struct kvm_pic *s = pic_irqchip(kvm);
|
||||||
|
|
||||||
irq = pic_get_irq(&s->pics[0]);
|
irq = pic_get_irq(&s->pics[0]);
|
||||||
if (irq >= 0) {
|
if (irq >= 0) {
|
||||||
|
@ -181,16 +196,32 @@ int kvm_pic_read_irq(struct kvm_pic *s)
|
||||||
intno = s->pics[0].irq_base + irq;
|
intno = s->pics[0].irq_base + irq;
|
||||||
}
|
}
|
||||||
pic_update_irq(s);
|
pic_update_irq(s);
|
||||||
|
kvm_notify_acked_irq(kvm, irq);
|
||||||
|
|
||||||
return intno;
|
return intno;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_pic_reset(struct kvm_kpic_state *s)
|
void kvm_pic_reset(struct kvm_kpic_state *s)
|
||||||
{
|
{
|
||||||
|
int irq, irqbase;
|
||||||
|
struct kvm *kvm = s->pics_state->irq_request_opaque;
|
||||||
|
struct kvm_vcpu *vcpu0 = kvm->vcpus[0];
|
||||||
|
|
||||||
|
if (s == &s->pics_state->pics[0])
|
||||||
|
irqbase = 0;
|
||||||
|
else
|
||||||
|
irqbase = 8;
|
||||||
|
|
||||||
|
for (irq = 0; irq < PIC_NUM_PINS/2; irq++) {
|
||||||
|
if (vcpu0 && kvm_apic_accept_pic_intr(vcpu0))
|
||||||
|
if (s->irr & (1 << irq) || s->isr & (1 << irq))
|
||||||
|
kvm_notify_acked_irq(kvm, irq+irqbase);
|
||||||
|
}
|
||||||
s->last_irr = 0;
|
s->last_irr = 0;
|
||||||
s->irr = 0;
|
s->irr = 0;
|
||||||
s->imr = 0;
|
s->imr = 0;
|
||||||
s->isr = 0;
|
s->isr = 0;
|
||||||
|
s->isr_ack = 0xff;
|
||||||
s->priority_add = 0;
|
s->priority_add = 0;
|
||||||
s->irq_base = 0;
|
s->irq_base = 0;
|
||||||
s->read_reg_select = 0;
|
s->read_reg_select = 0;
|
||||||
|
@ -243,7 +274,7 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
|
||||||
priority = get_priority(s, s->isr);
|
priority = get_priority(s, s->isr);
|
||||||
if (priority != 8) {
|
if (priority != 8) {
|
||||||
irq = (priority + s->priority_add) & 7;
|
irq = (priority + s->priority_add) & 7;
|
||||||
s->isr &= ~(1 << irq);
|
pic_clear_isr(s, irq);
|
||||||
if (cmd == 5)
|
if (cmd == 5)
|
||||||
s->priority_add = (irq + 1) & 7;
|
s->priority_add = (irq + 1) & 7;
|
||||||
pic_update_irq(s->pics_state);
|
pic_update_irq(s->pics_state);
|
||||||
|
@ -251,7 +282,7 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
irq = val & 7;
|
irq = val & 7;
|
||||||
s->isr &= ~(1 << irq);
|
pic_clear_isr(s, irq);
|
||||||
pic_update_irq(s->pics_state);
|
pic_update_irq(s->pics_state);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
|
@ -260,8 +291,8 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
irq = val & 7;
|
irq = val & 7;
|
||||||
s->isr &= ~(1 << irq);
|
|
||||||
s->priority_add = (irq + 1) & 7;
|
s->priority_add = (irq + 1) & 7;
|
||||||
|
pic_clear_isr(s, irq);
|
||||||
pic_update_irq(s->pics_state);
|
pic_update_irq(s->pics_state);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -303,7 +334,7 @@ static u32 pic_poll_read(struct kvm_kpic_state *s, u32 addr1)
|
||||||
s->pics_state->pics[0].irr &= ~(1 << 2);
|
s->pics_state->pics[0].irr &= ~(1 << 2);
|
||||||
}
|
}
|
||||||
s->irr &= ~(1 << ret);
|
s->irr &= ~(1 << ret);
|
||||||
s->isr &= ~(1 << ret);
|
pic_clear_isr(s, ret);
|
||||||
if (addr1 >> 7 || ret != 2)
|
if (addr1 >> 7 || ret != 2)
|
||||||
pic_update_irq(s->pics_state);
|
pic_update_irq(s->pics_state);
|
||||||
} else {
|
} else {
|
||||||
|
@ -422,10 +453,14 @@ static void pic_irq_request(void *opaque, int level)
|
||||||
{
|
{
|
||||||
struct kvm *kvm = opaque;
|
struct kvm *kvm = opaque;
|
||||||
struct kvm_vcpu *vcpu = kvm->vcpus[0];
|
struct kvm_vcpu *vcpu = kvm->vcpus[0];
|
||||||
|
struct kvm_pic *s = pic_irqchip(kvm);
|
||||||
|
int irq = pic_get_irq(&s->pics[0]);
|
||||||
|
|
||||||
pic_irqchip(kvm)->output = level;
|
s->output = level;
|
||||||
if (vcpu)
|
if (vcpu && level && (s->pics[0].isr_ack & (1 << irq))) {
|
||||||
|
s->pics[0].isr_ack &= ~(1 << irq);
|
||||||
kvm_vcpu_kick(vcpu);
|
kvm_vcpu_kick(vcpu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct kvm_pic *kvm_create_pic(struct kvm *kvm)
|
struct kvm_pic *kvm_create_pic(struct kvm *kvm)
|
||||||
|
|
|
@ -72,7 +72,7 @@ int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
|
||||||
if (kvm_apic_accept_pic_intr(v)) {
|
if (kvm_apic_accept_pic_intr(v)) {
|
||||||
s = pic_irqchip(v->kvm);
|
s = pic_irqchip(v->kvm);
|
||||||
s->output = 0; /* PIC */
|
s->output = 0; /* PIC */
|
||||||
vector = kvm_pic_read_irq(s);
|
vector = kvm_pic_read_irq(v->kvm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vector;
|
return vector;
|
||||||
|
@ -90,7 +90,6 @@ EXPORT_SYMBOL_GPL(kvm_inject_pending_timer_irqs);
|
||||||
void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec)
|
void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec)
|
||||||
{
|
{
|
||||||
kvm_apic_timer_intr_post(vcpu, vec);
|
kvm_apic_timer_intr_post(vcpu, vec);
|
||||||
kvm_pit_timer_intr_post(vcpu, vec);
|
|
||||||
/* TODO: PIT, RTC etc. */
|
/* TODO: PIT, RTC etc. */
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_timer_intr_post);
|
EXPORT_SYMBOL_GPL(kvm_timer_intr_post);
|
||||||
|
|
|
@ -42,6 +42,7 @@ struct kvm_kpic_state {
|
||||||
u8 irr; /* interrupt request register */
|
u8 irr; /* interrupt request register */
|
||||||
u8 imr; /* interrupt mask register */
|
u8 imr; /* interrupt mask register */
|
||||||
u8 isr; /* interrupt service register */
|
u8 isr; /* interrupt service register */
|
||||||
|
u8 isr_ack; /* interrupt ack detection */
|
||||||
u8 priority_add; /* highest irq priority */
|
u8 priority_add; /* highest irq priority */
|
||||||
u8 irq_base;
|
u8 irq_base;
|
||||||
u8 read_reg_select;
|
u8 read_reg_select;
|
||||||
|
@ -63,12 +64,13 @@ struct kvm_pic {
|
||||||
void *irq_request_opaque;
|
void *irq_request_opaque;
|
||||||
int output; /* intr from master PIC */
|
int output; /* intr from master PIC */
|
||||||
struct kvm_io_device dev;
|
struct kvm_io_device dev;
|
||||||
|
void (*ack_notifier)(void *opaque, int irq);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_pic *kvm_create_pic(struct kvm *kvm);
|
struct kvm_pic *kvm_create_pic(struct kvm *kvm);
|
||||||
void kvm_pic_set_irq(void *opaque, int irq, int level);
|
int kvm_pic_read_irq(struct kvm *kvm);
|
||||||
int kvm_pic_read_irq(struct kvm_pic *s);
|
|
||||||
void kvm_pic_update_irq(struct kvm_pic *s);
|
void kvm_pic_update_irq(struct kvm_pic *s);
|
||||||
|
void kvm_pic_clear_isr_ack(struct kvm *kvm);
|
||||||
|
|
||||||
static inline struct kvm_pic *pic_irqchip(struct kvm *kvm)
|
static inline struct kvm_pic *pic_irqchip(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef ASM_KVM_CACHE_REGS_H
|
||||||
|
#define ASM_KVM_CACHE_REGS_H
|
||||||
|
|
||||||
|
static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu,
|
||||||
|
enum kvm_reg reg)
|
||||||
|
{
|
||||||
|
if (!test_bit(reg, (unsigned long *)&vcpu->arch.regs_avail))
|
||||||
|
kvm_x86_ops->cache_reg(vcpu, reg);
|
||||||
|
|
||||||
|
return vcpu->arch.regs[reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kvm_register_write(struct kvm_vcpu *vcpu,
|
||||||
|
enum kvm_reg reg,
|
||||||
|
unsigned long val)
|
||||||
|
{
|
||||||
|
vcpu->arch.regs[reg] = val;
|
||||||
|
__set_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty);
|
||||||
|
__set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long kvm_rip_read(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return kvm_register_read(vcpu, VCPU_REGS_RIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kvm_rip_write(struct kvm_vcpu *vcpu, unsigned long val)
|
||||||
|
{
|
||||||
|
kvm_register_write(vcpu, VCPU_REGS_RIP, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -32,6 +32,7 @@
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <asm/apicdef.h>
|
#include <asm/apicdef.h>
|
||||||
#include <asm/atomic.h>
|
#include <asm/atomic.h>
|
||||||
|
#include "kvm_cache_regs.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
|
|
||||||
#define PRId64 "d"
|
#define PRId64 "d"
|
||||||
|
@ -338,13 +339,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
|
||||||
} else
|
} else
|
||||||
apic_clear_vector(vector, apic->regs + APIC_TMR);
|
apic_clear_vector(vector, apic->regs + APIC_TMR);
|
||||||
|
|
||||||
if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE)
|
kvm_vcpu_kick(vcpu);
|
||||||
kvm_vcpu_kick(vcpu);
|
|
||||||
else if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED) {
|
|
||||||
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
|
||||||
if (waitqueue_active(&vcpu->wq))
|
|
||||||
wake_up_interruptible(&vcpu->wq);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = (orig_irr == 0);
|
result = (orig_irr == 0);
|
||||||
break;
|
break;
|
||||||
|
@ -370,21 +365,18 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
|
||||||
vcpu->arch.mp_state = KVM_MP_STATE_INIT_RECEIVED;
|
vcpu->arch.mp_state = KVM_MP_STATE_INIT_RECEIVED;
|
||||||
kvm_vcpu_kick(vcpu);
|
kvm_vcpu_kick(vcpu);
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_DEBUG
|
apic_debug("Ignoring de-assert INIT to vcpu %d\n",
|
||||||
"Ignoring de-assert INIT to vcpu %d\n",
|
vcpu->vcpu_id);
|
||||||
vcpu->vcpu_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case APIC_DM_STARTUP:
|
case APIC_DM_STARTUP:
|
||||||
printk(KERN_DEBUG "SIPI to vcpu %d vector 0x%02x\n",
|
apic_debug("SIPI to vcpu %d vector 0x%02x\n",
|
||||||
vcpu->vcpu_id, vector);
|
vcpu->vcpu_id, vector);
|
||||||
if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED) {
|
if (vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED) {
|
||||||
vcpu->arch.sipi_vector = vector;
|
vcpu->arch.sipi_vector = vector;
|
||||||
vcpu->arch.mp_state = KVM_MP_STATE_SIPI_RECEIVED;
|
vcpu->arch.mp_state = KVM_MP_STATE_SIPI_RECEIVED;
|
||||||
if (waitqueue_active(&vcpu->wq))
|
kvm_vcpu_kick(vcpu);
|
||||||
wake_up_interruptible(&vcpu->wq);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -438,7 +430,7 @@ struct kvm_vcpu *kvm_get_lowest_prio_vcpu(struct kvm *kvm, u8 vector,
|
||||||
static void apic_set_eoi(struct kvm_lapic *apic)
|
static void apic_set_eoi(struct kvm_lapic *apic)
|
||||||
{
|
{
|
||||||
int vector = apic_find_highest_isr(apic);
|
int vector = apic_find_highest_isr(apic);
|
||||||
|
int trigger_mode;
|
||||||
/*
|
/*
|
||||||
* Not every write EOI will has corresponding ISR,
|
* Not every write EOI will has corresponding ISR,
|
||||||
* one example is when Kernel check timer on setup_IO_APIC
|
* one example is when Kernel check timer on setup_IO_APIC
|
||||||
|
@ -450,7 +442,10 @@ static void apic_set_eoi(struct kvm_lapic *apic)
|
||||||
apic_update_ppr(apic);
|
apic_update_ppr(apic);
|
||||||
|
|
||||||
if (apic_test_and_clear_vector(vector, apic->regs + APIC_TMR))
|
if (apic_test_and_clear_vector(vector, apic->regs + APIC_TMR))
|
||||||
kvm_ioapic_update_eoi(apic->vcpu->kvm, vector);
|
trigger_mode = IOAPIC_LEVEL_TRIG;
|
||||||
|
else
|
||||||
|
trigger_mode = IOAPIC_EDGE_TRIG;
|
||||||
|
kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void apic_send_ipi(struct kvm_lapic *apic)
|
static void apic_send_ipi(struct kvm_lapic *apic)
|
||||||
|
@ -558,8 +553,7 @@ static void __report_tpr_access(struct kvm_lapic *apic, bool write)
|
||||||
struct kvm_run *run = vcpu->run;
|
struct kvm_run *run = vcpu->run;
|
||||||
|
|
||||||
set_bit(KVM_REQ_REPORT_TPR_ACCESS, &vcpu->requests);
|
set_bit(KVM_REQ_REPORT_TPR_ACCESS, &vcpu->requests);
|
||||||
kvm_x86_ops->cache_regs(vcpu);
|
run->tpr_access.rip = kvm_rip_read(vcpu);
|
||||||
run->tpr_access.rip = vcpu->arch.rip;
|
|
||||||
run->tpr_access.is_write = write;
|
run->tpr_access.is_write = write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,9 +677,9 @@ static void apic_mmio_write(struct kvm_io_device *this,
|
||||||
* Refer SDM 8.4.1
|
* Refer SDM 8.4.1
|
||||||
*/
|
*/
|
||||||
if (len != 4 || alignment) {
|
if (len != 4 || alignment) {
|
||||||
if (printk_ratelimit())
|
/* Don't shout loud, $infamous_os would cause only noise. */
|
||||||
printk(KERN_ERR "apic write: bad size=%d %lx\n",
|
apic_debug("apic write: bad size=%d %lx\n",
|
||||||
len, (long)address);
|
len, (long)address);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,10 +941,9 @@ static int __apic_timer_fn(struct kvm_lapic *apic)
|
||||||
|
|
||||||
if(!atomic_inc_and_test(&apic->timer.pending))
|
if(!atomic_inc_and_test(&apic->timer.pending))
|
||||||
set_bit(KVM_REQ_PENDING_TIMER, &apic->vcpu->requests);
|
set_bit(KVM_REQ_PENDING_TIMER, &apic->vcpu->requests);
|
||||||
if (waitqueue_active(q)) {
|
if (waitqueue_active(q))
|
||||||
apic->vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
|
||||||
wake_up_interruptible(q);
|
wake_up_interruptible(q);
|
||||||
}
|
|
||||||
if (apic_lvtt_period(apic)) {
|
if (apic_lvtt_period(apic)) {
|
||||||
result = 1;
|
result = 1;
|
||||||
apic->timer.dev.expires = ktime_add_ns(
|
apic->timer.dev.expires = ktime_add_ns(
|
||||||
|
|
|
@ -70,6 +70,9 @@ static int dbg = 0;
|
||||||
module_param(dbg, bool, 0644);
|
module_param(dbg, bool, 0644);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int oos_shadow = 1;
|
||||||
|
module_param(oos_shadow, bool, 0644);
|
||||||
|
|
||||||
#ifndef MMU_DEBUG
|
#ifndef MMU_DEBUG
|
||||||
#define ASSERT(x) do { } while (0)
|
#define ASSERT(x) do { } while (0)
|
||||||
#else
|
#else
|
||||||
|
@ -135,18 +138,24 @@ module_param(dbg, bool, 0644);
|
||||||
#define ACC_USER_MASK PT_USER_MASK
|
#define ACC_USER_MASK PT_USER_MASK
|
||||||
#define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK)
|
#define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK)
|
||||||
|
|
||||||
struct kvm_pv_mmu_op_buffer {
|
#define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
|
||||||
void *ptr;
|
|
||||||
unsigned len;
|
|
||||||
unsigned processed;
|
|
||||||
char buf[512] __aligned(sizeof(long));
|
|
||||||
};
|
|
||||||
|
|
||||||
struct kvm_rmap_desc {
|
struct kvm_rmap_desc {
|
||||||
u64 *shadow_ptes[RMAP_EXT];
|
u64 *shadow_ptes[RMAP_EXT];
|
||||||
struct kvm_rmap_desc *more;
|
struct kvm_rmap_desc *more;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct kvm_shadow_walk {
|
||||||
|
int (*entry)(struct kvm_shadow_walk *walk, struct kvm_vcpu *vcpu,
|
||||||
|
u64 addr, u64 *spte, int level);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kvm_unsync_walk {
|
||||||
|
int (*entry) (struct kvm_mmu_page *sp, struct kvm_unsync_walk *walk);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*mmu_parent_walk_fn) (struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp);
|
||||||
|
|
||||||
static struct kmem_cache *pte_chain_cache;
|
static struct kmem_cache *pte_chain_cache;
|
||||||
static struct kmem_cache *rmap_desc_cache;
|
static struct kmem_cache *rmap_desc_cache;
|
||||||
static struct kmem_cache *mmu_page_header_cache;
|
static struct kmem_cache *mmu_page_header_cache;
|
||||||
|
@ -405,16 +414,19 @@ static int host_largepage_backed(struct kvm *kvm, gfn_t gfn)
|
||||||
{
|
{
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
addr = gfn_to_hva(kvm, gfn);
|
addr = gfn_to_hva(kvm, gfn);
|
||||||
if (kvm_is_error_hva(addr))
|
if (kvm_is_error_hva(addr))
|
||||||
return 0;
|
return ret;
|
||||||
|
|
||||||
|
down_read(¤t->mm->mmap_sem);
|
||||||
vma = find_vma(current->mm, addr);
|
vma = find_vma(current->mm, addr);
|
||||||
if (vma && is_vm_hugetlb_page(vma))
|
if (vma && is_vm_hugetlb_page(vma))
|
||||||
return 1;
|
ret = 1;
|
||||||
|
up_read(¤t->mm->mmap_sem);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_largepage_backed(struct kvm_vcpu *vcpu, gfn_t large_gfn)
|
static int is_largepage_backed(struct kvm_vcpu *vcpu, gfn_t large_gfn)
|
||||||
|
@ -649,8 +661,6 @@ static void rmap_write_protect(struct kvm *kvm, u64 gfn)
|
||||||
|
|
||||||
if (write_protected)
|
if (write_protected)
|
||||||
kvm_flush_remote_tlbs(kvm);
|
kvm_flush_remote_tlbs(kvm);
|
||||||
|
|
||||||
account_shadowed(kvm, gfn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp)
|
static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp)
|
||||||
|
@ -859,6 +869,77 @@ static void mmu_page_remove_parent_pte(struct kvm_mmu_page *sp,
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mmu_parent_walk(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
|
||||||
|
mmu_parent_walk_fn fn)
|
||||||
|
{
|
||||||
|
struct kvm_pte_chain *pte_chain;
|
||||||
|
struct hlist_node *node;
|
||||||
|
struct kvm_mmu_page *parent_sp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!sp->multimapped && sp->parent_pte) {
|
||||||
|
parent_sp = page_header(__pa(sp->parent_pte));
|
||||||
|
fn(vcpu, parent_sp);
|
||||||
|
mmu_parent_walk(vcpu, parent_sp, fn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hlist_for_each_entry(pte_chain, node, &sp->parent_ptes, link)
|
||||||
|
for (i = 0; i < NR_PTE_CHAIN_ENTRIES; ++i) {
|
||||||
|
if (!pte_chain->parent_ptes[i])
|
||||||
|
break;
|
||||||
|
parent_sp = page_header(__pa(pte_chain->parent_ptes[i]));
|
||||||
|
fn(vcpu, parent_sp);
|
||||||
|
mmu_parent_walk(vcpu, parent_sp, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_mmu_update_unsync_bitmap(u64 *spte)
|
||||||
|
{
|
||||||
|
unsigned int index;
|
||||||
|
struct kvm_mmu_page *sp = page_header(__pa(spte));
|
||||||
|
|
||||||
|
index = spte - sp->spt;
|
||||||
|
__set_bit(index, sp->unsync_child_bitmap);
|
||||||
|
sp->unsync_children = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_mmu_update_parents_unsync(struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
struct kvm_pte_chain *pte_chain;
|
||||||
|
struct hlist_node *node;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!sp->parent_pte)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!sp->multimapped) {
|
||||||
|
kvm_mmu_update_unsync_bitmap(sp->parent_pte);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hlist_for_each_entry(pte_chain, node, &sp->parent_ptes, link)
|
||||||
|
for (i = 0; i < NR_PTE_CHAIN_ENTRIES; ++i) {
|
||||||
|
if (!pte_chain->parent_ptes[i])
|
||||||
|
break;
|
||||||
|
kvm_mmu_update_unsync_bitmap(pte_chain->parent_ptes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unsync_walk_fn(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
sp->unsync_children = 1;
|
||||||
|
kvm_mmu_update_parents_unsync(sp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_mmu_mark_parents_unsync(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
mmu_parent_walk(vcpu, sp, unsync_walk_fn);
|
||||||
|
kvm_mmu_update_parents_unsync(sp);
|
||||||
|
}
|
||||||
|
|
||||||
static void nonpaging_prefetch_page(struct kvm_vcpu *vcpu,
|
static void nonpaging_prefetch_page(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_mmu_page *sp)
|
struct kvm_mmu_page *sp)
|
||||||
{
|
{
|
||||||
|
@ -868,6 +949,58 @@ static void nonpaging_prefetch_page(struct kvm_vcpu *vcpu,
|
||||||
sp->spt[i] = shadow_trap_nonpresent_pte;
|
sp->spt[i] = shadow_trap_nonpresent_pte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nonpaging_sync_page(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nonpaging_invlpg(struct kvm_vcpu *vcpu, gva_t gva)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#define for_each_unsync_children(bitmap, idx) \
|
||||||
|
for (idx = find_first_bit(bitmap, 512); \
|
||||||
|
idx < 512; \
|
||||||
|
idx = find_next_bit(bitmap, 512, idx+1))
|
||||||
|
|
||||||
|
static int mmu_unsync_walk(struct kvm_mmu_page *sp,
|
||||||
|
struct kvm_unsync_walk *walker)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
if (!sp->unsync_children)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for_each_unsync_children(sp->unsync_child_bitmap, i) {
|
||||||
|
u64 ent = sp->spt[i];
|
||||||
|
|
||||||
|
if (is_shadow_present_pte(ent)) {
|
||||||
|
struct kvm_mmu_page *child;
|
||||||
|
child = page_header(ent & PT64_BASE_ADDR_MASK);
|
||||||
|
|
||||||
|
if (child->unsync_children) {
|
||||||
|
ret = mmu_unsync_walk(child, walker);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
__clear_bit(i, sp->unsync_child_bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child->unsync) {
|
||||||
|
ret = walker->entry(child, walker);
|
||||||
|
__clear_bit(i, sp->unsync_child_bitmap);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (find_first_bit(sp->unsync_child_bitmap, 512) == 512)
|
||||||
|
sp->unsync_children = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct kvm_mmu_page *kvm_mmu_lookup_page(struct kvm *kvm, gfn_t gfn)
|
static struct kvm_mmu_page *kvm_mmu_lookup_page(struct kvm *kvm, gfn_t gfn)
|
||||||
{
|
{
|
||||||
unsigned index;
|
unsigned index;
|
||||||
|
@ -888,6 +1021,59 @@ static struct kvm_mmu_page *kvm_mmu_lookup_page(struct kvm *kvm, gfn_t gfn)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kvm_unlink_unsync_page(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
WARN_ON(!sp->unsync);
|
||||||
|
sp->unsync = 0;
|
||||||
|
--kvm->stat.mmu_unsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_mmu_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp);
|
||||||
|
|
||||||
|
static int kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
if (sp->role.glevels != vcpu->arch.mmu.root_level) {
|
||||||
|
kvm_mmu_zap_page(vcpu->kvm, sp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmap_write_protect(vcpu->kvm, sp->gfn);
|
||||||
|
if (vcpu->arch.mmu.sync_page(vcpu, sp)) {
|
||||||
|
kvm_mmu_zap_page(vcpu->kvm, sp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_mmu_flush_tlb(vcpu);
|
||||||
|
kvm_unlink_unsync_page(vcpu->kvm, sp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sync_walker {
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
struct kvm_unsync_walk walker;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mmu_sync_fn(struct kvm_mmu_page *sp, struct kvm_unsync_walk *walk)
|
||||||
|
{
|
||||||
|
struct sync_walker *sync_walk = container_of(walk, struct sync_walker,
|
||||||
|
walker);
|
||||||
|
struct kvm_vcpu *vcpu = sync_walk->vcpu;
|
||||||
|
|
||||||
|
kvm_sync_page(vcpu, sp);
|
||||||
|
return (need_resched() || spin_needbreak(&vcpu->kvm->mmu_lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmu_sync_children(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
struct sync_walker walker = {
|
||||||
|
.walker = { .entry = mmu_sync_fn, },
|
||||||
|
.vcpu = vcpu,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (mmu_unsync_walk(sp, &walker.walker))
|
||||||
|
cond_resched_lock(&vcpu->kvm->mmu_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
||||||
gfn_t gfn,
|
gfn_t gfn,
|
||||||
gva_t gaddr,
|
gva_t gaddr,
|
||||||
|
@ -901,7 +1087,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
||||||
unsigned quadrant;
|
unsigned quadrant;
|
||||||
struct hlist_head *bucket;
|
struct hlist_head *bucket;
|
||||||
struct kvm_mmu_page *sp;
|
struct kvm_mmu_page *sp;
|
||||||
struct hlist_node *node;
|
struct hlist_node *node, *tmp;
|
||||||
|
|
||||||
role.word = 0;
|
role.word = 0;
|
||||||
role.glevels = vcpu->arch.mmu.root_level;
|
role.glevels = vcpu->arch.mmu.root_level;
|
||||||
|
@ -917,9 +1103,20 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
||||||
gfn, role.word);
|
gfn, role.word);
|
||||||
index = kvm_page_table_hashfn(gfn);
|
index = kvm_page_table_hashfn(gfn);
|
||||||
bucket = &vcpu->kvm->arch.mmu_page_hash[index];
|
bucket = &vcpu->kvm->arch.mmu_page_hash[index];
|
||||||
hlist_for_each_entry(sp, node, bucket, hash_link)
|
hlist_for_each_entry_safe(sp, node, tmp, bucket, hash_link)
|
||||||
if (sp->gfn == gfn && sp->role.word == role.word) {
|
if (sp->gfn == gfn) {
|
||||||
|
if (sp->unsync)
|
||||||
|
if (kvm_sync_page(vcpu, sp))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sp->role.word != role.word)
|
||||||
|
continue;
|
||||||
|
|
||||||
mmu_page_add_parent_pte(vcpu, sp, parent_pte);
|
mmu_page_add_parent_pte(vcpu, sp, parent_pte);
|
||||||
|
if (sp->unsync_children) {
|
||||||
|
set_bit(KVM_REQ_MMU_SYNC, &vcpu->requests);
|
||||||
|
kvm_mmu_mark_parents_unsync(vcpu, sp);
|
||||||
|
}
|
||||||
pgprintk("%s: found\n", __func__);
|
pgprintk("%s: found\n", __func__);
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
@ -931,8 +1128,10 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
||||||
sp->gfn = gfn;
|
sp->gfn = gfn;
|
||||||
sp->role = role;
|
sp->role = role;
|
||||||
hlist_add_head(&sp->hash_link, bucket);
|
hlist_add_head(&sp->hash_link, bucket);
|
||||||
if (!metaphysical)
|
if (!metaphysical) {
|
||||||
rmap_write_protect(vcpu->kvm, gfn);
|
rmap_write_protect(vcpu->kvm, gfn);
|
||||||
|
account_shadowed(vcpu->kvm, gfn);
|
||||||
|
}
|
||||||
if (shadow_trap_nonpresent_pte != shadow_notrap_nonpresent_pte)
|
if (shadow_trap_nonpresent_pte != shadow_notrap_nonpresent_pte)
|
||||||
vcpu->arch.mmu.prefetch_page(vcpu, sp);
|
vcpu->arch.mmu.prefetch_page(vcpu, sp);
|
||||||
else
|
else
|
||||||
|
@ -940,6 +1139,35 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int walk_shadow(struct kvm_shadow_walk *walker,
|
||||||
|
struct kvm_vcpu *vcpu, u64 addr)
|
||||||
|
{
|
||||||
|
hpa_t shadow_addr;
|
||||||
|
int level;
|
||||||
|
int r;
|
||||||
|
u64 *sptep;
|
||||||
|
unsigned index;
|
||||||
|
|
||||||
|
shadow_addr = vcpu->arch.mmu.root_hpa;
|
||||||
|
level = vcpu->arch.mmu.shadow_root_level;
|
||||||
|
if (level == PT32E_ROOT_LEVEL) {
|
||||||
|
shadow_addr = vcpu->arch.mmu.pae_root[(addr >> 30) & 3];
|
||||||
|
shadow_addr &= PT64_BASE_ADDR_MASK;
|
||||||
|
--level;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (level >= PT_PAGE_TABLE_LEVEL) {
|
||||||
|
index = SHADOW_PT_INDEX(addr, level);
|
||||||
|
sptep = ((u64 *)__va(shadow_addr)) + index;
|
||||||
|
r = walker->entry(walker, vcpu, addr, sptep, level);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
shadow_addr = *sptep & PT64_BASE_ADDR_MASK;
|
||||||
|
--level;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void kvm_mmu_page_unlink_children(struct kvm *kvm,
|
static void kvm_mmu_page_unlink_children(struct kvm *kvm,
|
||||||
struct kvm_mmu_page *sp)
|
struct kvm_mmu_page *sp)
|
||||||
{
|
{
|
||||||
|
@ -955,7 +1183,6 @@ static void kvm_mmu_page_unlink_children(struct kvm *kvm,
|
||||||
rmap_remove(kvm, &pt[i]);
|
rmap_remove(kvm, &pt[i]);
|
||||||
pt[i] = shadow_trap_nonpresent_pte;
|
pt[i] = shadow_trap_nonpresent_pte;
|
||||||
}
|
}
|
||||||
kvm_flush_remote_tlbs(kvm);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,7 +1201,6 @@ static void kvm_mmu_page_unlink_children(struct kvm *kvm,
|
||||||
}
|
}
|
||||||
pt[i] = shadow_trap_nonpresent_pte;
|
pt[i] = shadow_trap_nonpresent_pte;
|
||||||
}
|
}
|
||||||
kvm_flush_remote_tlbs(kvm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_mmu_put_page(struct kvm_mmu_page *sp, u64 *parent_pte)
|
static void kvm_mmu_put_page(struct kvm_mmu_page *sp, u64 *parent_pte)
|
||||||
|
@ -991,11 +1217,10 @@ static void kvm_mmu_reset_last_pte_updated(struct kvm *kvm)
|
||||||
kvm->vcpus[i]->arch.last_pte_updated = NULL;
|
kvm->vcpus[i]->arch.last_pte_updated = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_mmu_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp)
|
static void kvm_mmu_unlink_parents(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
{
|
{
|
||||||
u64 *parent_pte;
|
u64 *parent_pte;
|
||||||
|
|
||||||
++kvm->stat.mmu_shadow_zapped;
|
|
||||||
while (sp->multimapped || sp->parent_pte) {
|
while (sp->multimapped || sp->parent_pte) {
|
||||||
if (!sp->multimapped)
|
if (!sp->multimapped)
|
||||||
parent_pte = sp->parent_pte;
|
parent_pte = sp->parent_pte;
|
||||||
|
@ -1010,21 +1235,59 @@ static void kvm_mmu_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
kvm_mmu_put_page(sp, parent_pte);
|
kvm_mmu_put_page(sp, parent_pte);
|
||||||
set_shadow_pte(parent_pte, shadow_trap_nonpresent_pte);
|
set_shadow_pte(parent_pte, shadow_trap_nonpresent_pte);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zap_walker {
|
||||||
|
struct kvm_unsync_walk walker;
|
||||||
|
struct kvm *kvm;
|
||||||
|
int zapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mmu_zap_fn(struct kvm_mmu_page *sp, struct kvm_unsync_walk *walk)
|
||||||
|
{
|
||||||
|
struct zap_walker *zap_walk = container_of(walk, struct zap_walker,
|
||||||
|
walker);
|
||||||
|
kvm_mmu_zap_page(zap_walk->kvm, sp);
|
||||||
|
zap_walk->zapped = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mmu_zap_unsync_children(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
struct zap_walker walker = {
|
||||||
|
.walker = { .entry = mmu_zap_fn, },
|
||||||
|
.kvm = kvm,
|
||||||
|
.zapped = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sp->role.level == PT_PAGE_TABLE_LEVEL)
|
||||||
|
return 0;
|
||||||
|
mmu_unsync_walk(sp, &walker.walker);
|
||||||
|
return walker.zapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_mmu_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
++kvm->stat.mmu_shadow_zapped;
|
||||||
|
ret = mmu_zap_unsync_children(kvm, sp);
|
||||||
kvm_mmu_page_unlink_children(kvm, sp);
|
kvm_mmu_page_unlink_children(kvm, sp);
|
||||||
|
kvm_mmu_unlink_parents(kvm, sp);
|
||||||
|
kvm_flush_remote_tlbs(kvm);
|
||||||
|
if (!sp->role.invalid && !sp->role.metaphysical)
|
||||||
|
unaccount_shadowed(kvm, sp->gfn);
|
||||||
|
if (sp->unsync)
|
||||||
|
kvm_unlink_unsync_page(kvm, sp);
|
||||||
if (!sp->root_count) {
|
if (!sp->root_count) {
|
||||||
if (!sp->role.metaphysical && !sp->role.invalid)
|
|
||||||
unaccount_shadowed(kvm, sp->gfn);
|
|
||||||
hlist_del(&sp->hash_link);
|
hlist_del(&sp->hash_link);
|
||||||
kvm_mmu_free_page(kvm, sp);
|
kvm_mmu_free_page(kvm, sp);
|
||||||
} else {
|
} else {
|
||||||
int invalid = sp->role.invalid;
|
|
||||||
list_move(&sp->link, &kvm->arch.active_mmu_pages);
|
|
||||||
sp->role.invalid = 1;
|
sp->role.invalid = 1;
|
||||||
|
list_move(&sp->link, &kvm->arch.active_mmu_pages);
|
||||||
kvm_reload_remote_mmus(kvm);
|
kvm_reload_remote_mmus(kvm);
|
||||||
if (!sp->role.metaphysical && !invalid)
|
|
||||||
unaccount_shadowed(kvm, sp->gfn);
|
|
||||||
}
|
}
|
||||||
kvm_mmu_reset_last_pte_updated(kvm);
|
kvm_mmu_reset_last_pte_updated(kvm);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1077,8 +1340,9 @@ static int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn)
|
||||||
if (sp->gfn == gfn && !sp->role.metaphysical) {
|
if (sp->gfn == gfn && !sp->role.metaphysical) {
|
||||||
pgprintk("%s: gfn %lx role %x\n", __func__, gfn,
|
pgprintk("%s: gfn %lx role %x\n", __func__, gfn,
|
||||||
sp->role.word);
|
sp->role.word);
|
||||||
kvm_mmu_zap_page(kvm, sp);
|
|
||||||
r = 1;
|
r = 1;
|
||||||
|
if (kvm_mmu_zap_page(kvm, sp))
|
||||||
|
n = bucket->first;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -1101,6 +1365,20 @@ static void page_header_update_slot(struct kvm *kvm, void *pte, gfn_t gfn)
|
||||||
__set_bit(slot, &sp->slot_bitmap);
|
__set_bit(slot, &sp->slot_bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mmu_convert_notrap(struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u64 *pt = sp->spt;
|
||||||
|
|
||||||
|
if (shadow_trap_nonpresent_pte == shadow_notrap_nonpresent_pte)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
|
||||||
|
if (pt[i] == shadow_notrap_nonpresent_pte)
|
||||||
|
set_shadow_pte(&pt[i], shadow_trap_nonpresent_pte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct page *gva_to_page(struct kvm_vcpu *vcpu, gva_t gva)
|
struct page *gva_to_page(struct kvm_vcpu *vcpu, gva_t gva)
|
||||||
{
|
{
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
@ -1110,20 +1388,116 @@ struct page *gva_to_page(struct kvm_vcpu *vcpu, gva_t gva)
|
||||||
if (gpa == UNMAPPED_GVA)
|
if (gpa == UNMAPPED_GVA)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
down_read(¤t->mm->mmap_sem);
|
|
||||||
page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
|
page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int kvm_unsync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
unsigned index;
|
||||||
|
struct hlist_head *bucket;
|
||||||
|
struct kvm_mmu_page *s;
|
||||||
|
struct hlist_node *node, *n;
|
||||||
|
|
||||||
|
index = kvm_page_table_hashfn(sp->gfn);
|
||||||
|
bucket = &vcpu->kvm->arch.mmu_page_hash[index];
|
||||||
|
/* don't unsync if pagetable is shadowed with multiple roles */
|
||||||
|
hlist_for_each_entry_safe(s, node, n, bucket, hash_link) {
|
||||||
|
if (s->gfn != sp->gfn || s->role.metaphysical)
|
||||||
|
continue;
|
||||||
|
if (s->role.word != sp->role.word)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
kvm_mmu_mark_parents_unsync(vcpu, sp);
|
||||||
|
++vcpu->kvm->stat.mmu_unsync;
|
||||||
|
sp->unsync = 1;
|
||||||
|
mmu_convert_notrap(sp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mmu_need_write_protect(struct kvm_vcpu *vcpu, gfn_t gfn,
|
||||||
|
bool can_unsync)
|
||||||
|
{
|
||||||
|
struct kvm_mmu_page *shadow;
|
||||||
|
|
||||||
|
shadow = kvm_mmu_lookup_page(vcpu->kvm, gfn);
|
||||||
|
if (shadow) {
|
||||||
|
if (shadow->role.level != PT_PAGE_TABLE_LEVEL)
|
||||||
|
return 1;
|
||||||
|
if (shadow->unsync)
|
||||||
|
return 0;
|
||||||
|
if (can_unsync && oos_shadow)
|
||||||
|
return kvm_unsync_page(vcpu, shadow);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
|
||||||
|
unsigned pte_access, int user_fault,
|
||||||
|
int write_fault, int dirty, int largepage,
|
||||||
|
gfn_t gfn, pfn_t pfn, bool speculative,
|
||||||
|
bool can_unsync)
|
||||||
|
{
|
||||||
|
u64 spte;
|
||||||
|
int ret = 0;
|
||||||
|
/*
|
||||||
|
* We don't set the accessed bit, since we sometimes want to see
|
||||||
|
* whether the guest actually used the pte (in order to detect
|
||||||
|
* demand paging).
|
||||||
|
*/
|
||||||
|
spte = shadow_base_present_pte | shadow_dirty_mask;
|
||||||
|
if (!speculative)
|
||||||
|
spte |= shadow_accessed_mask;
|
||||||
|
if (!dirty)
|
||||||
|
pte_access &= ~ACC_WRITE_MASK;
|
||||||
|
if (pte_access & ACC_EXEC_MASK)
|
||||||
|
spte |= shadow_x_mask;
|
||||||
|
else
|
||||||
|
spte |= shadow_nx_mask;
|
||||||
|
if (pte_access & ACC_USER_MASK)
|
||||||
|
spte |= shadow_user_mask;
|
||||||
|
if (largepage)
|
||||||
|
spte |= PT_PAGE_SIZE_MASK;
|
||||||
|
|
||||||
|
spte |= (u64)pfn << PAGE_SHIFT;
|
||||||
|
|
||||||
|
if ((pte_access & ACC_WRITE_MASK)
|
||||||
|
|| (write_fault && !is_write_protection(vcpu) && !user_fault)) {
|
||||||
|
|
||||||
|
if (largepage && has_wrprotected_page(vcpu->kvm, gfn)) {
|
||||||
|
ret = 1;
|
||||||
|
spte = shadow_trap_nonpresent_pte;
|
||||||
|
goto set_pte;
|
||||||
|
}
|
||||||
|
|
||||||
|
spte |= PT_WRITABLE_MASK;
|
||||||
|
|
||||||
|
if (mmu_need_write_protect(vcpu, gfn, can_unsync)) {
|
||||||
|
pgprintk("%s: found shadow page for %lx, marking ro\n",
|
||||||
|
__func__, gfn);
|
||||||
|
ret = 1;
|
||||||
|
pte_access &= ~ACC_WRITE_MASK;
|
||||||
|
if (is_writeble_pte(spte))
|
||||||
|
spte &= ~PT_WRITABLE_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pte_access & ACC_WRITE_MASK)
|
||||||
|
mark_page_dirty(vcpu->kvm, gfn);
|
||||||
|
|
||||||
|
set_pte:
|
||||||
|
set_shadow_pte(shadow_pte, spte);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
|
static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
|
||||||
unsigned pt_access, unsigned pte_access,
|
unsigned pt_access, unsigned pte_access,
|
||||||
int user_fault, int write_fault, int dirty,
|
int user_fault, int write_fault, int dirty,
|
||||||
int *ptwrite, int largepage, gfn_t gfn,
|
int *ptwrite, int largepage, gfn_t gfn,
|
||||||
pfn_t pfn, bool speculative)
|
pfn_t pfn, bool speculative)
|
||||||
{
|
{
|
||||||
u64 spte;
|
|
||||||
int was_rmapped = 0;
|
int was_rmapped = 0;
|
||||||
int was_writeble = is_writeble_pte(*shadow_pte);
|
int was_writeble = is_writeble_pte(*shadow_pte);
|
||||||
|
|
||||||
|
@ -1154,59 +1528,19 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
|
||||||
was_rmapped = 1;
|
was_rmapped = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (set_spte(vcpu, shadow_pte, pte_access, user_fault, write_fault,
|
||||||
/*
|
dirty, largepage, gfn, pfn, speculative, true)) {
|
||||||
* We don't set the accessed bit, since we sometimes want to see
|
if (write_fault)
|
||||||
* whether the guest actually used the pte (in order to detect
|
*ptwrite = 1;
|
||||||
* demand paging).
|
kvm_x86_ops->tlb_flush(vcpu);
|
||||||
*/
|
|
||||||
spte = shadow_base_present_pte | shadow_dirty_mask;
|
|
||||||
if (!speculative)
|
|
||||||
pte_access |= PT_ACCESSED_MASK;
|
|
||||||
if (!dirty)
|
|
||||||
pte_access &= ~ACC_WRITE_MASK;
|
|
||||||
if (pte_access & ACC_EXEC_MASK)
|
|
||||||
spte |= shadow_x_mask;
|
|
||||||
else
|
|
||||||
spte |= shadow_nx_mask;
|
|
||||||
if (pte_access & ACC_USER_MASK)
|
|
||||||
spte |= shadow_user_mask;
|
|
||||||
if (largepage)
|
|
||||||
spte |= PT_PAGE_SIZE_MASK;
|
|
||||||
|
|
||||||
spte |= (u64)pfn << PAGE_SHIFT;
|
|
||||||
|
|
||||||
if ((pte_access & ACC_WRITE_MASK)
|
|
||||||
|| (write_fault && !is_write_protection(vcpu) && !user_fault)) {
|
|
||||||
struct kvm_mmu_page *shadow;
|
|
||||||
|
|
||||||
spte |= PT_WRITABLE_MASK;
|
|
||||||
|
|
||||||
shadow = kvm_mmu_lookup_page(vcpu->kvm, gfn);
|
|
||||||
if (shadow ||
|
|
||||||
(largepage && has_wrprotected_page(vcpu->kvm, gfn))) {
|
|
||||||
pgprintk("%s: found shadow page for %lx, marking ro\n",
|
|
||||||
__func__, gfn);
|
|
||||||
pte_access &= ~ACC_WRITE_MASK;
|
|
||||||
if (is_writeble_pte(spte)) {
|
|
||||||
spte &= ~PT_WRITABLE_MASK;
|
|
||||||
kvm_x86_ops->tlb_flush(vcpu);
|
|
||||||
}
|
|
||||||
if (write_fault)
|
|
||||||
*ptwrite = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pte_access & ACC_WRITE_MASK)
|
pgprintk("%s: setting spte %llx\n", __func__, *shadow_pte);
|
||||||
mark_page_dirty(vcpu->kvm, gfn);
|
|
||||||
|
|
||||||
pgprintk("%s: setting spte %llx\n", __func__, spte);
|
|
||||||
pgprintk("instantiating %s PTE (%s) at %ld (%llx) addr %p\n",
|
pgprintk("instantiating %s PTE (%s) at %ld (%llx) addr %p\n",
|
||||||
(spte&PT_PAGE_SIZE_MASK)? "2MB" : "4kB",
|
is_large_pte(*shadow_pte)? "2MB" : "4kB",
|
||||||
(spte&PT_WRITABLE_MASK)?"RW":"R", gfn, spte, shadow_pte);
|
is_present_pte(*shadow_pte)?"RW":"R", gfn,
|
||||||
set_shadow_pte(shadow_pte, spte);
|
*shadow_pte, shadow_pte);
|
||||||
if (!was_rmapped && (spte & PT_PAGE_SIZE_MASK)
|
if (!was_rmapped && is_large_pte(*shadow_pte))
|
||||||
&& (spte & PT_PRESENT_MASK))
|
|
||||||
++vcpu->kvm->stat.lpages;
|
++vcpu->kvm->stat.lpages;
|
||||||
|
|
||||||
page_header_update_slot(vcpu->kvm, shadow_pte, gfn);
|
page_header_update_slot(vcpu->kvm, shadow_pte, gfn);
|
||||||
|
@ -1230,54 +1564,67 @@ static void nonpaging_new_cr3(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
|
struct direct_shadow_walk {
|
||||||
int largepage, gfn_t gfn, pfn_t pfn,
|
struct kvm_shadow_walk walker;
|
||||||
int level)
|
pfn_t pfn;
|
||||||
|
int write;
|
||||||
|
int largepage;
|
||||||
|
int pt_write;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int direct_map_entry(struct kvm_shadow_walk *_walk,
|
||||||
|
struct kvm_vcpu *vcpu,
|
||||||
|
u64 addr, u64 *sptep, int level)
|
||||||
{
|
{
|
||||||
hpa_t table_addr = vcpu->arch.mmu.root_hpa;
|
struct direct_shadow_walk *walk =
|
||||||
int pt_write = 0;
|
container_of(_walk, struct direct_shadow_walk, walker);
|
||||||
|
struct kvm_mmu_page *sp;
|
||||||
|
gfn_t pseudo_gfn;
|
||||||
|
gfn_t gfn = addr >> PAGE_SHIFT;
|
||||||
|
|
||||||
for (; ; level--) {
|
if (level == PT_PAGE_TABLE_LEVEL
|
||||||
u32 index = PT64_INDEX(v, level);
|
|| (walk->largepage && level == PT_DIRECTORY_LEVEL)) {
|
||||||
u64 *table;
|
mmu_set_spte(vcpu, sptep, ACC_ALL, ACC_ALL,
|
||||||
|
0, walk->write, 1, &walk->pt_write,
|
||||||
ASSERT(VALID_PAGE(table_addr));
|
walk->largepage, gfn, walk->pfn, false);
|
||||||
table = __va(table_addr);
|
++vcpu->stat.pf_fixed;
|
||||||
|
return 1;
|
||||||
if (level == 1) {
|
|
||||||
mmu_set_spte(vcpu, &table[index], ACC_ALL, ACC_ALL,
|
|
||||||
0, write, 1, &pt_write, 0, gfn, pfn, false);
|
|
||||||
return pt_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (largepage && level == 2) {
|
|
||||||
mmu_set_spte(vcpu, &table[index], ACC_ALL, ACC_ALL,
|
|
||||||
0, write, 1, &pt_write, 1, gfn, pfn, false);
|
|
||||||
return pt_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table[index] == shadow_trap_nonpresent_pte) {
|
|
||||||
struct kvm_mmu_page *new_table;
|
|
||||||
gfn_t pseudo_gfn;
|
|
||||||
|
|
||||||
pseudo_gfn = (v & PT64_DIR_BASE_ADDR_MASK)
|
|
||||||
>> PAGE_SHIFT;
|
|
||||||
new_table = kvm_mmu_get_page(vcpu, pseudo_gfn,
|
|
||||||
v, level - 1,
|
|
||||||
1, ACC_ALL, &table[index]);
|
|
||||||
if (!new_table) {
|
|
||||||
pgprintk("nonpaging_map: ENOMEM\n");
|
|
||||||
kvm_release_pfn_clean(pfn);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_shadow_pte(&table[index],
|
|
||||||
__pa(new_table->spt)
|
|
||||||
| PT_PRESENT_MASK | PT_WRITABLE_MASK
|
|
||||||
| shadow_user_mask | shadow_x_mask);
|
|
||||||
}
|
|
||||||
table_addr = table[index] & PT64_BASE_ADDR_MASK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*sptep == shadow_trap_nonpresent_pte) {
|
||||||
|
pseudo_gfn = (addr & PT64_DIR_BASE_ADDR_MASK) >> PAGE_SHIFT;
|
||||||
|
sp = kvm_mmu_get_page(vcpu, pseudo_gfn, (gva_t)addr, level - 1,
|
||||||
|
1, ACC_ALL, sptep);
|
||||||
|
if (!sp) {
|
||||||
|
pgprintk("nonpaging_map: ENOMEM\n");
|
||||||
|
kvm_release_pfn_clean(walk->pfn);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_shadow_pte(sptep,
|
||||||
|
__pa(sp->spt)
|
||||||
|
| PT_PRESENT_MASK | PT_WRITABLE_MASK
|
||||||
|
| shadow_user_mask | shadow_x_mask);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
|
||||||
|
int largepage, gfn_t gfn, pfn_t pfn)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct direct_shadow_walk walker = {
|
||||||
|
.walker = { .entry = direct_map_entry, },
|
||||||
|
.pfn = pfn,
|
||||||
|
.largepage = largepage,
|
||||||
|
.write = write,
|
||||||
|
.pt_write = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
r = walk_shadow(&walker.walker, vcpu, gfn << PAGE_SHIFT);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
return walker.pt_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
|
static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
|
||||||
|
@ -1287,16 +1634,14 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
unsigned long mmu_seq;
|
unsigned long mmu_seq;
|
||||||
|
|
||||||
down_read(¤t->mm->mmap_sem);
|
|
||||||
if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) {
|
if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) {
|
||||||
gfn &= ~(KVM_PAGES_PER_HPAGE-1);
|
gfn &= ~(KVM_PAGES_PER_HPAGE-1);
|
||||||
largepage = 1;
|
largepage = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
||||||
/* implicit mb(), we'll read before PT lock is unlocked */
|
smp_rmb();
|
||||||
pfn = gfn_to_pfn(vcpu->kvm, gfn);
|
pfn = gfn_to_pfn(vcpu->kvm, gfn);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
|
|
||||||
/* mmio */
|
/* mmio */
|
||||||
if (is_error_pfn(pfn)) {
|
if (is_error_pfn(pfn)) {
|
||||||
|
@ -1308,8 +1653,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
|
||||||
if (mmu_notifier_retry(vcpu, mmu_seq))
|
if (mmu_notifier_retry(vcpu, mmu_seq))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
kvm_mmu_free_some_pages(vcpu);
|
kvm_mmu_free_some_pages(vcpu);
|
||||||
r = __direct_map(vcpu, v, write, largepage, gfn, pfn,
|
r = __direct_map(vcpu, v, write, largepage, gfn, pfn);
|
||||||
PT32E_ROOT_LEVEL);
|
|
||||||
spin_unlock(&vcpu->kvm->mmu_lock);
|
spin_unlock(&vcpu->kvm->mmu_lock);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1405,6 +1749,37 @@ static void mmu_alloc_roots(struct kvm_vcpu *vcpu)
|
||||||
vcpu->arch.mmu.root_hpa = __pa(vcpu->arch.mmu.pae_root);
|
vcpu->arch.mmu.root_hpa = __pa(vcpu->arch.mmu.pae_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mmu_sync_roots(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct kvm_mmu_page *sp;
|
||||||
|
|
||||||
|
if (!VALID_PAGE(vcpu->arch.mmu.root_hpa))
|
||||||
|
return;
|
||||||
|
if (vcpu->arch.mmu.shadow_root_level == PT64_ROOT_LEVEL) {
|
||||||
|
hpa_t root = vcpu->arch.mmu.root_hpa;
|
||||||
|
sp = page_header(root);
|
||||||
|
mmu_sync_children(vcpu, sp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 4; ++i) {
|
||||||
|
hpa_t root = vcpu->arch.mmu.pae_root[i];
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
root &= PT64_BASE_ADDR_MASK;
|
||||||
|
sp = page_header(root);
|
||||||
|
mmu_sync_children(vcpu, sp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
spin_lock(&vcpu->kvm->mmu_lock);
|
||||||
|
mmu_sync_roots(vcpu);
|
||||||
|
spin_unlock(&vcpu->kvm->mmu_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr)
|
static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr)
|
||||||
{
|
{
|
||||||
return vaddr;
|
return vaddr;
|
||||||
|
@ -1446,15 +1821,13 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,
|
||||||
if (r)
|
if (r)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
down_read(¤t->mm->mmap_sem);
|
|
||||||
if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) {
|
if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) {
|
||||||
gfn &= ~(KVM_PAGES_PER_HPAGE-1);
|
gfn &= ~(KVM_PAGES_PER_HPAGE-1);
|
||||||
largepage = 1;
|
largepage = 1;
|
||||||
}
|
}
|
||||||
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
||||||
/* implicit mb(), we'll read before PT lock is unlocked */
|
smp_rmb();
|
||||||
pfn = gfn_to_pfn(vcpu->kvm, gfn);
|
pfn = gfn_to_pfn(vcpu->kvm, gfn);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
if (is_error_pfn(pfn)) {
|
if (is_error_pfn(pfn)) {
|
||||||
kvm_release_pfn_clean(pfn);
|
kvm_release_pfn_clean(pfn);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1464,7 +1837,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
kvm_mmu_free_some_pages(vcpu);
|
kvm_mmu_free_some_pages(vcpu);
|
||||||
r = __direct_map(vcpu, gpa, error_code & PFERR_WRITE_MASK,
|
r = __direct_map(vcpu, gpa, error_code & PFERR_WRITE_MASK,
|
||||||
largepage, gfn, pfn, kvm_x86_ops->get_tdp_level());
|
largepage, gfn, pfn);
|
||||||
spin_unlock(&vcpu->kvm->mmu_lock);
|
spin_unlock(&vcpu->kvm->mmu_lock);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
@ -1489,6 +1862,8 @@ static int nonpaging_init_context(struct kvm_vcpu *vcpu)
|
||||||
context->gva_to_gpa = nonpaging_gva_to_gpa;
|
context->gva_to_gpa = nonpaging_gva_to_gpa;
|
||||||
context->free = nonpaging_free;
|
context->free = nonpaging_free;
|
||||||
context->prefetch_page = nonpaging_prefetch_page;
|
context->prefetch_page = nonpaging_prefetch_page;
|
||||||
|
context->sync_page = nonpaging_sync_page;
|
||||||
|
context->invlpg = nonpaging_invlpg;
|
||||||
context->root_level = 0;
|
context->root_level = 0;
|
||||||
context->shadow_root_level = PT32E_ROOT_LEVEL;
|
context->shadow_root_level = PT32E_ROOT_LEVEL;
|
||||||
context->root_hpa = INVALID_PAGE;
|
context->root_hpa = INVALID_PAGE;
|
||||||
|
@ -1536,6 +1911,8 @@ static int paging64_init_context_common(struct kvm_vcpu *vcpu, int level)
|
||||||
context->page_fault = paging64_page_fault;
|
context->page_fault = paging64_page_fault;
|
||||||
context->gva_to_gpa = paging64_gva_to_gpa;
|
context->gva_to_gpa = paging64_gva_to_gpa;
|
||||||
context->prefetch_page = paging64_prefetch_page;
|
context->prefetch_page = paging64_prefetch_page;
|
||||||
|
context->sync_page = paging64_sync_page;
|
||||||
|
context->invlpg = paging64_invlpg;
|
||||||
context->free = paging_free;
|
context->free = paging_free;
|
||||||
context->root_level = level;
|
context->root_level = level;
|
||||||
context->shadow_root_level = level;
|
context->shadow_root_level = level;
|
||||||
|
@ -1557,6 +1934,8 @@ static int paging32_init_context(struct kvm_vcpu *vcpu)
|
||||||
context->gva_to_gpa = paging32_gva_to_gpa;
|
context->gva_to_gpa = paging32_gva_to_gpa;
|
||||||
context->free = paging_free;
|
context->free = paging_free;
|
||||||
context->prefetch_page = paging32_prefetch_page;
|
context->prefetch_page = paging32_prefetch_page;
|
||||||
|
context->sync_page = paging32_sync_page;
|
||||||
|
context->invlpg = paging32_invlpg;
|
||||||
context->root_level = PT32_ROOT_LEVEL;
|
context->root_level = PT32_ROOT_LEVEL;
|
||||||
context->shadow_root_level = PT32E_ROOT_LEVEL;
|
context->shadow_root_level = PT32E_ROOT_LEVEL;
|
||||||
context->root_hpa = INVALID_PAGE;
|
context->root_hpa = INVALID_PAGE;
|
||||||
|
@ -1576,6 +1955,8 @@ static int init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
|
||||||
context->page_fault = tdp_page_fault;
|
context->page_fault = tdp_page_fault;
|
||||||
context->free = nonpaging_free;
|
context->free = nonpaging_free;
|
||||||
context->prefetch_page = nonpaging_prefetch_page;
|
context->prefetch_page = nonpaging_prefetch_page;
|
||||||
|
context->sync_page = nonpaging_sync_page;
|
||||||
|
context->invlpg = nonpaging_invlpg;
|
||||||
context->shadow_root_level = kvm_x86_ops->get_tdp_level();
|
context->shadow_root_level = kvm_x86_ops->get_tdp_level();
|
||||||
context->root_hpa = INVALID_PAGE;
|
context->root_hpa = INVALID_PAGE;
|
||||||
|
|
||||||
|
@ -1647,6 +2028,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu)
|
||||||
spin_lock(&vcpu->kvm->mmu_lock);
|
spin_lock(&vcpu->kvm->mmu_lock);
|
||||||
kvm_mmu_free_some_pages(vcpu);
|
kvm_mmu_free_some_pages(vcpu);
|
||||||
mmu_alloc_roots(vcpu);
|
mmu_alloc_roots(vcpu);
|
||||||
|
mmu_sync_roots(vcpu);
|
||||||
spin_unlock(&vcpu->kvm->mmu_lock);
|
spin_unlock(&vcpu->kvm->mmu_lock);
|
||||||
kvm_x86_ops->set_cr3(vcpu, vcpu->arch.mmu.root_hpa);
|
kvm_x86_ops->set_cr3(vcpu, vcpu->arch.mmu.root_hpa);
|
||||||
kvm_mmu_flush_tlb(vcpu);
|
kvm_mmu_flush_tlb(vcpu);
|
||||||
|
@ -1767,15 +2149,13 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
|
||||||
return;
|
return;
|
||||||
gfn = (gpte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;
|
gfn = (gpte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;
|
||||||
|
|
||||||
down_read(¤t->mm->mmap_sem);
|
|
||||||
if (is_large_pte(gpte) && is_largepage_backed(vcpu, gfn)) {
|
if (is_large_pte(gpte) && is_largepage_backed(vcpu, gfn)) {
|
||||||
gfn &= ~(KVM_PAGES_PER_HPAGE-1);
|
gfn &= ~(KVM_PAGES_PER_HPAGE-1);
|
||||||
vcpu->arch.update_pte.largepage = 1;
|
vcpu->arch.update_pte.largepage = 1;
|
||||||
}
|
}
|
||||||
vcpu->arch.update_pte.mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
vcpu->arch.update_pte.mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
||||||
/* implicit mb(), we'll read before PT lock is unlocked */
|
smp_rmb();
|
||||||
pfn = gfn_to_pfn(vcpu->kvm, gfn);
|
pfn = gfn_to_pfn(vcpu->kvm, gfn);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
|
|
||||||
if (is_error_pfn(pfn)) {
|
if (is_error_pfn(pfn)) {
|
||||||
kvm_release_pfn_clean(pfn);
|
kvm_release_pfn_clean(pfn);
|
||||||
|
@ -1837,7 +2217,7 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
|
||||||
index = kvm_page_table_hashfn(gfn);
|
index = kvm_page_table_hashfn(gfn);
|
||||||
bucket = &vcpu->kvm->arch.mmu_page_hash[index];
|
bucket = &vcpu->kvm->arch.mmu_page_hash[index];
|
||||||
hlist_for_each_entry_safe(sp, node, n, bucket, hash_link) {
|
hlist_for_each_entry_safe(sp, node, n, bucket, hash_link) {
|
||||||
if (sp->gfn != gfn || sp->role.metaphysical)
|
if (sp->gfn != gfn || sp->role.metaphysical || sp->role.invalid)
|
||||||
continue;
|
continue;
|
||||||
pte_size = sp->role.glevels == PT32_ROOT_LEVEL ? 4 : 8;
|
pte_size = sp->role.glevels == PT32_ROOT_LEVEL ? 4 : 8;
|
||||||
misaligned = (offset ^ (offset + bytes - 1)) & ~(pte_size - 1);
|
misaligned = (offset ^ (offset + bytes - 1)) & ~(pte_size - 1);
|
||||||
|
@ -1855,7 +2235,8 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
|
||||||
*/
|
*/
|
||||||
pgprintk("misaligned: gpa %llx bytes %d role %x\n",
|
pgprintk("misaligned: gpa %llx bytes %d role %x\n",
|
||||||
gpa, bytes, sp->role.word);
|
gpa, bytes, sp->role.word);
|
||||||
kvm_mmu_zap_page(vcpu->kvm, sp);
|
if (kvm_mmu_zap_page(vcpu->kvm, sp))
|
||||||
|
n = bucket->first;
|
||||||
++vcpu->kvm->stat.mmu_flooded;
|
++vcpu->kvm->stat.mmu_flooded;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1969,6 +2350,16 @@ out:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_mmu_page_fault);
|
EXPORT_SYMBOL_GPL(kvm_mmu_page_fault);
|
||||||
|
|
||||||
|
void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva)
|
||||||
|
{
|
||||||
|
spin_lock(&vcpu->kvm->mmu_lock);
|
||||||
|
vcpu->arch.mmu.invlpg(vcpu, gva);
|
||||||
|
spin_unlock(&vcpu->kvm->mmu_lock);
|
||||||
|
kvm_mmu_flush_tlb(vcpu);
|
||||||
|
++vcpu->stat.invlpg;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kvm_mmu_invlpg);
|
||||||
|
|
||||||
void kvm_enable_tdp(void)
|
void kvm_enable_tdp(void)
|
||||||
{
|
{
|
||||||
tdp_enabled = true;
|
tdp_enabled = true;
|
||||||
|
@ -2055,6 +2446,7 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot)
|
||||||
{
|
{
|
||||||
struct kvm_mmu_page *sp;
|
struct kvm_mmu_page *sp;
|
||||||
|
|
||||||
|
spin_lock(&kvm->mmu_lock);
|
||||||
list_for_each_entry(sp, &kvm->arch.active_mmu_pages, link) {
|
list_for_each_entry(sp, &kvm->arch.active_mmu_pages, link) {
|
||||||
int i;
|
int i;
|
||||||
u64 *pt;
|
u64 *pt;
|
||||||
|
@ -2068,6 +2460,8 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot)
|
||||||
if (pt[i] & PT_WRITABLE_MASK)
|
if (pt[i] & PT_WRITABLE_MASK)
|
||||||
pt[i] &= ~PT_WRITABLE_MASK;
|
pt[i] &= ~PT_WRITABLE_MASK;
|
||||||
}
|
}
|
||||||
|
kvm_flush_remote_tlbs(kvm);
|
||||||
|
spin_unlock(&kvm->mmu_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_mmu_zap_all(struct kvm *kvm)
|
void kvm_mmu_zap_all(struct kvm *kvm)
|
||||||
|
@ -2076,7 +2470,9 @@ void kvm_mmu_zap_all(struct kvm *kvm)
|
||||||
|
|
||||||
spin_lock(&kvm->mmu_lock);
|
spin_lock(&kvm->mmu_lock);
|
||||||
list_for_each_entry_safe(sp, node, &kvm->arch.active_mmu_pages, link)
|
list_for_each_entry_safe(sp, node, &kvm->arch.active_mmu_pages, link)
|
||||||
kvm_mmu_zap_page(kvm, sp);
|
if (kvm_mmu_zap_page(kvm, sp))
|
||||||
|
node = container_of(kvm->arch.active_mmu_pages.next,
|
||||||
|
struct kvm_mmu_page, link);
|
||||||
spin_unlock(&kvm->mmu_lock);
|
spin_unlock(&kvm->mmu_lock);
|
||||||
|
|
||||||
kvm_flush_remote_tlbs(kvm);
|
kvm_flush_remote_tlbs(kvm);
|
||||||
|
@ -2291,18 +2687,18 @@ int kvm_pv_mmu_op(struct kvm_vcpu *vcpu, unsigned long bytes,
|
||||||
gpa_t addr, unsigned long *ret)
|
gpa_t addr, unsigned long *ret)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
struct kvm_pv_mmu_op_buffer buffer;
|
struct kvm_pv_mmu_op_buffer *buffer = &vcpu->arch.mmu_op_buffer;
|
||||||
|
|
||||||
buffer.ptr = buffer.buf;
|
buffer->ptr = buffer->buf;
|
||||||
buffer.len = min_t(unsigned long, bytes, sizeof buffer.buf);
|
buffer->len = min_t(unsigned long, bytes, sizeof buffer->buf);
|
||||||
buffer.processed = 0;
|
buffer->processed = 0;
|
||||||
|
|
||||||
r = kvm_read_guest(vcpu->kvm, addr, buffer.buf, buffer.len);
|
r = kvm_read_guest(vcpu->kvm, addr, buffer->buf, buffer->len);
|
||||||
if (r)
|
if (r)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
while (buffer.len) {
|
while (buffer->len) {
|
||||||
r = kvm_pv_mmu_op_one(vcpu, &buffer);
|
r = kvm_pv_mmu_op_one(vcpu, buffer);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto out;
|
goto out;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
|
@ -2311,7 +2707,7 @@ int kvm_pv_mmu_op(struct kvm_vcpu *vcpu, unsigned long bytes,
|
||||||
|
|
||||||
r = 1;
|
r = 1;
|
||||||
out:
|
out:
|
||||||
*ret = buffer.processed;
|
*ret = buffer->processed;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,11 @@
|
||||||
#if PTTYPE == 64
|
#if PTTYPE == 64
|
||||||
#define pt_element_t u64
|
#define pt_element_t u64
|
||||||
#define guest_walker guest_walker64
|
#define guest_walker guest_walker64
|
||||||
|
#define shadow_walker shadow_walker64
|
||||||
#define FNAME(name) paging##64_##name
|
#define FNAME(name) paging##64_##name
|
||||||
#define PT_BASE_ADDR_MASK PT64_BASE_ADDR_MASK
|
#define PT_BASE_ADDR_MASK PT64_BASE_ADDR_MASK
|
||||||
#define PT_DIR_BASE_ADDR_MASK PT64_DIR_BASE_ADDR_MASK
|
#define PT_DIR_BASE_ADDR_MASK PT64_DIR_BASE_ADDR_MASK
|
||||||
#define PT_INDEX(addr, level) PT64_INDEX(addr, level)
|
#define PT_INDEX(addr, level) PT64_INDEX(addr, level)
|
||||||
#define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
|
|
||||||
#define PT_LEVEL_MASK(level) PT64_LEVEL_MASK(level)
|
#define PT_LEVEL_MASK(level) PT64_LEVEL_MASK(level)
|
||||||
#define PT_LEVEL_BITS PT64_LEVEL_BITS
|
#define PT_LEVEL_BITS PT64_LEVEL_BITS
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
|
@ -42,11 +42,11 @@
|
||||||
#elif PTTYPE == 32
|
#elif PTTYPE == 32
|
||||||
#define pt_element_t u32
|
#define pt_element_t u32
|
||||||
#define guest_walker guest_walker32
|
#define guest_walker guest_walker32
|
||||||
|
#define shadow_walker shadow_walker32
|
||||||
#define FNAME(name) paging##32_##name
|
#define FNAME(name) paging##32_##name
|
||||||
#define PT_BASE_ADDR_MASK PT32_BASE_ADDR_MASK
|
#define PT_BASE_ADDR_MASK PT32_BASE_ADDR_MASK
|
||||||
#define PT_DIR_BASE_ADDR_MASK PT32_DIR_BASE_ADDR_MASK
|
#define PT_DIR_BASE_ADDR_MASK PT32_DIR_BASE_ADDR_MASK
|
||||||
#define PT_INDEX(addr, level) PT32_INDEX(addr, level)
|
#define PT_INDEX(addr, level) PT32_INDEX(addr, level)
|
||||||
#define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
|
|
||||||
#define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level)
|
#define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level)
|
||||||
#define PT_LEVEL_BITS PT32_LEVEL_BITS
|
#define PT_LEVEL_BITS PT32_LEVEL_BITS
|
||||||
#define PT_MAX_FULL_LEVELS 2
|
#define PT_MAX_FULL_LEVELS 2
|
||||||
|
@ -73,6 +73,17 @@ struct guest_walker {
|
||||||
u32 error_code;
|
u32 error_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct shadow_walker {
|
||||||
|
struct kvm_shadow_walk walker;
|
||||||
|
struct guest_walker *guest_walker;
|
||||||
|
int user_fault;
|
||||||
|
int write_fault;
|
||||||
|
int largepage;
|
||||||
|
int *ptwrite;
|
||||||
|
pfn_t pfn;
|
||||||
|
u64 *sptep;
|
||||||
|
};
|
||||||
|
|
||||||
static gfn_t gpte_to_gfn(pt_element_t gpte)
|
static gfn_t gpte_to_gfn(pt_element_t gpte)
|
||||||
{
|
{
|
||||||
return (gpte & PT_BASE_ADDR_MASK) >> PAGE_SHIFT;
|
return (gpte & PT_BASE_ADDR_MASK) >> PAGE_SHIFT;
|
||||||
|
@ -91,14 +102,10 @@ static bool FNAME(cmpxchg_gpte)(struct kvm *kvm,
|
||||||
pt_element_t *table;
|
pt_element_t *table;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
|
||||||
down_read(¤t->mm->mmap_sem);
|
|
||||||
page = gfn_to_page(kvm, table_gfn);
|
page = gfn_to_page(kvm, table_gfn);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
|
|
||||||
table = kmap_atomic(page, KM_USER0);
|
table = kmap_atomic(page, KM_USER0);
|
||||||
|
|
||||||
ret = CMPXCHG(&table[index], orig_pte, new_pte);
|
ret = CMPXCHG(&table[index], orig_pte, new_pte);
|
||||||
|
|
||||||
kunmap_atomic(table, KM_USER0);
|
kunmap_atomic(table, KM_USER0);
|
||||||
|
|
||||||
kvm_release_page_dirty(page);
|
kvm_release_page_dirty(page);
|
||||||
|
@ -274,86 +281,89 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *page,
|
||||||
/*
|
/*
|
||||||
* Fetch a shadow pte for a specific level in the paging hierarchy.
|
* Fetch a shadow pte for a specific level in the paging hierarchy.
|
||||||
*/
|
*/
|
||||||
|
static int FNAME(shadow_walk_entry)(struct kvm_shadow_walk *_sw,
|
||||||
|
struct kvm_vcpu *vcpu, u64 addr,
|
||||||
|
u64 *sptep, int level)
|
||||||
|
{
|
||||||
|
struct shadow_walker *sw =
|
||||||
|
container_of(_sw, struct shadow_walker, walker);
|
||||||
|
struct guest_walker *gw = sw->guest_walker;
|
||||||
|
unsigned access = gw->pt_access;
|
||||||
|
struct kvm_mmu_page *shadow_page;
|
||||||
|
u64 spte;
|
||||||
|
int metaphysical;
|
||||||
|
gfn_t table_gfn;
|
||||||
|
int r;
|
||||||
|
pt_element_t curr_pte;
|
||||||
|
|
||||||
|
if (level == PT_PAGE_TABLE_LEVEL
|
||||||
|
|| (sw->largepage && level == PT_DIRECTORY_LEVEL)) {
|
||||||
|
mmu_set_spte(vcpu, sptep, access, gw->pte_access & access,
|
||||||
|
sw->user_fault, sw->write_fault,
|
||||||
|
gw->ptes[gw->level-1] & PT_DIRTY_MASK,
|
||||||
|
sw->ptwrite, sw->largepage, gw->gfn, sw->pfn,
|
||||||
|
false);
|
||||||
|
sw->sptep = sptep;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (is_large_pte(*sptep)) {
|
||||||
|
set_shadow_pte(sptep, shadow_trap_nonpresent_pte);
|
||||||
|
kvm_flush_remote_tlbs(vcpu->kvm);
|
||||||
|
rmap_remove(vcpu->kvm, sptep);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level == PT_DIRECTORY_LEVEL && gw->level == PT_DIRECTORY_LEVEL) {
|
||||||
|
metaphysical = 1;
|
||||||
|
if (!is_dirty_pte(gw->ptes[level - 1]))
|
||||||
|
access &= ~ACC_WRITE_MASK;
|
||||||
|
table_gfn = gpte_to_gfn(gw->ptes[level - 1]);
|
||||||
|
} else {
|
||||||
|
metaphysical = 0;
|
||||||
|
table_gfn = gw->table_gfn[level - 2];
|
||||||
|
}
|
||||||
|
shadow_page = kvm_mmu_get_page(vcpu, table_gfn, (gva_t)addr, level-1,
|
||||||
|
metaphysical, access, sptep);
|
||||||
|
if (!metaphysical) {
|
||||||
|
r = kvm_read_guest_atomic(vcpu->kvm, gw->pte_gpa[level - 2],
|
||||||
|
&curr_pte, sizeof(curr_pte));
|
||||||
|
if (r || curr_pte != gw->ptes[level - 2]) {
|
||||||
|
kvm_release_pfn_clean(sw->pfn);
|
||||||
|
sw->sptep = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spte = __pa(shadow_page->spt) | PT_PRESENT_MASK | PT_ACCESSED_MASK
|
||||||
|
| PT_WRITABLE_MASK | PT_USER_MASK;
|
||||||
|
*sptep = spte;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
|
static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||||
struct guest_walker *walker,
|
struct guest_walker *guest_walker,
|
||||||
int user_fault, int write_fault, int largepage,
|
int user_fault, int write_fault, int largepage,
|
||||||
int *ptwrite, pfn_t pfn)
|
int *ptwrite, pfn_t pfn)
|
||||||
{
|
{
|
||||||
hpa_t shadow_addr;
|
struct shadow_walker walker = {
|
||||||
int level;
|
.walker = { .entry = FNAME(shadow_walk_entry), },
|
||||||
u64 *shadow_ent;
|
.guest_walker = guest_walker,
|
||||||
unsigned access = walker->pt_access;
|
.user_fault = user_fault,
|
||||||
|
.write_fault = write_fault,
|
||||||
|
.largepage = largepage,
|
||||||
|
.ptwrite = ptwrite,
|
||||||
|
.pfn = pfn,
|
||||||
|
};
|
||||||
|
|
||||||
if (!is_present_pte(walker->ptes[walker->level - 1]))
|
if (!is_present_pte(guest_walker->ptes[guest_walker->level - 1]))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
shadow_addr = vcpu->arch.mmu.root_hpa;
|
walk_shadow(&walker.walker, vcpu, addr);
|
||||||
level = vcpu->arch.mmu.shadow_root_level;
|
|
||||||
if (level == PT32E_ROOT_LEVEL) {
|
|
||||||
shadow_addr = vcpu->arch.mmu.pae_root[(addr >> 30) & 3];
|
|
||||||
shadow_addr &= PT64_BASE_ADDR_MASK;
|
|
||||||
--level;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; ; level--) {
|
return walker.sptep;
|
||||||
u32 index = SHADOW_PT_INDEX(addr, level);
|
|
||||||
struct kvm_mmu_page *shadow_page;
|
|
||||||
u64 shadow_pte;
|
|
||||||
int metaphysical;
|
|
||||||
gfn_t table_gfn;
|
|
||||||
|
|
||||||
shadow_ent = ((u64 *)__va(shadow_addr)) + index;
|
|
||||||
if (level == PT_PAGE_TABLE_LEVEL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (largepage && level == PT_DIRECTORY_LEVEL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (is_shadow_present_pte(*shadow_ent)
|
|
||||||
&& !is_large_pte(*shadow_ent)) {
|
|
||||||
shadow_addr = *shadow_ent & PT64_BASE_ADDR_MASK;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_large_pte(*shadow_ent))
|
|
||||||
rmap_remove(vcpu->kvm, shadow_ent);
|
|
||||||
|
|
||||||
if (level - 1 == PT_PAGE_TABLE_LEVEL
|
|
||||||
&& walker->level == PT_DIRECTORY_LEVEL) {
|
|
||||||
metaphysical = 1;
|
|
||||||
if (!is_dirty_pte(walker->ptes[level - 1]))
|
|
||||||
access &= ~ACC_WRITE_MASK;
|
|
||||||
table_gfn = gpte_to_gfn(walker->ptes[level - 1]);
|
|
||||||
} else {
|
|
||||||
metaphysical = 0;
|
|
||||||
table_gfn = walker->table_gfn[level - 2];
|
|
||||||
}
|
|
||||||
shadow_page = kvm_mmu_get_page(vcpu, table_gfn, addr, level-1,
|
|
||||||
metaphysical, access,
|
|
||||||
shadow_ent);
|
|
||||||
if (!metaphysical) {
|
|
||||||
int r;
|
|
||||||
pt_element_t curr_pte;
|
|
||||||
r = kvm_read_guest_atomic(vcpu->kvm,
|
|
||||||
walker->pte_gpa[level - 2],
|
|
||||||
&curr_pte, sizeof(curr_pte));
|
|
||||||
if (r || curr_pte != walker->ptes[level - 2]) {
|
|
||||||
kvm_release_pfn_clean(pfn);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shadow_addr = __pa(shadow_page->spt);
|
|
||||||
shadow_pte = shadow_addr | PT_PRESENT_MASK | PT_ACCESSED_MASK
|
|
||||||
| PT_WRITABLE_MASK | PT_USER_MASK;
|
|
||||||
set_shadow_pte(shadow_ent, shadow_pte);
|
|
||||||
}
|
|
||||||
|
|
||||||
mmu_set_spte(vcpu, shadow_ent, access, walker->pte_access & access,
|
|
||||||
user_fault, write_fault,
|
|
||||||
walker->ptes[walker->level-1] & PT_DIRTY_MASK,
|
|
||||||
ptwrite, largepage, walker->gfn, pfn, false);
|
|
||||||
|
|
||||||
return shadow_ent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -407,7 +417,6 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
down_read(¤t->mm->mmap_sem);
|
|
||||||
if (walker.level == PT_DIRECTORY_LEVEL) {
|
if (walker.level == PT_DIRECTORY_LEVEL) {
|
||||||
gfn_t large_gfn;
|
gfn_t large_gfn;
|
||||||
large_gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE-1);
|
large_gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE-1);
|
||||||
|
@ -417,9 +426,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
||||||
/* implicit mb(), we'll read before PT lock is unlocked */
|
smp_rmb();
|
||||||
pfn = gfn_to_pfn(vcpu->kvm, walker.gfn);
|
pfn = gfn_to_pfn(vcpu->kvm, walker.gfn);
|
||||||
up_read(¤t->mm->mmap_sem);
|
|
||||||
|
|
||||||
/* mmio */
|
/* mmio */
|
||||||
if (is_error_pfn(pfn)) {
|
if (is_error_pfn(pfn)) {
|
||||||
|
@ -453,6 +461,31 @@ out_unlock:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int FNAME(shadow_invlpg_entry)(struct kvm_shadow_walk *_sw,
|
||||||
|
struct kvm_vcpu *vcpu, u64 addr,
|
||||||
|
u64 *sptep, int level)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (level == PT_PAGE_TABLE_LEVEL) {
|
||||||
|
if (is_shadow_present_pte(*sptep))
|
||||||
|
rmap_remove(vcpu->kvm, sptep);
|
||||||
|
set_shadow_pte(sptep, shadow_trap_nonpresent_pte);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!is_shadow_present_pte(*sptep))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
|
||||||
|
{
|
||||||
|
struct shadow_walker walker = {
|
||||||
|
.walker = { .entry = FNAME(shadow_invlpg_entry), },
|
||||||
|
};
|
||||||
|
|
||||||
|
walk_shadow(&walker.walker, vcpu, gva);
|
||||||
|
}
|
||||||
|
|
||||||
static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr)
|
static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr)
|
||||||
{
|
{
|
||||||
struct guest_walker walker;
|
struct guest_walker walker;
|
||||||
|
@ -499,12 +532,66 @@ static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using the cached information from sp->gfns is safe because:
|
||||||
|
* - The spte has a reference to the struct page, so the pfn for a given gfn
|
||||||
|
* can't change unless all sptes pointing to it are nuked first.
|
||||||
|
* - Alias changes zap the entire shadow cache.
|
||||||
|
*/
|
||||||
|
static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
|
||||||
|
{
|
||||||
|
int i, offset, nr_present;
|
||||||
|
|
||||||
|
offset = nr_present = 0;
|
||||||
|
|
||||||
|
if (PTTYPE == 32)
|
||||||
|
offset = sp->role.quadrant << PT64_LEVEL_BITS;
|
||||||
|
|
||||||
|
for (i = 0; i < PT64_ENT_PER_PAGE; i++) {
|
||||||
|
unsigned pte_access;
|
||||||
|
pt_element_t gpte;
|
||||||
|
gpa_t pte_gpa;
|
||||||
|
gfn_t gfn = sp->gfns[i];
|
||||||
|
|
||||||
|
if (!is_shadow_present_pte(sp->spt[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pte_gpa = gfn_to_gpa(sp->gfn);
|
||||||
|
pte_gpa += (i+offset) * sizeof(pt_element_t);
|
||||||
|
|
||||||
|
if (kvm_read_guest_atomic(vcpu->kvm, pte_gpa, &gpte,
|
||||||
|
sizeof(pt_element_t)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (gpte_to_gfn(gpte) != gfn || !is_present_pte(gpte) ||
|
||||||
|
!(gpte & PT_ACCESSED_MASK)) {
|
||||||
|
u64 nonpresent;
|
||||||
|
|
||||||
|
rmap_remove(vcpu->kvm, &sp->spt[i]);
|
||||||
|
if (is_present_pte(gpte))
|
||||||
|
nonpresent = shadow_trap_nonpresent_pte;
|
||||||
|
else
|
||||||
|
nonpresent = shadow_notrap_nonpresent_pte;
|
||||||
|
set_shadow_pte(&sp->spt[i], nonpresent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nr_present++;
|
||||||
|
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
|
||||||
|
set_spte(vcpu, &sp->spt[i], pte_access, 0, 0,
|
||||||
|
is_dirty_pte(gpte), 0, gfn,
|
||||||
|
spte_to_pfn(sp->spt[i]), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !nr_present;
|
||||||
|
}
|
||||||
|
|
||||||
#undef pt_element_t
|
#undef pt_element_t
|
||||||
#undef guest_walker
|
#undef guest_walker
|
||||||
|
#undef shadow_walker
|
||||||
#undef FNAME
|
#undef FNAME
|
||||||
#undef PT_BASE_ADDR_MASK
|
#undef PT_BASE_ADDR_MASK
|
||||||
#undef PT_INDEX
|
#undef PT_INDEX
|
||||||
#undef SHADOW_PT_INDEX
|
|
||||||
#undef PT_LEVEL_MASK
|
#undef PT_LEVEL_MASK
|
||||||
#undef PT_DIR_BASE_ADDR_MASK
|
#undef PT_DIR_BASE_ADDR_MASK
|
||||||
#undef PT_LEVEL_BITS
|
#undef PT_LEVEL_BITS
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "kvm_svm.h"
|
#include "kvm_svm.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
#include "mmu.h"
|
#include "mmu.h"
|
||||||
|
#include "kvm_cache_regs.h"
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -35,10 +36,6 @@ MODULE_LICENSE("GPL");
|
||||||
#define IOPM_ALLOC_ORDER 2
|
#define IOPM_ALLOC_ORDER 2
|
||||||
#define MSRPM_ALLOC_ORDER 1
|
#define MSRPM_ALLOC_ORDER 1
|
||||||
|
|
||||||
#define DB_VECTOR 1
|
|
||||||
#define UD_VECTOR 6
|
|
||||||
#define GP_VECTOR 13
|
|
||||||
|
|
||||||
#define DR7_GD_MASK (1 << 13)
|
#define DR7_GD_MASK (1 << 13)
|
||||||
#define DR6_BD_MASK (1 << 13)
|
#define DR6_BD_MASK (1 << 13)
|
||||||
|
|
||||||
|
@ -47,7 +44,7 @@ MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
#define SVM_FEATURE_NPT (1 << 0)
|
#define SVM_FEATURE_NPT (1 << 0)
|
||||||
#define SVM_FEATURE_LBRV (1 << 1)
|
#define SVM_FEATURE_LBRV (1 << 1)
|
||||||
#define SVM_DEATURE_SVML (1 << 2)
|
#define SVM_FEATURE_SVML (1 << 2)
|
||||||
|
|
||||||
#define DEBUGCTL_RESERVED_BITS (~(0x3fULL))
|
#define DEBUGCTL_RESERVED_BITS (~(0x3fULL))
|
||||||
|
|
||||||
|
@ -236,13 +233,11 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
|
||||||
printk(KERN_DEBUG "%s: NOP\n", __func__);
|
printk(KERN_DEBUG "%s: NOP\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (svm->next_rip - svm->vmcb->save.rip > MAX_INST_SIZE)
|
if (svm->next_rip - kvm_rip_read(vcpu) > MAX_INST_SIZE)
|
||||||
printk(KERN_ERR "%s: ip 0x%llx next 0x%llx\n",
|
printk(KERN_ERR "%s: ip 0x%lx next 0x%llx\n",
|
||||||
__func__,
|
__func__, kvm_rip_read(vcpu), svm->next_rip);
|
||||||
svm->vmcb->save.rip,
|
|
||||||
svm->next_rip);
|
|
||||||
|
|
||||||
vcpu->arch.rip = svm->vmcb->save.rip = svm->next_rip;
|
kvm_rip_write(vcpu, svm->next_rip);
|
||||||
svm->vmcb->control.int_state &= ~SVM_INTERRUPT_SHADOW_MASK;
|
svm->vmcb->control.int_state &= ~SVM_INTERRUPT_SHADOW_MASK;
|
||||||
|
|
||||||
vcpu->arch.interrupt_window_open = 1;
|
vcpu->arch.interrupt_window_open = 1;
|
||||||
|
@ -530,6 +525,7 @@ static void init_vmcb(struct vcpu_svm *svm)
|
||||||
(1ULL << INTERCEPT_CPUID) |
|
(1ULL << INTERCEPT_CPUID) |
|
||||||
(1ULL << INTERCEPT_INVD) |
|
(1ULL << INTERCEPT_INVD) |
|
||||||
(1ULL << INTERCEPT_HLT) |
|
(1ULL << INTERCEPT_HLT) |
|
||||||
|
(1ULL << INTERCEPT_INVLPG) |
|
||||||
(1ULL << INTERCEPT_INVLPGA) |
|
(1ULL << INTERCEPT_INVLPGA) |
|
||||||
(1ULL << INTERCEPT_IOIO_PROT) |
|
(1ULL << INTERCEPT_IOIO_PROT) |
|
||||||
(1ULL << INTERCEPT_MSR_PROT) |
|
(1ULL << INTERCEPT_MSR_PROT) |
|
||||||
|
@ -581,6 +577,7 @@ static void init_vmcb(struct vcpu_svm *svm)
|
||||||
save->dr7 = 0x400;
|
save->dr7 = 0x400;
|
||||||
save->rflags = 2;
|
save->rflags = 2;
|
||||||
save->rip = 0x0000fff0;
|
save->rip = 0x0000fff0;
|
||||||
|
svm->vcpu.arch.regs[VCPU_REGS_RIP] = save->rip;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cr0 val on cpu init should be 0x60000010, we enable cpu
|
* cr0 val on cpu init should be 0x60000010, we enable cpu
|
||||||
|
@ -593,7 +590,8 @@ static void init_vmcb(struct vcpu_svm *svm)
|
||||||
if (npt_enabled) {
|
if (npt_enabled) {
|
||||||
/* Setup VMCB for Nested Paging */
|
/* Setup VMCB for Nested Paging */
|
||||||
control->nested_ctl = 1;
|
control->nested_ctl = 1;
|
||||||
control->intercept &= ~(1ULL << INTERCEPT_TASK_SWITCH);
|
control->intercept &= ~((1ULL << INTERCEPT_TASK_SWITCH) |
|
||||||
|
(1ULL << INTERCEPT_INVLPG));
|
||||||
control->intercept_exceptions &= ~(1 << PF_VECTOR);
|
control->intercept_exceptions &= ~(1 << PF_VECTOR);
|
||||||
control->intercept_cr_read &= ~(INTERCEPT_CR0_MASK|
|
control->intercept_cr_read &= ~(INTERCEPT_CR0_MASK|
|
||||||
INTERCEPT_CR3_MASK);
|
INTERCEPT_CR3_MASK);
|
||||||
|
@ -615,10 +613,12 @@ static int svm_vcpu_reset(struct kvm_vcpu *vcpu)
|
||||||
init_vmcb(svm);
|
init_vmcb(svm);
|
||||||
|
|
||||||
if (vcpu->vcpu_id != 0) {
|
if (vcpu->vcpu_id != 0) {
|
||||||
svm->vmcb->save.rip = 0;
|
kvm_rip_write(vcpu, 0);
|
||||||
svm->vmcb->save.cs.base = svm->vcpu.arch.sipi_vector << 12;
|
svm->vmcb->save.cs.base = svm->vcpu.arch.sipi_vector << 12;
|
||||||
svm->vmcb->save.cs.selector = svm->vcpu.arch.sipi_vector << 8;
|
svm->vmcb->save.cs.selector = svm->vcpu.arch.sipi_vector << 8;
|
||||||
}
|
}
|
||||||
|
vcpu->arch.regs_avail = ~0;
|
||||||
|
vcpu->arch.regs_dirty = ~0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -721,23 +721,6 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu)
|
||||||
rdtscll(vcpu->arch.host_tsc);
|
rdtscll(vcpu->arch.host_tsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void svm_cache_regs(struct kvm_vcpu *vcpu)
|
|
||||||
{
|
|
||||||
struct vcpu_svm *svm = to_svm(vcpu);
|
|
||||||
|
|
||||||
vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax;
|
|
||||||
vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
|
|
||||||
vcpu->arch.rip = svm->vmcb->save.rip;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void svm_decache_regs(struct kvm_vcpu *vcpu)
|
|
||||||
{
|
|
||||||
struct vcpu_svm *svm = to_svm(vcpu);
|
|
||||||
svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX];
|
|
||||||
svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP];
|
|
||||||
svm->vmcb->save.rip = vcpu->arch.rip;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
|
static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
return to_svm(vcpu)->vmcb->save.rflags;
|
return to_svm(vcpu)->vmcb->save.rflags;
|
||||||
|
@ -1040,7 +1023,7 @@ static int pf_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
if (npt_enabled)
|
if (npt_enabled)
|
||||||
svm_flush_tlb(&svm->vcpu);
|
svm_flush_tlb(&svm->vcpu);
|
||||||
|
|
||||||
if (event_injection)
|
if (!npt_enabled && event_injection)
|
||||||
kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
|
kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
|
||||||
return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code);
|
return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code);
|
||||||
}
|
}
|
||||||
|
@ -1139,14 +1122,14 @@ static int nop_on_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
|
|
||||||
static int halt_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
static int halt_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
{
|
{
|
||||||
svm->next_rip = svm->vmcb->save.rip + 1;
|
svm->next_rip = kvm_rip_read(&svm->vcpu) + 1;
|
||||||
skip_emulated_instruction(&svm->vcpu);
|
skip_emulated_instruction(&svm->vcpu);
|
||||||
return kvm_emulate_halt(&svm->vcpu);
|
return kvm_emulate_halt(&svm->vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmmcall_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
static int vmmcall_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
{
|
{
|
||||||
svm->next_rip = svm->vmcb->save.rip + 3;
|
svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
|
||||||
skip_emulated_instruction(&svm->vcpu);
|
skip_emulated_instruction(&svm->vcpu);
|
||||||
kvm_emulate_hypercall(&svm->vcpu);
|
kvm_emulate_hypercall(&svm->vcpu);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1178,11 +1161,18 @@ static int task_switch_interception(struct vcpu_svm *svm,
|
||||||
|
|
||||||
static int cpuid_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
static int cpuid_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
{
|
{
|
||||||
svm->next_rip = svm->vmcb->save.rip + 2;
|
svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
|
||||||
kvm_emulate_cpuid(&svm->vcpu);
|
kvm_emulate_cpuid(&svm->vcpu);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int invlpg_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
|
{
|
||||||
|
if (emulate_instruction(&svm->vcpu, kvm_run, 0, 0, 0) != EMULATE_DONE)
|
||||||
|
pr_unimpl(&svm->vcpu, "%s: failed\n", __func__);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int emulate_on_interception(struct vcpu_svm *svm,
|
static int emulate_on_interception(struct vcpu_svm *svm,
|
||||||
struct kvm_run *kvm_run)
|
struct kvm_run *kvm_run)
|
||||||
{
|
{
|
||||||
|
@ -1273,9 +1263,9 @@ static int rdmsr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
KVMTRACE_3D(MSR_READ, &svm->vcpu, ecx, (u32)data,
|
KVMTRACE_3D(MSR_READ, &svm->vcpu, ecx, (u32)data,
|
||||||
(u32)(data >> 32), handler);
|
(u32)(data >> 32), handler);
|
||||||
|
|
||||||
svm->vmcb->save.rax = data & 0xffffffff;
|
svm->vcpu.arch.regs[VCPU_REGS_RAX] = data & 0xffffffff;
|
||||||
svm->vcpu.arch.regs[VCPU_REGS_RDX] = data >> 32;
|
svm->vcpu.arch.regs[VCPU_REGS_RDX] = data >> 32;
|
||||||
svm->next_rip = svm->vmcb->save.rip + 2;
|
svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
|
||||||
skip_emulated_instruction(&svm->vcpu);
|
skip_emulated_instruction(&svm->vcpu);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1359,13 +1349,13 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data)
|
||||||
static int wrmsr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
static int wrmsr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
|
||||||
{
|
{
|
||||||
u32 ecx = svm->vcpu.arch.regs[VCPU_REGS_RCX];
|
u32 ecx = svm->vcpu.arch.regs[VCPU_REGS_RCX];
|
||||||
u64 data = (svm->vmcb->save.rax & -1u)
|
u64 data = (svm->vcpu.arch.regs[VCPU_REGS_RAX] & -1u)
|
||||||
| ((u64)(svm->vcpu.arch.regs[VCPU_REGS_RDX] & -1u) << 32);
|
| ((u64)(svm->vcpu.arch.regs[VCPU_REGS_RDX] & -1u) << 32);
|
||||||
|
|
||||||
KVMTRACE_3D(MSR_WRITE, &svm->vcpu, ecx, (u32)data, (u32)(data >> 32),
|
KVMTRACE_3D(MSR_WRITE, &svm->vcpu, ecx, (u32)data, (u32)(data >> 32),
|
||||||
handler);
|
handler);
|
||||||
|
|
||||||
svm->next_rip = svm->vmcb->save.rip + 2;
|
svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
|
||||||
if (svm_set_msr(&svm->vcpu, ecx, data))
|
if (svm_set_msr(&svm->vcpu, ecx, data))
|
||||||
kvm_inject_gp(&svm->vcpu, 0);
|
kvm_inject_gp(&svm->vcpu, 0);
|
||||||
else
|
else
|
||||||
|
@ -1436,7 +1426,7 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm,
|
||||||
[SVM_EXIT_CPUID] = cpuid_interception,
|
[SVM_EXIT_CPUID] = cpuid_interception,
|
||||||
[SVM_EXIT_INVD] = emulate_on_interception,
|
[SVM_EXIT_INVD] = emulate_on_interception,
|
||||||
[SVM_EXIT_HLT] = halt_interception,
|
[SVM_EXIT_HLT] = halt_interception,
|
||||||
[SVM_EXIT_INVLPG] = emulate_on_interception,
|
[SVM_EXIT_INVLPG] = invlpg_interception,
|
||||||
[SVM_EXIT_INVLPGA] = invalid_op_interception,
|
[SVM_EXIT_INVLPGA] = invalid_op_interception,
|
||||||
[SVM_EXIT_IOIO] = io_interception,
|
[SVM_EXIT_IOIO] = io_interception,
|
||||||
[SVM_EXIT_MSR] = msr_interception,
|
[SVM_EXIT_MSR] = msr_interception,
|
||||||
|
@ -1538,6 +1528,7 @@ static inline void svm_inject_irq(struct vcpu_svm *svm, int irq)
|
||||||
|
|
||||||
KVMTRACE_1D(INJ_VIRQ, &svm->vcpu, (u32)irq, handler);
|
KVMTRACE_1D(INJ_VIRQ, &svm->vcpu, (u32)irq, handler);
|
||||||
|
|
||||||
|
++svm->vcpu.stat.irq_injections;
|
||||||
control = &svm->vmcb->control;
|
control = &svm->vmcb->control;
|
||||||
control->int_vector = irq;
|
control->int_vector = irq;
|
||||||
control->int_ctl &= ~V_INTR_PRIO_MASK;
|
control->int_ctl &= ~V_INTR_PRIO_MASK;
|
||||||
|
@ -1716,6 +1707,12 @@ static inline void sync_lapic_to_cr8(struct kvm_vcpu *vcpu)
|
||||||
svm->vmcb->control.int_ctl |= cr8 & V_TPR_MASK;
|
svm->vmcb->control.int_ctl |= cr8 & V_TPR_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
#define R "r"
|
||||||
|
#else
|
||||||
|
#define R "e"
|
||||||
|
#endif
|
||||||
|
|
||||||
static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
{
|
{
|
||||||
struct vcpu_svm *svm = to_svm(vcpu);
|
struct vcpu_svm *svm = to_svm(vcpu);
|
||||||
|
@ -1723,6 +1720,10 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
u16 gs_selector;
|
u16 gs_selector;
|
||||||
u16 ldt_selector;
|
u16 ldt_selector;
|
||||||
|
|
||||||
|
svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX];
|
||||||
|
svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP];
|
||||||
|
svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP];
|
||||||
|
|
||||||
pre_svm_run(svm);
|
pre_svm_run(svm);
|
||||||
|
|
||||||
sync_lapic_to_cr8(vcpu);
|
sync_lapic_to_cr8(vcpu);
|
||||||
|
@ -1750,19 +1751,14 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
|
|
||||||
asm volatile (
|
asm volatile (
|
||||||
|
"push %%"R"bp; \n\t"
|
||||||
|
"mov %c[rbx](%[svm]), %%"R"bx \n\t"
|
||||||
|
"mov %c[rcx](%[svm]), %%"R"cx \n\t"
|
||||||
|
"mov %c[rdx](%[svm]), %%"R"dx \n\t"
|
||||||
|
"mov %c[rsi](%[svm]), %%"R"si \n\t"
|
||||||
|
"mov %c[rdi](%[svm]), %%"R"di \n\t"
|
||||||
|
"mov %c[rbp](%[svm]), %%"R"bp \n\t"
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
"push %%rbp; \n\t"
|
|
||||||
#else
|
|
||||||
"push %%ebp; \n\t"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
|
||||||
"mov %c[rbx](%[svm]), %%rbx \n\t"
|
|
||||||
"mov %c[rcx](%[svm]), %%rcx \n\t"
|
|
||||||
"mov %c[rdx](%[svm]), %%rdx \n\t"
|
|
||||||
"mov %c[rsi](%[svm]), %%rsi \n\t"
|
|
||||||
"mov %c[rdi](%[svm]), %%rdi \n\t"
|
|
||||||
"mov %c[rbp](%[svm]), %%rbp \n\t"
|
|
||||||
"mov %c[r8](%[svm]), %%r8 \n\t"
|
"mov %c[r8](%[svm]), %%r8 \n\t"
|
||||||
"mov %c[r9](%[svm]), %%r9 \n\t"
|
"mov %c[r9](%[svm]), %%r9 \n\t"
|
||||||
"mov %c[r10](%[svm]), %%r10 \n\t"
|
"mov %c[r10](%[svm]), %%r10 \n\t"
|
||||||
|
@ -1771,41 +1767,24 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
"mov %c[r13](%[svm]), %%r13 \n\t"
|
"mov %c[r13](%[svm]), %%r13 \n\t"
|
||||||
"mov %c[r14](%[svm]), %%r14 \n\t"
|
"mov %c[r14](%[svm]), %%r14 \n\t"
|
||||||
"mov %c[r15](%[svm]), %%r15 \n\t"
|
"mov %c[r15](%[svm]), %%r15 \n\t"
|
||||||
#else
|
|
||||||
"mov %c[rbx](%[svm]), %%ebx \n\t"
|
|
||||||
"mov %c[rcx](%[svm]), %%ecx \n\t"
|
|
||||||
"mov %c[rdx](%[svm]), %%edx \n\t"
|
|
||||||
"mov %c[rsi](%[svm]), %%esi \n\t"
|
|
||||||
"mov %c[rdi](%[svm]), %%edi \n\t"
|
|
||||||
"mov %c[rbp](%[svm]), %%ebp \n\t"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
|
||||||
/* Enter guest mode */
|
/* Enter guest mode */
|
||||||
"push %%rax \n\t"
|
"push %%"R"ax \n\t"
|
||||||
"mov %c[vmcb](%[svm]), %%rax \n\t"
|
"mov %c[vmcb](%[svm]), %%"R"ax \n\t"
|
||||||
__ex(SVM_VMLOAD) "\n\t"
|
__ex(SVM_VMLOAD) "\n\t"
|
||||||
__ex(SVM_VMRUN) "\n\t"
|
__ex(SVM_VMRUN) "\n\t"
|
||||||
__ex(SVM_VMSAVE) "\n\t"
|
__ex(SVM_VMSAVE) "\n\t"
|
||||||
"pop %%rax \n\t"
|
"pop %%"R"ax \n\t"
|
||||||
#else
|
|
||||||
/* Enter guest mode */
|
|
||||||
"push %%eax \n\t"
|
|
||||||
"mov %c[vmcb](%[svm]), %%eax \n\t"
|
|
||||||
__ex(SVM_VMLOAD) "\n\t"
|
|
||||||
__ex(SVM_VMRUN) "\n\t"
|
|
||||||
__ex(SVM_VMSAVE) "\n\t"
|
|
||||||
"pop %%eax \n\t"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Save guest registers, load host registers */
|
/* Save guest registers, load host registers */
|
||||||
|
"mov %%"R"bx, %c[rbx](%[svm]) \n\t"
|
||||||
|
"mov %%"R"cx, %c[rcx](%[svm]) \n\t"
|
||||||
|
"mov %%"R"dx, %c[rdx](%[svm]) \n\t"
|
||||||
|
"mov %%"R"si, %c[rsi](%[svm]) \n\t"
|
||||||
|
"mov %%"R"di, %c[rdi](%[svm]) \n\t"
|
||||||
|
"mov %%"R"bp, %c[rbp](%[svm]) \n\t"
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
"mov %%rbx, %c[rbx](%[svm]) \n\t"
|
|
||||||
"mov %%rcx, %c[rcx](%[svm]) \n\t"
|
|
||||||
"mov %%rdx, %c[rdx](%[svm]) \n\t"
|
|
||||||
"mov %%rsi, %c[rsi](%[svm]) \n\t"
|
|
||||||
"mov %%rdi, %c[rdi](%[svm]) \n\t"
|
|
||||||
"mov %%rbp, %c[rbp](%[svm]) \n\t"
|
|
||||||
"mov %%r8, %c[r8](%[svm]) \n\t"
|
"mov %%r8, %c[r8](%[svm]) \n\t"
|
||||||
"mov %%r9, %c[r9](%[svm]) \n\t"
|
"mov %%r9, %c[r9](%[svm]) \n\t"
|
||||||
"mov %%r10, %c[r10](%[svm]) \n\t"
|
"mov %%r10, %c[r10](%[svm]) \n\t"
|
||||||
|
@ -1814,18 +1793,8 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
"mov %%r13, %c[r13](%[svm]) \n\t"
|
"mov %%r13, %c[r13](%[svm]) \n\t"
|
||||||
"mov %%r14, %c[r14](%[svm]) \n\t"
|
"mov %%r14, %c[r14](%[svm]) \n\t"
|
||||||
"mov %%r15, %c[r15](%[svm]) \n\t"
|
"mov %%r15, %c[r15](%[svm]) \n\t"
|
||||||
|
|
||||||
"pop %%rbp; \n\t"
|
|
||||||
#else
|
|
||||||
"mov %%ebx, %c[rbx](%[svm]) \n\t"
|
|
||||||
"mov %%ecx, %c[rcx](%[svm]) \n\t"
|
|
||||||
"mov %%edx, %c[rdx](%[svm]) \n\t"
|
|
||||||
"mov %%esi, %c[rsi](%[svm]) \n\t"
|
|
||||||
"mov %%edi, %c[rdi](%[svm]) \n\t"
|
|
||||||
"mov %%ebp, %c[rbp](%[svm]) \n\t"
|
|
||||||
|
|
||||||
"pop %%ebp; \n\t"
|
|
||||||
#endif
|
#endif
|
||||||
|
"pop %%"R"bp"
|
||||||
:
|
:
|
||||||
: [svm]"a"(svm),
|
: [svm]"a"(svm),
|
||||||
[vmcb]"i"(offsetof(struct vcpu_svm, vmcb_pa)),
|
[vmcb]"i"(offsetof(struct vcpu_svm, vmcb_pa)),
|
||||||
|
@ -1846,11 +1815,9 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
[r15]"i"(offsetof(struct vcpu_svm, vcpu.arch.regs[VCPU_REGS_R15]))
|
[r15]"i"(offsetof(struct vcpu_svm, vcpu.arch.regs[VCPU_REGS_R15]))
|
||||||
#endif
|
#endif
|
||||||
: "cc", "memory"
|
: "cc", "memory"
|
||||||
|
, R"bx", R"cx", R"dx", R"si", R"di"
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
, "rbx", "rcx", "rdx", "rsi", "rdi"
|
|
||||||
, "r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15"
|
, "r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15"
|
||||||
#else
|
|
||||||
, "ebx", "ecx", "edx" , "esi", "edi"
|
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1858,6 +1825,9 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
load_db_regs(svm->host_db_regs);
|
load_db_regs(svm->host_db_regs);
|
||||||
|
|
||||||
vcpu->arch.cr2 = svm->vmcb->save.cr2;
|
vcpu->arch.cr2 = svm->vmcb->save.cr2;
|
||||||
|
vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax;
|
||||||
|
vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
|
||||||
|
vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
|
||||||
|
|
||||||
write_dr6(svm->host_dr6);
|
write_dr6(svm->host_dr6);
|
||||||
write_dr7(svm->host_dr7);
|
write_dr7(svm->host_dr7);
|
||||||
|
@ -1879,6 +1849,8 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
|
||||||
svm->next_rip = 0;
|
svm->next_rip = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef R
|
||||||
|
|
||||||
static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root)
|
static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root)
|
||||||
{
|
{
|
||||||
struct vcpu_svm *svm = to_svm(vcpu);
|
struct vcpu_svm *svm = to_svm(vcpu);
|
||||||
|
@ -1977,8 +1949,6 @@ static struct kvm_x86_ops svm_x86_ops = {
|
||||||
.set_gdt = svm_set_gdt,
|
.set_gdt = svm_set_gdt,
|
||||||
.get_dr = svm_get_dr,
|
.get_dr = svm_get_dr,
|
||||||
.set_dr = svm_set_dr,
|
.set_dr = svm_set_dr,
|
||||||
.cache_regs = svm_cache_regs,
|
|
||||||
.decache_regs = svm_decache_regs,
|
|
||||||
.get_rflags = svm_get_rflags,
|
.get_rflags = svm_get_rflags,
|
||||||
.set_rflags = svm_set_rflags,
|
.set_rflags = svm_set_rflags,
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -331,9 +331,6 @@ enum vmcs_field {
|
||||||
|
|
||||||
#define AR_RESERVD_MASK 0xfffe0f00
|
#define AR_RESERVD_MASK 0xfffe0f00
|
||||||
|
|
||||||
#define MSR_IA32_FEATURE_CONTROL_LOCKED 0x1
|
|
||||||
#define MSR_IA32_FEATURE_CONTROL_VMXON_ENABLED 0x4
|
|
||||||
|
|
||||||
#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT 9
|
#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT 9
|
||||||
#define IDENTITY_PAGETABLE_PRIVATE_MEMSLOT 10
|
#define IDENTITY_PAGETABLE_PRIVATE_MEMSLOT 10
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef ARCH_X86_KVM_X86_H
|
||||||
|
#define ARCH_X86_KVM_X86_H
|
||||||
|
|
||||||
|
#include <linux/kvm_host.h>
|
||||||
|
|
||||||
|
static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vcpu->arch.exception.pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kvm_queue_interrupt(struct kvm_vcpu *vcpu, u8 vector)
|
||||||
|
{
|
||||||
|
vcpu->arch.interrupt.pending = true;
|
||||||
|
vcpu->arch.interrupt.nr = vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kvm_clear_interrupt_queue(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vcpu->arch.interrupt.pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -26,6 +26,7 @@
|
||||||
#define DPRINTF(_f, _a ...) printf(_f , ## _a)
|
#define DPRINTF(_f, _a ...) printf(_f , ## _a)
|
||||||
#else
|
#else
|
||||||
#include <linux/kvm_host.h>
|
#include <linux/kvm_host.h>
|
||||||
|
#include "kvm_cache_regs.h"
|
||||||
#define DPRINTF(x...) do {} while (0)
|
#define DPRINTF(x...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -46,25 +47,26 @@
|
||||||
#define ImplicitOps (1<<1) /* Implicit in opcode. No generic decode. */
|
#define ImplicitOps (1<<1) /* Implicit in opcode. No generic decode. */
|
||||||
#define DstReg (2<<1) /* Register operand. */
|
#define DstReg (2<<1) /* Register operand. */
|
||||||
#define DstMem (3<<1) /* Memory operand. */
|
#define DstMem (3<<1) /* Memory operand. */
|
||||||
#define DstMask (3<<1)
|
#define DstAcc (4<<1) /* Destination Accumulator */
|
||||||
|
#define DstMask (7<<1)
|
||||||
/* Source operand type. */
|
/* Source operand type. */
|
||||||
#define SrcNone (0<<3) /* No source operand. */
|
#define SrcNone (0<<4) /* No source operand. */
|
||||||
#define SrcImplicit (0<<3) /* Source operand is implicit in the opcode. */
|
#define SrcImplicit (0<<4) /* Source operand is implicit in the opcode. */
|
||||||
#define SrcReg (1<<3) /* Register operand. */
|
#define SrcReg (1<<4) /* Register operand. */
|
||||||
#define SrcMem (2<<3) /* Memory operand. */
|
#define SrcMem (2<<4) /* Memory operand. */
|
||||||
#define SrcMem16 (3<<3) /* Memory operand (16-bit). */
|
#define SrcMem16 (3<<4) /* Memory operand (16-bit). */
|
||||||
#define SrcMem32 (4<<3) /* Memory operand (32-bit). */
|
#define SrcMem32 (4<<4) /* Memory operand (32-bit). */
|
||||||
#define SrcImm (5<<3) /* Immediate operand. */
|
#define SrcImm (5<<4) /* Immediate operand. */
|
||||||
#define SrcImmByte (6<<3) /* 8-bit sign-extended immediate operand. */
|
#define SrcImmByte (6<<4) /* 8-bit sign-extended immediate operand. */
|
||||||
#define SrcMask (7<<3)
|
#define SrcMask (7<<4)
|
||||||
/* Generic ModRM decode. */
|
/* Generic ModRM decode. */
|
||||||
#define ModRM (1<<6)
|
#define ModRM (1<<7)
|
||||||
/* Destination is only written; never read. */
|
/* Destination is only written; never read. */
|
||||||
#define Mov (1<<7)
|
#define Mov (1<<8)
|
||||||
#define BitOp (1<<8)
|
#define BitOp (1<<9)
|
||||||
#define MemAbs (1<<9) /* Memory operand is absolute displacement */
|
#define MemAbs (1<<10) /* Memory operand is absolute displacement */
|
||||||
#define String (1<<10) /* String instruction (rep capable) */
|
#define String (1<<12) /* String instruction (rep capable) */
|
||||||
#define Stack (1<<11) /* Stack instruction (push/pop) */
|
#define Stack (1<<13) /* Stack instruction (push/pop) */
|
||||||
#define Group (1<<14) /* Bits 3:5 of modrm byte extend opcode */
|
#define Group (1<<14) /* Bits 3:5 of modrm byte extend opcode */
|
||||||
#define GroupDual (1<<15) /* Alternate decoding of mod == 3 */
|
#define GroupDual (1<<15) /* Alternate decoding of mod == 3 */
|
||||||
#define GroupMask 0xff /* Group number stored in bits 0:7 */
|
#define GroupMask 0xff /* Group number stored in bits 0:7 */
|
||||||
|
@ -94,7 +96,7 @@ static u16 opcode_table[256] = {
|
||||||
/* 0x20 - 0x27 */
|
/* 0x20 - 0x27 */
|
||||||
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
|
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
|
||||||
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
|
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
|
||||||
SrcImmByte, SrcImm, 0, 0,
|
DstAcc | SrcImmByte, DstAcc | SrcImm, 0, 0,
|
||||||
/* 0x28 - 0x2F */
|
/* 0x28 - 0x2F */
|
||||||
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
|
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
|
||||||
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
|
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
|
||||||
|
@ -106,7 +108,8 @@ static u16 opcode_table[256] = {
|
||||||
/* 0x38 - 0x3F */
|
/* 0x38 - 0x3F */
|
||||||
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
|
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
|
||||||
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
|
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
|
||||||
0, 0, 0, 0,
|
ByteOp | DstAcc | SrcImm, DstAcc | SrcImm,
|
||||||
|
0, 0,
|
||||||
/* 0x40 - 0x47 */
|
/* 0x40 - 0x47 */
|
||||||
DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg,
|
DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg,
|
||||||
/* 0x48 - 0x4F */
|
/* 0x48 - 0x4F */
|
||||||
|
@ -153,9 +156,16 @@ static u16 opcode_table[256] = {
|
||||||
0, 0, ByteOp | ImplicitOps | Mov | String, ImplicitOps | Mov | String,
|
0, 0, ByteOp | ImplicitOps | Mov | String, ImplicitOps | Mov | String,
|
||||||
ByteOp | ImplicitOps | Mov | String, ImplicitOps | Mov | String,
|
ByteOp | ImplicitOps | Mov | String, ImplicitOps | Mov | String,
|
||||||
ByteOp | ImplicitOps | String, ImplicitOps | String,
|
ByteOp | ImplicitOps | String, ImplicitOps | String,
|
||||||
/* 0xB0 - 0xBF */
|
/* 0xB0 - 0xB7 */
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
ByteOp | DstReg | SrcImm | Mov, ByteOp | DstReg | SrcImm | Mov,
|
||||||
DstReg | SrcImm | Mov, 0, 0, 0, 0, 0, 0, 0,
|
ByteOp | DstReg | SrcImm | Mov, ByteOp | DstReg | SrcImm | Mov,
|
||||||
|
ByteOp | DstReg | SrcImm | Mov, ByteOp | DstReg | SrcImm | Mov,
|
||||||
|
ByteOp | DstReg | SrcImm | Mov, ByteOp | DstReg | SrcImm | Mov,
|
||||||
|
/* 0xB8 - 0xBF */
|
||||||
|
DstReg | SrcImm | Mov, DstReg | SrcImm | Mov,
|
||||||
|
DstReg | SrcImm | Mov, DstReg | SrcImm | Mov,
|
||||||
|
DstReg | SrcImm | Mov, DstReg | SrcImm | Mov,
|
||||||
|
DstReg | SrcImm | Mov, DstReg | SrcImm | Mov,
|
||||||
/* 0xC0 - 0xC7 */
|
/* 0xC0 - 0xC7 */
|
||||||
ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImmByte | ModRM,
|
ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImmByte | ModRM,
|
||||||
0, ImplicitOps | Stack, 0, 0,
|
0, ImplicitOps | Stack, 0, 0,
|
||||||
|
@ -169,17 +179,20 @@ static u16 opcode_table[256] = {
|
||||||
/* 0xD8 - 0xDF */
|
/* 0xD8 - 0xDF */
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
/* 0xE0 - 0xE7 */
|
/* 0xE0 - 0xE7 */
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
|
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps,
|
||||||
|
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps,
|
||||||
/* 0xE8 - 0xEF */
|
/* 0xE8 - 0xEF */
|
||||||
ImplicitOps | Stack, SrcImm | ImplicitOps,
|
ImplicitOps | Stack, SrcImm | ImplicitOps,
|
||||||
ImplicitOps, SrcImmByte | ImplicitOps,
|
ImplicitOps, SrcImmByte | ImplicitOps,
|
||||||
0, 0, 0, 0,
|
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps,
|
||||||
|
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps,
|
||||||
/* 0xF0 - 0xF7 */
|
/* 0xF0 - 0xF7 */
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
ImplicitOps, ImplicitOps, Group | Group3_Byte, Group | Group3,
|
ImplicitOps, ImplicitOps, Group | Group3_Byte, Group | Group3,
|
||||||
/* 0xF8 - 0xFF */
|
/* 0xF8 - 0xFF */
|
||||||
ImplicitOps, 0, ImplicitOps, ImplicitOps,
|
ImplicitOps, 0, ImplicitOps, ImplicitOps,
|
||||||
0, 0, Group | Group4, Group | Group5,
|
ImplicitOps, ImplicitOps, Group | Group4, Group | Group5,
|
||||||
};
|
};
|
||||||
|
|
||||||
static u16 twobyte_table[256] = {
|
static u16 twobyte_table[256] = {
|
||||||
|
@ -268,15 +281,16 @@ static u16 group_table[] = {
|
||||||
ByteOp | DstMem | SrcNone | ModRM, ByteOp | DstMem | SrcNone | ModRM,
|
ByteOp | DstMem | SrcNone | ModRM, ByteOp | DstMem | SrcNone | ModRM,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
[Group3*8] =
|
[Group3*8] =
|
||||||
DstMem | SrcImm | ModRM | SrcImm, 0,
|
DstMem | SrcImm | ModRM, 0,
|
||||||
DstMem | SrcNone | ModRM, ByteOp | DstMem | SrcNone | ModRM,
|
DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
[Group4*8] =
|
[Group4*8] =
|
||||||
ByteOp | DstMem | SrcNone | ModRM, ByteOp | DstMem | SrcNone | ModRM,
|
ByteOp | DstMem | SrcNone | ModRM, ByteOp | DstMem | SrcNone | ModRM,
|
||||||
0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0,
|
||||||
[Group5*8] =
|
[Group5*8] =
|
||||||
DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM, 0, 0,
|
DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM,
|
||||||
SrcMem | ModRM, 0, SrcMem | ModRM | Stack, 0,
|
SrcMem | ModRM | Stack, 0,
|
||||||
|
SrcMem | ModRM | Stack, 0, SrcMem | ModRM | Stack, 0,
|
||||||
[Group7*8] =
|
[Group7*8] =
|
||||||
0, 0, ModRM | SrcMem, ModRM | SrcMem,
|
0, 0, ModRM | SrcMem, ModRM | SrcMem,
|
||||||
SrcNone | ModRM | DstMem | Mov, 0,
|
SrcNone | ModRM | DstMem | Mov, 0,
|
||||||
|
@ -839,7 +853,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
|
||||||
/* Shadow copy of register state. Committed on successful emulation. */
|
/* Shadow copy of register state. Committed on successful emulation. */
|
||||||
|
|
||||||
memset(c, 0, sizeof(struct decode_cache));
|
memset(c, 0, sizeof(struct decode_cache));
|
||||||
c->eip = ctxt->vcpu->arch.rip;
|
c->eip = kvm_rip_read(ctxt->vcpu);
|
||||||
ctxt->cs_base = seg_base(ctxt, VCPU_SREG_CS);
|
ctxt->cs_base = seg_base(ctxt, VCPU_SREG_CS);
|
||||||
memcpy(c->regs, ctxt->vcpu->arch.regs, sizeof c->regs);
|
memcpy(c->regs, ctxt->vcpu->arch.regs, sizeof c->regs);
|
||||||
|
|
||||||
|
@ -1048,6 +1062,23 @@ done_prefixes:
|
||||||
}
|
}
|
||||||
c->dst.type = OP_MEM;
|
c->dst.type = OP_MEM;
|
||||||
break;
|
break;
|
||||||
|
case DstAcc:
|
||||||
|
c->dst.type = OP_REG;
|
||||||
|
c->dst.bytes = c->op_bytes;
|
||||||
|
c->dst.ptr = &c->regs[VCPU_REGS_RAX];
|
||||||
|
switch (c->op_bytes) {
|
||||||
|
case 1:
|
||||||
|
c->dst.val = *(u8 *)c->dst.ptr;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
c->dst.val = *(u16 *)c->dst.ptr;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
c->dst.val = *(u32 *)c->dst.ptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c->dst.orig_val = c->dst.val;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->rip_relative)
|
if (c->rip_relative)
|
||||||
|
@ -1151,6 +1182,14 @@ static inline int emulate_grp45(struct x86_emulate_ctxt *ctxt,
|
||||||
case 1: /* dec */
|
case 1: /* dec */
|
||||||
emulate_1op("dec", c->dst, ctxt->eflags);
|
emulate_1op("dec", c->dst, ctxt->eflags);
|
||||||
break;
|
break;
|
||||||
|
case 2: /* call near abs */ {
|
||||||
|
long int old_eip;
|
||||||
|
old_eip = c->eip;
|
||||||
|
c->eip = c->src.val;
|
||||||
|
c->src.val = old_eip;
|
||||||
|
emulate_push(ctxt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 4: /* jmp abs */
|
case 4: /* jmp abs */
|
||||||
c->eip = c->src.val;
|
c->eip = c->src.val;
|
||||||
break;
|
break;
|
||||||
|
@ -1251,6 +1290,8 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
|
||||||
u64 msr_data;
|
u64 msr_data;
|
||||||
unsigned long saved_eip = 0;
|
unsigned long saved_eip = 0;
|
||||||
struct decode_cache *c = &ctxt->decode;
|
struct decode_cache *c = &ctxt->decode;
|
||||||
|
unsigned int port;
|
||||||
|
int io_dir_in;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
/* Shadow copy of register state. Committed on successful emulation.
|
/* Shadow copy of register state. Committed on successful emulation.
|
||||||
|
@ -1267,7 +1308,7 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
|
||||||
if (c->rep_prefix && (c->d & String)) {
|
if (c->rep_prefix && (c->d & String)) {
|
||||||
/* All REP prefixes have the same first termination condition */
|
/* All REP prefixes have the same first termination condition */
|
||||||
if (c->regs[VCPU_REGS_RCX] == 0) {
|
if (c->regs[VCPU_REGS_RCX] == 0) {
|
||||||
ctxt->vcpu->arch.rip = c->eip;
|
kvm_rip_write(ctxt->vcpu, c->eip);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* The second termination condition only applies for REPE
|
/* The second termination condition only applies for REPE
|
||||||
|
@ -1281,17 +1322,17 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
|
||||||
(c->b == 0xae) || (c->b == 0xaf)) {
|
(c->b == 0xae) || (c->b == 0xaf)) {
|
||||||
if ((c->rep_prefix == REPE_PREFIX) &&
|
if ((c->rep_prefix == REPE_PREFIX) &&
|
||||||
((ctxt->eflags & EFLG_ZF) == 0)) {
|
((ctxt->eflags & EFLG_ZF) == 0)) {
|
||||||
ctxt->vcpu->arch.rip = c->eip;
|
kvm_rip_write(ctxt->vcpu, c->eip);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((c->rep_prefix == REPNE_PREFIX) &&
|
if ((c->rep_prefix == REPNE_PREFIX) &&
|
||||||
((ctxt->eflags & EFLG_ZF) == EFLG_ZF)) {
|
((ctxt->eflags & EFLG_ZF) == EFLG_ZF)) {
|
||||||
ctxt->vcpu->arch.rip = c->eip;
|
kvm_rip_write(ctxt->vcpu, c->eip);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c->regs[VCPU_REGS_RCX]--;
|
c->regs[VCPU_REGS_RCX]--;
|
||||||
c->eip = ctxt->vcpu->arch.rip;
|
c->eip = kvm_rip_read(ctxt->vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->src.type == OP_MEM) {
|
if (c->src.type == OP_MEM) {
|
||||||
|
@ -1351,27 +1392,10 @@ special_insn:
|
||||||
sbb: /* sbb */
|
sbb: /* sbb */
|
||||||
emulate_2op_SrcV("sbb", c->src, c->dst, ctxt->eflags);
|
emulate_2op_SrcV("sbb", c->src, c->dst, ctxt->eflags);
|
||||||
break;
|
break;
|
||||||
case 0x20 ... 0x23:
|
case 0x20 ... 0x25:
|
||||||
and: /* and */
|
and: /* and */
|
||||||
emulate_2op_SrcV("and", c->src, c->dst, ctxt->eflags);
|
emulate_2op_SrcV("and", c->src, c->dst, ctxt->eflags);
|
||||||
break;
|
break;
|
||||||
case 0x24: /* and al imm8 */
|
|
||||||
c->dst.type = OP_REG;
|
|
||||||
c->dst.ptr = &c->regs[VCPU_REGS_RAX];
|
|
||||||
c->dst.val = *(u8 *)c->dst.ptr;
|
|
||||||
c->dst.bytes = 1;
|
|
||||||
c->dst.orig_val = c->dst.val;
|
|
||||||
goto and;
|
|
||||||
case 0x25: /* and ax imm16, or eax imm32 */
|
|
||||||
c->dst.type = OP_REG;
|
|
||||||
c->dst.bytes = c->op_bytes;
|
|
||||||
c->dst.ptr = &c->regs[VCPU_REGS_RAX];
|
|
||||||
if (c->op_bytes == 2)
|
|
||||||
c->dst.val = *(u16 *)c->dst.ptr;
|
|
||||||
else
|
|
||||||
c->dst.val = *(u32 *)c->dst.ptr;
|
|
||||||
c->dst.orig_val = c->dst.val;
|
|
||||||
goto and;
|
|
||||||
case 0x28 ... 0x2d:
|
case 0x28 ... 0x2d:
|
||||||
sub: /* sub */
|
sub: /* sub */
|
||||||
emulate_2op_SrcV("sub", c->src, c->dst, ctxt->eflags);
|
emulate_2op_SrcV("sub", c->src, c->dst, ctxt->eflags);
|
||||||
|
@ -1659,7 +1683,7 @@ special_insn:
|
||||||
case 0xae ... 0xaf: /* scas */
|
case 0xae ... 0xaf: /* scas */
|
||||||
DPRINTF("Urk! I don't handle SCAS.\n");
|
DPRINTF("Urk! I don't handle SCAS.\n");
|
||||||
goto cannot_emulate;
|
goto cannot_emulate;
|
||||||
case 0xb8: /* mov r, imm */
|
case 0xb0 ... 0xbf: /* mov r, imm */
|
||||||
goto mov;
|
goto mov;
|
||||||
case 0xc0 ... 0xc1:
|
case 0xc0 ... 0xc1:
|
||||||
emulate_grp2(ctxt);
|
emulate_grp2(ctxt);
|
||||||
|
@ -1679,6 +1703,16 @@ special_insn:
|
||||||
c->src.val = c->regs[VCPU_REGS_RCX];
|
c->src.val = c->regs[VCPU_REGS_RCX];
|
||||||
emulate_grp2(ctxt);
|
emulate_grp2(ctxt);
|
||||||
break;
|
break;
|
||||||
|
case 0xe4: /* inb */
|
||||||
|
case 0xe5: /* in */
|
||||||
|
port = insn_fetch(u8, 1, c->eip);
|
||||||
|
io_dir_in = 1;
|
||||||
|
goto do_io;
|
||||||
|
case 0xe6: /* outb */
|
||||||
|
case 0xe7: /* out */
|
||||||
|
port = insn_fetch(u8, 1, c->eip);
|
||||||
|
io_dir_in = 0;
|
||||||
|
goto do_io;
|
||||||
case 0xe8: /* call (near) */ {
|
case 0xe8: /* call (near) */ {
|
||||||
long int rel;
|
long int rel;
|
||||||
switch (c->op_bytes) {
|
switch (c->op_bytes) {
|
||||||
|
@ -1729,6 +1763,22 @@ special_insn:
|
||||||
jmp_rel(c, c->src.val);
|
jmp_rel(c, c->src.val);
|
||||||
c->dst.type = OP_NONE; /* Disable writeback. */
|
c->dst.type = OP_NONE; /* Disable writeback. */
|
||||||
break;
|
break;
|
||||||
|
case 0xec: /* in al,dx */
|
||||||
|
case 0xed: /* in (e/r)ax,dx */
|
||||||
|
port = c->regs[VCPU_REGS_RDX];
|
||||||
|
io_dir_in = 1;
|
||||||
|
goto do_io;
|
||||||
|
case 0xee: /* out al,dx */
|
||||||
|
case 0xef: /* out (e/r)ax,dx */
|
||||||
|
port = c->regs[VCPU_REGS_RDX];
|
||||||
|
io_dir_in = 0;
|
||||||
|
do_io: if (kvm_emulate_pio(ctxt->vcpu, NULL, io_dir_in,
|
||||||
|
(c->d & ByteOp) ? 1 : c->op_bytes,
|
||||||
|
port) != 0) {
|
||||||
|
c->eip = saved_eip;
|
||||||
|
goto cannot_emulate;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
case 0xf4: /* hlt */
|
case 0xf4: /* hlt */
|
||||||
ctxt->vcpu->arch.halt_request = 1;
|
ctxt->vcpu->arch.halt_request = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -1754,6 +1804,14 @@ special_insn:
|
||||||
ctxt->eflags |= X86_EFLAGS_IF;
|
ctxt->eflags |= X86_EFLAGS_IF;
|
||||||
c->dst.type = OP_NONE; /* Disable writeback. */
|
c->dst.type = OP_NONE; /* Disable writeback. */
|
||||||
break;
|
break;
|
||||||
|
case 0xfc: /* cld */
|
||||||
|
ctxt->eflags &= ~EFLG_DF;
|
||||||
|
c->dst.type = OP_NONE; /* Disable writeback. */
|
||||||
|
break;
|
||||||
|
case 0xfd: /* std */
|
||||||
|
ctxt->eflags |= EFLG_DF;
|
||||||
|
c->dst.type = OP_NONE; /* Disable writeback. */
|
||||||
|
break;
|
||||||
case 0xfe ... 0xff: /* Grp4/Grp5 */
|
case 0xfe ... 0xff: /* Grp4/Grp5 */
|
||||||
rc = emulate_grp45(ctxt, ops);
|
rc = emulate_grp45(ctxt, ops);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
|
@ -1768,7 +1826,7 @@ writeback:
|
||||||
|
|
||||||
/* Commit shadow register state. */
|
/* Commit shadow register state. */
|
||||||
memcpy(ctxt->vcpu->arch.regs, c->regs, sizeof c->regs);
|
memcpy(ctxt->vcpu->arch.regs, c->regs, sizeof c->regs);
|
||||||
ctxt->vcpu->arch.rip = c->eip;
|
kvm_rip_write(ctxt->vcpu, c->eip);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (rc == X86EMUL_UNHANDLEABLE) {
|
if (rc == X86EMUL_UNHANDLEABLE) {
|
||||||
|
@ -1793,7 +1851,7 @@ twobyte_insn:
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Let the processor re-execute the fixed hypercall */
|
/* Let the processor re-execute the fixed hypercall */
|
||||||
c->eip = ctxt->vcpu->arch.rip;
|
c->eip = kvm_rip_read(ctxt->vcpu);
|
||||||
/* Disable writeback. */
|
/* Disable writeback. */
|
||||||
c->dst.type = OP_NONE;
|
c->dst.type = OP_NONE;
|
||||||
break;
|
break;
|
||||||
|
@ -1889,7 +1947,7 @@ twobyte_insn:
|
||||||
rc = kvm_set_msr(ctxt->vcpu, c->regs[VCPU_REGS_RCX], msr_data);
|
rc = kvm_set_msr(ctxt->vcpu, c->regs[VCPU_REGS_RCX], msr_data);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kvm_inject_gp(ctxt->vcpu, 0);
|
kvm_inject_gp(ctxt->vcpu, 0);
|
||||||
c->eip = ctxt->vcpu->arch.rip;
|
c->eip = kvm_rip_read(ctxt->vcpu);
|
||||||
}
|
}
|
||||||
rc = X86EMUL_CONTINUE;
|
rc = X86EMUL_CONTINUE;
|
||||||
c->dst.type = OP_NONE;
|
c->dst.type = OP_NONE;
|
||||||
|
@ -1899,7 +1957,7 @@ twobyte_insn:
|
||||||
rc = kvm_get_msr(ctxt->vcpu, c->regs[VCPU_REGS_RCX], &msr_data);
|
rc = kvm_get_msr(ctxt->vcpu, c->regs[VCPU_REGS_RCX], &msr_data);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kvm_inject_gp(ctxt->vcpu, 0);
|
kvm_inject_gp(ctxt->vcpu, 0);
|
||||||
c->eip = ctxt->vcpu->arch.rip;
|
c->eip = kvm_rip_read(ctxt->vcpu);
|
||||||
} else {
|
} else {
|
||||||
c->regs[VCPU_REGS_RAX] = (u32)msr_data;
|
c->regs[VCPU_REGS_RAX] = (u32)msr_data;
|
||||||
c->regs[VCPU_REGS_RDX] = msr_data >> 32;
|
c->regs[VCPU_REGS_RDX] = msr_data >> 32;
|
||||||
|
|
|
@ -198,17 +198,10 @@ unsigned long long xen_sched_clock(void)
|
||||||
/* Get the TSC speed from Xen */
|
/* Get the TSC speed from Xen */
|
||||||
unsigned long xen_tsc_khz(void)
|
unsigned long xen_tsc_khz(void)
|
||||||
{
|
{
|
||||||
u64 xen_khz = 1000000ULL << 32;
|
struct pvclock_vcpu_time_info *info =
|
||||||
const struct pvclock_vcpu_time_info *info =
|
|
||||||
&HYPERVISOR_shared_info->vcpu_info[0].time;
|
&HYPERVISOR_shared_info->vcpu_info[0].time;
|
||||||
|
|
||||||
do_div(xen_khz, info->tsc_to_system_mul);
|
return pvclock_tsc_khz(info);
|
||||||
if (info->tsc_shift < 0)
|
|
||||||
xen_khz <<= -info->tsc_shift;
|
|
||||||
else
|
|
||||||
xen_khz >>= info->tsc_shift;
|
|
||||||
|
|
||||||
return xen_khz;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cycle_t xen_clocksource_read(void)
|
cycle_t xen_clocksource_read(void)
|
||||||
|
|
|
@ -28,9 +28,9 @@
|
||||||
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/dmar.h>
|
#include <linux/dmar.h>
|
||||||
|
#include <linux/iova.h>
|
||||||
|
#include <linux/intel-iommu.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include "iova.h"
|
|
||||||
#include "intel-iommu.h"
|
|
||||||
|
|
||||||
#undef PREFIX
|
#undef PREFIX
|
||||||
#define PREFIX "DMAR:"
|
#define PREFIX "DMAR:"
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/mempool.h>
|
#include <linux/mempool.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include "iova.h"
|
#include <linux/iova.h>
|
||||||
#include "intel-iommu.h"
|
#include <linux/intel-iommu.h>
|
||||||
#include <asm/proto.h> /* force_iommu in this header in x86-64*/
|
#include <asm/proto.h> /* force_iommu in this header in x86-64*/
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <asm/iommu.h>
|
#include <asm/iommu.h>
|
||||||
|
@ -156,7 +156,7 @@ static inline void *alloc_domain_mem(void)
|
||||||
return iommu_kmem_cache_alloc(iommu_domain_cache);
|
return iommu_kmem_cache_alloc(iommu_domain_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void free_domain_mem(void *vaddr)
|
static void free_domain_mem(void *vaddr)
|
||||||
{
|
{
|
||||||
kmem_cache_free(iommu_domain_cache, vaddr);
|
kmem_cache_free(iommu_domain_cache, vaddr);
|
||||||
}
|
}
|
||||||
|
@ -1341,7 +1341,7 @@ static void domain_remove_dev_info(struct dmar_domain *domain)
|
||||||
* find_domain
|
* find_domain
|
||||||
* Note: we use struct pci_dev->dev.archdata.iommu stores the info
|
* Note: we use struct pci_dev->dev.archdata.iommu stores the info
|
||||||
*/
|
*/
|
||||||
struct dmar_domain *
|
static struct dmar_domain *
|
||||||
find_domain(struct pci_dev *pdev)
|
find_domain(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct device_domain_info *info;
|
struct device_domain_info *info;
|
||||||
|
@ -2318,3 +2318,111 @@ int __init intel_iommu_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void intel_iommu_domain_exit(struct dmar_domain *domain)
|
||||||
|
{
|
||||||
|
u64 end;
|
||||||
|
|
||||||
|
/* Domain 0 is reserved, so dont process it */
|
||||||
|
if (!domain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
end = DOMAIN_MAX_ADDR(domain->gaw);
|
||||||
|
end = end & (~PAGE_MASK_4K);
|
||||||
|
|
||||||
|
/* clear ptes */
|
||||||
|
dma_pte_clear_range(domain, 0, end);
|
||||||
|
|
||||||
|
/* free page tables */
|
||||||
|
dma_pte_free_pagetable(domain, 0, end);
|
||||||
|
|
||||||
|
iommu_free_domain(domain);
|
||||||
|
free_domain_mem(domain);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_domain_exit);
|
||||||
|
|
||||||
|
struct dmar_domain *intel_iommu_domain_alloc(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct dmar_drhd_unit *drhd;
|
||||||
|
struct dmar_domain *domain;
|
||||||
|
struct intel_iommu *iommu;
|
||||||
|
|
||||||
|
drhd = dmar_find_matched_drhd_unit(pdev);
|
||||||
|
if (!drhd) {
|
||||||
|
printk(KERN_ERR "intel_iommu_domain_alloc: drhd == NULL\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iommu = drhd->iommu;
|
||||||
|
if (!iommu) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"intel_iommu_domain_alloc: iommu == NULL\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
domain = iommu_alloc_domain(iommu);
|
||||||
|
if (!domain) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"intel_iommu_domain_alloc: domain == NULL\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (domain_init(domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"intel_iommu_domain_alloc: domain_init() failed\n");
|
||||||
|
intel_iommu_domain_exit(domain);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_domain_alloc);
|
||||||
|
|
||||||
|
int intel_iommu_context_mapping(
|
||||||
|
struct dmar_domain *domain, struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
rc = domain_context_mapping(domain, pdev);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_context_mapping);
|
||||||
|
|
||||||
|
int intel_iommu_page_mapping(
|
||||||
|
struct dmar_domain *domain, dma_addr_t iova,
|
||||||
|
u64 hpa, size_t size, int prot)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
rc = domain_page_mapping(domain, iova, hpa, size, prot);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_page_mapping);
|
||||||
|
|
||||||
|
void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn)
|
||||||
|
{
|
||||||
|
detach_domain_for_dev(domain, bus, devfn);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_detach_dev);
|
||||||
|
|
||||||
|
struct dmar_domain *
|
||||||
|
intel_iommu_find_domain(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
return find_domain(pdev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_find_domain);
|
||||||
|
|
||||||
|
int intel_iommu_found(void)
|
||||||
|
{
|
||||||
|
return g_num_of_iommus;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_found);
|
||||||
|
|
||||||
|
u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova)
|
||||||
|
{
|
||||||
|
struct dma_pte *pte;
|
||||||
|
u64 pfn;
|
||||||
|
|
||||||
|
pfn = 0;
|
||||||
|
pte = addr_to_dma_pte(domain, iova);
|
||||||
|
|
||||||
|
if (pte)
|
||||||
|
pfn = dma_pte_addr(*pte);
|
||||||
|
|
||||||
|
return pfn >> PAGE_SHIFT_4K;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(intel_iommu_iova_to_pfn);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <asm/io_apic.h>
|
#include <asm/io_apic.h>
|
||||||
#include "intel-iommu.h"
|
#include <linux/intel-iommu.h>
|
||||||
#include "intr_remapping.h"
|
#include "intr_remapping.h"
|
||||||
|
|
||||||
static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
|
static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "intel-iommu.h"
|
#include <linux/intel-iommu.h>
|
||||||
|
|
||||||
struct ioapic_scope {
|
struct ioapic_scope {
|
||||||
struct intel_iommu *iommu;
|
struct intel_iommu *iommu;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
|
* Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "iova.h"
|
#include <linux/iova.h>
|
||||||
|
|
||||||
void
|
void
|
||||||
init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit)
|
init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit)
|
||||||
|
|
|
@ -208,26 +208,4 @@ struct kvm_pit_channel_state {
|
||||||
struct kvm_pit_state {
|
struct kvm_pit_state {
|
||||||
struct kvm_pit_channel_state channels[3];
|
struct kvm_pit_channel_state channels[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02)
|
|
||||||
#define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03)
|
|
||||||
#define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04)
|
|
||||||
#define KVM_TRC_IO_READ (KVM_TRC_HANDLER + 0x05)
|
|
||||||
#define KVM_TRC_IO_WRITE (KVM_TRC_HANDLER + 0x06)
|
|
||||||
#define KVM_TRC_CR_READ (KVM_TRC_HANDLER + 0x07)
|
|
||||||
#define KVM_TRC_CR_WRITE (KVM_TRC_HANDLER + 0x08)
|
|
||||||
#define KVM_TRC_DR_READ (KVM_TRC_HANDLER + 0x09)
|
|
||||||
#define KVM_TRC_DR_WRITE (KVM_TRC_HANDLER + 0x0A)
|
|
||||||
#define KVM_TRC_MSR_READ (KVM_TRC_HANDLER + 0x0B)
|
|
||||||
#define KVM_TRC_MSR_WRITE (KVM_TRC_HANDLER + 0x0C)
|
|
||||||
#define KVM_TRC_CPUID (KVM_TRC_HANDLER + 0x0D)
|
|
||||||
#define KVM_TRC_INTR (KVM_TRC_HANDLER + 0x0E)
|
|
||||||
#define KVM_TRC_NMI (KVM_TRC_HANDLER + 0x0F)
|
|
||||||
#define KVM_TRC_VMMCALL (KVM_TRC_HANDLER + 0x10)
|
|
||||||
#define KVM_TRC_HLT (KVM_TRC_HANDLER + 0x11)
|
|
||||||
#define KVM_TRC_CLTS (KVM_TRC_HANDLER + 0x12)
|
|
||||||
#define KVM_TRC_LMSW (KVM_TRC_HANDLER + 0x13)
|
|
||||||
#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14)
|
|
||||||
#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15)
|
|
||||||
|
|
||||||
#endif /* ASM_X86__KVM_H */
|
#endif /* ASM_X86__KVM_H */
|
||||||
|
|
|
@ -57,6 +57,10 @@
|
||||||
#define KVM_PAGES_PER_HPAGE (KVM_HPAGE_SIZE / PAGE_SIZE)
|
#define KVM_PAGES_PER_HPAGE (KVM_HPAGE_SIZE / PAGE_SIZE)
|
||||||
|
|
||||||
#define DE_VECTOR 0
|
#define DE_VECTOR 0
|
||||||
|
#define DB_VECTOR 1
|
||||||
|
#define BP_VECTOR 3
|
||||||
|
#define OF_VECTOR 4
|
||||||
|
#define BR_VECTOR 5
|
||||||
#define UD_VECTOR 6
|
#define UD_VECTOR 6
|
||||||
#define NM_VECTOR 7
|
#define NM_VECTOR 7
|
||||||
#define DF_VECTOR 8
|
#define DF_VECTOR 8
|
||||||
|
@ -65,6 +69,7 @@
|
||||||
#define SS_VECTOR 12
|
#define SS_VECTOR 12
|
||||||
#define GP_VECTOR 13
|
#define GP_VECTOR 13
|
||||||
#define PF_VECTOR 14
|
#define PF_VECTOR 14
|
||||||
|
#define MF_VECTOR 16
|
||||||
#define MC_VECTOR 18
|
#define MC_VECTOR 18
|
||||||
|
|
||||||
#define SELECTOR_TI_MASK (1 << 2)
|
#define SELECTOR_TI_MASK (1 << 2)
|
||||||
|
@ -89,7 +94,7 @@ extern struct list_head vm_list;
|
||||||
struct kvm_vcpu;
|
struct kvm_vcpu;
|
||||||
struct kvm;
|
struct kvm;
|
||||||
|
|
||||||
enum {
|
enum kvm_reg {
|
||||||
VCPU_REGS_RAX = 0,
|
VCPU_REGS_RAX = 0,
|
||||||
VCPU_REGS_RCX = 1,
|
VCPU_REGS_RCX = 1,
|
||||||
VCPU_REGS_RDX = 2,
|
VCPU_REGS_RDX = 2,
|
||||||
|
@ -108,6 +113,7 @@ enum {
|
||||||
VCPU_REGS_R14 = 14,
|
VCPU_REGS_R14 = 14,
|
||||||
VCPU_REGS_R15 = 15,
|
VCPU_REGS_R15 = 15,
|
||||||
#endif
|
#endif
|
||||||
|
VCPU_REGS_RIP,
|
||||||
NR_VCPU_REGS
|
NR_VCPU_REGS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,10 +195,20 @@ struct kvm_mmu_page {
|
||||||
*/
|
*/
|
||||||
int multimapped; /* More than one parent_pte? */
|
int multimapped; /* More than one parent_pte? */
|
||||||
int root_count; /* Currently serving as active root */
|
int root_count; /* Currently serving as active root */
|
||||||
|
bool unsync;
|
||||||
|
bool unsync_children;
|
||||||
union {
|
union {
|
||||||
u64 *parent_pte; /* !multimapped */
|
u64 *parent_pte; /* !multimapped */
|
||||||
struct hlist_head parent_ptes; /* multimapped, kvm_pte_chain */
|
struct hlist_head parent_ptes; /* multimapped, kvm_pte_chain */
|
||||||
};
|
};
|
||||||
|
DECLARE_BITMAP(unsync_child_bitmap, 512);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kvm_pv_mmu_op_buffer {
|
||||||
|
void *ptr;
|
||||||
|
unsigned len;
|
||||||
|
unsigned processed;
|
||||||
|
char buf[512] __aligned(sizeof(long));
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -207,6 +223,9 @@ struct kvm_mmu {
|
||||||
gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva);
|
gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva);
|
||||||
void (*prefetch_page)(struct kvm_vcpu *vcpu,
|
void (*prefetch_page)(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_mmu_page *page);
|
struct kvm_mmu_page *page);
|
||||||
|
int (*sync_page)(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_mmu_page *sp);
|
||||||
|
void (*invlpg)(struct kvm_vcpu *vcpu, gva_t gva);
|
||||||
hpa_t root_hpa;
|
hpa_t root_hpa;
|
||||||
int root_level;
|
int root_level;
|
||||||
int shadow_root_level;
|
int shadow_root_level;
|
||||||
|
@ -219,8 +238,13 @@ struct kvm_vcpu_arch {
|
||||||
int interrupt_window_open;
|
int interrupt_window_open;
|
||||||
unsigned long irq_summary; /* bit vector: 1 per word in irq_pending */
|
unsigned long irq_summary; /* bit vector: 1 per word in irq_pending */
|
||||||
DECLARE_BITMAP(irq_pending, KVM_NR_INTERRUPTS);
|
DECLARE_BITMAP(irq_pending, KVM_NR_INTERRUPTS);
|
||||||
unsigned long regs[NR_VCPU_REGS]; /* for rsp: vcpu_load_rsp_rip() */
|
/*
|
||||||
unsigned long rip; /* needs vcpu_load_rsp_rip() */
|
* rip and regs accesses must go through
|
||||||
|
* kvm_{register,rip}_{read,write} functions.
|
||||||
|
*/
|
||||||
|
unsigned long regs[NR_VCPU_REGS];
|
||||||
|
u32 regs_avail;
|
||||||
|
u32 regs_dirty;
|
||||||
|
|
||||||
unsigned long cr0;
|
unsigned long cr0;
|
||||||
unsigned long cr2;
|
unsigned long cr2;
|
||||||
|
@ -237,6 +261,9 @@ struct kvm_vcpu_arch {
|
||||||
bool tpr_access_reporting;
|
bool tpr_access_reporting;
|
||||||
|
|
||||||
struct kvm_mmu mmu;
|
struct kvm_mmu mmu;
|
||||||
|
/* only needed in kvm_pv_mmu_op() path, but it's hot so
|
||||||
|
* put it here to avoid allocation */
|
||||||
|
struct kvm_pv_mmu_op_buffer mmu_op_buffer;
|
||||||
|
|
||||||
struct kvm_mmu_memory_cache mmu_pte_chain_cache;
|
struct kvm_mmu_memory_cache mmu_pte_chain_cache;
|
||||||
struct kvm_mmu_memory_cache mmu_rmap_desc_cache;
|
struct kvm_mmu_memory_cache mmu_rmap_desc_cache;
|
||||||
|
@ -269,6 +296,11 @@ struct kvm_vcpu_arch {
|
||||||
u32 error_code;
|
u32 error_code;
|
||||||
} exception;
|
} exception;
|
||||||
|
|
||||||
|
struct kvm_queued_interrupt {
|
||||||
|
bool pending;
|
||||||
|
u8 nr;
|
||||||
|
} interrupt;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int active;
|
int active;
|
||||||
u8 save_iopl;
|
u8 save_iopl;
|
||||||
|
@ -294,6 +326,7 @@ struct kvm_vcpu_arch {
|
||||||
struct page *time_page;
|
struct page *time_page;
|
||||||
|
|
||||||
bool nmi_pending;
|
bool nmi_pending;
|
||||||
|
bool nmi_injected;
|
||||||
|
|
||||||
u64 mtrr[0x100];
|
u64 mtrr[0x100];
|
||||||
};
|
};
|
||||||
|
@ -316,9 +349,12 @@ struct kvm_arch{
|
||||||
* Hash table of struct kvm_mmu_page.
|
* Hash table of struct kvm_mmu_page.
|
||||||
*/
|
*/
|
||||||
struct list_head active_mmu_pages;
|
struct list_head active_mmu_pages;
|
||||||
|
struct list_head assigned_dev_head;
|
||||||
|
struct dmar_domain *intel_iommu_domain;
|
||||||
struct kvm_pic *vpic;
|
struct kvm_pic *vpic;
|
||||||
struct kvm_ioapic *vioapic;
|
struct kvm_ioapic *vioapic;
|
||||||
struct kvm_pit *vpit;
|
struct kvm_pit *vpit;
|
||||||
|
struct hlist_head irq_ack_notifier_list;
|
||||||
|
|
||||||
int round_robin_prev_vcpu;
|
int round_robin_prev_vcpu;
|
||||||
unsigned int tss_addr;
|
unsigned int tss_addr;
|
||||||
|
@ -338,6 +374,7 @@ struct kvm_vm_stat {
|
||||||
u32 mmu_flooded;
|
u32 mmu_flooded;
|
||||||
u32 mmu_recycled;
|
u32 mmu_recycled;
|
||||||
u32 mmu_cache_miss;
|
u32 mmu_cache_miss;
|
||||||
|
u32 mmu_unsync;
|
||||||
u32 remote_tlb_flush;
|
u32 remote_tlb_flush;
|
||||||
u32 lpages;
|
u32 lpages;
|
||||||
};
|
};
|
||||||
|
@ -364,6 +401,7 @@ struct kvm_vcpu_stat {
|
||||||
u32 insn_emulation;
|
u32 insn_emulation;
|
||||||
u32 insn_emulation_fail;
|
u32 insn_emulation_fail;
|
||||||
u32 hypercalls;
|
u32 hypercalls;
|
||||||
|
u32 irq_injections;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct descriptor_table {
|
struct descriptor_table {
|
||||||
|
@ -414,8 +452,7 @@ struct kvm_x86_ops {
|
||||||
unsigned long (*get_dr)(struct kvm_vcpu *vcpu, int dr);
|
unsigned long (*get_dr)(struct kvm_vcpu *vcpu, int dr);
|
||||||
void (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value,
|
void (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value,
|
||||||
int *exception);
|
int *exception);
|
||||||
void (*cache_regs)(struct kvm_vcpu *vcpu);
|
void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg);
|
||||||
void (*decache_regs)(struct kvm_vcpu *vcpu);
|
|
||||||
unsigned long (*get_rflags)(struct kvm_vcpu *vcpu);
|
unsigned long (*get_rflags)(struct kvm_vcpu *vcpu);
|
||||||
void (*set_rflags)(struct kvm_vcpu *vcpu, unsigned long rflags);
|
void (*set_rflags)(struct kvm_vcpu *vcpu, unsigned long rflags);
|
||||||
|
|
||||||
|
@ -528,6 +565,8 @@ void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
|
||||||
void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long cr2,
|
void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long cr2,
|
||||||
u32 error_code);
|
u32 error_code);
|
||||||
|
|
||||||
|
void kvm_pic_set_irq(void *opaque, int irq, int level);
|
||||||
|
|
||||||
void kvm_inject_nmi(struct kvm_vcpu *vcpu);
|
void kvm_inject_nmi(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
void fx_init(struct kvm_vcpu *vcpu);
|
void fx_init(struct kvm_vcpu *vcpu);
|
||||||
|
@ -550,12 +589,14 @@ int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva);
|
||||||
void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu);
|
void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu);
|
||||||
int kvm_mmu_load(struct kvm_vcpu *vcpu);
|
int kvm_mmu_load(struct kvm_vcpu *vcpu);
|
||||||
void kvm_mmu_unload(struct kvm_vcpu *vcpu);
|
void kvm_mmu_unload(struct kvm_vcpu *vcpu);
|
||||||
|
void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
|
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
int kvm_fix_hypercall(struct kvm_vcpu *vcpu);
|
int kvm_fix_hypercall(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code);
|
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code);
|
||||||
|
void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva);
|
||||||
|
|
||||||
void kvm_enable_tdp(void);
|
void kvm_enable_tdp(void);
|
||||||
void kvm_disable_tdp(void);
|
void kvm_disable_tdp(void);
|
||||||
|
@ -686,33 +727,6 @@ enum {
|
||||||
TASK_SWITCH_GATE = 3,
|
TASK_SWITCH_GATE = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KVMTRACE_5D(evt, vcpu, d1, d2, d3, d4, d5, name) \
|
|
||||||
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
|
||||||
vcpu, 5, d1, d2, d3, d4, d5)
|
|
||||||
#define KVMTRACE_4D(evt, vcpu, d1, d2, d3, d4, name) \
|
|
||||||
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
|
||||||
vcpu, 4, d1, d2, d3, d4, 0)
|
|
||||||
#define KVMTRACE_3D(evt, vcpu, d1, d2, d3, name) \
|
|
||||||
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
|
||||||
vcpu, 3, d1, d2, d3, 0, 0)
|
|
||||||
#define KVMTRACE_2D(evt, vcpu, d1, d2, name) \
|
|
||||||
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
|
||||||
vcpu, 2, d1, d2, 0, 0, 0)
|
|
||||||
#define KVMTRACE_1D(evt, vcpu, d1, name) \
|
|
||||||
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
|
||||||
vcpu, 1, d1, 0, 0, 0, 0)
|
|
||||||
#define KVMTRACE_0D(evt, vcpu, name) \
|
|
||||||
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
|
||||||
vcpu, 0, 0, 0, 0, 0, 0)
|
|
||||||
|
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
# define KVM_EX_ENTRY ".quad"
|
|
||||||
# define KVM_EX_PUSH "pushq"
|
|
||||||
#else
|
|
||||||
# define KVM_EX_ENTRY ".long"
|
|
||||||
# define KVM_EX_PUSH "pushl"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hardware virtualization extension instructions may fault if a
|
* Hardware virtualization extension instructions may fault if a
|
||||||
* reboot turns off virtualization while processes are running.
|
* reboot turns off virtualization while processes are running.
|
||||||
|
@ -724,11 +738,11 @@ asmlinkage void kvm_handle_fault_on_reboot(void);
|
||||||
"666: " insn "\n\t" \
|
"666: " insn "\n\t" \
|
||||||
".pushsection .fixup, \"ax\" \n" \
|
".pushsection .fixup, \"ax\" \n" \
|
||||||
"667: \n\t" \
|
"667: \n\t" \
|
||||||
KVM_EX_PUSH " $666b \n\t" \
|
__ASM_SIZE(push) " $666b \n\t" \
|
||||||
"jmp kvm_handle_fault_on_reboot \n\t" \
|
"jmp kvm_handle_fault_on_reboot \n\t" \
|
||||||
".popsection \n\t" \
|
".popsection \n\t" \
|
||||||
".pushsection __ex_table, \"a\" \n\t" \
|
".pushsection __ex_table, \"a\" \n\t" \
|
||||||
KVM_EX_ENTRY " 666b, 667b \n\t" \
|
_ASM_PTR " 666b, 667b \n\t" \
|
||||||
".popsection"
|
".popsection"
|
||||||
|
|
||||||
#define KVM_ARCH_WANT_MMU_NOTIFIER
|
#define KVM_ARCH_WANT_MMU_NOTIFIER
|
||||||
|
|
|
@ -178,6 +178,9 @@
|
||||||
#define MSR_IA32_EBL_CR_POWERON 0x0000002a
|
#define MSR_IA32_EBL_CR_POWERON 0x0000002a
|
||||||
#define MSR_IA32_FEATURE_CONTROL 0x0000003a
|
#define MSR_IA32_FEATURE_CONTROL 0x0000003a
|
||||||
|
|
||||||
|
#define FEATURE_CONTROL_LOCKED (1<<0)
|
||||||
|
#define FEATURE_CONTROL_VMXON_ENABLED (1<<2)
|
||||||
|
|
||||||
#define MSR_IA32_APICBASE 0x0000001b
|
#define MSR_IA32_APICBASE 0x0000001b
|
||||||
#define MSR_IA32_APICBASE_BSP (1<<8)
|
#define MSR_IA32_APICBASE_BSP (1<<8)
|
||||||
#define MSR_IA32_APICBASE_ENABLE (1<<11)
|
#define MSR_IA32_APICBASE_ENABLE (1<<11)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
/* some helper functions for xen and kvm pv clock sources */
|
/* some helper functions for xen and kvm pv clock sources */
|
||||||
cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src);
|
cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src);
|
||||||
|
unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src);
|
||||||
void pvclock_read_wallclock(struct pvclock_wall_clock *wall,
|
void pvclock_read_wallclock(struct pvclock_wall_clock *wall,
|
||||||
struct pvclock_vcpu_time_info *vcpu,
|
struct pvclock_vcpu_time_info *vcpu,
|
||||||
struct timespec *ts);
|
struct timespec *ts);
|
||||||
|
|
|
@ -25,10 +25,10 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/msi.h>
|
#include <linux/msi.h>
|
||||||
#include <linux/sysdev.h>
|
#include <linux/sysdev.h>
|
||||||
#include "iova.h"
|
#include <linux/iova.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/dma_remapping.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include "dma_remapping.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Intel IOMMU register specification per version 1.0 public spec.
|
* Intel IOMMU register specification per version 1.0 public spec.
|
||||||
|
@ -304,4 +304,24 @@ extern int dmar_enable_qi(struct intel_iommu *iommu);
|
||||||
extern void qi_global_iec(struct intel_iommu *iommu);
|
extern void qi_global_iec(struct intel_iommu *iommu);
|
||||||
|
|
||||||
extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
|
extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
|
||||||
|
|
||||||
|
void intel_iommu_domain_exit(struct dmar_domain *domain);
|
||||||
|
struct dmar_domain *intel_iommu_domain_alloc(struct pci_dev *pdev);
|
||||||
|
int intel_iommu_context_mapping(struct dmar_domain *domain,
|
||||||
|
struct pci_dev *pdev);
|
||||||
|
int intel_iommu_page_mapping(struct dmar_domain *domain, dma_addr_t iova,
|
||||||
|
u64 hpa, size_t size, int prot);
|
||||||
|
void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn);
|
||||||
|
struct dmar_domain *intel_iommu_find_domain(struct pci_dev *pdev);
|
||||||
|
u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DMAR
|
||||||
|
int intel_iommu_found(void);
|
||||||
|
#else /* CONFIG_DMAR */
|
||||||
|
static inline int intel_iommu_found(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_DMAR */
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -311,22 +311,33 @@ struct kvm_s390_interrupt {
|
||||||
|
|
||||||
/* This structure represents a single trace buffer record. */
|
/* This structure represents a single trace buffer record. */
|
||||||
struct kvm_trace_rec {
|
struct kvm_trace_rec {
|
||||||
__u32 event:28;
|
/* variable rec_val
|
||||||
__u32 extra_u32:3;
|
* is split into:
|
||||||
__u32 cycle_in:1;
|
* bits 0 - 27 -> event id
|
||||||
|
* bits 28 -30 -> number of extra data args of size u32
|
||||||
|
* bits 31 -> binary indicator for if tsc is in record
|
||||||
|
*/
|
||||||
|
__u32 rec_val;
|
||||||
__u32 pid;
|
__u32 pid;
|
||||||
__u32 vcpu_id;
|
__u32 vcpu_id;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
__u64 cycle_u64;
|
__u64 timestamp;
|
||||||
__u32 extra_u32[KVM_TRC_EXTRA_MAX];
|
__u32 extra_u32[KVM_TRC_EXTRA_MAX];
|
||||||
} __attribute__((packed)) cycle;
|
} __attribute__((packed)) timestamp;
|
||||||
struct {
|
struct {
|
||||||
__u32 extra_u32[KVM_TRC_EXTRA_MAX];
|
__u32 extra_u32[KVM_TRC_EXTRA_MAX];
|
||||||
} nocycle;
|
} notimestamp;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define TRACE_REC_EVENT_ID(val) \
|
||||||
|
(0x0fffffff & (val))
|
||||||
|
#define TRACE_REC_NUM_DATA_ARGS(val) \
|
||||||
|
(0x70000000 & ((val) << 28))
|
||||||
|
#define TRACE_REC_TCS(val) \
|
||||||
|
(0x80000000 & ((val) << 31))
|
||||||
|
|
||||||
#define KVMIO 0xAE
|
#define KVMIO 0xAE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -372,6 +383,10 @@ struct kvm_trace_rec {
|
||||||
#define KVM_CAP_MP_STATE 14
|
#define KVM_CAP_MP_STATE 14
|
||||||
#define KVM_CAP_COALESCED_MMIO 15
|
#define KVM_CAP_COALESCED_MMIO 15
|
||||||
#define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */
|
#define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */
|
||||||
|
#if defined(CONFIG_X86)||defined(CONFIG_IA64)
|
||||||
|
#define KVM_CAP_DEVICE_ASSIGNMENT 17
|
||||||
|
#endif
|
||||||
|
#define KVM_CAP_IOMMU 18
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ioctls for VM fds
|
* ioctls for VM fds
|
||||||
|
@ -401,6 +416,10 @@ struct kvm_trace_rec {
|
||||||
_IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone)
|
_IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone)
|
||||||
#define KVM_UNREGISTER_COALESCED_MMIO \
|
#define KVM_UNREGISTER_COALESCED_MMIO \
|
||||||
_IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone)
|
_IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone)
|
||||||
|
#define KVM_ASSIGN_PCI_DEVICE _IOR(KVMIO, 0x69, \
|
||||||
|
struct kvm_assigned_pci_dev)
|
||||||
|
#define KVM_ASSIGN_IRQ _IOR(KVMIO, 0x70, \
|
||||||
|
struct kvm_assigned_irq)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ioctls for vcpu fds
|
* ioctls for vcpu fds
|
||||||
|
@ -440,4 +459,45 @@ struct kvm_trace_rec {
|
||||||
#define KVM_GET_MP_STATE _IOR(KVMIO, 0x98, struct kvm_mp_state)
|
#define KVM_GET_MP_STATE _IOR(KVMIO, 0x98, struct kvm_mp_state)
|
||||||
#define KVM_SET_MP_STATE _IOW(KVMIO, 0x99, struct kvm_mp_state)
|
#define KVM_SET_MP_STATE _IOW(KVMIO, 0x99, struct kvm_mp_state)
|
||||||
|
|
||||||
|
#define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02)
|
||||||
|
#define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03)
|
||||||
|
#define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04)
|
||||||
|
#define KVM_TRC_IO_READ (KVM_TRC_HANDLER + 0x05)
|
||||||
|
#define KVM_TRC_IO_WRITE (KVM_TRC_HANDLER + 0x06)
|
||||||
|
#define KVM_TRC_CR_READ (KVM_TRC_HANDLER + 0x07)
|
||||||
|
#define KVM_TRC_CR_WRITE (KVM_TRC_HANDLER + 0x08)
|
||||||
|
#define KVM_TRC_DR_READ (KVM_TRC_HANDLER + 0x09)
|
||||||
|
#define KVM_TRC_DR_WRITE (KVM_TRC_HANDLER + 0x0A)
|
||||||
|
#define KVM_TRC_MSR_READ (KVM_TRC_HANDLER + 0x0B)
|
||||||
|
#define KVM_TRC_MSR_WRITE (KVM_TRC_HANDLER + 0x0C)
|
||||||
|
#define KVM_TRC_CPUID (KVM_TRC_HANDLER + 0x0D)
|
||||||
|
#define KVM_TRC_INTR (KVM_TRC_HANDLER + 0x0E)
|
||||||
|
#define KVM_TRC_NMI (KVM_TRC_HANDLER + 0x0F)
|
||||||
|
#define KVM_TRC_VMMCALL (KVM_TRC_HANDLER + 0x10)
|
||||||
|
#define KVM_TRC_HLT (KVM_TRC_HANDLER + 0x11)
|
||||||
|
#define KVM_TRC_CLTS (KVM_TRC_HANDLER + 0x12)
|
||||||
|
#define KVM_TRC_LMSW (KVM_TRC_HANDLER + 0x13)
|
||||||
|
#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14)
|
||||||
|
#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15)
|
||||||
|
#define KVM_TRC_GTLB_WRITE (KVM_TRC_HANDLER + 0x16)
|
||||||
|
#define KVM_TRC_STLB_WRITE (KVM_TRC_HANDLER + 0x17)
|
||||||
|
#define KVM_TRC_STLB_INVAL (KVM_TRC_HANDLER + 0x18)
|
||||||
|
#define KVM_TRC_PPC_INSTR (KVM_TRC_HANDLER + 0x19)
|
||||||
|
|
||||||
|
struct kvm_assigned_pci_dev {
|
||||||
|
__u32 assigned_dev_id;
|
||||||
|
__u32 busnr;
|
||||||
|
__u32 devfn;
|
||||||
|
__u32 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kvm_assigned_irq {
|
||||||
|
__u32 assigned_dev_id;
|
||||||
|
__u32 host_irq;
|
||||||
|
__u32 guest_irq;
|
||||||
|
__u32 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#define KVM_REQ_MMU_RELOAD 3
|
#define KVM_REQ_MMU_RELOAD 3
|
||||||
#define KVM_REQ_TRIPLE_FAULT 4
|
#define KVM_REQ_TRIPLE_FAULT 4
|
||||||
#define KVM_REQ_PENDING_TIMER 5
|
#define KVM_REQ_PENDING_TIMER 5
|
||||||
|
#define KVM_REQ_UNHALT 6
|
||||||
|
#define KVM_REQ_MMU_SYNC 7
|
||||||
|
|
||||||
struct kvm_vcpu;
|
struct kvm_vcpu;
|
||||||
extern struct kmem_cache *kvm_vcpu_cache;
|
extern struct kmem_cache *kvm_vcpu_cache;
|
||||||
|
@ -279,12 +281,68 @@ void kvm_free_physmem(struct kvm *kvm);
|
||||||
|
|
||||||
struct kvm *kvm_arch_create_vm(void);
|
struct kvm *kvm_arch_create_vm(void);
|
||||||
void kvm_arch_destroy_vm(struct kvm *kvm);
|
void kvm_arch_destroy_vm(struct kvm *kvm);
|
||||||
|
void kvm_free_all_assigned_devices(struct kvm *kvm);
|
||||||
|
|
||||||
int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
|
int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
|
||||||
int kvm_cpu_has_interrupt(struct kvm_vcpu *v);
|
int kvm_cpu_has_interrupt(struct kvm_vcpu *v);
|
||||||
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu);
|
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu);
|
||||||
void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
|
void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
|
int kvm_is_mmio_pfn(pfn_t pfn);
|
||||||
|
|
||||||
|
struct kvm_irq_ack_notifier {
|
||||||
|
struct hlist_node link;
|
||||||
|
unsigned gsi;
|
||||||
|
void (*irq_acked)(struct kvm_irq_ack_notifier *kian);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kvm_assigned_dev_kernel {
|
||||||
|
struct kvm_irq_ack_notifier ack_notifier;
|
||||||
|
struct work_struct interrupt_work;
|
||||||
|
struct list_head list;
|
||||||
|
int assigned_dev_id;
|
||||||
|
int host_busnr;
|
||||||
|
int host_devfn;
|
||||||
|
int host_irq;
|
||||||
|
int guest_irq;
|
||||||
|
int irq_requested;
|
||||||
|
struct pci_dev *dev;
|
||||||
|
struct kvm *kvm;
|
||||||
|
};
|
||||||
|
void kvm_set_irq(struct kvm *kvm, int irq, int level);
|
||||||
|
void kvm_notify_acked_irq(struct kvm *kvm, unsigned gsi);
|
||||||
|
void kvm_register_irq_ack_notifier(struct kvm *kvm,
|
||||||
|
struct kvm_irq_ack_notifier *kian);
|
||||||
|
void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
|
||||||
|
struct kvm_irq_ack_notifier *kian);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DMAR
|
||||||
|
int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn,
|
||||||
|
unsigned long npages);
|
||||||
|
int kvm_iommu_map_guest(struct kvm *kvm,
|
||||||
|
struct kvm_assigned_dev_kernel *assigned_dev);
|
||||||
|
int kvm_iommu_unmap_guest(struct kvm *kvm);
|
||||||
|
#else /* CONFIG_DMAR */
|
||||||
|
static inline int kvm_iommu_map_pages(struct kvm *kvm,
|
||||||
|
gfn_t base_gfn,
|
||||||
|
unsigned long npages)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int kvm_iommu_map_guest(struct kvm *kvm,
|
||||||
|
struct kvm_assigned_dev_kernel
|
||||||
|
*assigned_dev)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int kvm_iommu_unmap_guest(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_DMAR */
|
||||||
|
|
||||||
static inline void kvm_guest_enter(void)
|
static inline void kvm_guest_enter(void)
|
||||||
{
|
{
|
||||||
account_system_vtime(current);
|
account_system_vtime(current);
|
||||||
|
@ -307,6 +365,11 @@ static inline gpa_t gfn_to_gpa(gfn_t gfn)
|
||||||
return (gpa_t)gfn << PAGE_SHIFT;
|
return (gpa_t)gfn << PAGE_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline hpa_t pfn_to_hpa(pfn_t pfn)
|
||||||
|
{
|
||||||
|
return (hpa_t)pfn << PAGE_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu)
|
static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests);
|
set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests);
|
||||||
|
@ -326,6 +389,25 @@ struct kvm_stats_debugfs_item {
|
||||||
extern struct kvm_stats_debugfs_item debugfs_entries[];
|
extern struct kvm_stats_debugfs_item debugfs_entries[];
|
||||||
extern struct dentry *kvm_debugfs_dir;
|
extern struct dentry *kvm_debugfs_dir;
|
||||||
|
|
||||||
|
#define KVMTRACE_5D(evt, vcpu, d1, d2, d3, d4, d5, name) \
|
||||||
|
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
||||||
|
vcpu, 5, d1, d2, d3, d4, d5)
|
||||||
|
#define KVMTRACE_4D(evt, vcpu, d1, d2, d3, d4, name) \
|
||||||
|
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
||||||
|
vcpu, 4, d1, d2, d3, d4, 0)
|
||||||
|
#define KVMTRACE_3D(evt, vcpu, d1, d2, d3, name) \
|
||||||
|
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
||||||
|
vcpu, 3, d1, d2, d3, 0, 0)
|
||||||
|
#define KVMTRACE_2D(evt, vcpu, d1, d2, name) \
|
||||||
|
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
||||||
|
vcpu, 2, d1, d2, 0, 0, 0)
|
||||||
|
#define KVMTRACE_1D(evt, vcpu, d1, name) \
|
||||||
|
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
||||||
|
vcpu, 1, d1, 0, 0, 0, 0)
|
||||||
|
#define KVMTRACE_0D(evt, vcpu, name) \
|
||||||
|
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
|
||||||
|
vcpu, 0, 0, 0, 0, 0, 0)
|
||||||
|
|
||||||
#ifdef CONFIG_KVM_TRACE
|
#ifdef CONFIG_KVM_TRACE
|
||||||
int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg);
|
int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg);
|
||||||
void kvm_trace_cleanup(void);
|
void kvm_trace_cleanup(void);
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
|
|
||||||
#include "ioapic.h"
|
#include "ioapic.h"
|
||||||
#include "lapic.h"
|
#include "lapic.h"
|
||||||
|
#include "irq.h"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define ioapic_debug(fmt,arg...) printk(KERN_WARNING fmt,##arg)
|
#define ioapic_debug(fmt,arg...) printk(KERN_WARNING fmt,##arg)
|
||||||
|
@ -285,26 +286,31 @@ void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __kvm_ioapic_update_eoi(struct kvm_ioapic *ioapic, int gsi)
|
static void __kvm_ioapic_update_eoi(struct kvm_ioapic *ioapic, int gsi,
|
||||||
|
int trigger_mode)
|
||||||
{
|
{
|
||||||
union ioapic_redir_entry *ent;
|
union ioapic_redir_entry *ent;
|
||||||
|
|
||||||
ent = &ioapic->redirtbl[gsi];
|
ent = &ioapic->redirtbl[gsi];
|
||||||
ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
|
|
||||||
|
|
||||||
ent->fields.remote_irr = 0;
|
kvm_notify_acked_irq(ioapic->kvm, gsi);
|
||||||
if (!ent->fields.mask && (ioapic->irr & (1 << gsi)))
|
|
||||||
ioapic_service(ioapic, gsi);
|
if (trigger_mode == IOAPIC_LEVEL_TRIG) {
|
||||||
|
ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
|
||||||
|
ent->fields.remote_irr = 0;
|
||||||
|
if (!ent->fields.mask && (ioapic->irr & (1 << gsi)))
|
||||||
|
ioapic_service(ioapic, gsi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_ioapic_update_eoi(struct kvm *kvm, int vector)
|
void kvm_ioapic_update_eoi(struct kvm *kvm, int vector, int trigger_mode)
|
||||||
{
|
{
|
||||||
struct kvm_ioapic *ioapic = kvm->arch.vioapic;
|
struct kvm_ioapic *ioapic = kvm->arch.vioapic;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < IOAPIC_NUM_PINS; i++)
|
for (i = 0; i < IOAPIC_NUM_PINS; i++)
|
||||||
if (ioapic->redirtbl[i].fields.vector == vector)
|
if (ioapic->redirtbl[i].fields.vector == vector)
|
||||||
__kvm_ioapic_update_eoi(ioapic, i);
|
__kvm_ioapic_update_eoi(ioapic, i, trigger_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr,
|
static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr,
|
||||||
|
@ -380,7 +386,7 @@ static void ioapic_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
|
||||||
break;
|
break;
|
||||||
#ifdef CONFIG_IA64
|
#ifdef CONFIG_IA64
|
||||||
case IOAPIC_REG_EOI:
|
case IOAPIC_REG_EOI:
|
||||||
kvm_ioapic_update_eoi(ioapic->kvm, data);
|
kvm_ioapic_update_eoi(ioapic->kvm, data, IOAPIC_LEVEL_TRIG);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ struct kvm_ioapic {
|
||||||
} redirtbl[IOAPIC_NUM_PINS];
|
} redirtbl[IOAPIC_NUM_PINS];
|
||||||
struct kvm_io_device dev;
|
struct kvm_io_device dev;
|
||||||
struct kvm *kvm;
|
struct kvm *kvm;
|
||||||
|
void (*ack_notifier)(void *opaque, int irq);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -78,16 +79,9 @@ static inline struct kvm_ioapic *ioapic_irqchip(struct kvm *kvm)
|
||||||
return kvm->arch.vioapic;
|
return kvm->arch.vioapic;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_IA64
|
|
||||||
static inline int irqchip_in_kernel(struct kvm *kvm)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct kvm_vcpu *kvm_get_lowest_prio_vcpu(struct kvm *kvm, u8 vector,
|
struct kvm_vcpu *kvm_get_lowest_prio_vcpu(struct kvm *kvm, u8 vector,
|
||||||
unsigned long bitmap);
|
unsigned long bitmap);
|
||||||
void kvm_ioapic_update_eoi(struct kvm *kvm, int vector);
|
void kvm_ioapic_update_eoi(struct kvm *kvm, int vector, int trigger_mode);
|
||||||
int kvm_ioapic_init(struct kvm *kvm);
|
int kvm_ioapic_init(struct kvm *kvm);
|
||||||
void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level);
|
void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level);
|
||||||
void kvm_ioapic_reset(struct kvm_ioapic *ioapic);
|
void kvm_ioapic_reset(struct kvm_ioapic *ioapic);
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* irq_comm.c: Common API for in kernel interrupt controller
|
||||||
|
* Copyright (c) 2007, Intel Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||||
|
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
* Authors:
|
||||||
|
* Yaozu (Eddie) Dong <Eddie.dong@intel.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kvm_host.h>
|
||||||
|
#include "irq.h"
|
||||||
|
|
||||||
|
#include "ioapic.h"
|
||||||
|
|
||||||
|
/* This should be called with the kvm->lock mutex held */
|
||||||
|
void kvm_set_irq(struct kvm *kvm, int irq, int level)
|
||||||
|
{
|
||||||
|
/* Not possible to detect if the guest uses the PIC or the
|
||||||
|
* IOAPIC. So set the bit in both. The guest will ignore
|
||||||
|
* writes to the unused one.
|
||||||
|
*/
|
||||||
|
kvm_ioapic_set_irq(kvm->arch.vioapic, irq, level);
|
||||||
|
#ifdef CONFIG_X86
|
||||||
|
kvm_pic_set_irq(pic_irqchip(kvm), irq, level);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_notify_acked_irq(struct kvm *kvm, unsigned gsi)
|
||||||
|
{
|
||||||
|
struct kvm_irq_ack_notifier *kian;
|
||||||
|
struct hlist_node *n;
|
||||||
|
|
||||||
|
hlist_for_each_entry(kian, n, &kvm->arch.irq_ack_notifier_list, link)
|
||||||
|
if (kian->gsi == gsi)
|
||||||
|
kian->irq_acked(kian);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_register_irq_ack_notifier(struct kvm *kvm,
|
||||||
|
struct kvm_irq_ack_notifier *kian)
|
||||||
|
{
|
||||||
|
hlist_add_head(&kian->link, &kvm->arch.irq_ack_notifier_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
|
||||||
|
struct kvm_irq_ack_notifier *kian)
|
||||||
|
{
|
||||||
|
hlist_del(&kian->link);
|
||||||
|
}
|
|
@ -51,6 +51,12 @@
|
||||||
#include "coalesced_mmio.h"
|
#include "coalesced_mmio.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef KVM_CAP_DEVICE_ASSIGNMENT
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include "irq.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
MODULE_AUTHOR("Qumranet");
|
MODULE_AUTHOR("Qumranet");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
@ -71,11 +77,253 @@ static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
|
||||||
|
|
||||||
bool kvm_rebooting;
|
bool kvm_rebooting;
|
||||||
|
|
||||||
|
#ifdef KVM_CAP_DEVICE_ASSIGNMENT
|
||||||
|
static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head,
|
||||||
|
int assigned_dev_id)
|
||||||
|
{
|
||||||
|
struct list_head *ptr;
|
||||||
|
struct kvm_assigned_dev_kernel *match;
|
||||||
|
|
||||||
|
list_for_each(ptr, head) {
|
||||||
|
match = list_entry(ptr, struct kvm_assigned_dev_kernel, list);
|
||||||
|
if (match->assigned_dev_id == assigned_dev_id)
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct kvm_assigned_dev_kernel *assigned_dev;
|
||||||
|
|
||||||
|
assigned_dev = container_of(work, struct kvm_assigned_dev_kernel,
|
||||||
|
interrupt_work);
|
||||||
|
|
||||||
|
/* This is taken to safely inject irq inside the guest. When
|
||||||
|
* the interrupt injection (or the ioapic code) uses a
|
||||||
|
* finer-grained lock, update this
|
||||||
|
*/
|
||||||
|
mutex_lock(&assigned_dev->kvm->lock);
|
||||||
|
kvm_set_irq(assigned_dev->kvm,
|
||||||
|
assigned_dev->guest_irq, 1);
|
||||||
|
mutex_unlock(&assigned_dev->kvm->lock);
|
||||||
|
kvm_put_kvm(assigned_dev->kvm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: Implement the OR logic needed to make shared interrupts on
|
||||||
|
* this line behave properly
|
||||||
|
*/
|
||||||
|
static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct kvm_assigned_dev_kernel *assigned_dev =
|
||||||
|
(struct kvm_assigned_dev_kernel *) dev_id;
|
||||||
|
|
||||||
|
kvm_get_kvm(assigned_dev->kvm);
|
||||||
|
schedule_work(&assigned_dev->interrupt_work);
|
||||||
|
disable_irq_nosync(irq);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ack the irq line for an assigned device */
|
||||||
|
static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian)
|
||||||
|
{
|
||||||
|
struct kvm_assigned_dev_kernel *dev;
|
||||||
|
|
||||||
|
if (kian->gsi == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dev = container_of(kian, struct kvm_assigned_dev_kernel,
|
||||||
|
ack_notifier);
|
||||||
|
kvm_set_irq(dev->kvm, dev->guest_irq, 0);
|
||||||
|
enable_irq(dev->host_irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_free_assigned_device(struct kvm *kvm,
|
||||||
|
struct kvm_assigned_dev_kernel
|
||||||
|
*assigned_dev)
|
||||||
|
{
|
||||||
|
if (irqchip_in_kernel(kvm) && assigned_dev->irq_requested)
|
||||||
|
free_irq(assigned_dev->host_irq, (void *)assigned_dev);
|
||||||
|
|
||||||
|
kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier);
|
||||||
|
|
||||||
|
if (cancel_work_sync(&assigned_dev->interrupt_work))
|
||||||
|
/* We had pending work. That means we will have to take
|
||||||
|
* care of kvm_put_kvm.
|
||||||
|
*/
|
||||||
|
kvm_put_kvm(kvm);
|
||||||
|
|
||||||
|
pci_release_regions(assigned_dev->dev);
|
||||||
|
pci_disable_device(assigned_dev->dev);
|
||||||
|
pci_dev_put(assigned_dev->dev);
|
||||||
|
|
||||||
|
list_del(&assigned_dev->list);
|
||||||
|
kfree(assigned_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_free_all_assigned_devices(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
struct list_head *ptr, *ptr2;
|
||||||
|
struct kvm_assigned_dev_kernel *assigned_dev;
|
||||||
|
|
||||||
|
list_for_each_safe(ptr, ptr2, &kvm->arch.assigned_dev_head) {
|
||||||
|
assigned_dev = list_entry(ptr,
|
||||||
|
struct kvm_assigned_dev_kernel,
|
||||||
|
list);
|
||||||
|
|
||||||
|
kvm_free_assigned_device(kvm, assigned_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_vm_ioctl_assign_irq(struct kvm *kvm,
|
||||||
|
struct kvm_assigned_irq
|
||||||
|
*assigned_irq)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
struct kvm_assigned_dev_kernel *match;
|
||||||
|
|
||||||
|
mutex_lock(&kvm->lock);
|
||||||
|
|
||||||
|
match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
|
||||||
|
assigned_irq->assigned_dev_id);
|
||||||
|
if (!match) {
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match->irq_requested) {
|
||||||
|
match->guest_irq = assigned_irq->guest_irq;
|
||||||
|
match->ack_notifier.gsi = assigned_irq->guest_irq;
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&match->interrupt_work,
|
||||||
|
kvm_assigned_dev_interrupt_work_handler);
|
||||||
|
|
||||||
|
if (irqchip_in_kernel(kvm)) {
|
||||||
|
if (!capable(CAP_SYS_RAWIO)) {
|
||||||
|
r = -EPERM;
|
||||||
|
goto out_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assigned_irq->host_irq)
|
||||||
|
match->host_irq = assigned_irq->host_irq;
|
||||||
|
else
|
||||||
|
match->host_irq = match->dev->irq;
|
||||||
|
match->guest_irq = assigned_irq->guest_irq;
|
||||||
|
match->ack_notifier.gsi = assigned_irq->guest_irq;
|
||||||
|
match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq;
|
||||||
|
kvm_register_irq_ack_notifier(kvm, &match->ack_notifier);
|
||||||
|
|
||||||
|
/* Even though this is PCI, we don't want to use shared
|
||||||
|
* interrupts. Sharing host devices with guest-assigned devices
|
||||||
|
* on the same interrupt line is not a happy situation: there
|
||||||
|
* are going to be long delays in accepting, acking, etc.
|
||||||
|
*/
|
||||||
|
if (request_irq(match->host_irq, kvm_assigned_dev_intr, 0,
|
||||||
|
"kvm_assigned_device", (void *)match)) {
|
||||||
|
r = -EIO;
|
||||||
|
goto out_release;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match->irq_requested = true;
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
return r;
|
||||||
|
out_release:
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
kvm_free_assigned_device(kvm, match);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
|
||||||
|
struct kvm_assigned_pci_dev *assigned_dev)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
struct kvm_assigned_dev_kernel *match;
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
mutex_lock(&kvm->lock);
|
||||||
|
|
||||||
|
match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
|
||||||
|
assigned_dev->assigned_dev_id);
|
||||||
|
if (match) {
|
||||||
|
/* device already assigned */
|
||||||
|
r = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
match = kzalloc(sizeof(struct kvm_assigned_dev_kernel), GFP_KERNEL);
|
||||||
|
if (match == NULL) {
|
||||||
|
printk(KERN_INFO "%s: Couldn't allocate memory\n",
|
||||||
|
__func__);
|
||||||
|
r = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
dev = pci_get_bus_and_slot(assigned_dev->busnr,
|
||||||
|
assigned_dev->devfn);
|
||||||
|
if (!dev) {
|
||||||
|
printk(KERN_INFO "%s: host device not found\n", __func__);
|
||||||
|
r = -EINVAL;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
if (pci_enable_device(dev)) {
|
||||||
|
printk(KERN_INFO "%s: Could not enable PCI device\n", __func__);
|
||||||
|
r = -EBUSY;
|
||||||
|
goto out_put;
|
||||||
|
}
|
||||||
|
r = pci_request_regions(dev, "kvm_assigned_device");
|
||||||
|
if (r) {
|
||||||
|
printk(KERN_INFO "%s: Could not get access to device regions\n",
|
||||||
|
__func__);
|
||||||
|
goto out_disable;
|
||||||
|
}
|
||||||
|
match->assigned_dev_id = assigned_dev->assigned_dev_id;
|
||||||
|
match->host_busnr = assigned_dev->busnr;
|
||||||
|
match->host_devfn = assigned_dev->devfn;
|
||||||
|
match->dev = dev;
|
||||||
|
|
||||||
|
match->kvm = kvm;
|
||||||
|
|
||||||
|
list_add(&match->list, &kvm->arch.assigned_dev_head);
|
||||||
|
|
||||||
|
if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) {
|
||||||
|
r = kvm_iommu_map_guest(kvm, match);
|
||||||
|
if (r)
|
||||||
|
goto out_list_del;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
return r;
|
||||||
|
out_list_del:
|
||||||
|
list_del(&match->list);
|
||||||
|
pci_release_regions(dev);
|
||||||
|
out_disable:
|
||||||
|
pci_disable_device(dev);
|
||||||
|
out_put:
|
||||||
|
pci_dev_put(dev);
|
||||||
|
out_free:
|
||||||
|
kfree(match);
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline int valid_vcpu(int n)
|
static inline int valid_vcpu(int n)
|
||||||
{
|
{
|
||||||
return likely(n >= 0 && n < KVM_MAX_VCPUS);
|
return likely(n >= 0 && n < KVM_MAX_VCPUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int kvm_is_mmio_pfn(pfn_t pfn)
|
||||||
|
{
|
||||||
|
if (pfn_valid(pfn))
|
||||||
|
return PageReserved(pfn_to_page(pfn));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switches to specified vcpu, until a matching vcpu_put()
|
* Switches to specified vcpu, until a matching vcpu_put()
|
||||||
*/
|
*/
|
||||||
|
@ -570,6 +818,12 @@ int __kvm_set_memory_region(struct kvm *kvm,
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_free_physmem_slot(&old, &new);
|
kvm_free_physmem_slot(&old, &new);
|
||||||
|
#ifdef CONFIG_DMAR
|
||||||
|
/* map the pages in iommu page table */
|
||||||
|
r = kvm_iommu_map_pages(kvm, base_gfn, npages);
|
||||||
|
if (r)
|
||||||
|
goto out;
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
|
@ -708,9 +962,6 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gfn_to_hva);
|
EXPORT_SYMBOL_GPL(gfn_to_hva);
|
||||||
|
|
||||||
/*
|
|
||||||
* Requires current->mm->mmap_sem to be held
|
|
||||||
*/
|
|
||||||
pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
|
pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
|
||||||
{
|
{
|
||||||
struct page *page[1];
|
struct page *page[1];
|
||||||
|
@ -726,21 +977,24 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
|
||||||
return page_to_pfn(bad_page);
|
return page_to_pfn(bad_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
npages = get_user_pages(current, current->mm, addr, 1, 1, 1, page,
|
npages = get_user_pages_fast(addr, 1, 1, page);
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (unlikely(npages != 1)) {
|
if (unlikely(npages != 1)) {
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
|
|
||||||
|
down_read(¤t->mm->mmap_sem);
|
||||||
vma = find_vma(current->mm, addr);
|
vma = find_vma(current->mm, addr);
|
||||||
|
|
||||||
if (vma == NULL || addr < vma->vm_start ||
|
if (vma == NULL || addr < vma->vm_start ||
|
||||||
!(vma->vm_flags & VM_PFNMAP)) {
|
!(vma->vm_flags & VM_PFNMAP)) {
|
||||||
|
up_read(¤t->mm->mmap_sem);
|
||||||
get_page(bad_page);
|
get_page(bad_page);
|
||||||
return page_to_pfn(bad_page);
|
return page_to_pfn(bad_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
|
pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
|
||||||
BUG_ON(pfn_valid(pfn));
|
up_read(¤t->mm->mmap_sem);
|
||||||
|
BUG_ON(!kvm_is_mmio_pfn(pfn));
|
||||||
} else
|
} else
|
||||||
pfn = page_to_pfn(page[0]);
|
pfn = page_to_pfn(page[0]);
|
||||||
|
|
||||||
|
@ -754,10 +1008,10 @@ struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
|
|
||||||
pfn = gfn_to_pfn(kvm, gfn);
|
pfn = gfn_to_pfn(kvm, gfn);
|
||||||
if (pfn_valid(pfn))
|
if (!kvm_is_mmio_pfn(pfn))
|
||||||
return pfn_to_page(pfn);
|
return pfn_to_page(pfn);
|
||||||
|
|
||||||
WARN_ON(!pfn_valid(pfn));
|
WARN_ON(kvm_is_mmio_pfn(pfn));
|
||||||
|
|
||||||
get_page(bad_page);
|
get_page(bad_page);
|
||||||
return bad_page;
|
return bad_page;
|
||||||
|
@ -773,7 +1027,7 @@ EXPORT_SYMBOL_GPL(kvm_release_page_clean);
|
||||||
|
|
||||||
void kvm_release_pfn_clean(pfn_t pfn)
|
void kvm_release_pfn_clean(pfn_t pfn)
|
||||||
{
|
{
|
||||||
if (pfn_valid(pfn))
|
if (!kvm_is_mmio_pfn(pfn))
|
||||||
put_page(pfn_to_page(pfn));
|
put_page(pfn_to_page(pfn));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_release_pfn_clean);
|
EXPORT_SYMBOL_GPL(kvm_release_pfn_clean);
|
||||||
|
@ -799,7 +1053,7 @@ EXPORT_SYMBOL_GPL(kvm_set_page_dirty);
|
||||||
|
|
||||||
void kvm_set_pfn_dirty(pfn_t pfn)
|
void kvm_set_pfn_dirty(pfn_t pfn)
|
||||||
{
|
{
|
||||||
if (pfn_valid(pfn)) {
|
if (!kvm_is_mmio_pfn(pfn)) {
|
||||||
struct page *page = pfn_to_page(pfn);
|
struct page *page = pfn_to_page(pfn);
|
||||||
if (!PageReserved(page))
|
if (!PageReserved(page))
|
||||||
SetPageDirty(page);
|
SetPageDirty(page);
|
||||||
|
@ -809,14 +1063,14 @@ EXPORT_SYMBOL_GPL(kvm_set_pfn_dirty);
|
||||||
|
|
||||||
void kvm_set_pfn_accessed(pfn_t pfn)
|
void kvm_set_pfn_accessed(pfn_t pfn)
|
||||||
{
|
{
|
||||||
if (pfn_valid(pfn))
|
if (!kvm_is_mmio_pfn(pfn))
|
||||||
mark_page_accessed(pfn_to_page(pfn));
|
mark_page_accessed(pfn_to_page(pfn));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed);
|
EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed);
|
||||||
|
|
||||||
void kvm_get_pfn(pfn_t pfn)
|
void kvm_get_pfn(pfn_t pfn)
|
||||||
{
|
{
|
||||||
if (pfn_valid(pfn))
|
if (!kvm_is_mmio_pfn(pfn))
|
||||||
get_page(pfn_to_page(pfn));
|
get_page(pfn_to_page(pfn));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_get_pfn);
|
EXPORT_SYMBOL_GPL(kvm_get_pfn);
|
||||||
|
@ -972,12 +1226,12 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
|
||||||
for (;;) {
|
for (;;) {
|
||||||
prepare_to_wait(&vcpu->wq, &wait, TASK_INTERRUPTIBLE);
|
prepare_to_wait(&vcpu->wq, &wait, TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
if (kvm_cpu_has_interrupt(vcpu))
|
if (kvm_cpu_has_interrupt(vcpu) ||
|
||||||
break;
|
kvm_cpu_has_pending_timer(vcpu) ||
|
||||||
if (kvm_cpu_has_pending_timer(vcpu))
|
kvm_arch_vcpu_runnable(vcpu)) {
|
||||||
break;
|
set_bit(KVM_REQ_UNHALT, &vcpu->requests);
|
||||||
if (kvm_arch_vcpu_runnable(vcpu))
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
if (signal_pending(current))
|
if (signal_pending(current))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1074,12 +1328,11 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, int n)
|
||||||
|
|
||||||
r = kvm_arch_vcpu_setup(vcpu);
|
r = kvm_arch_vcpu_setup(vcpu);
|
||||||
if (r)
|
if (r)
|
||||||
goto vcpu_destroy;
|
return r;
|
||||||
|
|
||||||
mutex_lock(&kvm->lock);
|
mutex_lock(&kvm->lock);
|
||||||
if (kvm->vcpus[n]) {
|
if (kvm->vcpus[n]) {
|
||||||
r = -EEXIST;
|
r = -EEXIST;
|
||||||
mutex_unlock(&kvm->lock);
|
|
||||||
goto vcpu_destroy;
|
goto vcpu_destroy;
|
||||||
}
|
}
|
||||||
kvm->vcpus[n] = vcpu;
|
kvm->vcpus[n] = vcpu;
|
||||||
|
@ -1095,8 +1348,8 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, int n)
|
||||||
unlink:
|
unlink:
|
||||||
mutex_lock(&kvm->lock);
|
mutex_lock(&kvm->lock);
|
||||||
kvm->vcpus[n] = NULL;
|
kvm->vcpus[n] = NULL;
|
||||||
mutex_unlock(&kvm->lock);
|
|
||||||
vcpu_destroy:
|
vcpu_destroy:
|
||||||
|
mutex_unlock(&kvm->lock);
|
||||||
kvm_arch_vcpu_destroy(vcpu);
|
kvm_arch_vcpu_destroy(vcpu);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -1118,6 +1371,8 @@ static long kvm_vcpu_ioctl(struct file *filp,
|
||||||
struct kvm_vcpu *vcpu = filp->private_data;
|
struct kvm_vcpu *vcpu = filp->private_data;
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
int r;
|
int r;
|
||||||
|
struct kvm_fpu *fpu = NULL;
|
||||||
|
struct kvm_sregs *kvm_sregs = NULL;
|
||||||
|
|
||||||
if (vcpu->kvm->mm != current->mm)
|
if (vcpu->kvm->mm != current->mm)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -1165,25 +1420,28 @@ out_free2:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KVM_GET_SREGS: {
|
case KVM_GET_SREGS: {
|
||||||
struct kvm_sregs kvm_sregs;
|
kvm_sregs = kzalloc(sizeof(struct kvm_sregs), GFP_KERNEL);
|
||||||
|
r = -ENOMEM;
|
||||||
memset(&kvm_sregs, 0, sizeof kvm_sregs);
|
if (!kvm_sregs)
|
||||||
r = kvm_arch_vcpu_ioctl_get_sregs(vcpu, &kvm_sregs);
|
goto out;
|
||||||
|
r = kvm_arch_vcpu_ioctl_get_sregs(vcpu, kvm_sregs);
|
||||||
if (r)
|
if (r)
|
||||||
goto out;
|
goto out;
|
||||||
r = -EFAULT;
|
r = -EFAULT;
|
||||||
if (copy_to_user(argp, &kvm_sregs, sizeof kvm_sregs))
|
if (copy_to_user(argp, kvm_sregs, sizeof(struct kvm_sregs)))
|
||||||
goto out;
|
goto out;
|
||||||
r = 0;
|
r = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KVM_SET_SREGS: {
|
case KVM_SET_SREGS: {
|
||||||
struct kvm_sregs kvm_sregs;
|
kvm_sregs = kmalloc(sizeof(struct kvm_sregs), GFP_KERNEL);
|
||||||
|
r = -ENOMEM;
|
||||||
r = -EFAULT;
|
if (!kvm_sregs)
|
||||||
if (copy_from_user(&kvm_sregs, argp, sizeof kvm_sregs))
|
|
||||||
goto out;
|
goto out;
|
||||||
r = kvm_arch_vcpu_ioctl_set_sregs(vcpu, &kvm_sregs);
|
r = -EFAULT;
|
||||||
|
if (copy_from_user(kvm_sregs, argp, sizeof(struct kvm_sregs)))
|
||||||
|
goto out;
|
||||||
|
r = kvm_arch_vcpu_ioctl_set_sregs(vcpu, kvm_sregs);
|
||||||
if (r)
|
if (r)
|
||||||
goto out;
|
goto out;
|
||||||
r = 0;
|
r = 0;
|
||||||
|
@ -1264,25 +1522,28 @@ out_free2:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KVM_GET_FPU: {
|
case KVM_GET_FPU: {
|
||||||
struct kvm_fpu fpu;
|
fpu = kzalloc(sizeof(struct kvm_fpu), GFP_KERNEL);
|
||||||
|
r = -ENOMEM;
|
||||||
memset(&fpu, 0, sizeof fpu);
|
if (!fpu)
|
||||||
r = kvm_arch_vcpu_ioctl_get_fpu(vcpu, &fpu);
|
goto out;
|
||||||
|
r = kvm_arch_vcpu_ioctl_get_fpu(vcpu, fpu);
|
||||||
if (r)
|
if (r)
|
||||||
goto out;
|
goto out;
|
||||||
r = -EFAULT;
|
r = -EFAULT;
|
||||||
if (copy_to_user(argp, &fpu, sizeof fpu))
|
if (copy_to_user(argp, fpu, sizeof(struct kvm_fpu)))
|
||||||
goto out;
|
goto out;
|
||||||
r = 0;
|
r = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KVM_SET_FPU: {
|
case KVM_SET_FPU: {
|
||||||
struct kvm_fpu fpu;
|
fpu = kmalloc(sizeof(struct kvm_fpu), GFP_KERNEL);
|
||||||
|
r = -ENOMEM;
|
||||||
r = -EFAULT;
|
if (!fpu)
|
||||||
if (copy_from_user(&fpu, argp, sizeof fpu))
|
|
||||||
goto out;
|
goto out;
|
||||||
r = kvm_arch_vcpu_ioctl_set_fpu(vcpu, &fpu);
|
r = -EFAULT;
|
||||||
|
if (copy_from_user(fpu, argp, sizeof(struct kvm_fpu)))
|
||||||
|
goto out;
|
||||||
|
r = kvm_arch_vcpu_ioctl_set_fpu(vcpu, fpu);
|
||||||
if (r)
|
if (r)
|
||||||
goto out;
|
goto out;
|
||||||
r = 0;
|
r = 0;
|
||||||
|
@ -1292,6 +1553,8 @@ out_free2:
|
||||||
r = kvm_arch_vcpu_ioctl(filp, ioctl, arg);
|
r = kvm_arch_vcpu_ioctl(filp, ioctl, arg);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
kfree(fpu);
|
||||||
|
kfree(kvm_sregs);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1359,6 +1622,30 @@ static long kvm_vm_ioctl(struct file *filp,
|
||||||
r = 0;
|
r = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef KVM_CAP_DEVICE_ASSIGNMENT
|
||||||
|
case KVM_ASSIGN_PCI_DEVICE: {
|
||||||
|
struct kvm_assigned_pci_dev assigned_dev;
|
||||||
|
|
||||||
|
r = -EFAULT;
|
||||||
|
if (copy_from_user(&assigned_dev, argp, sizeof assigned_dev))
|
||||||
|
goto out;
|
||||||
|
r = kvm_vm_ioctl_assign_device(kvm, &assigned_dev);
|
||||||
|
if (r)
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KVM_ASSIGN_IRQ: {
|
||||||
|
struct kvm_assigned_irq assigned_irq;
|
||||||
|
|
||||||
|
r = -EFAULT;
|
||||||
|
if (copy_from_user(&assigned_irq, argp, sizeof assigned_irq))
|
||||||
|
goto out;
|
||||||
|
r = kvm_vm_ioctl_assign_irq(kvm, &assigned_irq);
|
||||||
|
if (r)
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
r = kvm_arch_vm_ioctl(filp, ioctl, arg);
|
r = kvm_arch_vm_ioctl(filp, ioctl, arg);
|
||||||
|
@ -1369,17 +1656,22 @@ out:
|
||||||
|
|
||||||
static int kvm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
static int kvm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||||
{
|
{
|
||||||
|
struct page *page[1];
|
||||||
|
unsigned long addr;
|
||||||
|
int npages;
|
||||||
|
gfn_t gfn = vmf->pgoff;
|
||||||
struct kvm *kvm = vma->vm_file->private_data;
|
struct kvm *kvm = vma->vm_file->private_data;
|
||||||
struct page *page;
|
|
||||||
|
|
||||||
if (!kvm_is_visible_gfn(kvm, vmf->pgoff))
|
addr = gfn_to_hva(kvm, gfn);
|
||||||
|
if (kvm_is_error_hva(addr))
|
||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
page = gfn_to_page(kvm, vmf->pgoff);
|
|
||||||
if (is_error_page(page)) {
|
npages = get_user_pages(current, current->mm, addr, 1, 1, 0, page,
|
||||||
kvm_release_page_clean(page);
|
NULL);
|
||||||
|
if (unlikely(npages != 1))
|
||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
}
|
|
||||||
vmf->page = page;
|
vmf->page = page[0];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/relay.h>
|
#include <linux/relay.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
|
|
||||||
#include <linux/kvm_host.h>
|
#include <linux/kvm_host.h>
|
||||||
|
|
||||||
|
@ -35,16 +36,16 @@ static struct kvm_trace *kvm_trace;
|
||||||
struct kvm_trace_probe {
|
struct kvm_trace_probe {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *format;
|
const char *format;
|
||||||
u32 cycle_in;
|
u32 timestamp_in;
|
||||||
marker_probe_func *probe_func;
|
marker_probe_func *probe_func;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int calc_rec_size(int cycle, int extra)
|
static inline int calc_rec_size(int timestamp, int extra)
|
||||||
{
|
{
|
||||||
int rec_size = KVM_TRC_HEAD_SIZE;
|
int rec_size = KVM_TRC_HEAD_SIZE;
|
||||||
|
|
||||||
rec_size += extra;
|
rec_size += extra;
|
||||||
return cycle ? rec_size += KVM_TRC_CYCLE_SIZE : rec_size;
|
return timestamp ? rec_size += KVM_TRC_CYCLE_SIZE : rec_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_add_trace(void *probe_private, void *call_data,
|
static void kvm_add_trace(void *probe_private, void *call_data,
|
||||||
|
@ -54,12 +55,13 @@ static void kvm_add_trace(void *probe_private, void *call_data,
|
||||||
struct kvm_trace *kt = kvm_trace;
|
struct kvm_trace *kt = kvm_trace;
|
||||||
struct kvm_trace_rec rec;
|
struct kvm_trace_rec rec;
|
||||||
struct kvm_vcpu *vcpu;
|
struct kvm_vcpu *vcpu;
|
||||||
int i, extra, size;
|
int i, size;
|
||||||
|
u32 extra;
|
||||||
|
|
||||||
if (unlikely(kt->trace_state != KVM_TRACE_STATE_RUNNING))
|
if (unlikely(kt->trace_state != KVM_TRACE_STATE_RUNNING))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rec.event = va_arg(*args, u32);
|
rec.rec_val = TRACE_REC_EVENT_ID(va_arg(*args, u32));
|
||||||
vcpu = va_arg(*args, struct kvm_vcpu *);
|
vcpu = va_arg(*args, struct kvm_vcpu *);
|
||||||
rec.pid = current->tgid;
|
rec.pid = current->tgid;
|
||||||
rec.vcpu_id = vcpu->vcpu_id;
|
rec.vcpu_id = vcpu->vcpu_id;
|
||||||
|
@ -67,21 +69,21 @@ static void kvm_add_trace(void *probe_private, void *call_data,
|
||||||
extra = va_arg(*args, u32);
|
extra = va_arg(*args, u32);
|
||||||
WARN_ON(!(extra <= KVM_TRC_EXTRA_MAX));
|
WARN_ON(!(extra <= KVM_TRC_EXTRA_MAX));
|
||||||
extra = min_t(u32, extra, KVM_TRC_EXTRA_MAX);
|
extra = min_t(u32, extra, KVM_TRC_EXTRA_MAX);
|
||||||
rec.extra_u32 = extra;
|
|
||||||
|
|
||||||
rec.cycle_in = p->cycle_in;
|
rec.rec_val |= TRACE_REC_TCS(p->timestamp_in)
|
||||||
|
| TRACE_REC_NUM_DATA_ARGS(extra);
|
||||||
|
|
||||||
if (rec.cycle_in) {
|
if (p->timestamp_in) {
|
||||||
rec.u.cycle.cycle_u64 = get_cycles();
|
rec.u.timestamp.timestamp = ktime_to_ns(ktime_get());
|
||||||
|
|
||||||
for (i = 0; i < rec.extra_u32; i++)
|
for (i = 0; i < extra; i++)
|
||||||
rec.u.cycle.extra_u32[i] = va_arg(*args, u32);
|
rec.u.timestamp.extra_u32[i] = va_arg(*args, u32);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < rec.extra_u32; i++)
|
for (i = 0; i < extra; i++)
|
||||||
rec.u.nocycle.extra_u32[i] = va_arg(*args, u32);
|
rec.u.notimestamp.extra_u32[i] = va_arg(*args, u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
size = calc_rec_size(rec.cycle_in, rec.extra_u32 * sizeof(u32));
|
size = calc_rec_size(p->timestamp_in, extra * sizeof(u32));
|
||||||
relay_write(kt->rchan, &rec, size);
|
relay_write(kt->rchan, &rec, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2006, Intel Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||||
|
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006-2008 Intel Corporation
|
||||||
|
* Copyright IBM Corporation, 2008
|
||||||
|
* Author: Allen M. Kay <allen.m.kay@intel.com>
|
||||||
|
* Author: Weidong Han <weidong.han@intel.com>
|
||||||
|
* Author: Ben-Ami Yassour <benami@il.ibm.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/kvm_host.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/dmar.h>
|
||||||
|
#include <linux/intel-iommu.h>
|
||||||
|
|
||||||
|
static int kvm_iommu_unmap_memslots(struct kvm *kvm);
|
||||||
|
static void kvm_iommu_put_pages(struct kvm *kvm,
|
||||||
|
gfn_t base_gfn, unsigned long npages);
|
||||||
|
|
||||||
|
int kvm_iommu_map_pages(struct kvm *kvm,
|
||||||
|
gfn_t base_gfn, unsigned long npages)
|
||||||
|
{
|
||||||
|
gfn_t gfn = base_gfn;
|
||||||
|
pfn_t pfn;
|
||||||
|
int i, r = 0;
|
||||||
|
struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
|
||||||
|
|
||||||
|
/* check if iommu exists and in use */
|
||||||
|
if (!domain)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < npages; i++) {
|
||||||
|
/* check if already mapped */
|
||||||
|
pfn = (pfn_t)intel_iommu_iova_to_pfn(domain,
|
||||||
|
gfn_to_gpa(gfn));
|
||||||
|
if (pfn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pfn = gfn_to_pfn(kvm, gfn);
|
||||||
|
r = intel_iommu_page_mapping(domain,
|
||||||
|
gfn_to_gpa(gfn),
|
||||||
|
pfn_to_hpa(pfn),
|
||||||
|
PAGE_SIZE,
|
||||||
|
DMA_PTE_READ |
|
||||||
|
DMA_PTE_WRITE);
|
||||||
|
if (r) {
|
||||||
|
printk(KERN_ERR "kvm_iommu_map_pages:"
|
||||||
|
"iommu failed to map pfn=%lx\n", pfn);
|
||||||
|
goto unmap_pages;
|
||||||
|
}
|
||||||
|
gfn++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unmap_pages:
|
||||||
|
kvm_iommu_put_pages(kvm, base_gfn, i);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_iommu_map_memslots(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
|
||||||
|
down_read(&kvm->slots_lock);
|
||||||
|
for (i = 0; i < kvm->nmemslots; i++) {
|
||||||
|
r = kvm_iommu_map_pages(kvm, kvm->memslots[i].base_gfn,
|
||||||
|
kvm->memslots[i].npages);
|
||||||
|
if (r)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
up_read(&kvm->slots_lock);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_iommu_map_guest(struct kvm *kvm,
|
||||||
|
struct kvm_assigned_dev_kernel *assigned_dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!intel_iommu_found()) {
|
||||||
|
printk(KERN_ERR "%s: intel iommu not found\n", __func__);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "VT-d direct map: host bdf = %x:%x:%x\n",
|
||||||
|
assigned_dev->host_busnr,
|
||||||
|
PCI_SLOT(assigned_dev->host_devfn),
|
||||||
|
PCI_FUNC(assigned_dev->host_devfn));
|
||||||
|
|
||||||
|
pdev = assigned_dev->dev;
|
||||||
|
|
||||||
|
if (pdev == NULL) {
|
||||||
|
if (kvm->arch.intel_iommu_domain) {
|
||||||
|
intel_iommu_domain_exit(kvm->arch.intel_iommu_domain);
|
||||||
|
kvm->arch.intel_iommu_domain = NULL;
|
||||||
|
}
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm->arch.intel_iommu_domain = intel_iommu_domain_alloc(pdev);
|
||||||
|
if (!kvm->arch.intel_iommu_domain)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
r = kvm_iommu_map_memslots(kvm);
|
||||||
|
if (r)
|
||||||
|
goto out_unmap;
|
||||||
|
|
||||||
|
intel_iommu_detach_dev(kvm->arch.intel_iommu_domain,
|
||||||
|
pdev->bus->number, pdev->devfn);
|
||||||
|
|
||||||
|
r = intel_iommu_context_mapping(kvm->arch.intel_iommu_domain,
|
||||||
|
pdev);
|
||||||
|
if (r) {
|
||||||
|
printk(KERN_ERR "Domain context map for %s failed",
|
||||||
|
pci_name(pdev));
|
||||||
|
goto out_unmap;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_unmap:
|
||||||
|
kvm_iommu_unmap_memslots(kvm);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_iommu_put_pages(struct kvm *kvm,
|
||||||
|
gfn_t base_gfn, unsigned long npages)
|
||||||
|
{
|
||||||
|
gfn_t gfn = base_gfn;
|
||||||
|
pfn_t pfn;
|
||||||
|
struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < npages; i++) {
|
||||||
|
pfn = (pfn_t)intel_iommu_iova_to_pfn(domain,
|
||||||
|
gfn_to_gpa(gfn));
|
||||||
|
kvm_release_pfn_clean(pfn);
|
||||||
|
gfn++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_iommu_unmap_memslots(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
down_read(&kvm->slots_lock);
|
||||||
|
for (i = 0; i < kvm->nmemslots; i++) {
|
||||||
|
kvm_iommu_put_pages(kvm, kvm->memslots[i].base_gfn,
|
||||||
|
kvm->memslots[i].npages);
|
||||||
|
}
|
||||||
|
up_read(&kvm->slots_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_iommu_unmap_guest(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
struct kvm_assigned_dev_kernel *entry;
|
||||||
|
struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
|
||||||
|
|
||||||
|
/* check if iommu exists and in use */
|
||||||
|
if (!domain)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &kvm->arch.assigned_dev_head, list) {
|
||||||
|
printk(KERN_DEBUG "VT-d unmap: host bdf = %x:%x:%x\n",
|
||||||
|
entry->host_busnr,
|
||||||
|
PCI_SLOT(entry->host_devfn),
|
||||||
|
PCI_FUNC(entry->host_devfn));
|
||||||
|
|
||||||
|
/* detach kvm dmar domain */
|
||||||
|
intel_iommu_detach_dev(domain, entry->host_busnr,
|
||||||
|
entry->host_devfn);
|
||||||
|
}
|
||||||
|
kvm_iommu_unmap_memslots(kvm);
|
||||||
|
intel_iommu_domain_exit(domain);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue