Small release overall.
- x86: miscellaneous fixes, AVIC support (local APIC virtualization, AMD version) - s390: polling for interrupts after a VCPU goes to halted state is now enabled for s390; use hardware provided information about facility bits that do not need any hypervisor activity, and other fixes for cpu models and facilities; improve perf output; floating interrupt controller improvements. - MIPS: miscellaneous fixes - PPC: bugfixes only - ARM: 16K page size support, generic firmware probing layer for timer and GIC Christoffer Dall (KVM-ARM maintainer) says: "There are a few changes in this pull request touching things outside KVM, but they should all carry the necessary acks and it made the merge process much easier to do it this way." though actually the irqchip maintainers' acks didn't make it into the patches. Marc Zyngier, who is both irqchip and KVM-ARM maintainer, later acked at http://mid.gmane.org/573351D1.4060303@arm.com "more formally and for documentation purposes". -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJXPJjyAAoJEL/70l94x66DhioH/j4fwQ0FmfPSM9PArzaFHQdx LNE3tU4+bobbsy1BJr4DiAaOUQn3DAgwUvGLWXdeLiOXtoWXBiFHKaxlqEsCA6iQ xcTH1TgfxsVoqGQ6bT9X/2GCx70heYpcWG3f+zqBy7ZfFmQykLAC/HwOr52VQL8f hUFi3YmTHcnorp0n5Xg+9r3+RBS4D/kTbtdn6+KCLnPJ0RcgNkI3/NcafTemoofw Tkv8+YYFNvKV13qlIfVqxMa0GwWI3pP6YaNKhaS5XO8Pu16HuuF1JthJsUBDzwBa RInp8R9MoXgsBYhLpz3jc9vWG7G9yDl5LehsD9KOUGOaFYJ7sQN+QZOusa6jFgA= =llO5 -----END PGP SIGNATURE----- Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm Pull KVM updates from Paolo Bonzini: "Small release overall. x86: - miscellaneous fixes - AVIC support (local APIC virtualization, AMD version) s390: - polling for interrupts after a VCPU goes to halted state is now enabled for s390 - use hardware provided information about facility bits that do not need any hypervisor activity, and other fixes for cpu models and facilities - improve perf output - floating interrupt controller improvements. MIPS: - miscellaneous fixes PPC: - bugfixes only ARM: - 16K page size support - generic firmware probing layer for timer and GIC Christoffer Dall (KVM-ARM maintainer) says: "There are a few changes in this pull request touching things outside KVM, but they should all carry the necessary acks and it made the merge process much easier to do it this way." though actually the irqchip maintainers' acks didn't make it into the patches. Marc Zyngier, who is both irqchip and KVM-ARM maintainer, later acked at http://mid.gmane.org/573351D1.4060303@arm.com ('more formally and for documentation purposes')" * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (82 commits) KVM: MTRR: remove MSR 0x2f8 KVM: x86: make hwapic_isr_update and hwapic_irr_update look the same svm: Manage vcpu load/unload when enable AVIC svm: Do not intercept CR8 when enable AVIC svm: Do not expose x2APIC when enable AVIC KVM: x86: Introducing kvm_x86_ops.apicv_post_state_restore svm: Add VMEXIT handlers for AVIC svm: Add interrupt injection via AVIC KVM: x86: Detect and Initialize AVIC support svm: Introduce new AVIC VMCB registers KVM: split kvm_vcpu_wake_up from kvm_vcpu_kick KVM: x86: Introducing kvm_x86_ops VCPU blocking/unblocking hooks KVM: x86: Introducing kvm_x86_ops VM init/destroy hooks KVM: x86: Rename kvm_apic_get_reg to kvm_lapic_get_reg KVM: x86: Misc LAPIC changes to expose helper functions KVM: shrink halt polling even more for invalid wakeups KVM: s390: set halt polling to 80 microseconds KVM: halt_polling: provide a way to qualify wakeups during poll KVM: PPC: Book3S HV: Re-enable XICS fast path for irqfd-generated interrupts kvm: Conditionally register IRQ bypass consumer ...
This commit is contained in:
commit
7beaa24ba4
|
@ -199,8 +199,8 @@ Type: vm ioctl
|
|||
Parameters: vcpu id (apic id on x86)
|
||||
Returns: vcpu fd on success, -1 on error
|
||||
|
||||
This API adds a vcpu to a virtual machine. The vcpu id is a small integer
|
||||
in the range [0, max_vcpus).
|
||||
This API adds a vcpu to a virtual machine. No more than max_vcpus may be added.
|
||||
The vcpu id is an integer in the range [0, max_vcpu_id).
|
||||
|
||||
The recommended max_vcpus value can be retrieved using the KVM_CAP_NR_VCPUS of
|
||||
the KVM_CHECK_EXTENSION ioctl() at run-time.
|
||||
|
@ -212,6 +212,12 @@ cpus max.
|
|||
If the KVM_CAP_MAX_VCPUS does not exist, you should assume that max_vcpus is
|
||||
same as the value returned from KVM_CAP_NR_VCPUS.
|
||||
|
||||
The maximum possible value for max_vcpu_id can be retrieved using the
|
||||
KVM_CAP_MAX_VCPU_ID of the KVM_CHECK_EXTENSION ioctl() at run-time.
|
||||
|
||||
If the KVM_CAP_MAX_VCPU_ID does not exist, you should assume that max_vcpu_id
|
||||
is the same as the value returned from KVM_CAP_MAX_VCPUS.
|
||||
|
||||
On powerpc using book3s_hv mode, the vcpus are mapped onto virtual
|
||||
threads in one or more virtual CPU cores. (This is because the
|
||||
hardware requires all the hardware threads in a CPU core to be in the
|
||||
|
@ -3788,6 +3794,14 @@ a KVM_EXIT_IOAPIC_EOI vmexit will be reported to userspace.
|
|||
Fails if VCPU has already been created, or if the irqchip is already in the
|
||||
kernel (i.e. KVM_CREATE_IRQCHIP has already been called).
|
||||
|
||||
7.6 KVM_CAP_S390_RI
|
||||
|
||||
Architectures: s390
|
||||
Parameters: none
|
||||
|
||||
Allows use of runtime-instrumentation introduced with zEC12 processor.
|
||||
Will return -EINVAL if the machine does not support runtime-instrumentation.
|
||||
Will return -EBUSY if a VCPU has already been created.
|
||||
|
||||
8. Other capabilities.
|
||||
----------------------
|
||||
|
|
|
@ -11,6 +11,7 @@ FLIC provides support to
|
|||
- add interrupts (KVM_DEV_FLIC_ENQUEUE)
|
||||
- inspect currently pending interrupts (KVM_FLIC_GET_ALL_IRQS)
|
||||
- purge all pending floating interrupts (KVM_DEV_FLIC_CLEAR_IRQS)
|
||||
- purge one pending floating I/O interrupt (KVM_DEV_FLIC_CLEAR_IO_IRQ)
|
||||
- enable/disable for the guest transparent async page faults
|
||||
- register and modify adapter interrupt sources (KVM_DEV_FLIC_ADAPTER_*)
|
||||
|
||||
|
@ -40,6 +41,11 @@ Groups:
|
|||
Simply deletes all elements from the list of currently pending floating
|
||||
interrupts. No interrupts are injected into the guest.
|
||||
|
||||
KVM_DEV_FLIC_CLEAR_IO_IRQ
|
||||
Deletes one (if any) I/O interrupt for a subchannel identified by the
|
||||
subsystem identification word passed via the buffer specified by
|
||||
attr->addr (address) and attr->attr (length).
|
||||
|
||||
KVM_DEV_FLIC_APF_ENABLE
|
||||
Enables async page faults for the guest. So in case of a major page fault
|
||||
the host is allowed to handle this async and continues the guest.
|
||||
|
@ -68,7 +74,7 @@ struct kvm_s390_io_adapter {
|
|||
|
||||
KVM_DEV_FLIC_ADAPTER_MODIFY
|
||||
Modifies attributes of an existing I/O adapter interrupt source. Takes
|
||||
a kvm_s390_io_adapter_req specifiying the adapter and the operation:
|
||||
a kvm_s390_io_adapter_req specifying the adapter and the operation:
|
||||
|
||||
struct kvm_s390_io_adapter_req {
|
||||
__u32 id;
|
||||
|
@ -94,3 +100,9 @@ struct kvm_s390_io_adapter_req {
|
|||
KVM_S390_IO_ADAPTER_UNMAP
|
||||
release a userspace page for the translated address specified in addr
|
||||
from the list of mappings
|
||||
|
||||
Note: The KVM_SET_DEVICE_ATTR/KVM_GET_DEVICE_ATTR device ioctls executed on
|
||||
FLIC with an unknown group or attribute gives the error code EINVAL (instead of
|
||||
ENXIO, as specified in the API documentation). It is not possible to conclude
|
||||
that a FLIC operation is unavailable based on the error code resulting from a
|
||||
usage attempt.
|
||||
|
|
|
@ -187,6 +187,7 @@ struct kvm_vm_stat {
|
|||
struct kvm_vcpu_stat {
|
||||
u32 halt_successful_poll;
|
||||
u32 halt_attempted_poll;
|
||||
u32 halt_poll_invalid;
|
||||
u32 halt_wakeup;
|
||||
u32 hvc_exit_stat;
|
||||
u64 wfe_exit_stat;
|
||||
|
@ -290,6 +291,7 @@ static inline void kvm_arch_hardware_unsetup(void) {}
|
|||
static inline void kvm_arch_sync_events(struct kvm *kvm) {}
|
||||
static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
|
||||
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
static inline void kvm_arm_init_debug(void) {}
|
||||
static inline void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) {}
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include <linux/highmem.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/stage2_pgtable.h>
|
||||
|
||||
int create_hyp_mappings(void *from, void *to);
|
||||
int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
|
||||
|
@ -106,14 +107,16 @@ static inline void kvm_clean_pte(pte_t *pte)
|
|||
clean_pte_table(pte);
|
||||
}
|
||||
|
||||
static inline void kvm_set_s2pte_writable(pte_t *pte)
|
||||
static inline pte_t kvm_s2pte_mkwrite(pte_t pte)
|
||||
{
|
||||
pte_val(*pte) |= L_PTE_S2_RDWR;
|
||||
pte_val(pte) |= L_PTE_S2_RDWR;
|
||||
return pte;
|
||||
}
|
||||
|
||||
static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
|
||||
static inline pmd_t kvm_s2pmd_mkwrite(pmd_t pmd)
|
||||
{
|
||||
pmd_val(*pmd) |= L_PMD_S2_RDWR;
|
||||
pmd_val(pmd) |= L_PMD_S2_RDWR;
|
||||
return pmd;
|
||||
}
|
||||
|
||||
static inline void kvm_set_s2pte_readonly(pte_t *pte)
|
||||
|
@ -136,22 +139,6 @@ static inline bool kvm_s2pmd_readonly(pmd_t *pmd)
|
|||
return (pmd_val(*pmd) & L_PMD_S2_RDWR) == L_PMD_S2_RDONLY;
|
||||
}
|
||||
|
||||
|
||||
/* Open coded p*d_addr_end that can deal with 64bit addresses */
|
||||
#define kvm_pgd_addr_end(addr, end) \
|
||||
({ u64 __boundary = ((addr) + PGDIR_SIZE) & PGDIR_MASK; \
|
||||
(__boundary - 1 < (end) - 1)? __boundary: (end); \
|
||||
})
|
||||
|
||||
#define kvm_pud_addr_end(addr,end) (end)
|
||||
|
||||
#define kvm_pmd_addr_end(addr, end) \
|
||||
({ u64 __boundary = ((addr) + PMD_SIZE) & PMD_MASK; \
|
||||
(__boundary - 1 < (end) - 1)? __boundary: (end); \
|
||||
})
|
||||
|
||||
#define kvm_pgd_index(addr) pgd_index(addr)
|
||||
|
||||
static inline bool kvm_page_empty(void *ptr)
|
||||
{
|
||||
struct page *ptr_page = virt_to_page(ptr);
|
||||
|
@ -160,19 +147,11 @@ static inline bool kvm_page_empty(void *ptr)
|
|||
|
||||
#define kvm_pte_table_empty(kvm, ptep) kvm_page_empty(ptep)
|
||||
#define kvm_pmd_table_empty(kvm, pmdp) kvm_page_empty(pmdp)
|
||||
#define kvm_pud_table_empty(kvm, pudp) (0)
|
||||
#define kvm_pud_table_empty(kvm, pudp) false
|
||||
|
||||
#define KVM_PREALLOC_LEVEL 0
|
||||
|
||||
static inline void *kvm_get_hwpgd(struct kvm *kvm)
|
||||
{
|
||||
return kvm->arch.pgd;
|
||||
}
|
||||
|
||||
static inline unsigned int kvm_get_hwpgd_size(void)
|
||||
{
|
||||
return PTRS_PER_S2_PGD * sizeof(pgd_t);
|
||||
}
|
||||
#define hyp_pte_table_empty(ptep) kvm_page_empty(ptep)
|
||||
#define hyp_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
|
||||
#define hyp_pud_table_empty(pudp) false
|
||||
|
||||
struct kvm;
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2016 - ARM Ltd
|
||||
*
|
||||
* stage2 page table helpers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ARM_S2_PGTABLE_H_
|
||||
#define __ARM_S2_PGTABLE_H_
|
||||
|
||||
#define stage2_pgd_none(pgd) pgd_none(pgd)
|
||||
#define stage2_pgd_clear(pgd) pgd_clear(pgd)
|
||||
#define stage2_pgd_present(pgd) pgd_present(pgd)
|
||||
#define stage2_pgd_populate(pgd, pud) pgd_populate(NULL, pgd, pud)
|
||||
#define stage2_pud_offset(pgd, address) pud_offset(pgd, address)
|
||||
#define stage2_pud_free(pud) pud_free(NULL, pud)
|
||||
|
||||
#define stage2_pud_none(pud) pud_none(pud)
|
||||
#define stage2_pud_clear(pud) pud_clear(pud)
|
||||
#define stage2_pud_present(pud) pud_present(pud)
|
||||
#define stage2_pud_populate(pud, pmd) pud_populate(NULL, pud, pmd)
|
||||
#define stage2_pmd_offset(pud, address) pmd_offset(pud, address)
|
||||
#define stage2_pmd_free(pmd) pmd_free(NULL, pmd)
|
||||
|
||||
#define stage2_pud_huge(pud) pud_huge(pud)
|
||||
|
||||
/* Open coded p*d_addr_end that can deal with 64bit addresses */
|
||||
static inline phys_addr_t stage2_pgd_addr_end(phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t boundary = (addr + PGDIR_SIZE) & PGDIR_MASK;
|
||||
|
||||
return (boundary - 1 < end - 1) ? boundary : end;
|
||||
}
|
||||
|
||||
#define stage2_pud_addr_end(addr, end) (end)
|
||||
|
||||
static inline phys_addr_t stage2_pmd_addr_end(phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t boundary = (addr + PMD_SIZE) & PMD_MASK;
|
||||
|
||||
return (boundary - 1 < end - 1) ? boundary : end;
|
||||
}
|
||||
|
||||
#define stage2_pgd_index(addr) pgd_index(addr)
|
||||
|
||||
#define stage2_pte_table_empty(ptep) kvm_page_empty(ptep)
|
||||
#define stage2_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
|
||||
#define stage2_pud_table_empty(pudp) false
|
||||
|
||||
#endif /* __ARM_S2_PGTABLE_H_ */
|
|
@ -444,7 +444,7 @@ static void update_vttbr(struct kvm *kvm)
|
|||
kvm_next_vmid &= (1 << kvm_vmid_bits) - 1;
|
||||
|
||||
/* update vttbr to be used with the new vmid */
|
||||
pgd_phys = virt_to_phys(kvm_get_hwpgd(kvm));
|
||||
pgd_phys = virt_to_phys(kvm->arch.pgd);
|
||||
BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK);
|
||||
vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK(kvm_vmid_bits);
|
||||
kvm->arch.vttbr = pgd_phys | vmid;
|
||||
|
|
|
@ -43,11 +43,9 @@ static unsigned long hyp_idmap_start;
|
|||
static unsigned long hyp_idmap_end;
|
||||
static phys_addr_t hyp_idmap_vector;
|
||||
|
||||
#define S2_PGD_SIZE (PTRS_PER_S2_PGD * sizeof(pgd_t))
|
||||
#define hyp_pgd_order get_order(PTRS_PER_PGD * sizeof(pgd_t))
|
||||
|
||||
#define kvm_pmd_huge(_x) (pmd_huge(_x) || pmd_trans_huge(_x))
|
||||
#define kvm_pud_huge(_x) pud_huge(_x)
|
||||
|
||||
#define KVM_S2PTE_FLAG_IS_IOMAP (1UL << 0)
|
||||
#define KVM_S2_FLAG_LOGGING_ACTIVE (1UL << 1)
|
||||
|
||||
|
@ -69,14 +67,7 @@ void kvm_flush_remote_tlbs(struct kvm *kvm)
|
|||
|
||||
static void kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
|
||||
{
|
||||
/*
|
||||
* This function also gets called when dealing with HYP page
|
||||
* tables. As HYP doesn't have an associated struct kvm (and
|
||||
* the HYP page tables are fairly static), we don't do
|
||||
* anything there.
|
||||
*/
|
||||
if (kvm)
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, kvm, ipa);
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, kvm, ipa);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -115,7 +106,7 @@ static bool kvm_is_device_pfn(unsigned long pfn)
|
|||
*/
|
||||
static void stage2_dissolve_pmd(struct kvm *kvm, phys_addr_t addr, pmd_t *pmd)
|
||||
{
|
||||
if (!kvm_pmd_huge(*pmd))
|
||||
if (!pmd_thp_or_huge(*pmd))
|
||||
return;
|
||||
|
||||
pmd_clear(pmd);
|
||||
|
@ -155,29 +146,29 @@ static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc)
|
|||
return p;
|
||||
}
|
||||
|
||||
static void clear_pgd_entry(struct kvm *kvm, pgd_t *pgd, phys_addr_t addr)
|
||||
static void clear_stage2_pgd_entry(struct kvm *kvm, pgd_t *pgd, phys_addr_t addr)
|
||||
{
|
||||
pud_t *pud_table __maybe_unused = pud_offset(pgd, 0);
|
||||
pgd_clear(pgd);
|
||||
pud_t *pud_table __maybe_unused = stage2_pud_offset(pgd, 0UL);
|
||||
stage2_pgd_clear(pgd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
pud_free(NULL, pud_table);
|
||||
stage2_pud_free(pud_table);
|
||||
put_page(virt_to_page(pgd));
|
||||
}
|
||||
|
||||
static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
|
||||
static void clear_stage2_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
|
||||
{
|
||||
pmd_t *pmd_table = pmd_offset(pud, 0);
|
||||
VM_BUG_ON(pud_huge(*pud));
|
||||
pud_clear(pud);
|
||||
pmd_t *pmd_table __maybe_unused = stage2_pmd_offset(pud, 0);
|
||||
VM_BUG_ON(stage2_pud_huge(*pud));
|
||||
stage2_pud_clear(pud);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
pmd_free(NULL, pmd_table);
|
||||
stage2_pmd_free(pmd_table);
|
||||
put_page(virt_to_page(pud));
|
||||
}
|
||||
|
||||
static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
|
||||
static void clear_stage2_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
|
||||
{
|
||||
pte_t *pte_table = pte_offset_kernel(pmd, 0);
|
||||
VM_BUG_ON(kvm_pmd_huge(*pmd));
|
||||
VM_BUG_ON(pmd_thp_or_huge(*pmd));
|
||||
pmd_clear(pmd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
pte_free_kernel(NULL, pte_table);
|
||||
|
@ -204,7 +195,7 @@ static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
|
|||
* the corresponding TLBs, we call kvm_flush_dcache_p*() to make sure
|
||||
* the IO subsystem will never hit in the cache.
|
||||
*/
|
||||
static void unmap_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||
static void unmap_stage2_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t start_addr = addr;
|
||||
|
@ -226,21 +217,21 @@ static void unmap_ptes(struct kvm *kvm, pmd_t *pmd,
|
|||
}
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
|
||||
if (kvm_pte_table_empty(kvm, start_pte))
|
||||
clear_pmd_entry(kvm, pmd, start_addr);
|
||||
if (stage2_pte_table_empty(start_pte))
|
||||
clear_stage2_pmd_entry(kvm, pmd, start_addr);
|
||||
}
|
||||
|
||||
static void unmap_pmds(struct kvm *kvm, pud_t *pud,
|
||||
static void unmap_stage2_pmds(struct kvm *kvm, pud_t *pud,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t next, start_addr = addr;
|
||||
pmd_t *pmd, *start_pmd;
|
||||
|
||||
start_pmd = pmd = pmd_offset(pud, addr);
|
||||
start_pmd = pmd = stage2_pmd_offset(pud, addr);
|
||||
do {
|
||||
next = kvm_pmd_addr_end(addr, end);
|
||||
next = stage2_pmd_addr_end(addr, end);
|
||||
if (!pmd_none(*pmd)) {
|
||||
if (kvm_pmd_huge(*pmd)) {
|
||||
if (pmd_thp_or_huge(*pmd)) {
|
||||
pmd_t old_pmd = *pmd;
|
||||
|
||||
pmd_clear(pmd);
|
||||
|
@ -250,57 +241,64 @@ static void unmap_pmds(struct kvm *kvm, pud_t *pud,
|
|||
|
||||
put_page(virt_to_page(pmd));
|
||||
} else {
|
||||
unmap_ptes(kvm, pmd, addr, next);
|
||||
unmap_stage2_ptes(kvm, pmd, addr, next);
|
||||
}
|
||||
}
|
||||
} while (pmd++, addr = next, addr != end);
|
||||
|
||||
if (kvm_pmd_table_empty(kvm, start_pmd))
|
||||
clear_pud_entry(kvm, pud, start_addr);
|
||||
if (stage2_pmd_table_empty(start_pmd))
|
||||
clear_stage2_pud_entry(kvm, pud, start_addr);
|
||||
}
|
||||
|
||||
static void unmap_puds(struct kvm *kvm, pgd_t *pgd,
|
||||
static void unmap_stage2_puds(struct kvm *kvm, pgd_t *pgd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t next, start_addr = addr;
|
||||
pud_t *pud, *start_pud;
|
||||
|
||||
start_pud = pud = pud_offset(pgd, addr);
|
||||
start_pud = pud = stage2_pud_offset(pgd, addr);
|
||||
do {
|
||||
next = kvm_pud_addr_end(addr, end);
|
||||
if (!pud_none(*pud)) {
|
||||
if (pud_huge(*pud)) {
|
||||
next = stage2_pud_addr_end(addr, end);
|
||||
if (!stage2_pud_none(*pud)) {
|
||||
if (stage2_pud_huge(*pud)) {
|
||||
pud_t old_pud = *pud;
|
||||
|
||||
pud_clear(pud);
|
||||
stage2_pud_clear(pud);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
|
||||
kvm_flush_dcache_pud(old_pud);
|
||||
|
||||
put_page(virt_to_page(pud));
|
||||
} else {
|
||||
unmap_pmds(kvm, pud, addr, next);
|
||||
unmap_stage2_pmds(kvm, pud, addr, next);
|
||||
}
|
||||
}
|
||||
} while (pud++, addr = next, addr != end);
|
||||
|
||||
if (kvm_pud_table_empty(kvm, start_pud))
|
||||
clear_pgd_entry(kvm, pgd, start_addr);
|
||||
if (stage2_pud_table_empty(start_pud))
|
||||
clear_stage2_pgd_entry(kvm, pgd, start_addr);
|
||||
}
|
||||
|
||||
|
||||
static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
|
||||
phys_addr_t start, u64 size)
|
||||
/**
|
||||
* unmap_stage2_range -- Clear stage2 page table entries to unmap a range
|
||||
* @kvm: The VM pointer
|
||||
* @start: The intermediate physical base address of the range to unmap
|
||||
* @size: The size of the area to unmap
|
||||
*
|
||||
* Clear a range of stage-2 mappings, lowering the various ref-counts. Must
|
||||
* be called while holding mmu_lock (unless for freeing the stage2 pgd before
|
||||
* destroying the VM), otherwise another faulting VCPU may come in and mess
|
||||
* with things behind our backs.
|
||||
*/
|
||||
static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
phys_addr_t addr = start, end = start + size;
|
||||
phys_addr_t next;
|
||||
|
||||
pgd = pgdp + kvm_pgd_index(addr);
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(addr);
|
||||
do {
|
||||
next = kvm_pgd_addr_end(addr, end);
|
||||
if (!pgd_none(*pgd))
|
||||
unmap_puds(kvm, pgd, addr, next);
|
||||
next = stage2_pgd_addr_end(addr, end);
|
||||
if (!stage2_pgd_none(*pgd))
|
||||
unmap_stage2_puds(kvm, pgd, addr, next);
|
||||
} while (pgd++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
|
@ -322,11 +320,11 @@ static void stage2_flush_pmds(struct kvm *kvm, pud_t *pud,
|
|||
pmd_t *pmd;
|
||||
phys_addr_t next;
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
pmd = stage2_pmd_offset(pud, addr);
|
||||
do {
|
||||
next = kvm_pmd_addr_end(addr, end);
|
||||
next = stage2_pmd_addr_end(addr, end);
|
||||
if (!pmd_none(*pmd)) {
|
||||
if (kvm_pmd_huge(*pmd))
|
||||
if (pmd_thp_or_huge(*pmd))
|
||||
kvm_flush_dcache_pmd(*pmd);
|
||||
else
|
||||
stage2_flush_ptes(kvm, pmd, addr, next);
|
||||
|
@ -340,11 +338,11 @@ static void stage2_flush_puds(struct kvm *kvm, pgd_t *pgd,
|
|||
pud_t *pud;
|
||||
phys_addr_t next;
|
||||
|
||||
pud = pud_offset(pgd, addr);
|
||||
pud = stage2_pud_offset(pgd, addr);
|
||||
do {
|
||||
next = kvm_pud_addr_end(addr, end);
|
||||
if (!pud_none(*pud)) {
|
||||
if (pud_huge(*pud))
|
||||
next = stage2_pud_addr_end(addr, end);
|
||||
if (!stage2_pud_none(*pud)) {
|
||||
if (stage2_pud_huge(*pud))
|
||||
kvm_flush_dcache_pud(*pud);
|
||||
else
|
||||
stage2_flush_pmds(kvm, pud, addr, next);
|
||||
|
@ -360,9 +358,9 @@ static void stage2_flush_memslot(struct kvm *kvm,
|
|||
phys_addr_t next;
|
||||
pgd_t *pgd;
|
||||
|
||||
pgd = kvm->arch.pgd + kvm_pgd_index(addr);
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(addr);
|
||||
do {
|
||||
next = kvm_pgd_addr_end(addr, end);
|
||||
next = stage2_pgd_addr_end(addr, end);
|
||||
stage2_flush_puds(kvm, pgd, addr, next);
|
||||
} while (pgd++, addr = next, addr != end);
|
||||
}
|
||||
|
@ -391,6 +389,100 @@ static void stage2_flush_vm(struct kvm *kvm)
|
|||
srcu_read_unlock(&kvm->srcu, idx);
|
||||
}
|
||||
|
||||
static void clear_hyp_pgd_entry(pgd_t *pgd)
|
||||
{
|
||||
pud_t *pud_table __maybe_unused = pud_offset(pgd, 0UL);
|
||||
pgd_clear(pgd);
|
||||
pud_free(NULL, pud_table);
|
||||
put_page(virt_to_page(pgd));
|
||||
}
|
||||
|
||||
static void clear_hyp_pud_entry(pud_t *pud)
|
||||
{
|
||||
pmd_t *pmd_table __maybe_unused = pmd_offset(pud, 0);
|
||||
VM_BUG_ON(pud_huge(*pud));
|
||||
pud_clear(pud);
|
||||
pmd_free(NULL, pmd_table);
|
||||
put_page(virt_to_page(pud));
|
||||
}
|
||||
|
||||
static void clear_hyp_pmd_entry(pmd_t *pmd)
|
||||
{
|
||||
pte_t *pte_table = pte_offset_kernel(pmd, 0);
|
||||
VM_BUG_ON(pmd_thp_or_huge(*pmd));
|
||||
pmd_clear(pmd);
|
||||
pte_free_kernel(NULL, pte_table);
|
||||
put_page(virt_to_page(pmd));
|
||||
}
|
||||
|
||||
static void unmap_hyp_ptes(pmd_t *pmd, phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
pte_t *pte, *start_pte;
|
||||
|
||||
start_pte = pte = pte_offset_kernel(pmd, addr);
|
||||
do {
|
||||
if (!pte_none(*pte)) {
|
||||
kvm_set_pte(pte, __pte(0));
|
||||
put_page(virt_to_page(pte));
|
||||
}
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
|
||||
if (hyp_pte_table_empty(start_pte))
|
||||
clear_hyp_pmd_entry(pmd);
|
||||
}
|
||||
|
||||
static void unmap_hyp_pmds(pud_t *pud, phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t next;
|
||||
pmd_t *pmd, *start_pmd;
|
||||
|
||||
start_pmd = pmd = pmd_offset(pud, addr);
|
||||
do {
|
||||
next = pmd_addr_end(addr, end);
|
||||
/* Hyp doesn't use huge pmds */
|
||||
if (!pmd_none(*pmd))
|
||||
unmap_hyp_ptes(pmd, addr, next);
|
||||
} while (pmd++, addr = next, addr != end);
|
||||
|
||||
if (hyp_pmd_table_empty(start_pmd))
|
||||
clear_hyp_pud_entry(pud);
|
||||
}
|
||||
|
||||
static void unmap_hyp_puds(pgd_t *pgd, phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t next;
|
||||
pud_t *pud, *start_pud;
|
||||
|
||||
start_pud = pud = pud_offset(pgd, addr);
|
||||
do {
|
||||
next = pud_addr_end(addr, end);
|
||||
/* Hyp doesn't use huge puds */
|
||||
if (!pud_none(*pud))
|
||||
unmap_hyp_pmds(pud, addr, next);
|
||||
} while (pud++, addr = next, addr != end);
|
||||
|
||||
if (hyp_pud_table_empty(start_pud))
|
||||
clear_hyp_pgd_entry(pgd);
|
||||
}
|
||||
|
||||
static void unmap_hyp_range(pgd_t *pgdp, phys_addr_t start, u64 size)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
phys_addr_t addr = start, end = start + size;
|
||||
phys_addr_t next;
|
||||
|
||||
/*
|
||||
* We don't unmap anything from HYP, except at the hyp tear down.
|
||||
* Hence, we don't have to invalidate the TLBs here.
|
||||
*/
|
||||
pgd = pgdp + pgd_index(addr);
|
||||
do {
|
||||
next = pgd_addr_end(addr, end);
|
||||
if (!pgd_none(*pgd))
|
||||
unmap_hyp_puds(pgd, addr, next);
|
||||
} while (pgd++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
/**
|
||||
* free_boot_hyp_pgd - free HYP boot page tables
|
||||
*
|
||||
|
@ -401,14 +493,14 @@ void free_boot_hyp_pgd(void)
|
|||
mutex_lock(&kvm_hyp_pgd_mutex);
|
||||
|
||||
if (boot_hyp_pgd) {
|
||||
unmap_range(NULL, boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE);
|
||||
unmap_range(NULL, boot_hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE);
|
||||
unmap_hyp_range(boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE);
|
||||
unmap_hyp_range(boot_hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE);
|
||||
free_pages((unsigned long)boot_hyp_pgd, hyp_pgd_order);
|
||||
boot_hyp_pgd = NULL;
|
||||
}
|
||||
|
||||
if (hyp_pgd)
|
||||
unmap_range(NULL, hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE);
|
||||
unmap_hyp_range(hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE);
|
||||
|
||||
mutex_unlock(&kvm_hyp_pgd_mutex);
|
||||
}
|
||||
|
@ -433,9 +525,9 @@ void free_hyp_pgds(void)
|
|||
|
||||
if (hyp_pgd) {
|
||||
for (addr = PAGE_OFFSET; virt_addr_valid(addr); addr += PGDIR_SIZE)
|
||||
unmap_range(NULL, hyp_pgd, KERN_TO_HYP(addr), PGDIR_SIZE);
|
||||
unmap_hyp_range(hyp_pgd, KERN_TO_HYP(addr), PGDIR_SIZE);
|
||||
for (addr = VMALLOC_START; is_vmalloc_addr((void*)addr); addr += PGDIR_SIZE)
|
||||
unmap_range(NULL, hyp_pgd, KERN_TO_HYP(addr), PGDIR_SIZE);
|
||||
unmap_hyp_range(hyp_pgd, KERN_TO_HYP(addr), PGDIR_SIZE);
|
||||
|
||||
free_pages((unsigned long)hyp_pgd, hyp_pgd_order);
|
||||
hyp_pgd = NULL;
|
||||
|
@ -645,20 +737,6 @@ int create_hyp_io_mappings(void *from, void *to, phys_addr_t phys_addr)
|
|||
__phys_to_pfn(phys_addr), PAGE_HYP_DEVICE);
|
||||
}
|
||||
|
||||
/* Free the HW pgd, one page at a time */
|
||||
static void kvm_free_hwpgd(void *hwpgd)
|
||||
{
|
||||
free_pages_exact(hwpgd, kvm_get_hwpgd_size());
|
||||
}
|
||||
|
||||
/* Allocate the HW PGD, making sure that each page gets its own refcount */
|
||||
static void *kvm_alloc_hwpgd(void)
|
||||
{
|
||||
unsigned int size = kvm_get_hwpgd_size();
|
||||
|
||||
return alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
|
||||
* @kvm: The KVM struct pointer for the VM.
|
||||
|
@ -673,81 +751,22 @@ static void *kvm_alloc_hwpgd(void)
|
|||
int kvm_alloc_stage2_pgd(struct kvm *kvm)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
void *hwpgd;
|
||||
|
||||
if (kvm->arch.pgd != NULL) {
|
||||
kvm_err("kvm_arch already initialized?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hwpgd = kvm_alloc_hwpgd();
|
||||
if (!hwpgd)
|
||||
/* Allocate the HW PGD, making sure that each page gets its own refcount */
|
||||
pgd = alloc_pages_exact(S2_PGD_SIZE, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!pgd)
|
||||
return -ENOMEM;
|
||||
|
||||
/* When the kernel uses more levels of page tables than the
|
||||
* guest, we allocate a fake PGD and pre-populate it to point
|
||||
* to the next-level page table, which will be the real
|
||||
* initial page table pointed to by the VTTBR.
|
||||
*
|
||||
* When KVM_PREALLOC_LEVEL==2, we allocate a single page for
|
||||
* the PMD and the kernel will use folded pud.
|
||||
* When KVM_PREALLOC_LEVEL==1, we allocate 2 consecutive PUD
|
||||
* pages.
|
||||
*/
|
||||
if (KVM_PREALLOC_LEVEL > 0) {
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Allocate fake pgd for the page table manipulation macros to
|
||||
* work. This is not used by the hardware and we have no
|
||||
* alignment requirement for this allocation.
|
||||
*/
|
||||
pgd = kmalloc(PTRS_PER_S2_PGD * sizeof(pgd_t),
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
|
||||
if (!pgd) {
|
||||
kvm_free_hwpgd(hwpgd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Plug the HW PGD into the fake one. */
|
||||
for (i = 0; i < PTRS_PER_S2_PGD; i++) {
|
||||
if (KVM_PREALLOC_LEVEL == 1)
|
||||
pgd_populate(NULL, pgd + i,
|
||||
(pud_t *)hwpgd + i * PTRS_PER_PUD);
|
||||
else if (KVM_PREALLOC_LEVEL == 2)
|
||||
pud_populate(NULL, pud_offset(pgd, 0) + i,
|
||||
(pmd_t *)hwpgd + i * PTRS_PER_PMD);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Allocate actual first-level Stage-2 page table used by the
|
||||
* hardware for Stage-2 page table walks.
|
||||
*/
|
||||
pgd = (pgd_t *)hwpgd;
|
||||
}
|
||||
|
||||
kvm_clean_pgd(pgd);
|
||||
kvm->arch.pgd = pgd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* unmap_stage2_range -- Clear stage2 page table entries to unmap a range
|
||||
* @kvm: The VM pointer
|
||||
* @start: The intermediate physical base address of the range to unmap
|
||||
* @size: The size of the area to unmap
|
||||
*
|
||||
* Clear a range of stage-2 mappings, lowering the various ref-counts. Must
|
||||
* be called while holding mmu_lock (unless for freeing the stage2 pgd before
|
||||
* destroying the VM), otherwise another faulting VCPU may come in and mess
|
||||
* with things behind our backs.
|
||||
*/
|
||||
static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
|
||||
{
|
||||
unmap_range(kvm, kvm->arch.pgd, start, size);
|
||||
}
|
||||
|
||||
static void stage2_unmap_memslot(struct kvm *kvm,
|
||||
struct kvm_memory_slot *memslot)
|
||||
{
|
||||
|
@ -830,10 +849,8 @@ void kvm_free_stage2_pgd(struct kvm *kvm)
|
|||
return;
|
||||
|
||||
unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
|
||||
kvm_free_hwpgd(kvm_get_hwpgd(kvm));
|
||||
if (KVM_PREALLOC_LEVEL > 0)
|
||||
kfree(kvm->arch.pgd);
|
||||
|
||||
/* Free the HW pgd, one page at a time */
|
||||
free_pages_exact(kvm->arch.pgd, S2_PGD_SIZE);
|
||||
kvm->arch.pgd = NULL;
|
||||
}
|
||||
|
||||
|
@ -843,16 +860,16 @@ static pud_t *stage2_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache
|
|||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
|
||||
pgd = kvm->arch.pgd + kvm_pgd_index(addr);
|
||||
if (WARN_ON(pgd_none(*pgd))) {
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(addr);
|
||||
if (WARN_ON(stage2_pgd_none(*pgd))) {
|
||||
if (!cache)
|
||||
return NULL;
|
||||
pud = mmu_memory_cache_alloc(cache);
|
||||
pgd_populate(NULL, pgd, pud);
|
||||
stage2_pgd_populate(pgd, pud);
|
||||
get_page(virt_to_page(pgd));
|
||||
}
|
||||
|
||||
return pud_offset(pgd, addr);
|
||||
return stage2_pud_offset(pgd, addr);
|
||||
}
|
||||
|
||||
static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
||||
|
@ -862,15 +879,15 @@ static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache
|
|||
pmd_t *pmd;
|
||||
|
||||
pud = stage2_get_pud(kvm, cache, addr);
|
||||
if (pud_none(*pud)) {
|
||||
if (stage2_pud_none(*pud)) {
|
||||
if (!cache)
|
||||
return NULL;
|
||||
pmd = mmu_memory_cache_alloc(cache);
|
||||
pud_populate(NULL, pud, pmd);
|
||||
stage2_pud_populate(pud, pmd);
|
||||
get_page(virt_to_page(pud));
|
||||
}
|
||||
|
||||
return pmd_offset(pud, addr);
|
||||
return stage2_pmd_offset(pud, addr);
|
||||
}
|
||||
|
||||
static int stage2_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache
|
||||
|
@ -893,11 +910,14 @@ static int stage2_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache
|
|||
VM_BUG_ON(pmd_present(*pmd) && pmd_pfn(*pmd) != pmd_pfn(*new_pmd));
|
||||
|
||||
old_pmd = *pmd;
|
||||
kvm_set_pmd(pmd, *new_pmd);
|
||||
if (pmd_present(old_pmd))
|
||||
if (pmd_present(old_pmd)) {
|
||||
pmd_clear(pmd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
else
|
||||
} else {
|
||||
get_page(virt_to_page(pmd));
|
||||
}
|
||||
|
||||
kvm_set_pmd(pmd, *new_pmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -946,15 +966,38 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
|
|||
|
||||
/* Create 2nd stage page table mapping - Level 3 */
|
||||
old_pte = *pte;
|
||||
kvm_set_pte(pte, *new_pte);
|
||||
if (pte_present(old_pte))
|
||||
if (pte_present(old_pte)) {
|
||||
kvm_set_pte(pte, __pte(0));
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
else
|
||||
} else {
|
||||
get_page(virt_to_page(pte));
|
||||
}
|
||||
|
||||
kvm_set_pte(pte, *new_pte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
|
||||
static int stage2_ptep_test_and_clear_young(pte_t *pte)
|
||||
{
|
||||
if (pte_young(*pte)) {
|
||||
*pte = pte_mkold(*pte);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int stage2_ptep_test_and_clear_young(pte_t *pte)
|
||||
{
|
||||
return __ptep_test_and_clear_young(pte);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int stage2_pmdp_test_and_clear_young(pmd_t *pmd)
|
||||
{
|
||||
return stage2_ptep_test_and_clear_young((pte_t *)pmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_phys_addr_ioremap - map a device range to guest IPA
|
||||
*
|
||||
|
@ -978,7 +1021,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
|
|||
pte_t pte = pfn_pte(pfn, PAGE_S2_DEVICE);
|
||||
|
||||
if (writable)
|
||||
kvm_set_s2pte_writable(&pte);
|
||||
pte = kvm_s2pte_mkwrite(pte);
|
||||
|
||||
ret = mmu_topup_memory_cache(&cache, KVM_MMU_CACHE_MIN_PAGES,
|
||||
KVM_NR_MEM_OBJS);
|
||||
|
@ -1078,12 +1121,12 @@ static void stage2_wp_pmds(pud_t *pud, phys_addr_t addr, phys_addr_t end)
|
|||
pmd_t *pmd;
|
||||
phys_addr_t next;
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
pmd = stage2_pmd_offset(pud, addr);
|
||||
|
||||
do {
|
||||
next = kvm_pmd_addr_end(addr, end);
|
||||
next = stage2_pmd_addr_end(addr, end);
|
||||
if (!pmd_none(*pmd)) {
|
||||
if (kvm_pmd_huge(*pmd)) {
|
||||
if (pmd_thp_or_huge(*pmd)) {
|
||||
if (!kvm_s2pmd_readonly(pmd))
|
||||
kvm_set_s2pmd_readonly(pmd);
|
||||
} else {
|
||||
|
@ -1106,12 +1149,12 @@ static void stage2_wp_puds(pgd_t *pgd, phys_addr_t addr, phys_addr_t end)
|
|||
pud_t *pud;
|
||||
phys_addr_t next;
|
||||
|
||||
pud = pud_offset(pgd, addr);
|
||||
pud = stage2_pud_offset(pgd, addr);
|
||||
do {
|
||||
next = kvm_pud_addr_end(addr, end);
|
||||
if (!pud_none(*pud)) {
|
||||
next = stage2_pud_addr_end(addr, end);
|
||||
if (!stage2_pud_none(*pud)) {
|
||||
/* TODO:PUD not supported, revisit later if supported */
|
||||
BUG_ON(kvm_pud_huge(*pud));
|
||||
BUG_ON(stage2_pud_huge(*pud));
|
||||
stage2_wp_pmds(pud, addr, next);
|
||||
}
|
||||
} while (pud++, addr = next, addr != end);
|
||||
|
@ -1128,7 +1171,7 @@ static void stage2_wp_range(struct kvm *kvm, phys_addr_t addr, phys_addr_t end)
|
|||
pgd_t *pgd;
|
||||
phys_addr_t next;
|
||||
|
||||
pgd = kvm->arch.pgd + kvm_pgd_index(addr);
|
||||
pgd = kvm->arch.pgd + stage2_pgd_index(addr);
|
||||
do {
|
||||
/*
|
||||
* Release kvm_mmu_lock periodically if the memory region is
|
||||
|
@ -1140,8 +1183,8 @@ static void stage2_wp_range(struct kvm *kvm, phys_addr_t addr, phys_addr_t end)
|
|||
if (need_resched() || spin_needbreak(&kvm->mmu_lock))
|
||||
cond_resched_lock(&kvm->mmu_lock);
|
||||
|
||||
next = kvm_pgd_addr_end(addr, end);
|
||||
if (pgd_present(*pgd))
|
||||
next = stage2_pgd_addr_end(addr, end);
|
||||
if (stage2_pgd_present(*pgd))
|
||||
stage2_wp_puds(pgd, addr, next);
|
||||
} while (pgd++, addr = next, addr != end);
|
||||
}
|
||||
|
@ -1320,7 +1363,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
pmd_t new_pmd = pfn_pmd(pfn, mem_type);
|
||||
new_pmd = pmd_mkhuge(new_pmd);
|
||||
if (writable) {
|
||||
kvm_set_s2pmd_writable(&new_pmd);
|
||||
new_pmd = kvm_s2pmd_mkwrite(new_pmd);
|
||||
kvm_set_pfn_dirty(pfn);
|
||||
}
|
||||
coherent_cache_guest_page(vcpu, pfn, PMD_SIZE, fault_ipa_uncached);
|
||||
|
@ -1329,7 +1372,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
pte_t new_pte = pfn_pte(pfn, mem_type);
|
||||
|
||||
if (writable) {
|
||||
kvm_set_s2pte_writable(&new_pte);
|
||||
new_pte = kvm_s2pte_mkwrite(new_pte);
|
||||
kvm_set_pfn_dirty(pfn);
|
||||
mark_page_dirty(kvm, gfn);
|
||||
}
|
||||
|
@ -1348,6 +1391,8 @@ out_unlock:
|
|||
* Resolve the access fault by making the page young again.
|
||||
* Note that because the faulting entry is guaranteed not to be
|
||||
* cached in the TLB, we don't need to invalidate anything.
|
||||
* Only the HW Access Flag updates are supported for Stage 2 (no DBM),
|
||||
* so there is no need for atomic (pte|pmd)_mkyoung operations.
|
||||
*/
|
||||
static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
|
||||
{
|
||||
|
@ -1364,7 +1409,7 @@ static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
|
|||
if (!pmd || pmd_none(*pmd)) /* Nothing there */
|
||||
goto out;
|
||||
|
||||
if (kvm_pmd_huge(*pmd)) { /* THP, HugeTLB */
|
||||
if (pmd_thp_or_huge(*pmd)) { /* THP, HugeTLB */
|
||||
*pmd = pmd_mkyoung(*pmd);
|
||||
pfn = pmd_pfn(*pmd);
|
||||
pfn_valid = true;
|
||||
|
@ -1588,25 +1633,14 @@ static int kvm_age_hva_handler(struct kvm *kvm, gpa_t gpa, void *data)
|
|||
if (!pmd || pmd_none(*pmd)) /* Nothing there */
|
||||
return 0;
|
||||
|
||||
if (kvm_pmd_huge(*pmd)) { /* THP, HugeTLB */
|
||||
if (pmd_young(*pmd)) {
|
||||
*pmd = pmd_mkold(*pmd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (pmd_thp_or_huge(*pmd)) /* THP, HugeTLB */
|
||||
return stage2_pmdp_test_and_clear_young(pmd);
|
||||
|
||||
pte = pte_offset_kernel(pmd, gpa);
|
||||
if (pte_none(*pte))
|
||||
return 0;
|
||||
|
||||
if (pte_young(*pte)) {
|
||||
*pte = pte_mkold(*pte); /* Just a page... */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return stage2_ptep_test_and_clear_young(pte);
|
||||
}
|
||||
|
||||
static int kvm_test_age_hva_handler(struct kvm *kvm, gpa_t gpa, void *data)
|
||||
|
@ -1618,7 +1652,7 @@ static int kvm_test_age_hva_handler(struct kvm *kvm, gpa_t gpa, void *data)
|
|||
if (!pmd || pmd_none(*pmd)) /* Nothing there */
|
||||
return 0;
|
||||
|
||||
if (kvm_pmd_huge(*pmd)) /* THP, HugeTLB */
|
||||
if (pmd_thp_or_huge(*pmd)) /* THP, HugeTLB */
|
||||
return pmd_young(*pmd);
|
||||
|
||||
pte = pte_offset_kernel(pmd, gpa);
|
||||
|
|
|
@ -85,32 +85,37 @@
|
|||
#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
|
||||
|
||||
/* TCR_EL2 Registers bits */
|
||||
#define TCR_EL2_RES1 ((1 << 31) | (1 << 23))
|
||||
#define TCR_EL2_TBI (1 << 20)
|
||||
#define TCR_EL2_PS (7 << 16)
|
||||
#define TCR_EL2_PS_40B (2 << 16)
|
||||
#define TCR_EL2_TG0 (1 << 14)
|
||||
#define TCR_EL2_SH0 (3 << 12)
|
||||
#define TCR_EL2_ORGN0 (3 << 10)
|
||||
#define TCR_EL2_IRGN0 (3 << 8)
|
||||
#define TCR_EL2_T0SZ 0x3f
|
||||
#define TCR_EL2_MASK (TCR_EL2_TG0 | TCR_EL2_SH0 | \
|
||||
TCR_EL2_ORGN0 | TCR_EL2_IRGN0 | TCR_EL2_T0SZ)
|
||||
#define TCR_EL2_RES1 ((1 << 31) | (1 << 23))
|
||||
#define TCR_EL2_TBI (1 << 20)
|
||||
#define TCR_EL2_PS_SHIFT 16
|
||||
#define TCR_EL2_PS_MASK (7 << TCR_EL2_PS_SHIFT)
|
||||
#define TCR_EL2_PS_40B (2 << TCR_EL2_PS_SHIFT)
|
||||
#define TCR_EL2_TG0_MASK TCR_TG0_MASK
|
||||
#define TCR_EL2_SH0_MASK TCR_SH0_MASK
|
||||
#define TCR_EL2_ORGN0_MASK TCR_ORGN0_MASK
|
||||
#define TCR_EL2_IRGN0_MASK TCR_IRGN0_MASK
|
||||
#define TCR_EL2_T0SZ_MASK 0x3f
|
||||
#define TCR_EL2_MASK (TCR_EL2_TG0_MASK | TCR_EL2_SH0_MASK | \
|
||||
TCR_EL2_ORGN0_MASK | TCR_EL2_IRGN0_MASK | TCR_EL2_T0SZ_MASK)
|
||||
|
||||
/* VTCR_EL2 Registers bits */
|
||||
#define VTCR_EL2_RES1 (1 << 31)
|
||||
#define VTCR_EL2_PS_MASK (7 << 16)
|
||||
#define VTCR_EL2_TG0_MASK (1 << 14)
|
||||
#define VTCR_EL2_TG0_4K (0 << 14)
|
||||
#define VTCR_EL2_TG0_64K (1 << 14)
|
||||
#define VTCR_EL2_SH0_MASK (3 << 12)
|
||||
#define VTCR_EL2_SH0_INNER (3 << 12)
|
||||
#define VTCR_EL2_ORGN0_MASK (3 << 10)
|
||||
#define VTCR_EL2_ORGN0_WBWA (1 << 10)
|
||||
#define VTCR_EL2_IRGN0_MASK (3 << 8)
|
||||
#define VTCR_EL2_IRGN0_WBWA (1 << 8)
|
||||
#define VTCR_EL2_SL0_MASK (3 << 6)
|
||||
#define VTCR_EL2_SL0_LVL1 (1 << 6)
|
||||
#define VTCR_EL2_HD (1 << 22)
|
||||
#define VTCR_EL2_HA (1 << 21)
|
||||
#define VTCR_EL2_PS_MASK TCR_EL2_PS_MASK
|
||||
#define VTCR_EL2_TG0_MASK TCR_TG0_MASK
|
||||
#define VTCR_EL2_TG0_4K TCR_TG0_4K
|
||||
#define VTCR_EL2_TG0_16K TCR_TG0_16K
|
||||
#define VTCR_EL2_TG0_64K TCR_TG0_64K
|
||||
#define VTCR_EL2_SH0_MASK TCR_SH0_MASK
|
||||
#define VTCR_EL2_SH0_INNER TCR_SH0_INNER
|
||||
#define VTCR_EL2_ORGN0_MASK TCR_ORGN0_MASK
|
||||
#define VTCR_EL2_ORGN0_WBWA TCR_ORGN0_WBWA
|
||||
#define VTCR_EL2_IRGN0_MASK TCR_IRGN0_MASK
|
||||
#define VTCR_EL2_IRGN0_WBWA TCR_IRGN0_WBWA
|
||||
#define VTCR_EL2_SL0_SHIFT 6
|
||||
#define VTCR_EL2_SL0_MASK (3 << VTCR_EL2_SL0_SHIFT)
|
||||
#define VTCR_EL2_SL0_LVL1 (1 << VTCR_EL2_SL0_SHIFT)
|
||||
#define VTCR_EL2_T0SZ_MASK 0x3f
|
||||
#define VTCR_EL2_T0SZ_40B 24
|
||||
#define VTCR_EL2_VS_SHIFT 19
|
||||
|
@ -126,35 +131,45 @@
|
|||
* (see hyp-init.S).
|
||||
*
|
||||
* Note that when using 4K pages, we concatenate two first level page tables
|
||||
* together.
|
||||
* together. With 16K pages, we concatenate 16 first level page tables.
|
||||
*
|
||||
* The magic numbers used for VTTBR_X in this patch can be found in Tables
|
||||
* D4-23 and D4-25 in ARM DDI 0487A.b.
|
||||
*/
|
||||
|
||||
#define VTCR_EL2_T0SZ_IPA VTCR_EL2_T0SZ_40B
|
||||
#define VTCR_EL2_COMMON_BITS (VTCR_EL2_SH0_INNER | VTCR_EL2_ORGN0_WBWA | \
|
||||
VTCR_EL2_IRGN0_WBWA | VTCR_EL2_RES1)
|
||||
|
||||
#ifdef CONFIG_ARM64_64K_PAGES
|
||||
/*
|
||||
* Stage2 translation configuration:
|
||||
* 40bits input (T0SZ = 24)
|
||||
* 64kB pages (TG0 = 1)
|
||||
* 2 level page tables (SL = 1)
|
||||
*/
|
||||
#define VTCR_EL2_FLAGS (VTCR_EL2_TG0_64K | VTCR_EL2_SH0_INNER | \
|
||||
VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \
|
||||
VTCR_EL2_SL0_LVL1 | VTCR_EL2_RES1)
|
||||
#define VTTBR_X (38 - VTCR_EL2_T0SZ_40B)
|
||||
#else
|
||||
#define VTCR_EL2_TGRAN_FLAGS (VTCR_EL2_TG0_64K | VTCR_EL2_SL0_LVL1)
|
||||
#define VTTBR_X_TGRAN_MAGIC 38
|
||||
#elif defined(CONFIG_ARM64_16K_PAGES)
|
||||
/*
|
||||
* Stage2 translation configuration:
|
||||
* 16kB pages (TG0 = 2)
|
||||
* 2 level page tables (SL = 1)
|
||||
*/
|
||||
#define VTCR_EL2_TGRAN_FLAGS (VTCR_EL2_TG0_16K | VTCR_EL2_SL0_LVL1)
|
||||
#define VTTBR_X_TGRAN_MAGIC 42
|
||||
#else /* 4K */
|
||||
/*
|
||||
* Stage2 translation configuration:
|
||||
* 40bits input (T0SZ = 24)
|
||||
* 4kB pages (TG0 = 0)
|
||||
* 3 level page tables (SL = 1)
|
||||
*/
|
||||
#define VTCR_EL2_FLAGS (VTCR_EL2_TG0_4K | VTCR_EL2_SH0_INNER | \
|
||||
VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \
|
||||
VTCR_EL2_SL0_LVL1 | VTCR_EL2_RES1)
|
||||
#define VTTBR_X (37 - VTCR_EL2_T0SZ_40B)
|
||||
#define VTCR_EL2_TGRAN_FLAGS (VTCR_EL2_TG0_4K | VTCR_EL2_SL0_LVL1)
|
||||
#define VTTBR_X_TGRAN_MAGIC 37
|
||||
#endif
|
||||
|
||||
#define VTCR_EL2_FLAGS (VTCR_EL2_COMMON_BITS | VTCR_EL2_TGRAN_FLAGS)
|
||||
#define VTTBR_X (VTTBR_X_TGRAN_MAGIC - VTCR_EL2_T0SZ_IPA)
|
||||
|
||||
#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
|
||||
#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
|
||||
#define VTTBR_VMID_SHIFT (UL(48))
|
||||
|
|
|
@ -295,6 +295,7 @@ struct kvm_vm_stat {
|
|||
struct kvm_vcpu_stat {
|
||||
u32 halt_successful_poll;
|
||||
u32 halt_attempted_poll;
|
||||
u32 halt_poll_invalid;
|
||||
u32 halt_wakeup;
|
||||
u32 hvc_exit_stat;
|
||||
u64 wfe_exit_stat;
|
||||
|
@ -369,6 +370,7 @@ static inline void kvm_arch_hardware_unsetup(void) {}
|
|||
static inline void kvm_arch_sync_events(struct kvm *kvm) {}
|
||||
static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
|
||||
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
void kvm_arm_init_debug(void);
|
||||
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
|
||||
|
|
|
@ -45,18 +45,6 @@
|
|||
*/
|
||||
#define TRAMPOLINE_VA (HYP_PAGE_OFFSET_MASK & PAGE_MASK)
|
||||
|
||||
/*
|
||||
* KVM_MMU_CACHE_MIN_PAGES is the number of stage2 page table translation
|
||||
* levels in addition to the PGD and potentially the PUD which are
|
||||
* pre-allocated (we pre-allocate the fake PGD and the PUD when the Stage-2
|
||||
* tables use one level of tables less than the kernel.
|
||||
*/
|
||||
#ifdef CONFIG_ARM64_64K_PAGES
|
||||
#define KVM_MMU_CACHE_MIN_PAGES 1
|
||||
#else
|
||||
#define KVM_MMU_CACHE_MIN_PAGES 2
|
||||
#endif
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
#include <asm/alternative.h>
|
||||
|
@ -91,6 +79,8 @@ alternative_endif
|
|||
#define KVM_PHYS_SIZE (1UL << KVM_PHYS_SHIFT)
|
||||
#define KVM_PHYS_MASK (KVM_PHYS_SIZE - 1UL)
|
||||
|
||||
#include <asm/stage2_pgtable.h>
|
||||
|
||||
int create_hyp_mappings(void *from, void *to);
|
||||
int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
|
||||
void free_boot_hyp_pgd(void);
|
||||
|
@ -122,19 +112,32 @@ static inline void kvm_clean_pmd_entry(pmd_t *pmd) {}
|
|||
static inline void kvm_clean_pte(pte_t *pte) {}
|
||||
static inline void kvm_clean_pte_entry(pte_t *pte) {}
|
||||
|
||||
static inline void kvm_set_s2pte_writable(pte_t *pte)
|
||||
static inline pte_t kvm_s2pte_mkwrite(pte_t pte)
|
||||
{
|
||||
pte_val(*pte) |= PTE_S2_RDWR;
|
||||
pte_val(pte) |= PTE_S2_RDWR;
|
||||
return pte;
|
||||
}
|
||||
|
||||
static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
|
||||
static inline pmd_t kvm_s2pmd_mkwrite(pmd_t pmd)
|
||||
{
|
||||
pmd_val(*pmd) |= PMD_S2_RDWR;
|
||||
pmd_val(pmd) |= PMD_S2_RDWR;
|
||||
return pmd;
|
||||
}
|
||||
|
||||
static inline void kvm_set_s2pte_readonly(pte_t *pte)
|
||||
{
|
||||
pte_val(*pte) = (pte_val(*pte) & ~PTE_S2_RDWR) | PTE_S2_RDONLY;
|
||||
pteval_t pteval;
|
||||
unsigned long tmp;
|
||||
|
||||
asm volatile("// kvm_set_s2pte_readonly\n"
|
||||
" prfm pstl1strm, %2\n"
|
||||
"1: ldxr %0, %2\n"
|
||||
" and %0, %0, %3 // clear PTE_S2_RDWR\n"
|
||||
" orr %0, %0, %4 // set PTE_S2_RDONLY\n"
|
||||
" stxr %w1, %0, %2\n"
|
||||
" cbnz %w1, 1b\n"
|
||||
: "=&r" (pteval), "=&r" (tmp), "+Q" (pte_val(*pte))
|
||||
: "L" (~PTE_S2_RDWR), "L" (PTE_S2_RDONLY));
|
||||
}
|
||||
|
||||
static inline bool kvm_s2pte_readonly(pte_t *pte)
|
||||
|
@ -144,69 +147,12 @@ static inline bool kvm_s2pte_readonly(pte_t *pte)
|
|||
|
||||
static inline void kvm_set_s2pmd_readonly(pmd_t *pmd)
|
||||
{
|
||||
pmd_val(*pmd) = (pmd_val(*pmd) & ~PMD_S2_RDWR) | PMD_S2_RDONLY;
|
||||
kvm_set_s2pte_readonly((pte_t *)pmd);
|
||||
}
|
||||
|
||||
static inline bool kvm_s2pmd_readonly(pmd_t *pmd)
|
||||
{
|
||||
return (pmd_val(*pmd) & PMD_S2_RDWR) == PMD_S2_RDONLY;
|
||||
}
|
||||
|
||||
|
||||
#define kvm_pgd_addr_end(addr, end) pgd_addr_end(addr, end)
|
||||
#define kvm_pud_addr_end(addr, end) pud_addr_end(addr, end)
|
||||
#define kvm_pmd_addr_end(addr, end) pmd_addr_end(addr, end)
|
||||
|
||||
/*
|
||||
* In the case where PGDIR_SHIFT is larger than KVM_PHYS_SHIFT, we can address
|
||||
* the entire IPA input range with a single pgd entry, and we would only need
|
||||
* one pgd entry. Note that in this case, the pgd is actually not used by
|
||||
* the MMU for Stage-2 translations, but is merely a fake pgd used as a data
|
||||
* structure for the kernel pgtable macros to work.
|
||||
*/
|
||||
#if PGDIR_SHIFT > KVM_PHYS_SHIFT
|
||||
#define PTRS_PER_S2_PGD_SHIFT 0
|
||||
#else
|
||||
#define PTRS_PER_S2_PGD_SHIFT (KVM_PHYS_SHIFT - PGDIR_SHIFT)
|
||||
#endif
|
||||
#define PTRS_PER_S2_PGD (1 << PTRS_PER_S2_PGD_SHIFT)
|
||||
|
||||
#define kvm_pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_S2_PGD - 1))
|
||||
|
||||
/*
|
||||
* If we are concatenating first level stage-2 page tables, we would have less
|
||||
* than or equal to 16 pointers in the fake PGD, because that's what the
|
||||
* architecture allows. In this case, (4 - CONFIG_PGTABLE_LEVELS)
|
||||
* represents the first level for the host, and we add 1 to go to the next
|
||||
* level (which uses contatenation) for the stage-2 tables.
|
||||
*/
|
||||
#if PTRS_PER_S2_PGD <= 16
|
||||
#define KVM_PREALLOC_LEVEL (4 - CONFIG_PGTABLE_LEVELS + 1)
|
||||
#else
|
||||
#define KVM_PREALLOC_LEVEL (0)
|
||||
#endif
|
||||
|
||||
static inline void *kvm_get_hwpgd(struct kvm *kvm)
|
||||
{
|
||||
pgd_t *pgd = kvm->arch.pgd;
|
||||
pud_t *pud;
|
||||
|
||||
if (KVM_PREALLOC_LEVEL == 0)
|
||||
return pgd;
|
||||
|
||||
pud = pud_offset(pgd, 0);
|
||||
if (KVM_PREALLOC_LEVEL == 1)
|
||||
return pud;
|
||||
|
||||
BUG_ON(KVM_PREALLOC_LEVEL != 2);
|
||||
return pmd_offset(pud, 0);
|
||||
}
|
||||
|
||||
static inline unsigned int kvm_get_hwpgd_size(void)
|
||||
{
|
||||
if (KVM_PREALLOC_LEVEL > 0)
|
||||
return PTRS_PER_S2_PGD * PAGE_SIZE;
|
||||
return PTRS_PER_S2_PGD * sizeof(pgd_t);
|
||||
return kvm_s2pte_readonly((pte_t *)pmd);
|
||||
}
|
||||
|
||||
static inline bool kvm_page_empty(void *ptr)
|
||||
|
@ -215,23 +161,20 @@ static inline bool kvm_page_empty(void *ptr)
|
|||
return page_count(ptr_page) == 1;
|
||||
}
|
||||
|
||||
#define kvm_pte_table_empty(kvm, ptep) kvm_page_empty(ptep)
|
||||
#define hyp_pte_table_empty(ptep) kvm_page_empty(ptep)
|
||||
|
||||
#ifdef __PAGETABLE_PMD_FOLDED
|
||||
#define kvm_pmd_table_empty(kvm, pmdp) (0)
|
||||
#define hyp_pmd_table_empty(pmdp) (0)
|
||||
#else
|
||||
#define kvm_pmd_table_empty(kvm, pmdp) \
|
||||
(kvm_page_empty(pmdp) && (!(kvm) || KVM_PREALLOC_LEVEL < 2))
|
||||
#define hyp_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
|
||||
#endif
|
||||
|
||||
#ifdef __PAGETABLE_PUD_FOLDED
|
||||
#define kvm_pud_table_empty(kvm, pudp) (0)
|
||||
#define hyp_pud_table_empty(pudp) (0)
|
||||
#else
|
||||
#define kvm_pud_table_empty(kvm, pudp) \
|
||||
(kvm_page_empty(pudp) && (!(kvm) || KVM_PREALLOC_LEVEL < 1))
|
||||
#define hyp_pud_table_empty(pudp) kvm_page_empty(pudp)
|
||||
#endif
|
||||
|
||||
|
||||
struct kvm;
|
||||
|
||||
#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
|
||||
|
|
|
@ -207,23 +207,69 @@
|
|||
#define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_T1SZ_OFFSET)
|
||||
#define TCR_TxSZ(x) (TCR_T0SZ(x) | TCR_T1SZ(x))
|
||||
#define TCR_TxSZ_WIDTH 6
|
||||
#define TCR_IRGN_NC ((UL(0) << 8) | (UL(0) << 24))
|
||||
#define TCR_IRGN_WBWA ((UL(1) << 8) | (UL(1) << 24))
|
||||
#define TCR_IRGN_WT ((UL(2) << 8) | (UL(2) << 24))
|
||||
#define TCR_IRGN_WBnWA ((UL(3) << 8) | (UL(3) << 24))
|
||||
#define TCR_IRGN_MASK ((UL(3) << 8) | (UL(3) << 24))
|
||||
#define TCR_ORGN_NC ((UL(0) << 10) | (UL(0) << 26))
|
||||
#define TCR_ORGN_WBWA ((UL(1) << 10) | (UL(1) << 26))
|
||||
#define TCR_ORGN_WT ((UL(2) << 10) | (UL(2) << 26))
|
||||
#define TCR_ORGN_WBnWA ((UL(3) << 10) | (UL(3) << 26))
|
||||
#define TCR_ORGN_MASK ((UL(3) << 10) | (UL(3) << 26))
|
||||
#define TCR_SHARED ((UL(3) << 12) | (UL(3) << 28))
|
||||
#define TCR_TG0_4K (UL(0) << 14)
|
||||
#define TCR_TG0_64K (UL(1) << 14)
|
||||
#define TCR_TG0_16K (UL(2) << 14)
|
||||
#define TCR_TG1_16K (UL(1) << 30)
|
||||
#define TCR_TG1_4K (UL(2) << 30)
|
||||
#define TCR_TG1_64K (UL(3) << 30)
|
||||
|
||||
#define TCR_IRGN0_SHIFT 8
|
||||
#define TCR_IRGN0_MASK (UL(3) << TCR_IRGN0_SHIFT)
|
||||
#define TCR_IRGN0_NC (UL(0) << TCR_IRGN0_SHIFT)
|
||||
#define TCR_IRGN0_WBWA (UL(1) << TCR_IRGN0_SHIFT)
|
||||
#define TCR_IRGN0_WT (UL(2) << TCR_IRGN0_SHIFT)
|
||||
#define TCR_IRGN0_WBnWA (UL(3) << TCR_IRGN0_SHIFT)
|
||||
|
||||
#define TCR_IRGN1_SHIFT 24
|
||||
#define TCR_IRGN1_MASK (UL(3) << TCR_IRGN1_SHIFT)
|
||||
#define TCR_IRGN1_NC (UL(0) << TCR_IRGN1_SHIFT)
|
||||
#define TCR_IRGN1_WBWA (UL(1) << TCR_IRGN1_SHIFT)
|
||||
#define TCR_IRGN1_WT (UL(2) << TCR_IRGN1_SHIFT)
|
||||
#define TCR_IRGN1_WBnWA (UL(3) << TCR_IRGN1_SHIFT)
|
||||
|
||||
#define TCR_IRGN_NC (TCR_IRGN0_NC | TCR_IRGN1_NC)
|
||||
#define TCR_IRGN_WBWA (TCR_IRGN0_WBWA | TCR_IRGN1_WBWA)
|
||||
#define TCR_IRGN_WT (TCR_IRGN0_WT | TCR_IRGN1_WT)
|
||||
#define TCR_IRGN_WBnWA (TCR_IRGN0_WBnWA | TCR_IRGN1_WBnWA)
|
||||
#define TCR_IRGN_MASK (TCR_IRGN0_MASK | TCR_IRGN1_MASK)
|
||||
|
||||
|
||||
#define TCR_ORGN0_SHIFT 10
|
||||
#define TCR_ORGN0_MASK (UL(3) << TCR_ORGN0_SHIFT)
|
||||
#define TCR_ORGN0_NC (UL(0) << TCR_ORGN0_SHIFT)
|
||||
#define TCR_ORGN0_WBWA (UL(1) << TCR_ORGN0_SHIFT)
|
||||
#define TCR_ORGN0_WT (UL(2) << TCR_ORGN0_SHIFT)
|
||||
#define TCR_ORGN0_WBnWA (UL(3) << TCR_ORGN0_SHIFT)
|
||||
|
||||
#define TCR_ORGN1_SHIFT 26
|
||||
#define TCR_ORGN1_MASK (UL(3) << TCR_ORGN1_SHIFT)
|
||||
#define TCR_ORGN1_NC (UL(0) << TCR_ORGN1_SHIFT)
|
||||
#define TCR_ORGN1_WBWA (UL(1) << TCR_ORGN1_SHIFT)
|
||||
#define TCR_ORGN1_WT (UL(2) << TCR_ORGN1_SHIFT)
|
||||
#define TCR_ORGN1_WBnWA (UL(3) << TCR_ORGN1_SHIFT)
|
||||
|
||||
#define TCR_ORGN_NC (TCR_ORGN0_NC | TCR_ORGN1_NC)
|
||||
#define TCR_ORGN_WBWA (TCR_ORGN0_WBWA | TCR_ORGN1_WBWA)
|
||||
#define TCR_ORGN_WT (TCR_ORGN0_WT | TCR_ORGN1_WT)
|
||||
#define TCR_ORGN_WBnWA (TCR_ORGN0_WBnWA | TCR_ORGN1_WBnWA)
|
||||
#define TCR_ORGN_MASK (TCR_ORGN0_MASK | TCR_ORGN1_MASK)
|
||||
|
||||
#define TCR_SH0_SHIFT 12
|
||||
#define TCR_SH0_MASK (UL(3) << TCR_SH0_SHIFT)
|
||||
#define TCR_SH0_INNER (UL(3) << TCR_SH0_SHIFT)
|
||||
|
||||
#define TCR_SH1_SHIFT 28
|
||||
#define TCR_SH1_MASK (UL(3) << TCR_SH1_SHIFT)
|
||||
#define TCR_SH1_INNER (UL(3) << TCR_SH1_SHIFT)
|
||||
#define TCR_SHARED (TCR_SH0_INNER | TCR_SH1_INNER)
|
||||
|
||||
#define TCR_TG0_SHIFT 14
|
||||
#define TCR_TG0_MASK (UL(3) << TCR_TG0_SHIFT)
|
||||
#define TCR_TG0_4K (UL(0) << TCR_TG0_SHIFT)
|
||||
#define TCR_TG0_64K (UL(1) << TCR_TG0_SHIFT)
|
||||
#define TCR_TG0_16K (UL(2) << TCR_TG0_SHIFT)
|
||||
|
||||
#define TCR_TG1_SHIFT 30
|
||||
#define TCR_TG1_MASK (UL(3) << TCR_TG1_SHIFT)
|
||||
#define TCR_TG1_16K (UL(1) << TCR_TG1_SHIFT)
|
||||
#define TCR_TG1_4K (UL(2) << TCR_TG1_SHIFT)
|
||||
#define TCR_TG1_64K (UL(3) << TCR_TG1_SHIFT)
|
||||
|
||||
#define TCR_ASID16 (UL(1) << 36)
|
||||
#define TCR_TBI0 (UL(1) << 37)
|
||||
#define TCR_HA (UL(1) << 39)
|
||||
|
|
|
@ -300,6 +300,8 @@ static inline int pmd_protnone(pmd_t pmd)
|
|||
#define pmd_mkyoung(pmd) pte_pmd(pte_mkyoung(pmd_pte(pmd)))
|
||||
#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) & ~PMD_SECT_VALID))
|
||||
|
||||
#define pmd_thp_or_huge(pmd) (pmd_huge(pmd) || pmd_trans_huge(pmd))
|
||||
|
||||
#define __HAVE_ARCH_PMD_WRITE
|
||||
#define pmd_write(pmd) pte_write(pmd_pte(pmd))
|
||||
|
||||
|
@ -554,14 +556,12 @@ static inline int pmdp_set_access_flags(struct vm_area_struct *vma,
|
|||
* Atomic pte/pmd modifications.
|
||||
*/
|
||||
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
|
||||
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long address,
|
||||
pte_t *ptep)
|
||||
static inline int __ptep_test_and_clear_young(pte_t *ptep)
|
||||
{
|
||||
pteval_t pteval;
|
||||
unsigned int tmp, res;
|
||||
|
||||
asm volatile("// ptep_test_and_clear_young\n"
|
||||
asm volatile("// __ptep_test_and_clear_young\n"
|
||||
" prfm pstl1strm, %2\n"
|
||||
"1: ldxr %0, %2\n"
|
||||
" ubfx %w3, %w0, %5, #1 // extract PTE_AF (young)\n"
|
||||
|
@ -574,6 +574,13 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
|||
return res;
|
||||
}
|
||||
|
||||
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
||||
unsigned long address,
|
||||
pte_t *ptep)
|
||||
{
|
||||
return __ptep_test_and_clear_young(ptep);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
#define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG
|
||||
static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2016 - ARM Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ARM64_S2_PGTABLE_NOPMD_H_
|
||||
#define __ARM64_S2_PGTABLE_NOPMD_H_
|
||||
|
||||
#include <asm/stage2_pgtable-nopud.h>
|
||||
|
||||
#define __S2_PGTABLE_PMD_FOLDED
|
||||
|
||||
#define S2_PMD_SHIFT S2_PUD_SHIFT
|
||||
#define S2_PTRS_PER_PMD 1
|
||||
#define S2_PMD_SIZE (1UL << S2_PMD_SHIFT)
|
||||
#define S2_PMD_MASK (~(S2_PMD_SIZE-1))
|
||||
|
||||
#define stage2_pud_none(pud) (0)
|
||||
#define stage2_pud_present(pud) (1)
|
||||
#define stage2_pud_clear(pud) do { } while (0)
|
||||
#define stage2_pud_populate(pud, pmd) do { } while (0)
|
||||
#define stage2_pmd_offset(pud, address) ((pmd_t *)(pud))
|
||||
|
||||
#define stage2_pmd_free(pmd) do { } while (0)
|
||||
|
||||
#define stage2_pmd_addr_end(addr, end) (end)
|
||||
|
||||
#define stage2_pud_huge(pud) (0)
|
||||
#define stage2_pmd_table_empty(pmdp) (0)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2016 - ARM Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ARM64_S2_PGTABLE_NOPUD_H_
|
||||
#define __ARM64_S2_PGTABLE_NOPUD_H_
|
||||
|
||||
#define __S2_PGTABLE_PUD_FOLDED
|
||||
|
||||
#define S2_PUD_SHIFT S2_PGDIR_SHIFT
|
||||
#define S2_PTRS_PER_PUD 1
|
||||
#define S2_PUD_SIZE (_AC(1, UL) << S2_PUD_SHIFT)
|
||||
#define S2_PUD_MASK (~(S2_PUD_SIZE-1))
|
||||
|
||||
#define stage2_pgd_none(pgd) (0)
|
||||
#define stage2_pgd_present(pgd) (1)
|
||||
#define stage2_pgd_clear(pgd) do { } while (0)
|
||||
#define stage2_pgd_populate(pgd, pud) do { } while (0)
|
||||
|
||||
#define stage2_pud_offset(pgd, address) ((pud_t *)(pgd))
|
||||
|
||||
#define stage2_pud_free(x) do { } while (0)
|
||||
|
||||
#define stage2_pud_addr_end(addr, end) (end)
|
||||
#define stage2_pud_table_empty(pmdp) (0)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (C) 2016 - ARM Ltd
|
||||
*
|
||||
* stage2 page table helpers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ARM64_S2_PGTABLE_H_
|
||||
#define __ARM64_S2_PGTABLE_H_
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
/*
|
||||
* The hardware supports concatenation of up to 16 tables at stage2 entry level
|
||||
* and we use the feature whenever possible.
|
||||
*
|
||||
* Now, the minimum number of bits resolved at any level is (PAGE_SHIFT - 3).
|
||||
* On arm64, the smallest PAGE_SIZE supported is 4k, which means
|
||||
* (PAGE_SHIFT - 3) > 4 holds for all page sizes.
|
||||
* This implies, the total number of page table levels at stage2 expected
|
||||
* by the hardware is actually the number of levels required for (KVM_PHYS_SHIFT - 4)
|
||||
* in normal translations(e.g, stage1), since we cannot have another level in
|
||||
* the range (KVM_PHYS_SHIFT, KVM_PHYS_SHIFT - 4).
|
||||
*/
|
||||
#define STAGE2_PGTABLE_LEVELS ARM64_HW_PGTABLE_LEVELS(KVM_PHYS_SHIFT - 4)
|
||||
|
||||
/*
|
||||
* With all the supported VA_BITs and 40bit guest IPA, the following condition
|
||||
* is always true:
|
||||
*
|
||||
* STAGE2_PGTABLE_LEVELS <= CONFIG_PGTABLE_LEVELS
|
||||
*
|
||||
* We base our stage-2 page table walker helpers on this assumption and
|
||||
* fall back to using the host version of the helper wherever possible.
|
||||
* i.e, if a particular level is not folded (e.g, PUD) at stage2, we fall back
|
||||
* to using the host version, since it is guaranteed it is not folded at host.
|
||||
*
|
||||
* If the condition breaks in the future, we can rearrange the host level
|
||||
* definitions and reuse them for stage2. Till then...
|
||||
*/
|
||||
#if STAGE2_PGTABLE_LEVELS > CONFIG_PGTABLE_LEVELS
|
||||
#error "Unsupported combination of guest IPA and host VA_BITS."
|
||||
#endif
|
||||
|
||||
/* S2_PGDIR_SHIFT is the size mapped by top-level stage2 entry */
|
||||
#define S2_PGDIR_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - STAGE2_PGTABLE_LEVELS)
|
||||
#define S2_PGDIR_SIZE (_AC(1, UL) << S2_PGDIR_SHIFT)
|
||||
#define S2_PGDIR_MASK (~(S2_PGDIR_SIZE - 1))
|
||||
|
||||
/*
|
||||
* The number of PTRS across all concatenated stage2 tables given by the
|
||||
* number of bits resolved at the initial level.
|
||||
*/
|
||||
#define PTRS_PER_S2_PGD (1 << (KVM_PHYS_SHIFT - S2_PGDIR_SHIFT))
|
||||
|
||||
/*
|
||||
* KVM_MMU_CACHE_MIN_PAGES is the number of stage2 page table translation
|
||||
* levels in addition to the PGD.
|
||||
*/
|
||||
#define KVM_MMU_CACHE_MIN_PAGES (STAGE2_PGTABLE_LEVELS - 1)
|
||||
|
||||
|
||||
#if STAGE2_PGTABLE_LEVELS > 3
|
||||
|
||||
#define S2_PUD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(1)
|
||||
#define S2_PUD_SIZE (_AC(1, UL) << S2_PUD_SHIFT)
|
||||
#define S2_PUD_MASK (~(S2_PUD_SIZE - 1))
|
||||
|
||||
#define stage2_pgd_none(pgd) pgd_none(pgd)
|
||||
#define stage2_pgd_clear(pgd) pgd_clear(pgd)
|
||||
#define stage2_pgd_present(pgd) pgd_present(pgd)
|
||||
#define stage2_pgd_populate(pgd, pud) pgd_populate(NULL, pgd, pud)
|
||||
#define stage2_pud_offset(pgd, address) pud_offset(pgd, address)
|
||||
#define stage2_pud_free(pud) pud_free(NULL, pud)
|
||||
|
||||
#define stage2_pud_table_empty(pudp) kvm_page_empty(pudp)
|
||||
|
||||
static inline phys_addr_t stage2_pud_addr_end(phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t boundary = (addr + S2_PUD_SIZE) & S2_PUD_MASK;
|
||||
|
||||
return (boundary - 1 < end - 1) ? boundary : end;
|
||||
}
|
||||
|
||||
#endif /* STAGE2_PGTABLE_LEVELS > 3 */
|
||||
|
||||
|
||||
#if STAGE2_PGTABLE_LEVELS > 2
|
||||
|
||||
#define S2_PMD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(2)
|
||||
#define S2_PMD_SIZE (_AC(1, UL) << S2_PMD_SHIFT)
|
||||
#define S2_PMD_MASK (~(S2_PMD_SIZE - 1))
|
||||
|
||||
#define stage2_pud_none(pud) pud_none(pud)
|
||||
#define stage2_pud_clear(pud) pud_clear(pud)
|
||||
#define stage2_pud_present(pud) pud_present(pud)
|
||||
#define stage2_pud_populate(pud, pmd) pud_populate(NULL, pud, pmd)
|
||||
#define stage2_pmd_offset(pud, address) pmd_offset(pud, address)
|
||||
#define stage2_pmd_free(pmd) pmd_free(NULL, pmd)
|
||||
|
||||
#define stage2_pud_huge(pud) pud_huge(pud)
|
||||
#define stage2_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
|
||||
|
||||
static inline phys_addr_t stage2_pmd_addr_end(phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t boundary = (addr + S2_PMD_SIZE) & S2_PMD_MASK;
|
||||
|
||||
return (boundary - 1 < end - 1) ? boundary : end;
|
||||
}
|
||||
|
||||
#endif /* STAGE2_PGTABLE_LEVELS > 2 */
|
||||
|
||||
#define stage2_pte_table_empty(ptep) kvm_page_empty(ptep)
|
||||
|
||||
#if STAGE2_PGTABLE_LEVELS == 2
|
||||
#include <asm/stage2_pgtable-nopmd.h>
|
||||
#elif STAGE2_PGTABLE_LEVELS == 3
|
||||
#include <asm/stage2_pgtable-nopud.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define stage2_pgd_index(addr) (((addr) >> S2_PGDIR_SHIFT) & (PTRS_PER_S2_PGD - 1))
|
||||
|
||||
static inline phys_addr_t stage2_pgd_addr_end(phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
phys_addr_t boundary = (addr + S2_PGDIR_SIZE) & S2_PGDIR_MASK;
|
||||
|
||||
return (boundary - 1 < end - 1) ? boundary : end;
|
||||
}
|
||||
|
||||
#endif /* __ARM64_S2_PGTABLE_H_ */
|
|
@ -22,7 +22,6 @@ config KVM_ARM_VGIC_V3
|
|||
config KVM
|
||||
bool "Kernel-based Virtual Machine (KVM) support"
|
||||
depends on OF
|
||||
depends on !ARM64_16K_PAGES
|
||||
select MMU_NOTIFIER
|
||||
select PREEMPT_NOTIFIERS
|
||||
select ANON_INODES
|
||||
|
|
|
@ -65,6 +65,14 @@ u32 __hyp_text __init_stage2_translation(void)
|
|||
*/
|
||||
val |= 64 - (parange > 40 ? 40 : parange);
|
||||
|
||||
/*
|
||||
* Check the availability of Hardware Access Flag / Dirty Bit
|
||||
* Management in ID_AA64MMFR1_EL1 and enable the feature in VTCR_EL2.
|
||||
*/
|
||||
tmp = (read_sysreg(id_aa64mmfr1_el1) >> ID_AA64MMFR1_HADBS_SHIFT) & 0xf;
|
||||
if (IS_ENABLED(CONFIG_ARM64_HW_AFDBM) && tmp)
|
||||
val |= VTCR_EL2_HA;
|
||||
|
||||
/*
|
||||
* Read the VMIDBits bits from ID_AA64MMFR1_EL1 and set the VS
|
||||
* bit in VTCR_EL2.
|
||||
|
|
|
@ -122,6 +122,7 @@ struct kvm_vcpu_stat {
|
|||
u32 flush_dcache_exits;
|
||||
u32 halt_successful_poll;
|
||||
u32 halt_attempted_poll;
|
||||
u32 halt_poll_invalid;
|
||||
u32 halt_wakeup;
|
||||
};
|
||||
|
||||
|
@ -748,7 +749,7 @@ extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
|
|||
|
||||
uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu);
|
||||
void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count);
|
||||
void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare);
|
||||
void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare, bool ack);
|
||||
void kvm_mips_init_count(struct kvm_vcpu *vcpu);
|
||||
int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl);
|
||||
int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume);
|
||||
|
@ -813,5 +814,6 @@ static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
|
|||
static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
|
||||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
#endif /* __MIPS_KVM_HOST_H__ */
|
||||
|
|
|
@ -302,12 +302,31 @@ static inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
|
||||
{
|
||||
ktime_t expires;
|
||||
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
||||
ktime_t expires, threshold;
|
||||
uint32_t count, compare;
|
||||
int running;
|
||||
|
||||
/* Is the hrtimer pending? */
|
||||
/* Calculate the biased and scaled guest CP0_Count */
|
||||
count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
|
||||
compare = kvm_read_c0_guest_compare(cop0);
|
||||
|
||||
/*
|
||||
* Find whether CP0_Count has reached the closest timer interrupt. If
|
||||
* not, we shouldn't inject it.
|
||||
*/
|
||||
if ((int32_t)(count - compare) < 0)
|
||||
return count;
|
||||
|
||||
/*
|
||||
* The CP0_Count we're going to return has already reached the closest
|
||||
* timer interrupt. Quickly check if it really is a new interrupt by
|
||||
* looking at whether the interval until the hrtimer expiry time is
|
||||
* less than 1/4 of the timer period.
|
||||
*/
|
||||
expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer);
|
||||
if (ktime_compare(now, expires) >= 0) {
|
||||
threshold = ktime_add_ns(now, vcpu->arch.count_period / 4);
|
||||
if (ktime_before(expires, threshold)) {
|
||||
/*
|
||||
* Cancel it while we handle it so there's no chance of
|
||||
* interference with the timeout handler.
|
||||
|
@ -329,8 +348,7 @@ static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
|
|||
}
|
||||
}
|
||||
|
||||
/* Return the biased and scaled guest CP0_Count */
|
||||
return vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -419,32 +437,6 @@ static void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu,
|
|||
hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_mips_update_hrtimer() - Update next expiry time of hrtimer.
|
||||
* @vcpu: Virtual CPU.
|
||||
*
|
||||
* Recalculates and updates the expiry time of the hrtimer. This can be used
|
||||
* after timer parameters have been altered which do not depend on the time that
|
||||
* the change occurs (in those cases kvm_mips_freeze_hrtimer() and
|
||||
* kvm_mips_resume_hrtimer() are used directly).
|
||||
*
|
||||
* It is guaranteed that no timer interrupts will be lost in the process.
|
||||
*
|
||||
* Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
|
||||
*/
|
||||
static void kvm_mips_update_hrtimer(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
ktime_t now;
|
||||
uint32_t count;
|
||||
|
||||
/*
|
||||
* freeze_hrtimer takes care of a timer interrupts <= count, and
|
||||
* resume_hrtimer the hrtimer takes care of a timer interrupts > count.
|
||||
*/
|
||||
now = kvm_mips_freeze_hrtimer(vcpu, &count);
|
||||
kvm_mips_resume_hrtimer(vcpu, now, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_mips_write_count() - Modify the count and update timer.
|
||||
* @vcpu: Virtual CPU.
|
||||
|
@ -540,23 +532,42 @@ int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz)
|
|||
* kvm_mips_write_compare() - Modify compare and update timer.
|
||||
* @vcpu: Virtual CPU.
|
||||
* @compare: New CP0_Compare value.
|
||||
* @ack: Whether to acknowledge timer interrupt.
|
||||
*
|
||||
* Update CP0_Compare to a new value and update the timeout.
|
||||
* If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure
|
||||
* any pending timer interrupt is preserved.
|
||||
*/
|
||||
void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare)
|
||||
void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare, bool ack)
|
||||
{
|
||||
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
||||
int dc;
|
||||
u32 old_compare = kvm_read_c0_guest_compare(cop0);
|
||||
ktime_t now;
|
||||
uint32_t count;
|
||||
|
||||
/* if unchanged, must just be an ack */
|
||||
if (kvm_read_c0_guest_compare(cop0) == compare)
|
||||
if (old_compare == compare) {
|
||||
if (!ack)
|
||||
return;
|
||||
kvm_mips_callbacks->dequeue_timer_int(vcpu);
|
||||
kvm_write_c0_guest_compare(cop0, compare);
|
||||
return;
|
||||
}
|
||||
|
||||
/* freeze_hrtimer() takes care of timer interrupts <= count */
|
||||
dc = kvm_mips_count_disabled(vcpu);
|
||||
if (!dc)
|
||||
now = kvm_mips_freeze_hrtimer(vcpu, &count);
|
||||
|
||||
if (ack)
|
||||
kvm_mips_callbacks->dequeue_timer_int(vcpu);
|
||||
|
||||
/* Update compare */
|
||||
kvm_write_c0_guest_compare(cop0, compare);
|
||||
|
||||
/* Update timeout if count enabled */
|
||||
if (!kvm_mips_count_disabled(vcpu))
|
||||
kvm_mips_update_hrtimer(vcpu);
|
||||
/* resume_hrtimer() takes care of timer interrupts > count */
|
||||
if (!dc)
|
||||
kvm_mips_resume_hrtimer(vcpu, now, count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1095,9 +1106,9 @@ enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc,
|
|||
|
||||
/* If we are writing to COMPARE */
|
||||
/* Clear pending timer interrupt, if any */
|
||||
kvm_mips_callbacks->dequeue_timer_int(vcpu);
|
||||
kvm_mips_write_compare(vcpu,
|
||||
vcpu->arch.gprs[rt]);
|
||||
vcpu->arch.gprs[rt],
|
||||
true);
|
||||
} else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) {
|
||||
unsigned int old_val, val, change;
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|||
{ "flush_dcache", VCPU_STAT(flush_dcache_exits), KVM_STAT_VCPU },
|
||||
{ "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU },
|
||||
{ "halt_attempted_poll", VCPU_STAT(halt_attempted_poll), KVM_STAT_VCPU },
|
||||
{ "halt_poll_invalid", VCPU_STAT(halt_poll_invalid), KVM_STAT_VCPU },
|
||||
{ "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU },
|
||||
{NULL}
|
||||
};
|
||||
|
@ -1079,7 +1080,8 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
|||
r = KVM_COALESCED_MMIO_PAGE_OFFSET;
|
||||
break;
|
||||
case KVM_CAP_MIPS_FPU:
|
||||
r = !!cpu_has_fpu;
|
||||
/* We don't handle systems with inconsistent cpu_has_fpu */
|
||||
r = !!raw_cpu_has_fpu;
|
||||
break;
|
||||
case KVM_CAP_MIPS_MSA:
|
||||
/*
|
||||
|
@ -1555,8 +1557,10 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu)
|
|||
|
||||
/* Disable MSA & FPU */
|
||||
disable_msa();
|
||||
if (vcpu->arch.fpu_inuse & KVM_MIPS_FPU_FPU)
|
||||
if (vcpu->arch.fpu_inuse & KVM_MIPS_FPU_FPU) {
|
||||
clear_c0_status(ST0_CU1 | ST0_FR);
|
||||
disable_fpu_hazard();
|
||||
}
|
||||
vcpu->arch.fpu_inuse &= ~(KVM_MIPS_FPU_FPU | KVM_MIPS_FPU_MSA);
|
||||
} else if (vcpu->arch.fpu_inuse & KVM_MIPS_FPU_FPU) {
|
||||
set_c0_status(ST0_CU1);
|
||||
|
@ -1567,6 +1571,7 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu)
|
|||
|
||||
/* Disable FPU */
|
||||
clear_c0_status(ST0_CU1 | ST0_FR);
|
||||
disable_fpu_hazard();
|
||||
}
|
||||
preempt_enable();
|
||||
}
|
||||
|
|
|
@ -275,6 +275,7 @@ int kvm_mips_handle_kseg0_tlb_fault(unsigned long badvaddr,
|
|||
int even;
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
const int flush_dcache_mask = 0;
|
||||
int ret;
|
||||
|
||||
if (KVM_GUEST_KSEGX(badvaddr) != KVM_GUEST_KSEG0) {
|
||||
kvm_err("%s: Invalid BadVaddr: %#lx\n", __func__, badvaddr);
|
||||
|
@ -306,14 +307,18 @@ int kvm_mips_handle_kseg0_tlb_fault(unsigned long badvaddr,
|
|||
pfn1 = kvm->arch.guest_pmap[gfn];
|
||||
}
|
||||
|
||||
entryhi = (vaddr | kvm_mips_get_kernel_asid(vcpu));
|
||||
entrylo0 = mips3_paddr_to_tlbpfn(pfn0 << PAGE_SHIFT) | (0x3 << 3) |
|
||||
(1 << 2) | (0x1 << 1);
|
||||
entrylo1 = mips3_paddr_to_tlbpfn(pfn1 << PAGE_SHIFT) | (0x3 << 3) |
|
||||
(1 << 2) | (0x1 << 1);
|
||||
|
||||
return kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1,
|
||||
flush_dcache_mask);
|
||||
preempt_disable();
|
||||
entryhi = (vaddr | kvm_mips_get_kernel_asid(vcpu));
|
||||
ret = kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1,
|
||||
flush_dcache_mask);
|
||||
preempt_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_mips_handle_kseg0_tlb_fault);
|
||||
|
||||
|
@ -368,6 +373,7 @@ int kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu,
|
|||
unsigned long entryhi = 0, entrylo0 = 0, entrylo1 = 0;
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
kvm_pfn_t pfn0, pfn1;
|
||||
int ret;
|
||||
|
||||
if ((tlb->tlb_hi & VPN2_MASK) == 0) {
|
||||
pfn0 = 0;
|
||||
|
@ -394,9 +400,6 @@ int kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu,
|
|||
*hpa1 = pfn1 << PAGE_SHIFT;
|
||||
|
||||
/* Get attributes from the Guest TLB */
|
||||
entryhi = (tlb->tlb_hi & VPN2_MASK) | (KVM_GUEST_KERNEL_MODE(vcpu) ?
|
||||
kvm_mips_get_kernel_asid(vcpu) :
|
||||
kvm_mips_get_user_asid(vcpu));
|
||||
entrylo0 = mips3_paddr_to_tlbpfn(pfn0 << PAGE_SHIFT) | (0x3 << 3) |
|
||||
(tlb->tlb_lo0 & MIPS3_PG_D) | (tlb->tlb_lo0 & MIPS3_PG_V);
|
||||
entrylo1 = mips3_paddr_to_tlbpfn(pfn1 << PAGE_SHIFT) | (0x3 << 3) |
|
||||
|
@ -405,8 +408,15 @@ int kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu,
|
|||
kvm_debug("@ %#lx tlb_lo0: 0x%08lx tlb_lo1: 0x%08lx\n", vcpu->arch.pc,
|
||||
tlb->tlb_lo0, tlb->tlb_lo1);
|
||||
|
||||
return kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1,
|
||||
tlb->tlb_mask);
|
||||
preempt_disable();
|
||||
entryhi = (tlb->tlb_hi & VPN2_MASK) | (KVM_GUEST_KERNEL_MODE(vcpu) ?
|
||||
kvm_mips_get_kernel_asid(vcpu) :
|
||||
kvm_mips_get_user_asid(vcpu));
|
||||
ret = kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1,
|
||||
tlb->tlb_mask);
|
||||
preempt_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_mips_handle_mapped_seg_tlb_fault);
|
||||
|
||||
|
|
|
@ -547,7 +547,7 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu,
|
|||
kvm_mips_write_count(vcpu, v);
|
||||
break;
|
||||
case KVM_REG_MIPS_CP0_COMPARE:
|
||||
kvm_mips_write_compare(vcpu, v);
|
||||
kvm_mips_write_compare(vcpu, v, false);
|
||||
break;
|
||||
case KVM_REG_MIPS_CP0_CAUSE:
|
||||
/*
|
||||
|
|
|
@ -40,6 +40,9 @@
|
|||
#define KVM_MAX_VCORES NR_CPUS
|
||||
#define KVM_USER_MEM_SLOTS 512
|
||||
|
||||
#include <asm/cputhreads.h>
|
||||
#define KVM_MAX_VCPU_ID (threads_per_subcore * KVM_MAX_VCORES)
|
||||
|
||||
#ifdef CONFIG_KVM_MMIO
|
||||
#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
|
||||
#endif
|
||||
|
@ -113,6 +116,7 @@ struct kvm_vcpu_stat {
|
|||
u32 ext_intr_exits;
|
||||
u32 halt_successful_poll;
|
||||
u32 halt_attempted_poll;
|
||||
u32 halt_poll_invalid;
|
||||
u32 halt_wakeup;
|
||||
u32 dbell_exits;
|
||||
u32 gdbell_exits;
|
||||
|
@ -724,5 +728,6 @@ static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
|
|||
static inline void kvm_arch_exit(void) {}
|
||||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
#endif /* __POWERPC_KVM_HOST_H__ */
|
||||
|
|
|
@ -54,6 +54,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|||
{ "queue_intr", VCPU_STAT(queue_intr) },
|
||||
{ "halt_successful_poll", VCPU_STAT(halt_successful_poll), },
|
||||
{ "halt_attempted_poll", VCPU_STAT(halt_attempted_poll), },
|
||||
{ "halt_poll_invalid", VCPU_STAT(halt_poll_invalid) },
|
||||
{ "halt_wakeup", VCPU_STAT(halt_wakeup) },
|
||||
{ "pf_storage", VCPU_STAT(pf_storage) },
|
||||
{ "sp_storage", VCPU_STAT(sp_storage) },
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/page-flags.h>
|
||||
|
|
|
@ -882,6 +882,24 @@ void kvmppc_set_fscr(struct kvm_vcpu *vcpu, u64 fscr)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void kvmppc_setup_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
||||
u64 msr = kvmppc_get_msr(vcpu);
|
||||
|
||||
kvmppc_set_msr(vcpu, msr | MSR_SE);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvmppc_clear_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
||||
u64 msr = kvmppc_get_msr(vcpu);
|
||||
|
||||
kvmppc_set_msr(vcpu, msr & ~MSR_SE);
|
||||
}
|
||||
}
|
||||
|
||||
int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||
unsigned int exit_nr)
|
||||
{
|
||||
|
@ -1207,10 +1225,18 @@ program_interrupt:
|
|||
break;
|
||||
#endif
|
||||
case BOOK3S_INTERRUPT_MACHINE_CHECK:
|
||||
case BOOK3S_INTERRUPT_TRACE:
|
||||
kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
|
||||
r = RESUME_GUEST;
|
||||
break;
|
||||
case BOOK3S_INTERRUPT_TRACE:
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
||||
run->exit_reason = KVM_EXIT_DEBUG;
|
||||
r = RESUME_HOST;
|
||||
} else {
|
||||
kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
|
||||
r = RESUME_GUEST;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
ulong shadow_srr1 = vcpu->arch.shadow_srr1;
|
||||
|
@ -1479,6 +1505,8 @@ static int kvmppc_vcpu_run_pr(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
|
|||
goto out;
|
||||
}
|
||||
|
||||
kvmppc_setup_debug(vcpu);
|
||||
|
||||
/*
|
||||
* Interrupts could be timers for the guest which we have to inject
|
||||
* again, so let's postpone them until we're in the guest and if we
|
||||
|
@ -1501,6 +1529,8 @@ static int kvmppc_vcpu_run_pr(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
|
|||
|
||||
ret = __kvmppc_vcpu_run(kvm_run, vcpu);
|
||||
|
||||
kvmppc_clear_debug(vcpu);
|
||||
|
||||
/* No need for kvm_guest_exit. It's done in handle_exit.
|
||||
We also get here with interrupts enabled. */
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level)
|
|||
* we are the only setter, thus concurrent access is undefined
|
||||
* to begin with.
|
||||
*/
|
||||
if (level == 1 || level == KVM_INTERRUPT_SET_LEVEL)
|
||||
if ((level == 1 && state->lsi) || level == KVM_INTERRUPT_SET_LEVEL)
|
||||
state->asserted = 1;
|
||||
else if (level == 0 || level == KVM_INTERRUPT_UNSET) {
|
||||
state->asserted = 0;
|
||||
|
@ -280,7 +280,7 @@ static inline bool icp_try_update(struct kvmppc_icp *icp,
|
|||
if (!success)
|
||||
goto bail;
|
||||
|
||||
XICS_DBG("UPD [%04x] - C:%02x M:%02x PP: %02x PI:%06x R:%d O:%d\n",
|
||||
XICS_DBG("UPD [%04lx] - C:%02x M:%02x PP: %02x PI:%06x R:%d O:%d\n",
|
||||
icp->server_num,
|
||||
old.cppr, old.mfrr, old.pending_pri, old.xisr,
|
||||
old.need_resend, old.out_ee);
|
||||
|
@ -336,7 +336,7 @@ static bool icp_try_to_deliver(struct kvmppc_icp *icp, u32 irq, u8 priority,
|
|||
union kvmppc_icp_state old_state, new_state;
|
||||
bool success;
|
||||
|
||||
XICS_DBG("try deliver %#x(P:%#x) to server %#x\n", irq, priority,
|
||||
XICS_DBG("try deliver %#x(P:%#x) to server %#lx\n", irq, priority,
|
||||
icp->server_num);
|
||||
|
||||
do {
|
||||
|
@ -1174,9 +1174,11 @@ static int xics_get_source(struct kvmppc_xics *xics, long irq, u64 addr)
|
|||
prio = irqp->saved_priority;
|
||||
}
|
||||
val |= prio << KVM_XICS_PRIORITY_SHIFT;
|
||||
if (irqp->asserted)
|
||||
val |= KVM_XICS_LEVEL_SENSITIVE | KVM_XICS_PENDING;
|
||||
else if (irqp->masked_pending || irqp->resend)
|
||||
if (irqp->lsi) {
|
||||
val |= KVM_XICS_LEVEL_SENSITIVE;
|
||||
if (irqp->asserted)
|
||||
val |= KVM_XICS_PENDING;
|
||||
} else if (irqp->masked_pending || irqp->resend)
|
||||
val |= KVM_XICS_PENDING;
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -1228,9 +1230,13 @@ static int xics_set_source(struct kvmppc_xics *xics, long irq, u64 addr)
|
|||
irqp->priority = prio;
|
||||
irqp->resend = 0;
|
||||
irqp->masked_pending = 0;
|
||||
irqp->lsi = 0;
|
||||
irqp->asserted = 0;
|
||||
if ((val & KVM_XICS_PENDING) && (val & KVM_XICS_LEVEL_SENSITIVE))
|
||||
irqp->asserted = 1;
|
||||
if (val & KVM_XICS_LEVEL_SENSITIVE) {
|
||||
irqp->lsi = 1;
|
||||
if (val & KVM_XICS_PENDING)
|
||||
irqp->asserted = 1;
|
||||
}
|
||||
irqp->exists = 1;
|
||||
arch_spin_unlock(&ics->lock);
|
||||
local_irq_restore(flags);
|
||||
|
@ -1249,11 +1255,10 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
|
|||
return ics_deliver_irq(xics, irq, level);
|
||||
}
|
||||
|
||||
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *irq_entry, struct kvm *kvm,
|
||||
int irq_source_id, int level, bool line_status)
|
||||
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *irq_entry,
|
||||
struct kvm *kvm, int irq_source_id,
|
||||
int level, bool line_status)
|
||||
{
|
||||
if (!level)
|
||||
return -1;
|
||||
return kvm_set_irq(kvm, irq_source_id, irq_entry->gsi,
|
||||
level, line_status);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ struct ics_irq_state {
|
|||
u8 saved_priority;
|
||||
u8 resend;
|
||||
u8 masked_pending;
|
||||
u8 lsi; /* level-sensitive interrupt */
|
||||
u8 asserted; /* Only for LSI */
|
||||
u8 exists;
|
||||
};
|
||||
|
|
|
@ -64,6 +64,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|||
{ "ext_intr", VCPU_STAT(ext_intr_exits) },
|
||||
{ "halt_successful_poll", VCPU_STAT(halt_successful_poll) },
|
||||
{ "halt_attempted_poll", VCPU_STAT(halt_attempted_poll) },
|
||||
{ "halt_poll_invalid", VCPU_STAT(halt_poll_invalid) },
|
||||
{ "halt_wakeup", VCPU_STAT(halt_wakeup) },
|
||||
{ "doorbell", VCPU_STAT(dbell_exits) },
|
||||
{ "guest doorbell", VCPU_STAT(gdbell_exits) },
|
||||
|
|
|
@ -800,9 +800,9 @@ static void kvmppc_complete_mmio_load(struct kvm_vcpu *vcpu,
|
|||
}
|
||||
}
|
||||
|
||||
int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||
unsigned int rt, unsigned int bytes,
|
||||
int is_default_endian)
|
||||
static int __kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||
unsigned int rt, unsigned int bytes,
|
||||
int is_default_endian, int sign_extend)
|
||||
{
|
||||
int idx, ret;
|
||||
bool host_swabbed;
|
||||
|
@ -827,7 +827,7 @@ int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||
vcpu->arch.mmio_host_swabbed = host_swabbed;
|
||||
vcpu->mmio_needed = 1;
|
||||
vcpu->mmio_is_write = 0;
|
||||
vcpu->arch.mmio_sign_extend = 0;
|
||||
vcpu->arch.mmio_sign_extend = sign_extend;
|
||||
|
||||
idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
|
||||
|
@ -844,6 +844,13 @@ int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||
|
||||
return EMULATE_DO_MMIO;
|
||||
}
|
||||
|
||||
int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||
unsigned int rt, unsigned int bytes,
|
||||
int is_default_endian)
|
||||
{
|
||||
return __kvmppc_handle_load(run, vcpu, rt, bytes, is_default_endian, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvmppc_handle_load);
|
||||
|
||||
/* Same as above, but sign extends */
|
||||
|
@ -851,12 +858,7 @@ int kvmppc_handle_loads(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||
unsigned int rt, unsigned int bytes,
|
||||
int is_default_endian)
|
||||
{
|
||||
int r;
|
||||
|
||||
vcpu->arch.mmio_sign_extend = 1;
|
||||
r = kvmppc_handle_load(run, vcpu, rt, bytes, is_default_endian);
|
||||
|
||||
return r;
|
||||
return __kvmppc_handle_load(run, vcpu, rt, bytes, is_default_endian, 1);
|
||||
}
|
||||
|
||||
int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
*/
|
||||
#define KVM_NR_IRQCHIPS 1
|
||||
#define KVM_IRQCHIP_NUM_PINS 4096
|
||||
#define KVM_HALT_POLL_NS_DEFAULT 0
|
||||
#define KVM_HALT_POLL_NS_DEFAULT 80000
|
||||
|
||||
/* s390-specific vcpu->requests bit members */
|
||||
#define KVM_REQ_ENABLE_IBS 8
|
||||
|
@ -247,6 +247,7 @@ struct kvm_vcpu_stat {
|
|||
u32 exit_instruction;
|
||||
u32 halt_successful_poll;
|
||||
u32 halt_attempted_poll;
|
||||
u32 halt_poll_invalid;
|
||||
u32 halt_wakeup;
|
||||
u32 instruction_lctl;
|
||||
u32 instruction_lctlg;
|
||||
|
@ -544,10 +545,6 @@ struct kvm_vcpu_arch {
|
|||
struct kvm_s390_local_interrupt local_int;
|
||||
struct hrtimer ckc_timer;
|
||||
struct kvm_s390_pgm_info pgm;
|
||||
union {
|
||||
struct cpuid cpu_id;
|
||||
u64 stidp_data;
|
||||
};
|
||||
struct gmap *gmap;
|
||||
struct kvm_guestdbg_info_arch guestdbg;
|
||||
unsigned long pfault_token;
|
||||
|
@ -605,7 +602,7 @@ struct kvm_s390_cpu_model {
|
|||
__u64 fac_mask[S390_ARCH_FAC_LIST_SIZE_U64];
|
||||
/* facility list requested by guest (in dma page) */
|
||||
__u64 *fac_list;
|
||||
struct cpuid cpu_id;
|
||||
u64 cpuid;
|
||||
unsigned short ibc;
|
||||
};
|
||||
|
||||
|
@ -700,4 +697,6 @@ static inline void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
|||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -69,6 +69,7 @@ struct sclp_info {
|
|||
unsigned int max_cores;
|
||||
unsigned long hsa_size;
|
||||
unsigned long facilities;
|
||||
unsigned int hmfai;
|
||||
};
|
||||
extern struct sclp_info sclp;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
/* SIGP cpu status bits */
|
||||
|
||||
#define SIGP_STATUS_INVALID_ORDER 0x00000002UL
|
||||
#define SIGP_STATUS_CHECK_STOP 0x00000010UL
|
||||
#define SIGP_STATUS_STOPPED 0x00000040UL
|
||||
#define SIGP_STATUS_EXT_CALL_PENDING 0x00000080UL
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#define KVM_DEV_FLIC_APF_DISABLE_WAIT 5
|
||||
#define KVM_DEV_FLIC_ADAPTER_REGISTER 6
|
||||
#define KVM_DEV_FLIC_ADAPTER_MODIFY 7
|
||||
#define KVM_DEV_FLIC_CLEAR_IO_IRQ 8
|
||||
/*
|
||||
* We can have up to 4*64k pending subchannels + 8 adapter interrupts,
|
||||
* as well as up to ASYNC_PF_PER_VCPU*KVM_MAX_VCPUS pfault done interrupts.
|
||||
|
|
|
@ -16,14 +16,19 @@
|
|||
{ 0x01, "SIGP sense" }, \
|
||||
{ 0x02, "SIGP external call" }, \
|
||||
{ 0x03, "SIGP emergency signal" }, \
|
||||
{ 0x04, "SIGP start" }, \
|
||||
{ 0x05, "SIGP stop" }, \
|
||||
{ 0x06, "SIGP restart" }, \
|
||||
{ 0x09, "SIGP stop and store status" }, \
|
||||
{ 0x0b, "SIGP initial cpu reset" }, \
|
||||
{ 0x0c, "SIGP cpu reset" }, \
|
||||
{ 0x0d, "SIGP set prefix" }, \
|
||||
{ 0x0e, "SIGP store status at address" }, \
|
||||
{ 0x12, "SIGP set architecture" }, \
|
||||
{ 0x15, "SIGP sense running" }
|
||||
{ 0x13, "SIGP conditional emergency signal" }, \
|
||||
{ 0x15, "SIGP sense running" }, \
|
||||
{ 0x16, "SIGP set multithreading"}, \
|
||||
{ 0x17, "SIGP store additional status ait address"}
|
||||
|
||||
#define icpt_prog_codes \
|
||||
{ 0x0001, "Prog Operation" }, \
|
||||
|
|
|
@ -28,6 +28,7 @@ config KVM
|
|||
select HAVE_KVM_IRQCHIP
|
||||
select HAVE_KVM_IRQFD
|
||||
select HAVE_KVM_IRQ_ROUTING
|
||||
select HAVE_KVM_INVALID_WAKEUPS
|
||||
select SRCU
|
||||
select KVM_VFIO
|
||||
---help---
|
||||
|
|
|
@ -977,6 +977,11 @@ no_timer:
|
|||
|
||||
void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* We cannot move this into the if, as the CPU might be already
|
||||
* in kvm_vcpu_block without having the waitqueue set (polling)
|
||||
*/
|
||||
vcpu->valid_wakeup = true;
|
||||
if (swait_active(&vcpu->wq)) {
|
||||
/*
|
||||
* The vcpu gave up the cpu voluntarily, mark it as a good
|
||||
|
@ -2034,6 +2039,27 @@ static int modify_io_adapter(struct kvm_device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int clear_io_irq(struct kvm *kvm, struct kvm_device_attr *attr)
|
||||
|
||||
{
|
||||
const u64 isc_mask = 0xffUL << 24; /* all iscs set */
|
||||
u32 schid;
|
||||
|
||||
if (attr->flags)
|
||||
return -EINVAL;
|
||||
if (attr->attr != sizeof(schid))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&schid, (void __user *) attr->addr, sizeof(schid)))
|
||||
return -EFAULT;
|
||||
kfree(kvm_s390_get_io_int(kvm, isc_mask, schid));
|
||||
/*
|
||||
* If userspace is conforming to the architecture, we can have at most
|
||||
* one pending I/O interrupt per subchannel, so this is effectively a
|
||||
* clear all.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
||||
{
|
||||
int r = 0;
|
||||
|
@ -2067,6 +2093,9 @@ static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||
case KVM_DEV_FLIC_ADAPTER_MODIFY:
|
||||
r = modify_io_adapter(dev, attr);
|
||||
break;
|
||||
case KVM_DEV_FLIC_CLEAR_IO_IRQ:
|
||||
r = clear_io_irq(dev->kvm, attr);
|
||||
break;
|
||||
default:
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
@ -2074,6 +2103,23 @@ static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||
return r;
|
||||
}
|
||||
|
||||
static int flic_has_attr(struct kvm_device *dev,
|
||||
struct kvm_device_attr *attr)
|
||||
{
|
||||
switch (attr->group) {
|
||||
case KVM_DEV_FLIC_GET_ALL_IRQS:
|
||||
case KVM_DEV_FLIC_ENQUEUE:
|
||||
case KVM_DEV_FLIC_CLEAR_IRQS:
|
||||
case KVM_DEV_FLIC_APF_ENABLE:
|
||||
case KVM_DEV_FLIC_APF_DISABLE_WAIT:
|
||||
case KVM_DEV_FLIC_ADAPTER_REGISTER:
|
||||
case KVM_DEV_FLIC_ADAPTER_MODIFY:
|
||||
case KVM_DEV_FLIC_CLEAR_IO_IRQ:
|
||||
return 0;
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int flic_create(struct kvm_device *dev, u32 type)
|
||||
{
|
||||
if (!dev)
|
||||
|
@ -2095,6 +2141,7 @@ struct kvm_device_ops kvm_flic_ops = {
|
|||
.name = "kvm-flic",
|
||||
.get_attr = flic_get_attr,
|
||||
.set_attr = flic_set_attr,
|
||||
.has_attr = flic_has_attr,
|
||||
.create = flic_create,
|
||||
.destroy = flic_destroy,
|
||||
};
|
||||
|
|
|
@ -65,6 +65,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|||
{ "exit_instr_and_program_int", VCPU_STAT(exit_instr_and_program) },
|
||||
{ "halt_successful_poll", VCPU_STAT(halt_successful_poll) },
|
||||
{ "halt_attempted_poll", VCPU_STAT(halt_attempted_poll) },
|
||||
{ "halt_poll_invalid", VCPU_STAT(halt_poll_invalid) },
|
||||
{ "halt_wakeup", VCPU_STAT(halt_wakeup) },
|
||||
{ "instruction_lctlg", VCPU_STAT(instruction_lctlg) },
|
||||
{ "instruction_lctl", VCPU_STAT(instruction_lctl) },
|
||||
|
@ -118,9 +119,9 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|||
};
|
||||
|
||||
/* upper facilities limit for kvm */
|
||||
unsigned long kvm_s390_fac_list_mask[] = {
|
||||
0xffe6fffbfcfdfc40UL,
|
||||
0x005e800000000000UL,
|
||||
unsigned long kvm_s390_fac_list_mask[16] = {
|
||||
0xffe6000000000000UL,
|
||||
0x005e000000000000UL,
|
||||
};
|
||||
|
||||
unsigned long kvm_s390_fac_list_mask_size(void)
|
||||
|
@ -638,6 +639,7 @@ static int kvm_s390_get_tod(struct kvm *kvm, struct kvm_device_attr *attr)
|
|||
static int kvm_s390_set_processor(struct kvm *kvm, struct kvm_device_attr *attr)
|
||||
{
|
||||
struct kvm_s390_vm_cpu_processor *proc;
|
||||
u16 lowest_ibc, unblocked_ibc;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&kvm->lock);
|
||||
|
@ -652,9 +654,17 @@ static int kvm_s390_set_processor(struct kvm *kvm, struct kvm_device_attr *attr)
|
|||
}
|
||||
if (!copy_from_user(proc, (void __user *)attr->addr,
|
||||
sizeof(*proc))) {
|
||||
memcpy(&kvm->arch.model.cpu_id, &proc->cpuid,
|
||||
sizeof(struct cpuid));
|
||||
kvm->arch.model.ibc = proc->ibc;
|
||||
kvm->arch.model.cpuid = proc->cpuid;
|
||||
lowest_ibc = sclp.ibc >> 16 & 0xfff;
|
||||
unblocked_ibc = sclp.ibc & 0xfff;
|
||||
if (lowest_ibc) {
|
||||
if (proc->ibc > unblocked_ibc)
|
||||
kvm->arch.model.ibc = unblocked_ibc;
|
||||
else if (proc->ibc < lowest_ibc)
|
||||
kvm->arch.model.ibc = lowest_ibc;
|
||||
else
|
||||
kvm->arch.model.ibc = proc->ibc;
|
||||
}
|
||||
memcpy(kvm->arch.model.fac_list, proc->fac_list,
|
||||
S390_ARCH_FAC_LIST_SIZE_BYTE);
|
||||
} else
|
||||
|
@ -687,7 +697,7 @@ static int kvm_s390_get_processor(struct kvm *kvm, struct kvm_device_attr *attr)
|
|||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memcpy(&proc->cpuid, &kvm->arch.model.cpu_id, sizeof(struct cpuid));
|
||||
proc->cpuid = kvm->arch.model.cpuid;
|
||||
proc->ibc = kvm->arch.model.ibc;
|
||||
memcpy(&proc->fac_list, kvm->arch.model.fac_list,
|
||||
S390_ARCH_FAC_LIST_SIZE_BYTE);
|
||||
|
@ -1081,10 +1091,13 @@ static void kvm_s390_set_crycb_format(struct kvm *kvm)
|
|||
kvm->arch.crypto.crycbd |= CRYCB_FORMAT1;
|
||||
}
|
||||
|
||||
static void kvm_s390_get_cpu_id(struct cpuid *cpu_id)
|
||||
static u64 kvm_s390_get_initial_cpuid(void)
|
||||
{
|
||||
get_cpu_id(cpu_id);
|
||||
cpu_id->version = 0xff;
|
||||
struct cpuid cpuid;
|
||||
|
||||
get_cpu_id(&cpuid);
|
||||
cpuid.version = 0xff;
|
||||
return *((u64 *) &cpuid);
|
||||
}
|
||||
|
||||
static void kvm_s390_crypto_init(struct kvm *kvm)
|
||||
|
@ -1175,7 +1188,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
memcpy(kvm->arch.model.fac_list, kvm->arch.model.fac_mask,
|
||||
S390_ARCH_FAC_LIST_SIZE_BYTE);
|
||||
|
||||
kvm_s390_get_cpu_id(&kvm->arch.model.cpu_id);
|
||||
kvm->arch.model.cpuid = kvm_s390_get_initial_cpuid();
|
||||
kvm->arch.model.ibc = sclp.ibc & 0x0fff;
|
||||
|
||||
kvm_s390_crypto_init(kvm);
|
||||
|
@ -1624,7 +1637,6 @@ static void kvm_s390_vcpu_setup_model(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct kvm_s390_cpu_model *model = &vcpu->kvm->arch.model;
|
||||
|
||||
vcpu->arch.cpu_id = model->cpu_id;
|
||||
vcpu->arch.sie_block->ibc = model->ibc;
|
||||
if (test_kvm_facility(vcpu->kvm, 7))
|
||||
vcpu->arch.sie_block->fac = (u32)(u64) model->fac_list;
|
||||
|
@ -1645,11 +1657,14 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
|
|||
|
||||
kvm_s390_vcpu_setup_model(vcpu);
|
||||
|
||||
vcpu->arch.sie_block->ecb = 6;
|
||||
vcpu->arch.sie_block->ecb = 0x02;
|
||||
if (test_kvm_facility(vcpu->kvm, 9))
|
||||
vcpu->arch.sie_block->ecb |= 0x04;
|
||||
if (test_kvm_facility(vcpu->kvm, 50) && test_kvm_facility(vcpu->kvm, 73))
|
||||
vcpu->arch.sie_block->ecb |= 0x10;
|
||||
|
||||
vcpu->arch.sie_block->ecb2 = 8;
|
||||
if (test_kvm_facility(vcpu->kvm, 8))
|
||||
vcpu->arch.sie_block->ecb2 |= 0x08;
|
||||
vcpu->arch.sie_block->eca = 0xC1002000U;
|
||||
if (sclp.has_siif)
|
||||
vcpu->arch.sie_block->eca |= 1;
|
||||
|
@ -2971,13 +2986,31 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
|
|||
return;
|
||||
}
|
||||
|
||||
static inline unsigned long nonhyp_mask(int i)
|
||||
{
|
||||
unsigned int nonhyp_fai = (sclp.hmfai << i * 2) >> 30;
|
||||
|
||||
return 0x0000ffffffffffffUL >> (nonhyp_fai << 4);
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->valid_wakeup = false;
|
||||
}
|
||||
|
||||
static int __init kvm_s390_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!sclp.has_sief2) {
|
||||
pr_info("SIE not available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
kvm_s390_fac_list_mask[i] |=
|
||||
S390_lowcore.stfle_fac_list[i] & nonhyp_mask(i);
|
||||
|
||||
return kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
|
||||
}
|
||||
|
||||
|
|
|
@ -439,7 +439,7 @@ static int handle_lpswe(struct kvm_vcpu *vcpu)
|
|||
|
||||
static int handle_stidp(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 stidp_data = vcpu->arch.stidp_data;
|
||||
u64 stidp_data = vcpu->kvm->arch.model.cpuid;
|
||||
u64 operand2;
|
||||
int rc;
|
||||
ar_t ar;
|
||||
|
@ -670,8 +670,9 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
|
|||
if (vcpu->run->s.regs.gprs[reg1] & PFMF_RESERVED)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
/* Only provide non-quiescing support if the host supports it */
|
||||
if (vcpu->run->s.regs.gprs[reg1] & PFMF_NQ && !test_facility(14))
|
||||
/* Only provide non-quiescing support if enabled for the guest */
|
||||
if (vcpu->run->s.regs.gprs[reg1] & PFMF_NQ &&
|
||||
!test_kvm_facility(vcpu->kvm, 14))
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
|
||||
/* No support for conditional-SSKE */
|
||||
|
@ -744,7 +745,7 @@ static int handle_essa(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
/* entries expected to be 1FF */
|
||||
int entries = (vcpu->arch.sie_block->cbrlo & ~PAGE_MASK) >> 3;
|
||||
unsigned long *cbrlo, cbrle;
|
||||
unsigned long *cbrlo;
|
||||
struct gmap *gmap;
|
||||
int i;
|
||||
|
||||
|
@ -765,17 +766,9 @@ static int handle_essa(struct kvm_vcpu *vcpu)
|
|||
vcpu->arch.sie_block->cbrlo &= PAGE_MASK; /* reset nceo */
|
||||
cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo);
|
||||
down_read(&gmap->mm->mmap_sem);
|
||||
for (i = 0; i < entries; ++i) {
|
||||
cbrle = cbrlo[i];
|
||||
if (unlikely(cbrle & ~PAGE_MASK || cbrle < 2 * PAGE_SIZE))
|
||||
/* invalid entry */
|
||||
break;
|
||||
/* try to free backing */
|
||||
__gmap_zap(gmap, cbrle);
|
||||
}
|
||||
for (i = 0; i < entries; ++i)
|
||||
__gmap_zap(gmap, cbrlo[i]);
|
||||
up_read(&gmap->mm->mmap_sem);
|
||||
if (i < entries)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,6 +240,12 @@ static int __sigp_sense_running(struct kvm_vcpu *vcpu,
|
|||
struct kvm_s390_local_interrupt *li;
|
||||
int rc;
|
||||
|
||||
if (!test_kvm_facility(vcpu->kvm, 9)) {
|
||||
*reg &= 0xffffffff00000000UL;
|
||||
*reg |= SIGP_STATUS_INVALID_ORDER;
|
||||
return SIGP_CC_STATUS_STORED;
|
||||
}
|
||||
|
||||
li = &dst_vcpu->arch.local_int;
|
||||
if (atomic_read(li->cpuflags) & CPUSTAT_RUNNING) {
|
||||
/* running */
|
||||
|
|
|
@ -562,7 +562,6 @@ struct kvm_vcpu_arch {
|
|||
struct {
|
||||
u64 msr_val;
|
||||
u64 last_steal;
|
||||
u64 accum_steal;
|
||||
struct gfn_to_hva_cache stime;
|
||||
struct kvm_steal_time steal;
|
||||
} st;
|
||||
|
@ -774,6 +773,11 @@ struct kvm_arch {
|
|||
u8 nr_reserved_ioapic_pins;
|
||||
|
||||
bool disabled_lapic_found;
|
||||
|
||||
/* Struct members for AVIC */
|
||||
u32 ldr_mode;
|
||||
struct page *avic_logical_id_table_page;
|
||||
struct page *avic_physical_id_table_page;
|
||||
};
|
||||
|
||||
struct kvm_vm_stat {
|
||||
|
@ -804,6 +808,7 @@ struct kvm_vcpu_stat {
|
|||
u32 halt_exits;
|
||||
u32 halt_successful_poll;
|
||||
u32 halt_attempted_poll;
|
||||
u32 halt_poll_invalid;
|
||||
u32 halt_wakeup;
|
||||
u32 request_irq_exits;
|
||||
u32 irq_exits;
|
||||
|
@ -848,6 +853,9 @@ struct kvm_x86_ops {
|
|||
bool (*cpu_has_high_real_mode_segbase)(void);
|
||||
void (*cpuid_update)(struct kvm_vcpu *vcpu);
|
||||
|
||||
int (*vm_init)(struct kvm *kvm);
|
||||
void (*vm_destroy)(struct kvm *kvm);
|
||||
|
||||
/* Create, but do not attach this VCPU */
|
||||
struct kvm_vcpu *(*vcpu_create)(struct kvm *kvm, unsigned id);
|
||||
void (*vcpu_free)(struct kvm_vcpu *vcpu);
|
||||
|
@ -914,7 +922,7 @@ struct kvm_x86_ops {
|
|||
bool (*get_enable_apicv)(void);
|
||||
void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);
|
||||
void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr);
|
||||
void (*hwapic_isr_update)(struct kvm *kvm, int isr);
|
||||
void (*hwapic_isr_update)(struct kvm_vcpu *vcpu, int isr);
|
||||
void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
|
||||
void (*set_virtual_x2apic_mode)(struct kvm_vcpu *vcpu, bool set);
|
||||
void (*set_apic_access_page_addr)(struct kvm_vcpu *vcpu, hpa_t hpa);
|
||||
|
@ -990,8 +998,13 @@ struct kvm_x86_ops {
|
|||
*/
|
||||
int (*pre_block)(struct kvm_vcpu *vcpu);
|
||||
void (*post_block)(struct kvm_vcpu *vcpu);
|
||||
|
||||
void (*vcpu_blocking)(struct kvm_vcpu *vcpu);
|
||||
void (*vcpu_unblocking)(struct kvm_vcpu *vcpu);
|
||||
|
||||
int (*update_pi_irte)(struct kvm *kvm, unsigned int host_irq,
|
||||
uint32_t guest_irq, bool set);
|
||||
void (*apicv_post_state_restore)(struct kvm_vcpu *vcpu);
|
||||
};
|
||||
|
||||
struct kvm_arch_async_pf {
|
||||
|
@ -1341,7 +1354,18 @@ bool kvm_intr_is_single_vcpu(struct kvm *kvm, struct kvm_lapic_irq *irq,
|
|||
void kvm_set_msi_irq(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm_lapic_irq *irq);
|
||||
|
||||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
|
||||
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_x86_ops->vcpu_blocking)
|
||||
kvm_x86_ops->vcpu_blocking(vcpu);
|
||||
}
|
||||
|
||||
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_x86_ops->vcpu_unblocking)
|
||||
kvm_x86_ops->vcpu_unblocking(vcpu);
|
||||
}
|
||||
|
||||
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
|
||||
|
||||
#endif /* _ASM_X86_KVM_HOST_H */
|
||||
|
|
|
@ -78,7 +78,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
|
|||
u32 exit_int_info;
|
||||
u32 exit_int_info_err;
|
||||
u64 nested_ctl;
|
||||
u8 reserved_4[16];
|
||||
u64 avic_vapic_bar;
|
||||
u8 reserved_4[8];
|
||||
u32 event_inj;
|
||||
u32 event_inj_err;
|
||||
u64 nested_cr3;
|
||||
|
@ -88,7 +89,11 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
|
|||
u64 next_rip;
|
||||
u8 insn_len;
|
||||
u8 insn_bytes[15];
|
||||
u8 reserved_6[800];
|
||||
u64 avic_backing_page; /* Offset 0xe0 */
|
||||
u8 reserved_6[8]; /* Offset 0xe8 */
|
||||
u64 avic_logical_id; /* Offset 0xf0 */
|
||||
u64 avic_physical_id; /* Offset 0xf8 */
|
||||
u8 reserved_7[768];
|
||||
};
|
||||
|
||||
|
||||
|
@ -111,6 +116,9 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
|
|||
#define V_INTR_MASKING_SHIFT 24
|
||||
#define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT)
|
||||
|
||||
#define AVIC_ENABLE_SHIFT 31
|
||||
#define AVIC_ENABLE_MASK (1 << AVIC_ENABLE_SHIFT)
|
||||
|
||||
#define SVM_INTERRUPT_SHADOW_MASK 1
|
||||
|
||||
#define SVM_IOIO_STR_SHIFT 2
|
||||
|
|
|
@ -216,9 +216,9 @@ struct kvm_cpuid_entry2 {
|
|||
__u32 padding[3];
|
||||
};
|
||||
|
||||
#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX BIT(0)
|
||||
#define KVM_CPUID_FLAG_STATEFUL_FUNC BIT(1)
|
||||
#define KVM_CPUID_FLAG_STATE_READ_NEXT BIT(2)
|
||||
#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX (1 << 0)
|
||||
#define KVM_CPUID_FLAG_STATEFUL_FUNC (1 << 1)
|
||||
#define KVM_CPUID_FLAG_STATE_READ_NEXT (1 << 2)
|
||||
|
||||
/* for KVM_SET_CPUID2 */
|
||||
struct kvm_cpuid2 {
|
||||
|
|
|
@ -73,6 +73,8 @@
|
|||
#define SVM_EXIT_MWAIT_COND 0x08c
|
||||
#define SVM_EXIT_XSETBV 0x08d
|
||||
#define SVM_EXIT_NPF 0x400
|
||||
#define SVM_EXIT_AVIC_INCOMPLETE_IPI 0x401
|
||||
#define SVM_EXIT_AVIC_UNACCELERATED_ACCESS 0x402
|
||||
|
||||
#define SVM_EXIT_ERR -1
|
||||
|
||||
|
@ -107,8 +109,10 @@
|
|||
{ SVM_EXIT_SMI, "smi" }, \
|
||||
{ SVM_EXIT_INIT, "init" }, \
|
||||
{ SVM_EXIT_VINTR, "vintr" }, \
|
||||
{ SVM_EXIT_CR0_SEL_WRITE, "cr0_sel_write" }, \
|
||||
{ SVM_EXIT_CPUID, "cpuid" }, \
|
||||
{ SVM_EXIT_INVD, "invd" }, \
|
||||
{ SVM_EXIT_PAUSE, "pause" }, \
|
||||
{ SVM_EXIT_HLT, "hlt" }, \
|
||||
{ SVM_EXIT_INVLPG, "invlpg" }, \
|
||||
{ SVM_EXIT_INVLPGA, "invlpga" }, \
|
||||
|
@ -127,7 +131,10 @@
|
|||
{ SVM_EXIT_MONITOR, "monitor" }, \
|
||||
{ SVM_EXIT_MWAIT, "mwait" }, \
|
||||
{ SVM_EXIT_XSETBV, "xsetbv" }, \
|
||||
{ SVM_EXIT_NPF, "npf" }
|
||||
{ SVM_EXIT_NPF, "npf" }, \
|
||||
{ SVM_EXIT_RSM, "rsm" }, \
|
||||
{ SVM_EXIT_AVIC_INCOMPLETE_IPI, "avic_incomplete_ipi" }, \
|
||||
{ SVM_EXIT_AVIC_UNACCELERATED_ACCESS, "avic_unaccelerated_access" }
|
||||
|
||||
|
||||
#endif /* _UAPI__SVM_H */
|
||||
|
|
|
@ -443,7 +443,7 @@ static void __kvm_ioapic_update_eoi(struct kvm_vcpu *vcpu,
|
|||
spin_lock(&ioapic->lock);
|
||||
|
||||
if (trigger_mode != IOAPIC_LEVEL_TRIG ||
|
||||
kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)
|
||||
kvm_lapic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)
|
||||
continue;
|
||||
|
||||
ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
|
||||
|
|
|
@ -382,9 +382,6 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
|
|||
u32 i, nr_ioapic_pins;
|
||||
int idx;
|
||||
|
||||
/* kvm->irq_routing must be read after clearing
|
||||
* KVM_SCAN_IOAPIC. */
|
||||
smp_mb();
|
||||
idx = srcu_read_lock(&kvm->irq_srcu);
|
||||
table = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu);
|
||||
nr_ioapic_pins = min_t(u32, table->nr_rt_entries,
|
||||
|
|
|
@ -59,9 +59,8 @@
|
|||
/* #define apic_debug(fmt,arg...) printk(KERN_WARNING fmt,##arg) */
|
||||
#define apic_debug(fmt, arg...)
|
||||
|
||||
#define APIC_LVT_NUM 6
|
||||
/* 14 is the version for Xeon and Pentium 8.4.8*/
|
||||
#define APIC_VERSION (0x14UL | ((APIC_LVT_NUM - 1) << 16))
|
||||
#define APIC_VERSION (0x14UL | ((KVM_APIC_LVT_NUM - 1) << 16))
|
||||
#define LAPIC_MMIO_LENGTH (1 << 12)
|
||||
/* followed define is not in apicdef.h */
|
||||
#define APIC_SHORT_MASK 0xc0000
|
||||
|
@ -73,14 +72,6 @@
|
|||
#define APIC_BROADCAST 0xFF
|
||||
#define X2APIC_BROADCAST 0xFFFFFFFFul
|
||||
|
||||
#define VEC_POS(v) ((v) & (32 - 1))
|
||||
#define REG_POS(v) (((v) >> 5) << 4)
|
||||
|
||||
static inline void apic_set_reg(struct kvm_lapic *apic, int reg_off, u32 val)
|
||||
{
|
||||
*((u32 *) (apic->regs + reg_off)) = val;
|
||||
}
|
||||
|
||||
static inline int apic_test_vector(int vec, void *bitmap)
|
||||
{
|
||||
return test_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
|
||||
|
@ -94,11 +85,6 @@ bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector)
|
|||
apic_test_vector(vector, apic->regs + APIC_IRR);
|
||||
}
|
||||
|
||||
static inline void apic_set_vector(int vec, void *bitmap)
|
||||
{
|
||||
set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
|
||||
}
|
||||
|
||||
static inline void apic_clear_vector(int vec, void *bitmap)
|
||||
{
|
||||
clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
|
||||
|
@ -173,7 +159,7 @@ static void recalculate_apic_map(struct kvm *kvm)
|
|||
continue;
|
||||
|
||||
aid = kvm_apic_id(apic);
|
||||
ldr = kvm_apic_get_reg(apic, APIC_LDR);
|
||||
ldr = kvm_lapic_get_reg(apic, APIC_LDR);
|
||||
|
||||
if (aid < ARRAY_SIZE(new->phys_map))
|
||||
new->phys_map[aid] = apic;
|
||||
|
@ -182,7 +168,7 @@ static void recalculate_apic_map(struct kvm *kvm)
|
|||
new->mode |= KVM_APIC_MODE_X2APIC;
|
||||
} else if (ldr) {
|
||||
ldr = GET_APIC_LOGICAL_ID(ldr);
|
||||
if (kvm_apic_get_reg(apic, APIC_DFR) == APIC_DFR_FLAT)
|
||||
if (kvm_lapic_get_reg(apic, APIC_DFR) == APIC_DFR_FLAT)
|
||||
new->mode |= KVM_APIC_MODE_XAPIC_FLAT;
|
||||
else
|
||||
new->mode |= KVM_APIC_MODE_XAPIC_CLUSTER;
|
||||
|
@ -212,7 +198,7 @@ static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
|
|||
{
|
||||
bool enabled = val & APIC_SPIV_APIC_ENABLED;
|
||||
|
||||
apic_set_reg(apic, APIC_SPIV, val);
|
||||
kvm_lapic_set_reg(apic, APIC_SPIV, val);
|
||||
|
||||
if (enabled != apic->sw_enabled) {
|
||||
apic->sw_enabled = enabled;
|
||||
|
@ -226,13 +212,13 @@ static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
|
|||
|
||||
static inline void kvm_apic_set_id(struct kvm_lapic *apic, u8 id)
|
||||
{
|
||||
apic_set_reg(apic, APIC_ID, id << 24);
|
||||
kvm_lapic_set_reg(apic, APIC_ID, id << 24);
|
||||
recalculate_apic_map(apic->vcpu->kvm);
|
||||
}
|
||||
|
||||
static inline void kvm_apic_set_ldr(struct kvm_lapic *apic, u32 id)
|
||||
{
|
||||
apic_set_reg(apic, APIC_LDR, id);
|
||||
kvm_lapic_set_reg(apic, APIC_LDR, id);
|
||||
recalculate_apic_map(apic->vcpu->kvm);
|
||||
}
|
||||
|
||||
|
@ -240,19 +226,19 @@ static inline void kvm_apic_set_x2apic_id(struct kvm_lapic *apic, u8 id)
|
|||
{
|
||||
u32 ldr = ((id >> 4) << 16) | (1 << (id & 0xf));
|
||||
|
||||
apic_set_reg(apic, APIC_ID, id << 24);
|
||||
apic_set_reg(apic, APIC_LDR, ldr);
|
||||
kvm_lapic_set_reg(apic, APIC_ID, id << 24);
|
||||
kvm_lapic_set_reg(apic, APIC_LDR, ldr);
|
||||
recalculate_apic_map(apic->vcpu->kvm);
|
||||
}
|
||||
|
||||
static inline int apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type)
|
||||
{
|
||||
return !(kvm_apic_get_reg(apic, lvt_type) & APIC_LVT_MASKED);
|
||||
return !(kvm_lapic_get_reg(apic, lvt_type) & APIC_LVT_MASKED);
|
||||
}
|
||||
|
||||
static inline int apic_lvt_vector(struct kvm_lapic *apic, int lvt_type)
|
||||
{
|
||||
return kvm_apic_get_reg(apic, lvt_type) & APIC_VECTOR_MASK;
|
||||
return kvm_lapic_get_reg(apic, lvt_type) & APIC_VECTOR_MASK;
|
||||
}
|
||||
|
||||
static inline int apic_lvtt_oneshot(struct kvm_lapic *apic)
|
||||
|
@ -287,10 +273,10 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu)
|
|||
feat = kvm_find_cpuid_entry(apic->vcpu, 0x1, 0);
|
||||
if (feat && (feat->ecx & (1 << (X86_FEATURE_X2APIC & 31))))
|
||||
v |= APIC_LVR_DIRECTED_EOI;
|
||||
apic_set_reg(apic, APIC_LVR, v);
|
||||
kvm_lapic_set_reg(apic, APIC_LVR, v);
|
||||
}
|
||||
|
||||
static const unsigned int apic_lvt_mask[APIC_LVT_NUM] = {
|
||||
static const unsigned int apic_lvt_mask[KVM_APIC_LVT_NUM] = {
|
||||
LVT_MASK , /* part LVTT mask, timer mode mask added at runtime */
|
||||
LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */
|
||||
LVT_MASK | APIC_MODE_MASK, /* LVTPC */
|
||||
|
@ -349,16 +335,6 @@ void kvm_apic_update_irr(struct kvm_vcpu *vcpu, u32 *pir)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_apic_update_irr);
|
||||
|
||||
static inline void apic_set_irr(int vec, struct kvm_lapic *apic)
|
||||
{
|
||||
apic_set_vector(vec, apic->regs + APIC_IRR);
|
||||
/*
|
||||
* irr_pending must be true if any interrupt is pending; set it after
|
||||
* APIC_IRR to avoid race with apic_clear_irr
|
||||
*/
|
||||
apic->irr_pending = true;
|
||||
}
|
||||
|
||||
static inline int apic_search_irr(struct kvm_lapic *apic)
|
||||
{
|
||||
return find_highest_vector(apic->regs + APIC_IRR);
|
||||
|
@ -416,7 +392,7 @@ static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
|
|||
* just set SVI.
|
||||
*/
|
||||
if (unlikely(vcpu->arch.apicv_active))
|
||||
kvm_x86_ops->hwapic_isr_update(vcpu->kvm, vec);
|
||||
kvm_x86_ops->hwapic_isr_update(vcpu, vec);
|
||||
else {
|
||||
++apic->isr_count;
|
||||
BUG_ON(apic->isr_count > MAX_APIC_VECTOR);
|
||||
|
@ -464,7 +440,7 @@ static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)
|
|||
* and must be left alone.
|
||||
*/
|
||||
if (unlikely(vcpu->arch.apicv_active))
|
||||
kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
|
||||
kvm_x86_ops->hwapic_isr_update(vcpu,
|
||||
apic_find_highest_isr(apic));
|
||||
else {
|
||||
--apic->isr_count;
|
||||
|
@ -549,8 +525,8 @@ static void apic_update_ppr(struct kvm_lapic *apic)
|
|||
u32 tpr, isrv, ppr, old_ppr;
|
||||
int isr;
|
||||
|
||||
old_ppr = kvm_apic_get_reg(apic, APIC_PROCPRI);
|
||||
tpr = kvm_apic_get_reg(apic, APIC_TASKPRI);
|
||||
old_ppr = kvm_lapic_get_reg(apic, APIC_PROCPRI);
|
||||
tpr = kvm_lapic_get_reg(apic, APIC_TASKPRI);
|
||||
isr = apic_find_highest_isr(apic);
|
||||
isrv = (isr != -1) ? isr : 0;
|
||||
|
||||
|
@ -563,7 +539,7 @@ static void apic_update_ppr(struct kvm_lapic *apic)
|
|||
apic, ppr, isr, isrv);
|
||||
|
||||
if (old_ppr != ppr) {
|
||||
apic_set_reg(apic, APIC_PROCPRI, ppr);
|
||||
kvm_lapic_set_reg(apic, APIC_PROCPRI, ppr);
|
||||
if (ppr < old_ppr)
|
||||
kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
|
||||
}
|
||||
|
@ -571,7 +547,7 @@ static void apic_update_ppr(struct kvm_lapic *apic)
|
|||
|
||||
static void apic_set_tpr(struct kvm_lapic *apic, u32 tpr)
|
||||
{
|
||||
apic_set_reg(apic, APIC_TASKPRI, tpr);
|
||||
kvm_lapic_set_reg(apic, APIC_TASKPRI, tpr);
|
||||
apic_update_ppr(apic);
|
||||
}
|
||||
|
||||
|
@ -601,7 +577,7 @@ static bool kvm_apic_match_logical_addr(struct kvm_lapic *apic, u32 mda)
|
|||
if (kvm_apic_broadcast(apic, mda))
|
||||
return true;
|
||||
|
||||
logical_id = kvm_apic_get_reg(apic, APIC_LDR);
|
||||
logical_id = kvm_lapic_get_reg(apic, APIC_LDR);
|
||||
|
||||
if (apic_x2apic_mode(apic))
|
||||
return ((logical_id >> 16) == (mda >> 16))
|
||||
|
@ -610,7 +586,7 @@ static bool kvm_apic_match_logical_addr(struct kvm_lapic *apic, u32 mda)
|
|||
logical_id = GET_APIC_LOGICAL_ID(logical_id);
|
||||
mda = GET_APIC_DEST_FIELD(mda);
|
||||
|
||||
switch (kvm_apic_get_reg(apic, APIC_DFR)) {
|
||||
switch (kvm_lapic_get_reg(apic, APIC_DFR)) {
|
||||
case APIC_DFR_FLAT:
|
||||
return (logical_id & mda) != 0;
|
||||
case APIC_DFR_CLUSTER:
|
||||
|
@ -618,7 +594,7 @@ static bool kvm_apic_match_logical_addr(struct kvm_lapic *apic, u32 mda)
|
|||
&& (logical_id & mda & 0xf) != 0;
|
||||
default:
|
||||
apic_debug("Bad DFR vcpu %d: %08x\n",
|
||||
apic->vcpu->vcpu_id, kvm_apic_get_reg(apic, APIC_DFR));
|
||||
apic->vcpu->vcpu_id, kvm_lapic_get_reg(apic, APIC_DFR));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -668,6 +644,7 @@ bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_apic_match_dest);
|
||||
|
||||
int kvm_vector_to_index(u32 vector, u32 dest_vcpus,
|
||||
const unsigned long *bitmap, u32 bitmap_size)
|
||||
|
@ -921,7 +898,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
|
|||
|
||||
if (apic_test_vector(vector, apic->regs + APIC_TMR) != !!trig_mode) {
|
||||
if (trig_mode)
|
||||
apic_set_vector(vector, apic->regs + APIC_TMR);
|
||||
kvm_lapic_set_vector(vector, apic->regs + APIC_TMR);
|
||||
else
|
||||
apic_clear_vector(vector, apic->regs + APIC_TMR);
|
||||
}
|
||||
|
@ -929,7 +906,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
|
|||
if (vcpu->arch.apicv_active)
|
||||
kvm_x86_ops->deliver_posted_interrupt(vcpu, vector);
|
||||
else {
|
||||
apic_set_irr(vector, apic);
|
||||
kvm_lapic_set_irr(vector, apic);
|
||||
|
||||
kvm_make_request(KVM_REQ_EVENT, vcpu);
|
||||
kvm_vcpu_kick(vcpu);
|
||||
|
@ -1073,8 +1050,8 @@ EXPORT_SYMBOL_GPL(kvm_apic_set_eoi_accelerated);
|
|||
|
||||
static void apic_send_ipi(struct kvm_lapic *apic)
|
||||
{
|
||||
u32 icr_low = kvm_apic_get_reg(apic, APIC_ICR);
|
||||
u32 icr_high = kvm_apic_get_reg(apic, APIC_ICR2);
|
||||
u32 icr_low = kvm_lapic_get_reg(apic, APIC_ICR);
|
||||
u32 icr_high = kvm_lapic_get_reg(apic, APIC_ICR2);
|
||||
struct kvm_lapic_irq irq;
|
||||
|
||||
irq.vector = icr_low & APIC_VECTOR_MASK;
|
||||
|
@ -1111,7 +1088,7 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic)
|
|||
ASSERT(apic != NULL);
|
||||
|
||||
/* if initial count is 0, current count should also be 0 */
|
||||
if (kvm_apic_get_reg(apic, APIC_TMICT) == 0 ||
|
||||
if (kvm_lapic_get_reg(apic, APIC_TMICT) == 0 ||
|
||||
apic->lapic_timer.period == 0)
|
||||
return 0;
|
||||
|
||||
|
@ -1168,13 +1145,13 @@ static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset)
|
|||
break;
|
||||
case APIC_PROCPRI:
|
||||
apic_update_ppr(apic);
|
||||
val = kvm_apic_get_reg(apic, offset);
|
||||
val = kvm_lapic_get_reg(apic, offset);
|
||||
break;
|
||||
case APIC_TASKPRI:
|
||||
report_tpr_access(apic, false);
|
||||
/* fall thru */
|
||||
default:
|
||||
val = kvm_apic_get_reg(apic, offset);
|
||||
val = kvm_lapic_get_reg(apic, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1186,7 +1163,7 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev)
|
|||
return container_of(dev, struct kvm_lapic, dev);
|
||||
}
|
||||
|
||||
static int apic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
|
||||
int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
|
||||
void *data)
|
||||
{
|
||||
unsigned char alignment = offset & 0xf;
|
||||
|
@ -1223,6 +1200,7 @@ static int apic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_lapic_reg_read);
|
||||
|
||||
static int apic_mmio_in_range(struct kvm_lapic *apic, gpa_t addr)
|
||||
{
|
||||
|
@ -1240,7 +1218,7 @@ static int apic_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *this,
|
|||
if (!apic_mmio_in_range(apic, address))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
apic_reg_read(apic, offset, len, data);
|
||||
kvm_lapic_reg_read(apic, offset, len, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1249,7 +1227,7 @@ static void update_divide_count(struct kvm_lapic *apic)
|
|||
{
|
||||
u32 tmp1, tmp2, tdcr;
|
||||
|
||||
tdcr = kvm_apic_get_reg(apic, APIC_TDCR);
|
||||
tdcr = kvm_lapic_get_reg(apic, APIC_TDCR);
|
||||
tmp1 = tdcr & 0xf;
|
||||
tmp2 = ((tmp1 & 0x3) | ((tmp1 & 0x8) >> 1)) + 1;
|
||||
apic->divide_count = 0x1 << (tmp2 & 0x7);
|
||||
|
@ -1260,7 +1238,7 @@ static void update_divide_count(struct kvm_lapic *apic)
|
|||
|
||||
static void apic_update_lvtt(struct kvm_lapic *apic)
|
||||
{
|
||||
u32 timer_mode = kvm_apic_get_reg(apic, APIC_LVTT) &
|
||||
u32 timer_mode = kvm_lapic_get_reg(apic, APIC_LVTT) &
|
||||
apic->lapic_timer.timer_mode_mask;
|
||||
|
||||
if (apic->lapic_timer.timer_mode != timer_mode) {
|
||||
|
@ -1296,7 +1274,7 @@ static void apic_timer_expired(struct kvm_lapic *apic)
|
|||
static bool lapic_timer_int_injected(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_lapic *apic = vcpu->arch.apic;
|
||||
u32 reg = kvm_apic_get_reg(apic, APIC_LVTT);
|
||||
u32 reg = kvm_lapic_get_reg(apic, APIC_LVTT);
|
||||
|
||||
if (kvm_apic_hw_enabled(apic)) {
|
||||
int vec = reg & APIC_VECTOR_MASK;
|
||||
|
@ -1344,7 +1322,7 @@ static void start_apic_timer(struct kvm_lapic *apic)
|
|||
if (apic_lvtt_period(apic) || apic_lvtt_oneshot(apic)) {
|
||||
/* lapic timer in oneshot or periodic mode */
|
||||
now = apic->lapic_timer.timer.base->get_time();
|
||||
apic->lapic_timer.period = (u64)kvm_apic_get_reg(apic, APIC_TMICT)
|
||||
apic->lapic_timer.period = (u64)kvm_lapic_get_reg(apic, APIC_TMICT)
|
||||
* APIC_BUS_CYCLE_NS * apic->divide_count;
|
||||
|
||||
if (!apic->lapic_timer.period)
|
||||
|
@ -1376,7 +1354,7 @@ static void start_apic_timer(struct kvm_lapic *apic)
|
|||
"timer initial count 0x%x, period %lldns, "
|
||||
"expire @ 0x%016" PRIx64 ".\n", __func__,
|
||||
APIC_BUS_CYCLE_NS, ktime_to_ns(now),
|
||||
kvm_apic_get_reg(apic, APIC_TMICT),
|
||||
kvm_lapic_get_reg(apic, APIC_TMICT),
|
||||
apic->lapic_timer.period,
|
||||
ktime_to_ns(ktime_add_ns(now,
|
||||
apic->lapic_timer.period)));
|
||||
|
@ -1425,7 +1403,7 @@ static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val)
|
|||
}
|
||||
}
|
||||
|
||||
static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
||||
int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -1457,7 +1435,7 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
|
||||
case APIC_DFR:
|
||||
if (!apic_x2apic_mode(apic)) {
|
||||
apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
|
||||
kvm_lapic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
|
||||
recalculate_apic_map(apic->vcpu->kvm);
|
||||
} else
|
||||
ret = 1;
|
||||
|
@ -1465,17 +1443,17 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
|
||||
case APIC_SPIV: {
|
||||
u32 mask = 0x3ff;
|
||||
if (kvm_apic_get_reg(apic, APIC_LVR) & APIC_LVR_DIRECTED_EOI)
|
||||
if (kvm_lapic_get_reg(apic, APIC_LVR) & APIC_LVR_DIRECTED_EOI)
|
||||
mask |= APIC_SPIV_DIRECTED_EOI;
|
||||
apic_set_spiv(apic, val & mask);
|
||||
if (!(val & APIC_SPIV_APIC_ENABLED)) {
|
||||
int i;
|
||||
u32 lvt_val;
|
||||
|
||||
for (i = 0; i < APIC_LVT_NUM; i++) {
|
||||
lvt_val = kvm_apic_get_reg(apic,
|
||||
for (i = 0; i < KVM_APIC_LVT_NUM; i++) {
|
||||
lvt_val = kvm_lapic_get_reg(apic,
|
||||
APIC_LVTT + 0x10 * i);
|
||||
apic_set_reg(apic, APIC_LVTT + 0x10 * i,
|
||||
kvm_lapic_set_reg(apic, APIC_LVTT + 0x10 * i,
|
||||
lvt_val | APIC_LVT_MASKED);
|
||||
}
|
||||
apic_update_lvtt(apic);
|
||||
|
@ -1486,14 +1464,14 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
}
|
||||
case APIC_ICR:
|
||||
/* No delay here, so we always clear the pending bit */
|
||||
apic_set_reg(apic, APIC_ICR, val & ~(1 << 12));
|
||||
kvm_lapic_set_reg(apic, APIC_ICR, val & ~(1 << 12));
|
||||
apic_send_ipi(apic);
|
||||
break;
|
||||
|
||||
case APIC_ICR2:
|
||||
if (!apic_x2apic_mode(apic))
|
||||
val &= 0xff000000;
|
||||
apic_set_reg(apic, APIC_ICR2, val);
|
||||
kvm_lapic_set_reg(apic, APIC_ICR2, val);
|
||||
break;
|
||||
|
||||
case APIC_LVT0:
|
||||
|
@ -1507,7 +1485,7 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
val |= APIC_LVT_MASKED;
|
||||
|
||||
val &= apic_lvt_mask[(reg - APIC_LVTT) >> 4];
|
||||
apic_set_reg(apic, reg, val);
|
||||
kvm_lapic_set_reg(apic, reg, val);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -1515,7 +1493,7 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
if (!kvm_apic_sw_enabled(apic))
|
||||
val |= APIC_LVT_MASKED;
|
||||
val &= (apic_lvt_mask[0] | apic->lapic_timer.timer_mode_mask);
|
||||
apic_set_reg(apic, APIC_LVTT, val);
|
||||
kvm_lapic_set_reg(apic, APIC_LVTT, val);
|
||||
apic_update_lvtt(apic);
|
||||
break;
|
||||
|
||||
|
@ -1524,14 +1502,14 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
break;
|
||||
|
||||
hrtimer_cancel(&apic->lapic_timer.timer);
|
||||
apic_set_reg(apic, APIC_TMICT, val);
|
||||
kvm_lapic_set_reg(apic, APIC_TMICT, val);
|
||||
start_apic_timer(apic);
|
||||
break;
|
||||
|
||||
case APIC_TDCR:
|
||||
if (val & 4)
|
||||
apic_debug("KVM_WRITE:TDCR %x\n", val);
|
||||
apic_set_reg(apic, APIC_TDCR, val);
|
||||
kvm_lapic_set_reg(apic, APIC_TDCR, val);
|
||||
update_divide_count(apic);
|
||||
break;
|
||||
|
||||
|
@ -1544,7 +1522,7 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
|
||||
case APIC_SELF_IPI:
|
||||
if (apic_x2apic_mode(apic)) {
|
||||
apic_reg_write(apic, APIC_ICR, 0x40000 | (val & 0xff));
|
||||
kvm_lapic_reg_write(apic, APIC_ICR, 0x40000 | (val & 0xff));
|
||||
} else
|
||||
ret = 1;
|
||||
break;
|
||||
|
@ -1556,6 +1534,7 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
|
|||
apic_debug("Local APIC Write to read-only register %x\n", reg);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_lapic_reg_write);
|
||||
|
||||
static int apic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this,
|
||||
gpa_t address, int len, const void *data)
|
||||
|
@ -1585,14 +1564,14 @@ static int apic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this,
|
|||
apic_debug("%s: offset 0x%x with length 0x%x, and value is "
|
||||
"0x%x\n", __func__, offset, len, val);
|
||||
|
||||
apic_reg_write(apic, offset & 0xff0, val);
|
||||
kvm_lapic_reg_write(apic, offset & 0xff0, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
apic_reg_write(vcpu->arch.apic, APIC_EOI, 0);
|
||||
kvm_lapic_reg_write(vcpu->arch.apic, APIC_EOI, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi);
|
||||
|
||||
|
@ -1604,10 +1583,10 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset)
|
|||
/* hw has done the conditional check and inst decode */
|
||||
offset &= 0xff0;
|
||||
|
||||
apic_reg_read(vcpu->arch.apic, offset, 4, &val);
|
||||
kvm_lapic_reg_read(vcpu->arch.apic, offset, 4, &val);
|
||||
|
||||
/* TODO: optimize to just emulate side effect w/o one more write */
|
||||
apic_reg_write(vcpu->arch.apic, offset, val);
|
||||
kvm_lapic_reg_write(vcpu->arch.apic, offset, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_apic_write_nodecode);
|
||||
|
||||
|
@ -1667,14 +1646,14 @@ void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8)
|
|||
struct kvm_lapic *apic = vcpu->arch.apic;
|
||||
|
||||
apic_set_tpr(apic, ((cr8 & 0x0f) << 4)
|
||||
| (kvm_apic_get_reg(apic, APIC_TASKPRI) & 4));
|
||||
| (kvm_lapic_get_reg(apic, APIC_TASKPRI) & 4));
|
||||
}
|
||||
|
||||
u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 tpr;
|
||||
|
||||
tpr = (u64) kvm_apic_get_reg(vcpu->arch.apic, APIC_TASKPRI);
|
||||
tpr = (u64) kvm_lapic_get_reg(vcpu->arch.apic, APIC_TASKPRI);
|
||||
|
||||
return (tpr & 0xf0) >> 4;
|
||||
}
|
||||
|
@ -1740,28 +1719,28 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
|
|||
kvm_apic_set_id(apic, vcpu->vcpu_id);
|
||||
kvm_apic_set_version(apic->vcpu);
|
||||
|
||||
for (i = 0; i < APIC_LVT_NUM; i++)
|
||||
apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
|
||||
for (i = 0; i < KVM_APIC_LVT_NUM; i++)
|
||||
kvm_lapic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
|
||||
apic_update_lvtt(apic);
|
||||
if (kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_LINT0_REENABLED))
|
||||
apic_set_reg(apic, APIC_LVT0,
|
||||
kvm_lapic_set_reg(apic, APIC_LVT0,
|
||||
SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT));
|
||||
apic_manage_nmi_watchdog(apic, kvm_apic_get_reg(apic, APIC_LVT0));
|
||||
apic_manage_nmi_watchdog(apic, kvm_lapic_get_reg(apic, APIC_LVT0));
|
||||
|
||||
apic_set_reg(apic, APIC_DFR, 0xffffffffU);
|
||||
kvm_lapic_set_reg(apic, APIC_DFR, 0xffffffffU);
|
||||
apic_set_spiv(apic, 0xff);
|
||||
apic_set_reg(apic, APIC_TASKPRI, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_TASKPRI, 0);
|
||||
if (!apic_x2apic_mode(apic))
|
||||
kvm_apic_set_ldr(apic, 0);
|
||||
apic_set_reg(apic, APIC_ESR, 0);
|
||||
apic_set_reg(apic, APIC_ICR, 0);
|
||||
apic_set_reg(apic, APIC_ICR2, 0);
|
||||
apic_set_reg(apic, APIC_TDCR, 0);
|
||||
apic_set_reg(apic, APIC_TMICT, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_ESR, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_ICR, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_ICR2, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_TDCR, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_TMICT, 0);
|
||||
for (i = 0; i < 8; i++) {
|
||||
apic_set_reg(apic, APIC_IRR + 0x10 * i, 0);
|
||||
apic_set_reg(apic, APIC_ISR + 0x10 * i, 0);
|
||||
apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_IRR + 0x10 * i, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_ISR + 0x10 * i, 0);
|
||||
kvm_lapic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
|
||||
}
|
||||
apic->irr_pending = vcpu->arch.apicv_active;
|
||||
apic->isr_count = vcpu->arch.apicv_active ? 1 : 0;
|
||||
|
@ -1806,7 +1785,7 @@ int apic_has_pending_timer(struct kvm_vcpu *vcpu)
|
|||
|
||||
int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type)
|
||||
{
|
||||
u32 reg = kvm_apic_get_reg(apic, lvt_type);
|
||||
u32 reg = kvm_lapic_get_reg(apic, lvt_type);
|
||||
int vector, mode, trig_mode;
|
||||
|
||||
if (kvm_apic_hw_enabled(apic) && !(reg & APIC_LVT_MASKED)) {
|
||||
|
@ -1901,14 +1880,14 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)
|
|||
apic_update_ppr(apic);
|
||||
highest_irr = apic_find_highest_irr(apic);
|
||||
if ((highest_irr == -1) ||
|
||||
((highest_irr & 0xF0) <= kvm_apic_get_reg(apic, APIC_PROCPRI)))
|
||||
((highest_irr & 0xF0) <= kvm_lapic_get_reg(apic, APIC_PROCPRI)))
|
||||
return -1;
|
||||
return highest_irr;
|
||||
}
|
||||
|
||||
int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 lvt0 = kvm_apic_get_reg(vcpu->arch.apic, APIC_LVT0);
|
||||
u32 lvt0 = kvm_lapic_get_reg(vcpu->arch.apic, APIC_LVT0);
|
||||
int r = 0;
|
||||
|
||||
if (!kvm_apic_hw_enabled(vcpu->arch.apic))
|
||||
|
@ -1974,7 +1953,7 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
|
|||
apic_update_ppr(apic);
|
||||
hrtimer_cancel(&apic->lapic_timer.timer);
|
||||
apic_update_lvtt(apic);
|
||||
apic_manage_nmi_watchdog(apic, kvm_apic_get_reg(apic, APIC_LVT0));
|
||||
apic_manage_nmi_watchdog(apic, kvm_lapic_get_reg(apic, APIC_LVT0));
|
||||
update_divide_count(apic);
|
||||
start_apic_timer(apic);
|
||||
apic->irr_pending = true;
|
||||
|
@ -1982,9 +1961,11 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
|
|||
1 : count_vectors(apic->regs + APIC_ISR);
|
||||
apic->highest_isr_cache = -1;
|
||||
if (vcpu->arch.apicv_active) {
|
||||
if (kvm_x86_ops->apicv_post_state_restore)
|
||||
kvm_x86_ops->apicv_post_state_restore(vcpu);
|
||||
kvm_x86_ops->hwapic_irr_update(vcpu,
|
||||
apic_find_highest_irr(apic));
|
||||
kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
|
||||
kvm_x86_ops->hwapic_isr_update(vcpu,
|
||||
apic_find_highest_isr(apic));
|
||||
}
|
||||
kvm_make_request(KVM_REQ_EVENT, vcpu);
|
||||
|
@ -2097,7 +2078,7 @@ void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)
|
|||
if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention))
|
||||
return;
|
||||
|
||||
tpr = kvm_apic_get_reg(apic, APIC_TASKPRI) & 0xff;
|
||||
tpr = kvm_lapic_get_reg(apic, APIC_TASKPRI) & 0xff;
|
||||
max_irr = apic_find_highest_irr(apic);
|
||||
if (max_irr < 0)
|
||||
max_irr = 0;
|
||||
|
@ -2139,8 +2120,8 @@ int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data)
|
|||
|
||||
/* if this is ICR write vector before command */
|
||||
if (reg == APIC_ICR)
|
||||
apic_reg_write(apic, APIC_ICR2, (u32)(data >> 32));
|
||||
return apic_reg_write(apic, reg, (u32)data);
|
||||
kvm_lapic_reg_write(apic, APIC_ICR2, (u32)(data >> 32));
|
||||
return kvm_lapic_reg_write(apic, reg, (u32)data);
|
||||
}
|
||||
|
||||
int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
|
||||
|
@ -2157,10 +2138,10 @@ int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (apic_reg_read(apic, reg, 4, &low))
|
||||
if (kvm_lapic_reg_read(apic, reg, 4, &low))
|
||||
return 1;
|
||||
if (reg == APIC_ICR)
|
||||
apic_reg_read(apic, APIC_ICR2, 4, &high);
|
||||
kvm_lapic_reg_read(apic, APIC_ICR2, 4, &high);
|
||||
|
||||
*data = (((u64)high) << 32) | low;
|
||||
|
||||
|
@ -2176,8 +2157,8 @@ int kvm_hv_vapic_msr_write(struct kvm_vcpu *vcpu, u32 reg, u64 data)
|
|||
|
||||
/* if this is ICR write vector before command */
|
||||
if (reg == APIC_ICR)
|
||||
apic_reg_write(apic, APIC_ICR2, (u32)(data >> 32));
|
||||
return apic_reg_write(apic, reg, (u32)data);
|
||||
kvm_lapic_reg_write(apic, APIC_ICR2, (u32)(data >> 32));
|
||||
return kvm_lapic_reg_write(apic, reg, (u32)data);
|
||||
}
|
||||
|
||||
int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 reg, u64 *data)
|
||||
|
@ -2188,10 +2169,10 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 reg, u64 *data)
|
|||
if (!lapic_in_kernel(vcpu))
|
||||
return 1;
|
||||
|
||||
if (apic_reg_read(apic, reg, 4, &low))
|
||||
if (kvm_lapic_reg_read(apic, reg, 4, &low))
|
||||
return 1;
|
||||
if (reg == APIC_ICR)
|
||||
apic_reg_read(apic, APIC_ICR2, 4, &high);
|
||||
kvm_lapic_reg_read(apic, APIC_ICR2, 4, &high);
|
||||
|
||||
*data = (((u64)high) << 32) | low;
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
#define KVM_APIC_INIT 0
|
||||
#define KVM_APIC_SIPI 1
|
||||
#define KVM_APIC_LVT_NUM 6
|
||||
|
||||
#define KVM_APIC_SHORT_MASK 0xc0000
|
||||
#define KVM_APIC_DEST_MASK 0x800
|
||||
|
||||
struct kvm_timer {
|
||||
struct hrtimer timer;
|
||||
|
@ -59,6 +63,11 @@ void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu);
|
|||
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
|
||||
u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
|
||||
void kvm_apic_set_version(struct kvm_vcpu *vcpu);
|
||||
int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val);
|
||||
int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
|
||||
void *data);
|
||||
bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
|
||||
int short_hand, unsigned int dest, int dest_mode);
|
||||
|
||||
void __kvm_apic_update_irr(u32 *pir, void *regs);
|
||||
void kvm_apic_update_irr(struct kvm_vcpu *vcpu, u32 *pir);
|
||||
|
@ -99,9 +108,32 @@ static inline bool kvm_hv_vapic_assist_page_enabled(struct kvm_vcpu *vcpu)
|
|||
int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data);
|
||||
void kvm_lapic_init(void);
|
||||
|
||||
static inline u32 kvm_apic_get_reg(struct kvm_lapic *apic, int reg_off)
|
||||
#define VEC_POS(v) ((v) & (32 - 1))
|
||||
#define REG_POS(v) (((v) >> 5) << 4)
|
||||
|
||||
static inline void kvm_lapic_set_vector(int vec, void *bitmap)
|
||||
{
|
||||
return *((u32 *) (apic->regs + reg_off));
|
||||
set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
|
||||
}
|
||||
|
||||
static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic)
|
||||
{
|
||||
kvm_lapic_set_vector(vec, apic->regs + APIC_IRR);
|
||||
/*
|
||||
* irr_pending must be true if any interrupt is pending; set it after
|
||||
* APIC_IRR to avoid race with apic_clear_irr
|
||||
*/
|
||||
apic->irr_pending = true;
|
||||
}
|
||||
|
||||
static inline u32 kvm_lapic_get_reg(struct kvm_lapic *apic, int reg_off)
|
||||
{
|
||||
return *((u32 *) (apic->regs + reg_off));
|
||||
}
|
||||
|
||||
static inline void kvm_lapic_set_reg(struct kvm_lapic *apic, int reg_off, u32 val)
|
||||
{
|
||||
*((u32 *) (apic->regs + reg_off)) = val;
|
||||
}
|
||||
|
||||
extern struct static_key kvm_no_apic_vcpu;
|
||||
|
@ -169,7 +201,7 @@ static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
|
|||
|
||||
static inline int kvm_apic_id(struct kvm_lapic *apic)
|
||||
{
|
||||
return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
|
||||
return (kvm_lapic_get_reg(apic, APIC_ID) >> 24) & 0xff;
|
||||
}
|
||||
|
||||
bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
|
||||
|
|
|
@ -1909,18 +1909,17 @@ static void kvm_mmu_commit_zap_page(struct kvm *kvm,
|
|||
* since it has been deleted from active_mmu_pages but still can be found
|
||||
* at hast list.
|
||||
*
|
||||
* for_each_gfn_indirect_valid_sp has skipped that kind of page and
|
||||
* kvm_mmu_get_page(), the only user of for_each_gfn_sp(), has skipped
|
||||
* all the obsolete pages.
|
||||
* for_each_gfn_valid_sp() has skipped that kind of pages.
|
||||
*/
|
||||
#define for_each_gfn_sp(_kvm, _sp, _gfn) \
|
||||
#define for_each_gfn_valid_sp(_kvm, _sp, _gfn) \
|
||||
hlist_for_each_entry(_sp, \
|
||||
&(_kvm)->arch.mmu_page_hash[kvm_page_table_hashfn(_gfn)], hash_link) \
|
||||
if ((_sp)->gfn != (_gfn)) {} else
|
||||
if ((_sp)->gfn != (_gfn) || is_obsolete_sp((_kvm), (_sp)) \
|
||||
|| (_sp)->role.invalid) {} else
|
||||
|
||||
#define for_each_gfn_indirect_valid_sp(_kvm, _sp, _gfn) \
|
||||
for_each_gfn_sp(_kvm, _sp, _gfn) \
|
||||
if ((_sp)->role.direct || (_sp)->role.invalid) {} else
|
||||
for_each_gfn_valid_sp(_kvm, _sp, _gfn) \
|
||||
if ((_sp)->role.direct) {} else
|
||||
|
||||
/* @sp->gfn should be write-protected at the call site */
|
||||
static bool __kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
|
||||
|
@ -1961,6 +1960,11 @@ static void kvm_mmu_audit(struct kvm_vcpu *vcpu, int point) { }
|
|||
static void mmu_audit_disable(void) { }
|
||||
#endif
|
||||
|
||||
static bool is_obsolete_sp(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||
{
|
||||
return unlikely(sp->mmu_valid_gen != kvm->arch.mmu_valid_gen);
|
||||
}
|
||||
|
||||
static bool kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
|
||||
struct list_head *invalid_list)
|
||||
{
|
||||
|
@ -2105,11 +2109,6 @@ static void clear_sp_write_flooding_count(u64 *spte)
|
|||
__clear_sp_write_flooding_count(sp);
|
||||
}
|
||||
|
||||
static bool is_obsolete_sp(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||
{
|
||||
return unlikely(sp->mmu_valid_gen != kvm->arch.mmu_valid_gen);
|
||||
}
|
||||
|
||||
static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
||||
gfn_t gfn,
|
||||
gva_t gaddr,
|
||||
|
@ -2136,10 +2135,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
|
|||
quadrant &= (1 << ((PT32_PT_BITS - PT64_PT_BITS) * level)) - 1;
|
||||
role.quadrant = quadrant;
|
||||
}
|
||||
for_each_gfn_sp(vcpu->kvm, sp, gfn) {
|
||||
if (is_obsolete_sp(vcpu->kvm, sp))
|
||||
continue;
|
||||
|
||||
for_each_gfn_valid_sp(vcpu->kvm, sp, gfn) {
|
||||
if (!need_sync && sp->unsync)
|
||||
need_sync = true;
|
||||
|
||||
|
|
|
@ -44,8 +44,6 @@ static bool msr_mtrr_valid(unsigned msr)
|
|||
case MSR_MTRRdefType:
|
||||
case MSR_IA32_CR_PAT:
|
||||
return true;
|
||||
case 0x2f8:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SVM: " fmt
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include "irq.h"
|
||||
|
@ -32,6 +35,7 @@
|
|||
#include <linux/trace_events.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/apic.h>
|
||||
#include <asm/perf_event.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/desc.h>
|
||||
|
@ -68,6 +72,8 @@ MODULE_DEVICE_TABLE(x86cpu, svm_cpu_id);
|
|||
#define SVM_FEATURE_DECODE_ASSIST (1 << 7)
|
||||
#define SVM_FEATURE_PAUSE_FILTER (1 << 10)
|
||||
|
||||
#define SVM_AVIC_DOORBELL 0xc001011b
|
||||
|
||||
#define NESTED_EXIT_HOST 0 /* Exit handled on host level */
|
||||
#define NESTED_EXIT_DONE 1 /* Exit caused nested vmexit */
|
||||
#define NESTED_EXIT_CONTINUE 2 /* Further checks needed */
|
||||
|
@ -78,6 +84,18 @@ MODULE_DEVICE_TABLE(x86cpu, svm_cpu_id);
|
|||
#define TSC_RATIO_MIN 0x0000000000000001ULL
|
||||
#define TSC_RATIO_MAX 0x000000ffffffffffULL
|
||||
|
||||
#define AVIC_HPA_MASK ~((0xFFFULL << 52) || 0xFFF)
|
||||
|
||||
/*
|
||||
* 0xff is broadcast, so the max index allowed for physical APIC ID
|
||||
* table is 0xfe. APIC IDs above 0xff are reserved.
|
||||
*/
|
||||
#define AVIC_MAX_PHYSICAL_ID_COUNT 255
|
||||
|
||||
#define AVIC_UNACCEL_ACCESS_WRITE_MASK 1
|
||||
#define AVIC_UNACCEL_ACCESS_OFFSET_MASK 0xFF0
|
||||
#define AVIC_UNACCEL_ACCESS_VECTOR_MASK 0xFFFFFFFF
|
||||
|
||||
static bool erratum_383_found __read_mostly;
|
||||
|
||||
static const u32 host_save_user_msrs[] = {
|
||||
|
@ -162,8 +180,21 @@ struct vcpu_svm {
|
|||
|
||||
/* cached guest cpuid flags for faster access */
|
||||
bool nrips_enabled : 1;
|
||||
|
||||
u32 ldr_reg;
|
||||
struct page *avic_backing_page;
|
||||
u64 *avic_physical_id_cache;
|
||||
bool avic_is_running;
|
||||
};
|
||||
|
||||
#define AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK (0xFF)
|
||||
#define AVIC_LOGICAL_ID_ENTRY_VALID_MASK (1 << 31)
|
||||
|
||||
#define AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK (0xFFULL)
|
||||
#define AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK (0xFFFFFFFFFFULL << 12)
|
||||
#define AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK (1ULL << 62)
|
||||
#define AVIC_PHYSICAL_ID_ENTRY_VALID_MASK (1ULL << 63)
|
||||
|
||||
static DEFINE_PER_CPU(u64, current_tsc_ratio);
|
||||
#define TSC_RATIO_DEFAULT 0x0100000000ULL
|
||||
|
||||
|
@ -205,6 +236,10 @@ module_param(npt, int, S_IRUGO);
|
|||
static int nested = true;
|
||||
module_param(nested, int, S_IRUGO);
|
||||
|
||||
/* enable / disable AVIC */
|
||||
static int avic;
|
||||
module_param(avic, int, S_IRUGO);
|
||||
|
||||
static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);
|
||||
static void svm_flush_tlb(struct kvm_vcpu *vcpu);
|
||||
static void svm_complete_interrupts(struct vcpu_svm *svm);
|
||||
|
@ -228,12 +263,18 @@ enum {
|
|||
VMCB_SEG, /* CS, DS, SS, ES, CPL */
|
||||
VMCB_CR2, /* CR2 only */
|
||||
VMCB_LBR, /* DBGCTL, BR_FROM, BR_TO, LAST_EX_FROM, LAST_EX_TO */
|
||||
VMCB_AVIC, /* AVIC APIC_BAR, AVIC APIC_BACKING_PAGE,
|
||||
* AVIC PHYSICAL_TABLE pointer,
|
||||
* AVIC LOGICAL_TABLE pointer
|
||||
*/
|
||||
VMCB_DIRTY_MAX,
|
||||
};
|
||||
|
||||
/* TPR and CR2 are always written before VMRUN */
|
||||
#define VMCB_ALWAYS_DIRTY_MASK ((1U << VMCB_INTR) | (1U << VMCB_CR2))
|
||||
|
||||
#define VMCB_AVIC_APIC_BAR_MASK 0xFFFFFFFFFF000ULL
|
||||
|
||||
static inline void mark_all_dirty(struct vmcb *vmcb)
|
||||
{
|
||||
vmcb->control.clean = 0;
|
||||
|
@ -255,6 +296,23 @@ static inline struct vcpu_svm *to_svm(struct kvm_vcpu *vcpu)
|
|||
return container_of(vcpu, struct vcpu_svm, vcpu);
|
||||
}
|
||||
|
||||
static inline void avic_update_vapic_bar(struct vcpu_svm *svm, u64 data)
|
||||
{
|
||||
svm->vmcb->control.avic_vapic_bar = data & VMCB_AVIC_APIC_BAR_MASK;
|
||||
mark_dirty(svm->vmcb, VMCB_AVIC);
|
||||
}
|
||||
|
||||
static inline bool avic_vcpu_is_running(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
u64 *entry = svm->avic_physical_id_cache;
|
||||
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
return (READ_ONCE(*entry) & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK);
|
||||
}
|
||||
|
||||
static void recalc_intercepts(struct vcpu_svm *svm)
|
||||
{
|
||||
struct vmcb_control_area *c, *h;
|
||||
|
@ -923,6 +981,12 @@ static __init int svm_hardware_setup(void)
|
|||
} else
|
||||
kvm_disable_tdp();
|
||||
|
||||
if (avic && (!npt_enabled || !boot_cpu_has(X86_FEATURE_AVIC)))
|
||||
avic = false;
|
||||
|
||||
if (avic)
|
||||
pr_info("AVIC enabled\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -1000,6 +1064,22 @@ static void svm_adjust_tsc_offset_guest(struct kvm_vcpu *vcpu, s64 adjustment)
|
|||
mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
|
||||
}
|
||||
|
||||
static void avic_init_vmcb(struct vcpu_svm *svm)
|
||||
{
|
||||
struct vmcb *vmcb = svm->vmcb;
|
||||
struct kvm_arch *vm_data = &svm->vcpu.kvm->arch;
|
||||
phys_addr_t bpa = page_to_phys(svm->avic_backing_page);
|
||||
phys_addr_t lpa = page_to_phys(vm_data->avic_logical_id_table_page);
|
||||
phys_addr_t ppa = page_to_phys(vm_data->avic_physical_id_table_page);
|
||||
|
||||
vmcb->control.avic_backing_page = bpa & AVIC_HPA_MASK;
|
||||
vmcb->control.avic_logical_id = lpa & AVIC_HPA_MASK;
|
||||
vmcb->control.avic_physical_id = ppa & AVIC_HPA_MASK;
|
||||
vmcb->control.avic_physical_id |= AVIC_MAX_PHYSICAL_ID_COUNT;
|
||||
vmcb->control.int_ctl |= AVIC_ENABLE_MASK;
|
||||
svm->vcpu.arch.apicv_active = true;
|
||||
}
|
||||
|
||||
static void init_vmcb(struct vcpu_svm *svm)
|
||||
{
|
||||
struct vmcb_control_area *control = &svm->vmcb->control;
|
||||
|
@ -1014,7 +1094,8 @@ static void init_vmcb(struct vcpu_svm *svm)
|
|||
set_cr_intercept(svm, INTERCEPT_CR0_WRITE);
|
||||
set_cr_intercept(svm, INTERCEPT_CR3_WRITE);
|
||||
set_cr_intercept(svm, INTERCEPT_CR4_WRITE);
|
||||
set_cr_intercept(svm, INTERCEPT_CR8_WRITE);
|
||||
if (!kvm_vcpu_apicv_active(&svm->vcpu))
|
||||
set_cr_intercept(svm, INTERCEPT_CR8_WRITE);
|
||||
|
||||
set_dr_intercepts(svm);
|
||||
|
||||
|
@ -1110,9 +1191,197 @@ static void init_vmcb(struct vcpu_svm *svm)
|
|||
set_intercept(svm, INTERCEPT_PAUSE);
|
||||
}
|
||||
|
||||
if (avic)
|
||||
avic_init_vmcb(svm);
|
||||
|
||||
mark_all_dirty(svm->vmcb);
|
||||
|
||||
enable_gif(svm);
|
||||
|
||||
}
|
||||
|
||||
static u64 *avic_get_physical_id_entry(struct kvm_vcpu *vcpu, int index)
|
||||
{
|
||||
u64 *avic_physical_id_table;
|
||||
struct kvm_arch *vm_data = &vcpu->kvm->arch;
|
||||
|
||||
if (index >= AVIC_MAX_PHYSICAL_ID_COUNT)
|
||||
return NULL;
|
||||
|
||||
avic_physical_id_table = page_address(vm_data->avic_physical_id_table_page);
|
||||
|
||||
return &avic_physical_id_table[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* AVIC hardware walks the nested page table to check permissions,
|
||||
* but does not use the SPA address specified in the leaf page
|
||||
* table entry since it uses address in the AVIC_BACKING_PAGE pointer
|
||||
* field of the VMCB. Therefore, we set up the
|
||||
* APIC_ACCESS_PAGE_PRIVATE_MEMSLOT (4KB) here.
|
||||
*/
|
||||
static int avic_init_access_page(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
int ret;
|
||||
|
||||
if (kvm->arch.apic_access_page_done)
|
||||
return 0;
|
||||
|
||||
ret = x86_set_memory_region(kvm,
|
||||
APIC_ACCESS_PAGE_PRIVATE_MEMSLOT,
|
||||
APIC_DEFAULT_PHYS_BASE,
|
||||
PAGE_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kvm->arch.apic_access_page_done = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avic_init_backing_page(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
u64 *entry, new_entry;
|
||||
int id = vcpu->vcpu_id;
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
ret = avic_init_access_page(vcpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (id >= AVIC_MAX_PHYSICAL_ID_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!svm->vcpu.arch.apic->regs)
|
||||
return -EINVAL;
|
||||
|
||||
svm->avic_backing_page = virt_to_page(svm->vcpu.arch.apic->regs);
|
||||
|
||||
/* Setting AVIC backing page address in the phy APIC ID table */
|
||||
entry = avic_get_physical_id_entry(vcpu, id);
|
||||
if (!entry)
|
||||
return -EINVAL;
|
||||
|
||||
new_entry = READ_ONCE(*entry);
|
||||
new_entry = (page_to_phys(svm->avic_backing_page) &
|
||||
AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK) |
|
||||
AVIC_PHYSICAL_ID_ENTRY_VALID_MASK;
|
||||
WRITE_ONCE(*entry, new_entry);
|
||||
|
||||
svm->avic_physical_id_cache = entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void avic_vm_destroy(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_arch *vm_data = &kvm->arch;
|
||||
|
||||
if (vm_data->avic_logical_id_table_page)
|
||||
__free_page(vm_data->avic_logical_id_table_page);
|
||||
if (vm_data->avic_physical_id_table_page)
|
||||
__free_page(vm_data->avic_physical_id_table_page);
|
||||
}
|
||||
|
||||
static int avic_vm_init(struct kvm *kvm)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
struct kvm_arch *vm_data = &kvm->arch;
|
||||
struct page *p_page;
|
||||
struct page *l_page;
|
||||
|
||||
if (!avic)
|
||||
return 0;
|
||||
|
||||
/* Allocating physical APIC ID table (4KB) */
|
||||
p_page = alloc_page(GFP_KERNEL);
|
||||
if (!p_page)
|
||||
goto free_avic;
|
||||
|
||||
vm_data->avic_physical_id_table_page = p_page;
|
||||
clear_page(page_address(p_page));
|
||||
|
||||
/* Allocating logical APIC ID table (4KB) */
|
||||
l_page = alloc_page(GFP_KERNEL);
|
||||
if (!l_page)
|
||||
goto free_avic;
|
||||
|
||||
vm_data->avic_logical_id_table_page = l_page;
|
||||
clear_page(page_address(l_page));
|
||||
|
||||
return 0;
|
||||
|
||||
free_avic:
|
||||
avic_vm_destroy(kvm);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called during VCPU halt/unhalt.
|
||||
*/
|
||||
static void avic_set_running(struct kvm_vcpu *vcpu, bool is_run)
|
||||
{
|
||||
u64 entry;
|
||||
int h_physical_id = __default_cpu_present_to_apicid(vcpu->cpu);
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (!kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
svm->avic_is_running = is_run;
|
||||
|
||||
/* ID = 0xff (broadcast), ID > 0xff (reserved) */
|
||||
if (WARN_ON(h_physical_id >= AVIC_MAX_PHYSICAL_ID_COUNT))
|
||||
return;
|
||||
|
||||
entry = READ_ONCE(*(svm->avic_physical_id_cache));
|
||||
WARN_ON(is_run == !!(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK));
|
||||
|
||||
entry &= ~AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK;
|
||||
if (is_run)
|
||||
entry |= AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK;
|
||||
WRITE_ONCE(*(svm->avic_physical_id_cache), entry);
|
||||
}
|
||||
|
||||
static void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
{
|
||||
u64 entry;
|
||||
/* ID = 0xff (broadcast), ID > 0xff (reserved) */
|
||||
int h_physical_id = __default_cpu_present_to_apicid(cpu);
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (!kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
if (WARN_ON(h_physical_id >= AVIC_MAX_PHYSICAL_ID_COUNT))
|
||||
return;
|
||||
|
||||
entry = READ_ONCE(*(svm->avic_physical_id_cache));
|
||||
WARN_ON(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK);
|
||||
|
||||
entry &= ~AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK;
|
||||
entry |= (h_physical_id & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK);
|
||||
|
||||
entry &= ~AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK;
|
||||
if (svm->avic_is_running)
|
||||
entry |= AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK;
|
||||
|
||||
WRITE_ONCE(*(svm->avic_physical_id_cache), entry);
|
||||
}
|
||||
|
||||
static void avic_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 entry;
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (!kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
entry = READ_ONCE(*(svm->avic_physical_id_cache));
|
||||
entry &= ~AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK;
|
||||
WRITE_ONCE(*(svm->avic_physical_id_cache), entry);
|
||||
}
|
||||
|
||||
static void svm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
|
||||
|
@ -1131,6 +1400,9 @@ static void svm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
|
|||
|
||||
kvm_cpuid(vcpu, &eax, &dummy, &dummy, &dummy);
|
||||
kvm_register_write(vcpu, VCPU_REGS_RDX, eax);
|
||||
|
||||
if (kvm_vcpu_apicv_active(vcpu) && !init_event)
|
||||
avic_update_vapic_bar(svm, APIC_DEFAULT_PHYS_BASE);
|
||||
}
|
||||
|
||||
static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
|
||||
|
@ -1169,6 +1441,17 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
|
|||
if (!hsave_page)
|
||||
goto free_page3;
|
||||
|
||||
if (avic) {
|
||||
err = avic_init_backing_page(&svm->vcpu);
|
||||
if (err)
|
||||
goto free_page4;
|
||||
}
|
||||
|
||||
/* We initialize this flag to true to make sure that the is_running
|
||||
* bit would be set the first time the vcpu is loaded.
|
||||
*/
|
||||
svm->avic_is_running = true;
|
||||
|
||||
svm->nested.hsave = page_address(hsave_page);
|
||||
|
||||
svm->msrpm = page_address(msrpm_pages);
|
||||
|
@ -1187,6 +1470,8 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
|
|||
|
||||
return &svm->vcpu;
|
||||
|
||||
free_page4:
|
||||
__free_page(hsave_page);
|
||||
free_page3:
|
||||
__free_pages(nested_msrpm_pages, MSRPM_ALLOC_ORDER);
|
||||
free_page2:
|
||||
|
@ -1243,6 +1528,8 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||
/* This assumes that the kernel never uses MSR_TSC_AUX */
|
||||
if (static_cpu_has(X86_FEATURE_RDTSCP))
|
||||
wrmsrl(MSR_TSC_AUX, svm->tsc_aux);
|
||||
|
||||
avic_vcpu_load(vcpu, cpu);
|
||||
}
|
||||
|
||||
static void svm_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
|
@ -1250,6 +1537,8 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu)
|
|||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
int i;
|
||||
|
||||
avic_vcpu_put(vcpu);
|
||||
|
||||
++vcpu->stat.host_state_reload;
|
||||
kvm_load_ldt(svm->host.ldt);
|
||||
#ifdef CONFIG_X86_64
|
||||
|
@ -1265,6 +1554,16 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu)
|
|||
wrmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);
|
||||
}
|
||||
|
||||
static void svm_vcpu_blocking(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
avic_set_running(vcpu, false);
|
||||
}
|
||||
|
||||
static void svm_vcpu_unblocking(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
avic_set_running(vcpu, true);
|
||||
}
|
||||
|
||||
static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return to_svm(vcpu)->vmcb->save.rflags;
|
||||
|
@ -2673,10 +2972,11 @@ static int clgi_interception(struct vcpu_svm *svm)
|
|||
disable_gif(svm);
|
||||
|
||||
/* After a CLGI no interrupts should come */
|
||||
svm_clear_vintr(svm);
|
||||
svm->vmcb->control.int_ctl &= ~V_IRQ_MASK;
|
||||
|
||||
mark_dirty(svm->vmcb, VMCB_INTR);
|
||||
if (!kvm_vcpu_apicv_active(&svm->vcpu)) {
|
||||
svm_clear_vintr(svm);
|
||||
svm->vmcb->control.int_ctl &= ~V_IRQ_MASK;
|
||||
mark_dirty(svm->vmcb, VMCB_INTR);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -3212,6 +3512,10 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
|
|||
case MSR_VM_IGNNE:
|
||||
vcpu_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data);
|
||||
break;
|
||||
case MSR_IA32_APICBASE:
|
||||
if (kvm_vcpu_apicv_active(vcpu))
|
||||
avic_update_vapic_bar(to_svm(vcpu), data);
|
||||
/* Follow through */
|
||||
default:
|
||||
return kvm_set_msr_common(vcpu, msr);
|
||||
}
|
||||
|
@ -3281,6 +3585,278 @@ static int mwait_interception(struct vcpu_svm *svm)
|
|||
return nop_interception(svm);
|
||||
}
|
||||
|
||||
enum avic_ipi_failure_cause {
|
||||
AVIC_IPI_FAILURE_INVALID_INT_TYPE,
|
||||
AVIC_IPI_FAILURE_TARGET_NOT_RUNNING,
|
||||
AVIC_IPI_FAILURE_INVALID_TARGET,
|
||||
AVIC_IPI_FAILURE_INVALID_BACKING_PAGE,
|
||||
};
|
||||
|
||||
static int avic_incomplete_ipi_interception(struct vcpu_svm *svm)
|
||||
{
|
||||
u32 icrh = svm->vmcb->control.exit_info_1 >> 32;
|
||||
u32 icrl = svm->vmcb->control.exit_info_1;
|
||||
u32 id = svm->vmcb->control.exit_info_2 >> 32;
|
||||
u32 index = svm->vmcb->control.exit_info_2 && 0xFF;
|
||||
struct kvm_lapic *apic = svm->vcpu.arch.apic;
|
||||
|
||||
trace_kvm_avic_incomplete_ipi(svm->vcpu.vcpu_id, icrh, icrl, id, index);
|
||||
|
||||
switch (id) {
|
||||
case AVIC_IPI_FAILURE_INVALID_INT_TYPE:
|
||||
/*
|
||||
* AVIC hardware handles the generation of
|
||||
* IPIs when the specified Message Type is Fixed
|
||||
* (also known as fixed delivery mode) and
|
||||
* the Trigger Mode is edge-triggered. The hardware
|
||||
* also supports self and broadcast delivery modes
|
||||
* specified via the Destination Shorthand(DSH)
|
||||
* field of the ICRL. Logical and physical APIC ID
|
||||
* formats are supported. All other IPI types cause
|
||||
* a #VMEXIT, which needs to emulated.
|
||||
*/
|
||||
kvm_lapic_reg_write(apic, APIC_ICR2, icrh);
|
||||
kvm_lapic_reg_write(apic, APIC_ICR, icrl);
|
||||
break;
|
||||
case AVIC_IPI_FAILURE_TARGET_NOT_RUNNING: {
|
||||
int i;
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm *kvm = svm->vcpu.kvm;
|
||||
struct kvm_lapic *apic = svm->vcpu.arch.apic;
|
||||
|
||||
/*
|
||||
* At this point, we expect that the AVIC HW has already
|
||||
* set the appropriate IRR bits on the valid target
|
||||
* vcpus. So, we just need to kick the appropriate vcpu.
|
||||
*/
|
||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||
bool m = kvm_apic_match_dest(vcpu, apic,
|
||||
icrl & KVM_APIC_SHORT_MASK,
|
||||
GET_APIC_DEST_FIELD(icrh),
|
||||
icrl & KVM_APIC_DEST_MASK);
|
||||
|
||||
if (m && !avic_vcpu_is_running(vcpu))
|
||||
kvm_vcpu_wake_up(vcpu);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AVIC_IPI_FAILURE_INVALID_TARGET:
|
||||
break;
|
||||
case AVIC_IPI_FAILURE_INVALID_BACKING_PAGE:
|
||||
WARN_ONCE(1, "Invalid backing page\n");
|
||||
break;
|
||||
default:
|
||||
pr_err("Unknown IPI interception\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 *avic_get_logical_id_entry(struct kvm_vcpu *vcpu, u32 ldr, bool flat)
|
||||
{
|
||||
struct kvm_arch *vm_data = &vcpu->kvm->arch;
|
||||
int index;
|
||||
u32 *logical_apic_id_table;
|
||||
int dlid = GET_APIC_LOGICAL_ID(ldr);
|
||||
|
||||
if (!dlid)
|
||||
return NULL;
|
||||
|
||||
if (flat) { /* flat */
|
||||
index = ffs(dlid) - 1;
|
||||
if (index > 7)
|
||||
return NULL;
|
||||
} else { /* cluster */
|
||||
int cluster = (dlid & 0xf0) >> 4;
|
||||
int apic = ffs(dlid & 0x0f) - 1;
|
||||
|
||||
if ((apic < 0) || (apic > 7) ||
|
||||
(cluster >= 0xf))
|
||||
return NULL;
|
||||
index = (cluster << 2) + apic;
|
||||
}
|
||||
|
||||
logical_apic_id_table = (u32 *) page_address(vm_data->avic_logical_id_table_page);
|
||||
|
||||
return &logical_apic_id_table[index];
|
||||
}
|
||||
|
||||
static int avic_ldr_write(struct kvm_vcpu *vcpu, u8 g_physical_id, u32 ldr,
|
||||
bool valid)
|
||||
{
|
||||
bool flat;
|
||||
u32 *entry, new_entry;
|
||||
|
||||
flat = kvm_lapic_get_reg(vcpu->arch.apic, APIC_DFR) == APIC_DFR_FLAT;
|
||||
entry = avic_get_logical_id_entry(vcpu, ldr, flat);
|
||||
if (!entry)
|
||||
return -EINVAL;
|
||||
|
||||
new_entry = READ_ONCE(*entry);
|
||||
new_entry &= ~AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK;
|
||||
new_entry |= (g_physical_id & AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK);
|
||||
if (valid)
|
||||
new_entry |= AVIC_LOGICAL_ID_ENTRY_VALID_MASK;
|
||||
else
|
||||
new_entry &= ~AVIC_LOGICAL_ID_ENTRY_VALID_MASK;
|
||||
WRITE_ONCE(*entry, new_entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avic_handle_ldr_update(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
u32 ldr = kvm_lapic_get_reg(vcpu->arch.apic, APIC_LDR);
|
||||
|
||||
if (!ldr)
|
||||
return 1;
|
||||
|
||||
ret = avic_ldr_write(vcpu, vcpu->vcpu_id, ldr, true);
|
||||
if (ret && svm->ldr_reg) {
|
||||
avic_ldr_write(vcpu, 0, svm->ldr_reg, false);
|
||||
svm->ldr_reg = 0;
|
||||
} else {
|
||||
svm->ldr_reg = ldr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int avic_handle_apic_id_update(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 *old, *new;
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
u32 apic_id_reg = kvm_lapic_get_reg(vcpu->arch.apic, APIC_ID);
|
||||
u32 id = (apic_id_reg >> 24) & 0xff;
|
||||
|
||||
if (vcpu->vcpu_id == id)
|
||||
return 0;
|
||||
|
||||
old = avic_get_physical_id_entry(vcpu, vcpu->vcpu_id);
|
||||
new = avic_get_physical_id_entry(vcpu, id);
|
||||
if (!new || !old)
|
||||
return 1;
|
||||
|
||||
/* We need to move physical_id_entry to new offset */
|
||||
*new = *old;
|
||||
*old = 0ULL;
|
||||
to_svm(vcpu)->avic_physical_id_cache = new;
|
||||
|
||||
/*
|
||||
* Also update the guest physical APIC ID in the logical
|
||||
* APIC ID table entry if already setup the LDR.
|
||||
*/
|
||||
if (svm->ldr_reg)
|
||||
avic_handle_ldr_update(vcpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avic_handle_dfr_update(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
struct kvm_arch *vm_data = &vcpu->kvm->arch;
|
||||
u32 dfr = kvm_lapic_get_reg(vcpu->arch.apic, APIC_DFR);
|
||||
u32 mod = (dfr >> 28) & 0xf;
|
||||
|
||||
/*
|
||||
* We assume that all local APICs are using the same type.
|
||||
* If this changes, we need to flush the AVIC logical
|
||||
* APID id table.
|
||||
*/
|
||||
if (vm_data->ldr_mode == mod)
|
||||
return 0;
|
||||
|
||||
clear_page(page_address(vm_data->avic_logical_id_table_page));
|
||||
vm_data->ldr_mode = mod;
|
||||
|
||||
if (svm->ldr_reg)
|
||||
avic_handle_ldr_update(vcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avic_unaccel_trap_write(struct vcpu_svm *svm)
|
||||
{
|
||||
struct kvm_lapic *apic = svm->vcpu.arch.apic;
|
||||
u32 offset = svm->vmcb->control.exit_info_1 &
|
||||
AVIC_UNACCEL_ACCESS_OFFSET_MASK;
|
||||
|
||||
switch (offset) {
|
||||
case APIC_ID:
|
||||
if (avic_handle_apic_id_update(&svm->vcpu))
|
||||
return 0;
|
||||
break;
|
||||
case APIC_LDR:
|
||||
if (avic_handle_ldr_update(&svm->vcpu))
|
||||
return 0;
|
||||
break;
|
||||
case APIC_DFR:
|
||||
avic_handle_dfr_update(&svm->vcpu);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool is_avic_unaccelerated_access_trap(u32 offset)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
switch (offset) {
|
||||
case APIC_ID:
|
||||
case APIC_EOI:
|
||||
case APIC_RRR:
|
||||
case APIC_LDR:
|
||||
case APIC_DFR:
|
||||
case APIC_SPIV:
|
||||
case APIC_ESR:
|
||||
case APIC_ICR:
|
||||
case APIC_LVTT:
|
||||
case APIC_LVTTHMR:
|
||||
case APIC_LVTPC:
|
||||
case APIC_LVT0:
|
||||
case APIC_LVT1:
|
||||
case APIC_LVTERR:
|
||||
case APIC_TMICT:
|
||||
case APIC_TDCR:
|
||||
ret = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int avic_unaccelerated_access_interception(struct vcpu_svm *svm)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 offset = svm->vmcb->control.exit_info_1 &
|
||||
AVIC_UNACCEL_ACCESS_OFFSET_MASK;
|
||||
u32 vector = svm->vmcb->control.exit_info_2 &
|
||||
AVIC_UNACCEL_ACCESS_VECTOR_MASK;
|
||||
bool write = (svm->vmcb->control.exit_info_1 >> 32) &
|
||||
AVIC_UNACCEL_ACCESS_WRITE_MASK;
|
||||
bool trap = is_avic_unaccelerated_access_trap(offset);
|
||||
|
||||
trace_kvm_avic_unaccelerated_access(svm->vcpu.vcpu_id, offset,
|
||||
trap, write, vector);
|
||||
if (trap) {
|
||||
/* Handling Trap */
|
||||
WARN_ONCE(!write, "svm: Handling trap read.\n");
|
||||
ret = avic_unaccel_trap_write(svm);
|
||||
} else {
|
||||
/* Handling Fault */
|
||||
ret = (emulate_instruction(&svm->vcpu, 0) == EMULATE_DONE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = {
|
||||
[SVM_EXIT_READ_CR0] = cr_interception,
|
||||
[SVM_EXIT_READ_CR3] = cr_interception,
|
||||
|
@ -3344,6 +3920,8 @@ static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = {
|
|||
[SVM_EXIT_XSETBV] = xsetbv_interception,
|
||||
[SVM_EXIT_NPF] = pf_interception,
|
||||
[SVM_EXIT_RSM] = emulate_on_interception,
|
||||
[SVM_EXIT_AVIC_INCOMPLETE_IPI] = avic_incomplete_ipi_interception,
|
||||
[SVM_EXIT_AVIC_UNACCELERATED_ACCESS] = avic_unaccelerated_access_interception,
|
||||
};
|
||||
|
||||
static void dump_vmcb(struct kvm_vcpu *vcpu)
|
||||
|
@ -3375,10 +3953,14 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
|
|||
pr_err("%-20s%08x\n", "exit_int_info_err:", control->exit_int_info_err);
|
||||
pr_err("%-20s%lld\n", "nested_ctl:", control->nested_ctl);
|
||||
pr_err("%-20s%016llx\n", "nested_cr3:", control->nested_cr3);
|
||||
pr_err("%-20s%016llx\n", "avic_vapic_bar:", control->avic_vapic_bar);
|
||||
pr_err("%-20s%08x\n", "event_inj:", control->event_inj);
|
||||
pr_err("%-20s%08x\n", "event_inj_err:", control->event_inj_err);
|
||||
pr_err("%-20s%lld\n", "lbr_ctl:", control->lbr_ctl);
|
||||
pr_err("%-20s%016llx\n", "next_rip:", control->next_rip);
|
||||
pr_err("%-20s%016llx\n", "avic_backing_page:", control->avic_backing_page);
|
||||
pr_err("%-20s%016llx\n", "avic_logical_id:", control->avic_logical_id);
|
||||
pr_err("%-20s%016llx\n", "avic_physical_id:", control->avic_physical_id);
|
||||
pr_err("VMCB State Save Area:\n");
|
||||
pr_err("%-5s s: %04x a: %04x l: %08x b: %016llx\n",
|
||||
"es:",
|
||||
|
@ -3562,6 +4144,7 @@ static inline void svm_inject_irq(struct vcpu_svm *svm, int irq)
|
|||
{
|
||||
struct vmcb_control_area *control;
|
||||
|
||||
/* The following fields are ignored when AVIC is enabled */
|
||||
control = &svm->vmcb->control;
|
||||
control->int_vector = irq;
|
||||
control->int_ctl &= ~V_INTR_PRIO_MASK;
|
||||
|
@ -3583,11 +4166,17 @@ static void svm_set_irq(struct kvm_vcpu *vcpu)
|
|||
SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR;
|
||||
}
|
||||
|
||||
static inline bool svm_nested_virtualize_tpr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK);
|
||||
}
|
||||
|
||||
static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK))
|
||||
if (svm_nested_virtualize_tpr(vcpu) ||
|
||||
kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
clr_cr_intercept(svm, INTERCEPT_CR8_WRITE);
|
||||
|
@ -3606,11 +4195,28 @@ static void svm_set_virtual_x2apic_mode(struct kvm_vcpu *vcpu, bool set)
|
|||
|
||||
static bool svm_get_enable_apicv(void)
|
||||
{
|
||||
return false;
|
||||
return avic;
|
||||
}
|
||||
|
||||
static void svm_hwapic_irr_update(struct kvm_vcpu *vcpu, int max_irr)
|
||||
{
|
||||
}
|
||||
|
||||
static void svm_hwapic_isr_update(struct kvm_vcpu *vcpu, int max_isr)
|
||||
{
|
||||
}
|
||||
|
||||
/* Note: Currently only used by Hyper-V. */
|
||||
static void svm_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
struct vmcb *vmcb = svm->vmcb;
|
||||
|
||||
if (!avic)
|
||||
return;
|
||||
|
||||
vmcb->control.int_ctl &= ~AVIC_ENABLE_MASK;
|
||||
mark_dirty(vmcb, VMCB_INTR);
|
||||
}
|
||||
|
||||
static void svm_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
|
||||
|
@ -3623,6 +4229,18 @@ static void svm_sync_pir_to_irr(struct kvm_vcpu *vcpu)
|
|||
return;
|
||||
}
|
||||
|
||||
static void svm_deliver_avic_intr(struct kvm_vcpu *vcpu, int vec)
|
||||
{
|
||||
kvm_lapic_set_irr(vec, vcpu->arch.apic);
|
||||
smp_mb__after_atomic();
|
||||
|
||||
if (avic_vcpu_is_running(vcpu))
|
||||
wrmsrl(SVM_AVIC_DOORBELL,
|
||||
__default_cpu_present_to_apicid(vcpu->cpu));
|
||||
else
|
||||
kvm_vcpu_wake_up(vcpu);
|
||||
}
|
||||
|
||||
static int svm_nmi_allowed(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
@ -3677,6 +4295,9 @@ static void enable_irq_window(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
/*
|
||||
* In case GIF=0 we can't rely on the CPU to tell us when GIF becomes
|
||||
* 1, because that's a separate STGI/VMRUN intercept. The next time we
|
||||
|
@ -3728,7 +4349,7 @@ static inline void sync_cr8_to_lapic(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK))
|
||||
if (svm_nested_virtualize_tpr(vcpu))
|
||||
return;
|
||||
|
||||
if (!is_cr_intercept(svm, INTERCEPT_CR8_WRITE)) {
|
||||
|
@ -3742,7 +4363,8 @@ static inline void sync_lapic_to_cr8(struct kvm_vcpu *vcpu)
|
|||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
u64 cr8;
|
||||
|
||||
if (is_guest_mode(vcpu) && (vcpu->arch.hflags & HF_VINTR_MASK))
|
||||
if (svm_nested_virtualize_tpr(vcpu) ||
|
||||
kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
cr8 = kvm_get_cr8(vcpu);
|
||||
|
@ -4045,14 +4667,26 @@ static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
|
|||
static void svm_cpuid_update(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
struct kvm_cpuid_entry2 *entry;
|
||||
|
||||
/* Update nrips enabled cache */
|
||||
svm->nrips_enabled = !!guest_cpuid_has_nrips(&svm->vcpu);
|
||||
|
||||
if (!kvm_vcpu_apicv_active(vcpu))
|
||||
return;
|
||||
|
||||
entry = kvm_find_cpuid_entry(vcpu, 1, 0);
|
||||
if (entry)
|
||||
entry->ecx &= ~bit(X86_FEATURE_X2APIC);
|
||||
}
|
||||
|
||||
static void svm_set_supported_cpuid(u32 func, struct kvm_cpuid_entry2 *entry)
|
||||
{
|
||||
switch (func) {
|
||||
case 0x1:
|
||||
if (avic)
|
||||
entry->ecx &= ~bit(X86_FEATURE_X2APIC);
|
||||
break;
|
||||
case 0x80000001:
|
||||
if (nested)
|
||||
entry->ecx |= (1 << 2); /* Set SVM bit */
|
||||
|
@ -4307,6 +4941,15 @@ static void svm_sched_in(struct kvm_vcpu *vcpu, int cpu)
|
|||
{
|
||||
}
|
||||
|
||||
static inline void avic_post_state_restore(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (avic_handle_apic_id_update(vcpu) != 0)
|
||||
return;
|
||||
if (avic_handle_dfr_update(vcpu) != 0)
|
||||
return;
|
||||
avic_handle_ldr_update(vcpu);
|
||||
}
|
||||
|
||||
static struct kvm_x86_ops svm_x86_ops = {
|
||||
.cpu_has_kvm_support = has_svm,
|
||||
.disabled_by_bios = is_disabled,
|
||||
|
@ -4322,9 +4965,14 @@ static struct kvm_x86_ops svm_x86_ops = {
|
|||
.vcpu_free = svm_free_vcpu,
|
||||
.vcpu_reset = svm_vcpu_reset,
|
||||
|
||||
.vm_init = avic_vm_init,
|
||||
.vm_destroy = avic_vm_destroy,
|
||||
|
||||
.prepare_guest_switch = svm_prepare_guest_switch,
|
||||
.vcpu_load = svm_vcpu_load,
|
||||
.vcpu_put = svm_vcpu_put,
|
||||
.vcpu_blocking = svm_vcpu_blocking,
|
||||
.vcpu_unblocking = svm_vcpu_unblocking,
|
||||
|
||||
.update_bp_intercept = update_bp_intercept,
|
||||
.get_msr = svm_get_msr,
|
||||
|
@ -4382,6 +5030,9 @@ static struct kvm_x86_ops svm_x86_ops = {
|
|||
.refresh_apicv_exec_ctrl = svm_refresh_apicv_exec_ctrl,
|
||||
.load_eoi_exitmap = svm_load_eoi_exitmap,
|
||||
.sync_pir_to_irr = svm_sync_pir_to_irr,
|
||||
.hwapic_irr_update = svm_hwapic_irr_update,
|
||||
.hwapic_isr_update = svm_hwapic_isr_update,
|
||||
.apicv_post_state_restore = avic_post_state_restore,
|
||||
|
||||
.set_tss_addr = svm_set_tss_addr,
|
||||
.get_tdp_level = get_npt_level,
|
||||
|
@ -4415,6 +5066,7 @@ static struct kvm_x86_ops svm_x86_ops = {
|
|||
.sched_in = svm_sched_in,
|
||||
|
||||
.pmu_ops = &amd_pmu_ops,
|
||||
.deliver_posted_interrupt = svm_deliver_avic_intr,
|
||||
};
|
||||
|
||||
static int __init svm_init(void)
|
||||
|
|
|
@ -1291,6 +1291,63 @@ TRACE_EVENT(kvm_hv_stimer_cleanup,
|
|||
__entry->vcpu_id, __entry->timer_index)
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracepoint for AMD AVIC
|
||||
*/
|
||||
TRACE_EVENT(kvm_avic_incomplete_ipi,
|
||||
TP_PROTO(u32 vcpu, u32 icrh, u32 icrl, u32 id, u32 index),
|
||||
TP_ARGS(vcpu, icrh, icrl, id, index),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, vcpu)
|
||||
__field(u32, icrh)
|
||||
__field(u32, icrl)
|
||||
__field(u32, id)
|
||||
__field(u32, index)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu = vcpu;
|
||||
__entry->icrh = icrh;
|
||||
__entry->icrl = icrl;
|
||||
__entry->id = id;
|
||||
__entry->index = index;
|
||||
),
|
||||
|
||||
TP_printk("vcpu=%u, icrh:icrl=%#010x:%08x, id=%u, index=%u\n",
|
||||
__entry->vcpu, __entry->icrh, __entry->icrl,
|
||||
__entry->id, __entry->index)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_avic_unaccelerated_access,
|
||||
TP_PROTO(u32 vcpu, u32 offset, bool ft, bool rw, u32 vec),
|
||||
TP_ARGS(vcpu, offset, ft, rw, vec),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, vcpu)
|
||||
__field(u32, offset)
|
||||
__field(bool, ft)
|
||||
__field(bool, rw)
|
||||
__field(u32, vec)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu = vcpu;
|
||||
__entry->offset = offset;
|
||||
__entry->ft = ft;
|
||||
__entry->rw = rw;
|
||||
__entry->vec = vec;
|
||||
),
|
||||
|
||||
TP_printk("vcpu=%u, offset=%#x(%s), %s, %s, vec=%#x\n",
|
||||
__entry->vcpu,
|
||||
__entry->offset,
|
||||
__print_symbolic(__entry->offset, kvm_trace_symbol_apic),
|
||||
__entry->ft ? "trap" : "fault",
|
||||
__entry->rw ? "write" : "read",
|
||||
__entry->vec)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_KVM_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
|
|
@ -5050,8 +5050,8 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
|
|||
vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid);
|
||||
|
||||
cr0 = X86_CR0_NW | X86_CR0_CD | X86_CR0_ET;
|
||||
vmx_set_cr0(vcpu, cr0); /* enter rmode */
|
||||
vmx->vcpu.arch.cr0 = cr0;
|
||||
vmx_set_cr0(vcpu, cr0); /* enter rmode */
|
||||
vmx_set_cr4(vcpu, 0);
|
||||
vmx_set_efer(vcpu, 0);
|
||||
vmx_fpu_activate(vcpu);
|
||||
|
@ -8318,19 +8318,19 @@ static void vmx_set_apic_access_page_addr(struct kvm_vcpu *vcpu, hpa_t hpa)
|
|||
vmcs_write64(APIC_ACCESS_ADDR, hpa);
|
||||
}
|
||||
|
||||
static void vmx_hwapic_isr_update(struct kvm *kvm, int isr)
|
||||
static void vmx_hwapic_isr_update(struct kvm_vcpu *vcpu, int max_isr)
|
||||
{
|
||||
u16 status;
|
||||
u8 old;
|
||||
|
||||
if (isr == -1)
|
||||
isr = 0;
|
||||
if (max_isr == -1)
|
||||
max_isr = 0;
|
||||
|
||||
status = vmcs_read16(GUEST_INTR_STATUS);
|
||||
old = status >> 8;
|
||||
if (isr != old) {
|
||||
if (max_isr != old) {
|
||||
status &= 0xff;
|
||||
status |= isr << 8;
|
||||
status |= max_isr << 8;
|
||||
vmcs_write16(GUEST_INTR_STATUS, status);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,6 +161,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|||
{ "halt_exits", VCPU_STAT(halt_exits) },
|
||||
{ "halt_successful_poll", VCPU_STAT(halt_successful_poll) },
|
||||
{ "halt_attempted_poll", VCPU_STAT(halt_attempted_poll) },
|
||||
{ "halt_poll_invalid", VCPU_STAT(halt_poll_invalid) },
|
||||
{ "halt_wakeup", VCPU_STAT(halt_wakeup) },
|
||||
{ "hypercalls", VCPU_STAT(hypercalls) },
|
||||
{ "request_irq", VCPU_STAT(request_irq_exits) },
|
||||
|
@ -2002,22 +2003,8 @@ static void kvmclock_reset(struct kvm_vcpu *vcpu)
|
|||
vcpu->arch.pv_time_enabled = false;
|
||||
}
|
||||
|
||||
static void accumulate_steal_time(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 delta;
|
||||
|
||||
if (!(vcpu->arch.st.msr_val & KVM_MSR_ENABLED))
|
||||
return;
|
||||
|
||||
delta = current->sched_info.run_delay - vcpu->arch.st.last_steal;
|
||||
vcpu->arch.st.last_steal = current->sched_info.run_delay;
|
||||
vcpu->arch.st.accum_steal = delta;
|
||||
}
|
||||
|
||||
static void record_steal_time(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
accumulate_steal_time(vcpu);
|
||||
|
||||
if (!(vcpu->arch.st.msr_val & KVM_MSR_ENABLED))
|
||||
return;
|
||||
|
||||
|
@ -2025,9 +2012,26 @@ static void record_steal_time(struct kvm_vcpu *vcpu)
|
|||
&vcpu->arch.st.steal, sizeof(struct kvm_steal_time))))
|
||||
return;
|
||||
|
||||
vcpu->arch.st.steal.steal += vcpu->arch.st.accum_steal;
|
||||
vcpu->arch.st.steal.version += 2;
|
||||
vcpu->arch.st.accum_steal = 0;
|
||||
if (vcpu->arch.st.steal.version & 1)
|
||||
vcpu->arch.st.steal.version += 1; /* first time write, random junk */
|
||||
|
||||
vcpu->arch.st.steal.version += 1;
|
||||
|
||||
kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.st.stime,
|
||||
&vcpu->arch.st.steal, sizeof(struct kvm_steal_time));
|
||||
|
||||
smp_wmb();
|
||||
|
||||
vcpu->arch.st.steal.steal += current->sched_info.run_delay -
|
||||
vcpu->arch.st.last_steal;
|
||||
vcpu->arch.st.last_steal = current->sched_info.run_delay;
|
||||
|
||||
kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.st.stime,
|
||||
&vcpu->arch.st.steal, sizeof(struct kvm_steal_time));
|
||||
|
||||
smp_wmb();
|
||||
|
||||
vcpu->arch.st.steal.version += 1;
|
||||
|
||||
kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.st.stime,
|
||||
&vcpu->arch.st.steal, sizeof(struct kvm_steal_time));
|
||||
|
@ -7752,6 +7756,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
|||
kvm_page_track_init(kvm);
|
||||
kvm_mmu_init_vm(kvm);
|
||||
|
||||
if (kvm_x86_ops->vm_init)
|
||||
return kvm_x86_ops->vm_init(kvm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -7873,6 +7880,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
|
|||
x86_set_memory_region(kvm, IDENTITY_PAGETABLE_PRIVATE_MEMSLOT, 0, 0);
|
||||
x86_set_memory_region(kvm, TSS_PRIVATE_MEMSLOT, 0, 0);
|
||||
}
|
||||
if (kvm_x86_ops->vm_destroy)
|
||||
kvm_x86_ops->vm_destroy(kvm);
|
||||
kvm_iommu_unmap_guest(kvm);
|
||||
kfree(kvm->arch.vpic);
|
||||
kfree(kvm->arch.vioapic);
|
||||
|
@ -8355,19 +8364,21 @@ bool kvm_arch_has_noncoherent_dma(struct kvm *kvm)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_arch_has_noncoherent_dma);
|
||||
|
||||
bool kvm_arch_has_irq_bypass(void)
|
||||
{
|
||||
return kvm_x86_ops->update_pi_irte != NULL;
|
||||
}
|
||||
|
||||
int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
|
||||
struct irq_bypass_producer *prod)
|
||||
{
|
||||
struct kvm_kernel_irqfd *irqfd =
|
||||
container_of(cons, struct kvm_kernel_irqfd, consumer);
|
||||
|
||||
if (kvm_x86_ops->update_pi_irte) {
|
||||
irqfd->producer = prod;
|
||||
return kvm_x86_ops->update_pi_irte(irqfd->kvm,
|
||||
prod->irq, irqfd->gsi, 1);
|
||||
}
|
||||
irqfd->producer = prod;
|
||||
|
||||
return -EINVAL;
|
||||
return kvm_x86_ops->update_pi_irte(irqfd->kvm,
|
||||
prod->irq, irqfd->gsi, 1);
|
||||
}
|
||||
|
||||
void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
|
||||
|
@ -8377,11 +8388,6 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
|
|||
struct kvm_kernel_irqfd *irqfd =
|
||||
container_of(cons, struct kvm_kernel_irqfd, consumer);
|
||||
|
||||
if (!kvm_x86_ops->update_pi_irte) {
|
||||
WARN_ON(irqfd->producer != NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
WARN_ON(irqfd->producer != prod);
|
||||
irqfd->producer = NULL;
|
||||
|
||||
|
@ -8429,3 +8435,5 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_write_tsc_offset);
|
|||
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_ple_window);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_pml_full);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_pi_irte_update);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_unaccelerated_access);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_incomplete_ipi);
|
||||
|
|
|
@ -468,11 +468,11 @@ static struct cyclecounter cyclecounter = {
|
|||
.mask = CLOCKSOURCE_MASK(56),
|
||||
};
|
||||
|
||||
static struct timecounter timecounter;
|
||||
static struct arch_timer_kvm_info arch_timer_kvm_info;
|
||||
|
||||
struct timecounter *arch_timer_get_timecounter(void)
|
||||
struct arch_timer_kvm_info *arch_timer_get_kvm_info(void)
|
||||
{
|
||||
return &timecounter;
|
||||
return &arch_timer_kvm_info;
|
||||
}
|
||||
|
||||
static void __init arch_counter_register(unsigned type)
|
||||
|
@ -500,7 +500,8 @@ static void __init arch_counter_register(unsigned type)
|
|||
clocksource_register_hz(&clocksource_counter, arch_timer_rate);
|
||||
cyclecounter.mult = clocksource_counter.mult;
|
||||
cyclecounter.shift = clocksource_counter.shift;
|
||||
timecounter_init(&timecounter, &cyclecounter, start_count);
|
||||
timecounter_init(&arch_timer_kvm_info.timecounter,
|
||||
&cyclecounter, start_count);
|
||||
|
||||
/* 56 bits minimum, so we assume worst case rollover */
|
||||
sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate);
|
||||
|
@ -744,6 +745,8 @@ static void __init arch_timer_init(void)
|
|||
|
||||
arch_timer_register();
|
||||
arch_timer_common_init();
|
||||
|
||||
arch_timer_kvm_info.virtual_irq = arch_timer_ppi[VIRT_PPI];
|
||||
}
|
||||
|
||||
static void __init arch_timer_of_init(struct device_node *np)
|
||||
|
|
|
@ -21,6 +21,19 @@
|
|||
|
||||
#include "irq-gic-common.h"
|
||||
|
||||
static const struct gic_kvm_info *gic_kvm_info;
|
||||
|
||||
const struct gic_kvm_info *gic_get_kvm_info(void)
|
||||
{
|
||||
return gic_kvm_info;
|
||||
}
|
||||
|
||||
void gic_set_kvm_info(const struct gic_kvm_info *info)
|
||||
{
|
||||
BUG_ON(gic_kvm_info != NULL);
|
||||
gic_kvm_info = info;
|
||||
}
|
||||
|
||||
void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
|
||||
void *data)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <linux/of.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
struct gic_quirk {
|
||||
const char *desc;
|
||||
|
@ -35,4 +36,6 @@ void gic_cpu_config(void __iomem *base, void (*sync_access)(void));
|
|||
void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
|
||||
void *data);
|
||||
|
||||
void gic_set_kvm_info(const struct gic_kvm_info *info);
|
||||
|
||||
#endif /* _IRQ_GIC_COMMON_H */
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "GICv3: " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
|
@ -28,6 +30,7 @@
|
|||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/irqchip/irq-partition-percpu.h>
|
||||
|
||||
|
@ -59,6 +62,8 @@ struct gic_chip_data {
|
|||
static struct gic_chip_data gic_data __read_mostly;
|
||||
static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
|
||||
|
||||
static struct gic_kvm_info gic_v3_kvm_info;
|
||||
|
||||
#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist))
|
||||
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
|
||||
#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K)
|
||||
|
@ -1002,7 +1007,7 @@ static int get_cpu_number(struct device_node *dn)
|
|||
}
|
||||
|
||||
/* Create all possible partitions at boot time */
|
||||
static void gic_populate_ppi_partitions(struct device_node *gic_node)
|
||||
static void __init gic_populate_ppi_partitions(struct device_node *gic_node)
|
||||
{
|
||||
struct device_node *parts_node, *child_part;
|
||||
int part_idx = 0, i;
|
||||
|
@ -1089,6 +1094,30 @@ static void gic_populate_ppi_partitions(struct device_node *gic_node)
|
|||
}
|
||||
}
|
||||
|
||||
static void __init gic_of_setup_kvm_info(struct device_node *node)
|
||||
{
|
||||
int ret;
|
||||
struct resource r;
|
||||
u32 gicv_idx;
|
||||
|
||||
gic_v3_kvm_info.type = GIC_V3;
|
||||
|
||||
gic_v3_kvm_info.maint_irq = irq_of_parse_and_map(node, 0);
|
||||
if (!gic_v3_kvm_info.maint_irq)
|
||||
return;
|
||||
|
||||
if (of_property_read_u32(node, "#redistributor-regions",
|
||||
&gicv_idx))
|
||||
gicv_idx = 1;
|
||||
|
||||
gicv_idx += 3; /* Also skip GICD, GICC, GICH */
|
||||
ret = of_address_to_resource(node, gicv_idx, &r);
|
||||
if (!ret)
|
||||
gic_v3_kvm_info.vcpu = r;
|
||||
|
||||
gic_set_kvm_info(&gic_v3_kvm_info);
|
||||
}
|
||||
|
||||
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
void __iomem *dist_base;
|
||||
|
@ -1144,6 +1173,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
|
|||
goto out_unmap_rdist;
|
||||
|
||||
gic_populate_ppi_partitions(node);
|
||||
gic_of_setup_kvm_info(node);
|
||||
return 0;
|
||||
|
||||
out_unmap_rdist:
|
||||
|
@ -1159,19 +1189,25 @@ out_unmap_dist:
|
|||
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static void __iomem *dist_base;
|
||||
static struct redist_region *redist_regs __initdata;
|
||||
static u32 nr_redist_regions __initdata;
|
||||
static bool single_redist;
|
||||
static struct
|
||||
{
|
||||
void __iomem *dist_base;
|
||||
struct redist_region *redist_regs;
|
||||
u32 nr_redist_regions;
|
||||
bool single_redist;
|
||||
u32 maint_irq;
|
||||
int maint_irq_mode;
|
||||
phys_addr_t vcpu_base;
|
||||
} acpi_data __initdata;
|
||||
|
||||
static void __init
|
||||
gic_acpi_register_redist(phys_addr_t phys_base, void __iomem *redist_base)
|
||||
{
|
||||
static int count = 0;
|
||||
|
||||
redist_regs[count].phys_base = phys_base;
|
||||
redist_regs[count].redist_base = redist_base;
|
||||
redist_regs[count].single_redist = single_redist;
|
||||
acpi_data.redist_regs[count].phys_base = phys_base;
|
||||
acpi_data.redist_regs[count].redist_base = redist_base;
|
||||
acpi_data.redist_regs[count].single_redist = acpi_data.single_redist;
|
||||
count++;
|
||||
}
|
||||
|
||||
|
@ -1199,7 +1235,7 @@ gic_acpi_parse_madt_gicc(struct acpi_subtable_header *header,
|
|||
{
|
||||
struct acpi_madt_generic_interrupt *gicc =
|
||||
(struct acpi_madt_generic_interrupt *)header;
|
||||
u32 reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
|
||||
u32 reg = readl_relaxed(acpi_data.dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
|
||||
u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2;
|
||||
void __iomem *redist_base;
|
||||
|
||||
|
@ -1216,7 +1252,7 @@ static int __init gic_acpi_collect_gicr_base(void)
|
|||
acpi_tbl_entry_handler redist_parser;
|
||||
enum acpi_madt_type type;
|
||||
|
||||
if (single_redist) {
|
||||
if (acpi_data.single_redist) {
|
||||
type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
|
||||
redist_parser = gic_acpi_parse_madt_gicc;
|
||||
} else {
|
||||
|
@ -1267,14 +1303,14 @@ static int __init gic_acpi_count_gicr_regions(void)
|
|||
count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
|
||||
gic_acpi_match_gicr, 0);
|
||||
if (count > 0) {
|
||||
single_redist = false;
|
||||
acpi_data.single_redist = false;
|
||||
return count;
|
||||
}
|
||||
|
||||
count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
|
||||
gic_acpi_match_gicc, 0);
|
||||
if (count > 0)
|
||||
single_redist = true;
|
||||
acpi_data.single_redist = true;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -1294,36 +1330,117 @@ static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
|
|||
if (count <= 0)
|
||||
return false;
|
||||
|
||||
nr_redist_regions = count;
|
||||
acpi_data.nr_redist_regions = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __init gic_acpi_parse_virt_madt_gicc(struct acpi_subtable_header *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_generic_interrupt *gicc =
|
||||
(struct acpi_madt_generic_interrupt *)header;
|
||||
int maint_irq_mode;
|
||||
static int first_madt = true;
|
||||
|
||||
/* Skip unusable CPUs */
|
||||
if (!(gicc->flags & ACPI_MADT_ENABLED))
|
||||
return 0;
|
||||
|
||||
maint_irq_mode = (gicc->flags & ACPI_MADT_VGIC_IRQ_MODE) ?
|
||||
ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE;
|
||||
|
||||
if (first_madt) {
|
||||
first_madt = false;
|
||||
|
||||
acpi_data.maint_irq = gicc->vgic_interrupt;
|
||||
acpi_data.maint_irq_mode = maint_irq_mode;
|
||||
acpi_data.vcpu_base = gicc->gicv_base_address;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The maintenance interrupt and GICV should be the same for every CPU
|
||||
*/
|
||||
if ((acpi_data.maint_irq != gicc->vgic_interrupt) ||
|
||||
(acpi_data.maint_irq_mode != maint_irq_mode) ||
|
||||
(acpi_data.vcpu_base != gicc->gicv_base_address))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool __init gic_acpi_collect_virt_info(void)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
|
||||
gic_acpi_parse_virt_madt_gicc, 0);
|
||||
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
#define ACPI_GICV3_DIST_MEM_SIZE (SZ_64K)
|
||||
#define ACPI_GICV2_VCTRL_MEM_SIZE (SZ_4K)
|
||||
#define ACPI_GICV2_VCPU_MEM_SIZE (SZ_8K)
|
||||
|
||||
static void __init gic_acpi_setup_kvm_info(void)
|
||||
{
|
||||
int irq;
|
||||
|
||||
if (!gic_acpi_collect_virt_info()) {
|
||||
pr_warn("Unable to get hardware information used for virtualization\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gic_v3_kvm_info.type = GIC_V3;
|
||||
|
||||
irq = acpi_register_gsi(NULL, acpi_data.maint_irq,
|
||||
acpi_data.maint_irq_mode,
|
||||
ACPI_ACTIVE_HIGH);
|
||||
if (irq <= 0)
|
||||
return;
|
||||
|
||||
gic_v3_kvm_info.maint_irq = irq;
|
||||
|
||||
if (acpi_data.vcpu_base) {
|
||||
struct resource *vcpu = &gic_v3_kvm_info.vcpu;
|
||||
|
||||
vcpu->flags = IORESOURCE_MEM;
|
||||
vcpu->start = acpi_data.vcpu_base;
|
||||
vcpu->end = vcpu->start + ACPI_GICV2_VCPU_MEM_SIZE - 1;
|
||||
}
|
||||
|
||||
gic_set_kvm_info(&gic_v3_kvm_info);
|
||||
}
|
||||
|
||||
static int __init
|
||||
gic_acpi_init(struct acpi_subtable_header *header, const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_generic_distributor *dist;
|
||||
struct fwnode_handle *domain_handle;
|
||||
size_t size;
|
||||
int i, err;
|
||||
|
||||
/* Get distributor base address */
|
||||
dist = (struct acpi_madt_generic_distributor *)header;
|
||||
dist_base = ioremap(dist->base_address, ACPI_GICV3_DIST_MEM_SIZE);
|
||||
if (!dist_base) {
|
||||
acpi_data.dist_base = ioremap(dist->base_address,
|
||||
ACPI_GICV3_DIST_MEM_SIZE);
|
||||
if (!acpi_data.dist_base) {
|
||||
pr_err("Unable to map GICD registers\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = gic_validate_dist_version(dist_base);
|
||||
err = gic_validate_dist_version(acpi_data.dist_base);
|
||||
if (err) {
|
||||
pr_err("No distributor detected at @%p, giving up", dist_base);
|
||||
pr_err("No distributor detected at @%p, giving up",
|
||||
acpi_data.dist_base);
|
||||
goto out_dist_unmap;
|
||||
}
|
||||
|
||||
redist_regs = kzalloc(sizeof(*redist_regs) * nr_redist_regions,
|
||||
GFP_KERNEL);
|
||||
if (!redist_regs) {
|
||||
size = sizeof(*acpi_data.redist_regs) * acpi_data.nr_redist_regions;
|
||||
acpi_data.redist_regs = kzalloc(size, GFP_KERNEL);
|
||||
if (!acpi_data.redist_regs) {
|
||||
err = -ENOMEM;
|
||||
goto out_dist_unmap;
|
||||
}
|
||||
|
@ -1332,29 +1449,31 @@ gic_acpi_init(struct acpi_subtable_header *header, const unsigned long end)
|
|||
if (err)
|
||||
goto out_redist_unmap;
|
||||
|
||||
domain_handle = irq_domain_alloc_fwnode(dist_base);
|
||||
domain_handle = irq_domain_alloc_fwnode(acpi_data.dist_base);
|
||||
if (!domain_handle) {
|
||||
err = -ENOMEM;
|
||||
goto out_redist_unmap;
|
||||
}
|
||||
|
||||
err = gic_init_bases(dist_base, redist_regs, nr_redist_regions, 0,
|
||||
domain_handle);
|
||||
err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs,
|
||||
acpi_data.nr_redist_regions, 0, domain_handle);
|
||||
if (err)
|
||||
goto out_fwhandle_free;
|
||||
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
|
||||
gic_acpi_setup_kvm_info();
|
||||
|
||||
return 0;
|
||||
|
||||
out_fwhandle_free:
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
out_redist_unmap:
|
||||
for (i = 0; i < nr_redist_regions; i++)
|
||||
if (redist_regs[i].redist_base)
|
||||
iounmap(redist_regs[i].redist_base);
|
||||
kfree(redist_regs);
|
||||
for (i = 0; i < acpi_data.nr_redist_regions; i++)
|
||||
if (acpi_data.redist_regs[i].redist_base)
|
||||
iounmap(acpi_data.redist_regs[i].redist_base);
|
||||
kfree(acpi_data.redist_regs);
|
||||
out_dist_unmap:
|
||||
iounmap(dist_base);
|
||||
iounmap(acpi_data.dist_base);
|
||||
return err;
|
||||
}
|
||||
IRQCHIP_ACPI_DECLARE(gic_v3, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
|
||||
|
|
|
@ -105,6 +105,8 @@ static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
|
|||
|
||||
static struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly;
|
||||
|
||||
static struct gic_kvm_info gic_v2_kvm_info;
|
||||
|
||||
#ifdef CONFIG_GIC_NON_BANKED
|
||||
static void __iomem *gic_get_percpu_base(union gic_base *base)
|
||||
{
|
||||
|
@ -1248,7 +1250,7 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
|
|||
return true;
|
||||
}
|
||||
|
||||
static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
|
||||
static int __init gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
|
||||
{
|
||||
if (!gic || !node)
|
||||
return -EINVAL;
|
||||
|
@ -1272,6 +1274,29 @@ error:
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void __init gic_of_setup_kvm_info(struct device_node *node)
|
||||
{
|
||||
int ret;
|
||||
struct resource *vctrl_res = &gic_v2_kvm_info.vctrl;
|
||||
struct resource *vcpu_res = &gic_v2_kvm_info.vcpu;
|
||||
|
||||
gic_v2_kvm_info.type = GIC_V2;
|
||||
|
||||
gic_v2_kvm_info.maint_irq = irq_of_parse_and_map(node, 0);
|
||||
if (!gic_v2_kvm_info.maint_irq)
|
||||
return;
|
||||
|
||||
ret = of_address_to_resource(node, 2, vctrl_res);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = of_address_to_resource(node, 3, vcpu_res);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
gic_set_kvm_info(&gic_v2_kvm_info);
|
||||
}
|
||||
|
||||
int __init
|
||||
gic_of_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
|
@ -1303,8 +1328,10 @@ gic_of_init(struct device_node *node, struct device_node *parent)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!gic_cnt)
|
||||
if (!gic_cnt) {
|
||||
gic_init_physaddr(node);
|
||||
gic_of_setup_kvm_info(node);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
|
@ -1330,7 +1357,14 @@ IRQCHIP_DECLARE(pl390, "arm,pl390", gic_of_init);
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static phys_addr_t cpu_phy_base __initdata;
|
||||
static struct
|
||||
{
|
||||
phys_addr_t cpu_phys_base;
|
||||
u32 maint_irq;
|
||||
int maint_irq_mode;
|
||||
phys_addr_t vctrl_base;
|
||||
phys_addr_t vcpu_base;
|
||||
} acpi_data __initdata;
|
||||
|
||||
static int __init
|
||||
gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
|
||||
|
@ -1350,10 +1384,16 @@ gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
|
|||
* All CPU interface addresses have to be the same.
|
||||
*/
|
||||
gic_cpu_base = processor->base_address;
|
||||
if (cpu_base_assigned && gic_cpu_base != cpu_phy_base)
|
||||
if (cpu_base_assigned && gic_cpu_base != acpi_data.cpu_phys_base)
|
||||
return -EINVAL;
|
||||
|
||||
cpu_phy_base = gic_cpu_base;
|
||||
acpi_data.cpu_phys_base = gic_cpu_base;
|
||||
acpi_data.maint_irq = processor->vgic_interrupt;
|
||||
acpi_data.maint_irq_mode = (processor->flags & ACPI_MADT_VGIC_IRQ_MODE) ?
|
||||
ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE;
|
||||
acpi_data.vctrl_base = processor->gich_base_address;
|
||||
acpi_data.vcpu_base = processor->gicv_base_address;
|
||||
|
||||
cpu_base_assigned = 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1384,6 +1424,41 @@ static bool __init gic_validate_dist(struct acpi_subtable_header *header,
|
|||
|
||||
#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K)
|
||||
#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K)
|
||||
#define ACPI_GICV2_VCTRL_MEM_SIZE (SZ_4K)
|
||||
#define ACPI_GICV2_VCPU_MEM_SIZE (SZ_8K)
|
||||
|
||||
static void __init gic_acpi_setup_kvm_info(void)
|
||||
{
|
||||
int irq;
|
||||
struct resource *vctrl_res = &gic_v2_kvm_info.vctrl;
|
||||
struct resource *vcpu_res = &gic_v2_kvm_info.vcpu;
|
||||
|
||||
gic_v2_kvm_info.type = GIC_V2;
|
||||
|
||||
if (!acpi_data.vctrl_base)
|
||||
return;
|
||||
|
||||
vctrl_res->flags = IORESOURCE_MEM;
|
||||
vctrl_res->start = acpi_data.vctrl_base;
|
||||
vctrl_res->end = vctrl_res->start + ACPI_GICV2_VCTRL_MEM_SIZE - 1;
|
||||
|
||||
if (!acpi_data.vcpu_base)
|
||||
return;
|
||||
|
||||
vcpu_res->flags = IORESOURCE_MEM;
|
||||
vcpu_res->start = acpi_data.vcpu_base;
|
||||
vcpu_res->end = vcpu_res->start + ACPI_GICV2_VCPU_MEM_SIZE - 1;
|
||||
|
||||
irq = acpi_register_gsi(NULL, acpi_data.maint_irq,
|
||||
acpi_data.maint_irq_mode,
|
||||
ACPI_ACTIVE_HIGH);
|
||||
if (irq <= 0)
|
||||
return;
|
||||
|
||||
gic_v2_kvm_info.maint_irq = irq;
|
||||
|
||||
gic_set_kvm_info(&gic_v2_kvm_info);
|
||||
}
|
||||
|
||||
static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
|
||||
const unsigned long end)
|
||||
|
@ -1401,7 +1476,7 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
gic->raw_cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE);
|
||||
gic->raw_cpu_base = ioremap(acpi_data.cpu_phys_base, ACPI_GIC_CPU_IF_MEM_SIZE);
|
||||
if (!gic->raw_cpu_base) {
|
||||
pr_err("Unable to map GICC registers\n");
|
||||
return -ENOMEM;
|
||||
|
@ -1447,6 +1522,8 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
|
|||
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
|
||||
gicv2m_init(NULL, gic_data[0].domain);
|
||||
|
||||
gic_acpi_setup_kvm_info();
|
||||
|
||||
return 0;
|
||||
}
|
||||
IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
|
||||
|
|
|
@ -49,7 +49,9 @@ struct read_info_sccb {
|
|||
u8 _pad_117[119 - 117]; /* 117-118 */
|
||||
u8 fac119; /* 119 */
|
||||
u16 hcpua; /* 120-121 */
|
||||
u8 _pad_122[4096 - 122]; /* 122-4095 */
|
||||
u8 _pad_122[124 - 122]; /* 122-123 */
|
||||
u32 hmfai; /* 124-127 */
|
||||
u8 _pad_128[4096 - 128]; /* 128-4095 */
|
||||
} __packed __aligned(PAGE_SIZE);
|
||||
|
||||
static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata;
|
||||
|
@ -155,6 +157,8 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
|
|||
sclp.mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0;
|
||||
sclp.mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0;
|
||||
sclp.mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0;
|
||||
|
||||
sclp.hmfai = sccb->hmfai;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -49,11 +49,16 @@ enum arch_timer_reg {
|
|||
|
||||
#define ARCH_TIMER_EVT_STREAM_FREQ 10000 /* 100us */
|
||||
|
||||
struct arch_timer_kvm_info {
|
||||
struct timecounter timecounter;
|
||||
int virtual_irq;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARM_ARCH_TIMER
|
||||
|
||||
extern u32 arch_timer_get_rate(void);
|
||||
extern u64 (*arch_timer_read_counter)(void);
|
||||
extern struct timecounter *arch_timer_get_timecounter(void);
|
||||
extern struct arch_timer_kvm_info *arch_timer_get_kvm_info(void);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -67,11 +72,6 @@ static inline u64 arch_timer_read_counter(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline struct timecounter *arch_timer_get_timecounter(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <kvm/iodev.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
#define VGIC_NR_IRQS_LEGACY 256
|
||||
#define VGIC_NR_SGIS 16
|
||||
|
@ -353,15 +354,15 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
|
|||
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
|
||||
#define vgic_ready(k) ((k)->arch.vgic.ready)
|
||||
|
||||
int vgic_v2_probe(struct device_node *vgic_node,
|
||||
int vgic_v2_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params);
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
int vgic_v3_probe(struct device_node *vgic_node,
|
||||
int vgic_v3_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params);
|
||||
#else
|
||||
static inline int vgic_v3_probe(struct device_node *vgic_node,
|
||||
static inline int vgic_v3_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params)
|
||||
{
|
||||
|
|
|
@ -34,7 +34,7 @@ struct irq_bypass_consumer;
|
|||
/**
|
||||
* struct irq_bypass_producer - IRQ bypass producer definition
|
||||
* @node: IRQ bypass manager private list management
|
||||
* @token: opaque token to match between producer and consumer
|
||||
* @token: opaque token to match between producer and consumer (non-NULL)
|
||||
* @irq: Linux IRQ number for the producer device
|
||||
* @add_consumer: Connect the IRQ producer to an IRQ consumer (optional)
|
||||
* @del_consumer: Disconnect the IRQ producer from an IRQ consumer (optional)
|
||||
|
@ -60,7 +60,7 @@ struct irq_bypass_producer {
|
|||
/**
|
||||
* struct irq_bypass_consumer - IRQ bypass consumer definition
|
||||
* @node: IRQ bypass manager private list management
|
||||
* @token: opaque token to match between producer and consumer
|
||||
* @token: opaque token to match between producer and consumer (non-NULL)
|
||||
* @add_producer: Connect the IRQ consumer to an IRQ producer
|
||||
* @del_producer: Disconnect the IRQ consumer from an IRQ producer
|
||||
* @stop: Perform any quiesce operations necessary prior to add/del (optional)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* include/linux/irqchip/arm-gic-common.h
|
||||
*
|
||||
* Copyright (C) 2016 ARM Limited, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __LINUX_IRQCHIP_ARM_GIC_COMMON_H
|
||||
#define __LINUX_IRQCHIP_ARM_GIC_COMMON_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
enum gic_type {
|
||||
GIC_V2,
|
||||
GIC_V3,
|
||||
};
|
||||
|
||||
struct gic_kvm_info {
|
||||
/* GIC type */
|
||||
enum gic_type type;
|
||||
/* Virtual CPU interface */
|
||||
struct resource vcpu;
|
||||
/* Interrupt number */
|
||||
unsigned int maint_irq;
|
||||
/* Virtual control interface */
|
||||
struct resource vctrl;
|
||||
};
|
||||
|
||||
const struct gic_kvm_info *gic_get_kvm_info(void);
|
||||
|
||||
#endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */
|
|
@ -35,6 +35,10 @@
|
|||
|
||||
#include <asm/kvm_host.h>
|
||||
|
||||
#ifndef KVM_MAX_VCPU_ID
|
||||
#define KVM_MAX_VCPU_ID KVM_MAX_VCPUS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The bit 16 ~ bit 31 of kvm_memory_region::flags are internally used
|
||||
* in kvm, other bits are visible for userspace which are defined in
|
||||
|
@ -225,6 +229,7 @@ struct kvm_vcpu {
|
|||
sigset_t sigset;
|
||||
struct kvm_vcpu_stat stat;
|
||||
unsigned int halt_poll_ns;
|
||||
bool valid_wakeup;
|
||||
|
||||
#ifdef CONFIG_HAS_IOMEM
|
||||
int mmio_needed;
|
||||
|
@ -447,12 +452,13 @@ static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i)
|
|||
|
||||
static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vcpu *vcpu = NULL;
|
||||
int i;
|
||||
|
||||
if (id < 0 || id >= KVM_MAX_VCPUS)
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
vcpu = kvm_get_vcpu(kvm, id);
|
||||
if (id < KVM_MAX_VCPUS)
|
||||
vcpu = kvm_get_vcpu(kvm, id);
|
||||
if (vcpu && vcpu->vcpu_id == id)
|
||||
return vcpu;
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
|
@ -651,6 +657,7 @@ void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn);
|
|||
void kvm_vcpu_block(struct kvm_vcpu *vcpu);
|
||||
void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu);
|
||||
void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu);
|
||||
void kvm_vcpu_wake_up(struct kvm_vcpu *vcpu);
|
||||
void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
|
||||
int kvm_vcpu_yield_to(struct kvm_vcpu *target);
|
||||
void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu);
|
||||
|
@ -1091,6 +1098,11 @@ static inline bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu) { return true; }
|
|||
|
||||
static inline void kvm_make_request(int req, struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* Ensure the rest of the request is published to kvm_check_request's
|
||||
* caller. Paired with the smp_mb__after_atomic in kvm_check_request.
|
||||
*/
|
||||
smp_wmb();
|
||||
set_bit(req, &vcpu->requests);
|
||||
}
|
||||
|
||||
|
@ -1098,6 +1110,12 @@ static inline bool kvm_check_request(int req, struct kvm_vcpu *vcpu)
|
|||
{
|
||||
if (test_bit(req, &vcpu->requests)) {
|
||||
clear_bit(req, &vcpu->requests);
|
||||
|
||||
/*
|
||||
* Ensure the rest of the request is visible to kvm_check_request's
|
||||
* caller. Paired with the smp_wmb in kvm_make_request.
|
||||
*/
|
||||
smp_mb__after_atomic();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -1169,6 +1187,7 @@ static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val)
|
|||
#endif /* CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */
|
||||
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_BYPASS
|
||||
bool kvm_arch_has_irq_bypass(void);
|
||||
int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *,
|
||||
struct irq_bypass_producer *);
|
||||
void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *,
|
||||
|
@ -1179,4 +1198,18 @@ int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq,
|
|||
uint32_t guest_irq, bool set);
|
||||
#endif /* CONFIG_HAVE_KVM_IRQ_BYPASS */
|
||||
|
||||
#ifdef CONFIG_HAVE_KVM_INVALID_WAKEUPS
|
||||
/* If we wakeup during the poll time, was it a sucessful poll? */
|
||||
static inline bool vcpu_valid_wakeup(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->valid_wakeup;
|
||||
}
|
||||
|
||||
#else
|
||||
static inline bool vcpu_valid_wakeup(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_HAVE_KVM_INVALID_WAKEUPS */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -38,22 +38,25 @@ TRACE_EVENT(kvm_userspace_exit,
|
|||
);
|
||||
|
||||
TRACE_EVENT(kvm_vcpu_wakeup,
|
||||
TP_PROTO(__u64 ns, bool waited),
|
||||
TP_ARGS(ns, waited),
|
||||
TP_PROTO(__u64 ns, bool waited, bool valid),
|
||||
TP_ARGS(ns, waited, valid),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( __u64, ns )
|
||||
__field( bool, waited )
|
||||
__field( bool, valid )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->ns = ns;
|
||||
__entry->waited = waited;
|
||||
__entry->valid = valid;
|
||||
),
|
||||
|
||||
TP_printk("%s time %lld ns",
|
||||
TP_printk("%s time %lld ns, polling %s",
|
||||
__entry->waited ? "wait" : "poll",
|
||||
__entry->ns)
|
||||
__entry->ns,
|
||||
__entry->valid ? "valid" : "invalid")
|
||||
);
|
||||
|
||||
#if defined(CONFIG_HAVE_KVM_IRQFD)
|
||||
|
|
|
@ -865,6 +865,7 @@ struct kvm_ppc_smmu_info {
|
|||
#define KVM_CAP_SPAPR_TCE_64 125
|
||||
#define KVM_CAP_ARM_PMU_V3 126
|
||||
#define KVM_CAP_VCPU_ATTRIBUTES 127
|
||||
#define KVM_CAP_MAX_VCPU_ID 128
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
|
|
|
@ -41,6 +41,9 @@ config KVM_VFIO
|
|||
config HAVE_KVM_ARCH_TLB_FLUSH_ALL
|
||||
bool
|
||||
|
||||
config HAVE_KVM_INVALID_WAKEUPS
|
||||
bool
|
||||
|
||||
config KVM_GENERIC_DIRTYLOG_READ_PROTECT
|
||||
bool
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -438,45 +437,29 @@ static struct notifier_block kvm_timer_cpu_nb = {
|
|||
.notifier_call = kvm_timer_cpu_notify,
|
||||
};
|
||||
|
||||
static const struct of_device_id arch_timer_of_match[] = {
|
||||
{ .compatible = "arm,armv7-timer", },
|
||||
{ .compatible = "arm,armv8-timer", },
|
||||
{},
|
||||
};
|
||||
|
||||
int kvm_timer_hyp_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned int ppi;
|
||||
struct arch_timer_kvm_info *info;
|
||||
int err;
|
||||
|
||||
timecounter = arch_timer_get_timecounter();
|
||||
if (!timecounter)
|
||||
return -ENODEV;
|
||||
info = arch_timer_get_kvm_info();
|
||||
timecounter = &info->timecounter;
|
||||
|
||||
np = of_find_matching_node(NULL, arch_timer_of_match);
|
||||
if (!np) {
|
||||
kvm_err("kvm_arch_timer: can't find DT node\n");
|
||||
if (info->virtual_irq <= 0) {
|
||||
kvm_err("kvm_arch_timer: invalid virtual timer IRQ: %d\n",
|
||||
info->virtual_irq);
|
||||
return -ENODEV;
|
||||
}
|
||||
host_vtimer_irq = info->virtual_irq;
|
||||
|
||||
ppi = irq_of_parse_and_map(np, 2);
|
||||
if (!ppi) {
|
||||
kvm_err("kvm_arch_timer: no virtual timer interrupt\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = request_percpu_irq(ppi, kvm_arch_timer_handler,
|
||||
err = request_percpu_irq(host_vtimer_irq, kvm_arch_timer_handler,
|
||||
"kvm guest timer", kvm_get_running_vcpus());
|
||||
if (err) {
|
||||
kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
|
||||
ppi, err);
|
||||
host_vtimer_irq, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
host_vtimer_irq = ppi;
|
||||
|
||||
err = __register_cpu_notifier(&kvm_timer_cpu_nb);
|
||||
if (err) {
|
||||
kvm_err("Cannot register timer CPU notifier\n");
|
||||
|
@ -489,14 +472,13 @@ int kvm_timer_hyp_init(void)
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
kvm_info("%s IRQ%d\n", np->name, ppi);
|
||||
kvm_info("virtual timer IRQ%d\n", host_vtimer_irq);
|
||||
on_each_cpu(kvm_timer_init_interrupt, NULL, 1);
|
||||
|
||||
goto out;
|
||||
out_free:
|
||||
free_percpu_irq(ppi, kvm_get_running_vcpus());
|
||||
free_percpu_irq(host_vtimer_irq, kvm_get_running_vcpus());
|
||||
out:
|
||||
of_node_put(np);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
#include <linux/kvm_host.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
|
||||
|
@ -186,38 +183,39 @@ static void vgic_cpu_init_lrs(void *params)
|
|||
}
|
||||
|
||||
/**
|
||||
* vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
|
||||
* @node: pointer to the DT node
|
||||
* @ops: address of a pointer to the GICv2 operations
|
||||
* @params: address of a pointer to HW-specific parameters
|
||||
* vgic_v2_probe - probe for a GICv2 compatible interrupt controller
|
||||
* @gic_kvm_info: pointer to the GIC description
|
||||
* @ops: address of a pointer to the GICv2 operations
|
||||
* @params: address of a pointer to HW-specific parameters
|
||||
*
|
||||
* Returns 0 if a GICv2 has been found, with the low level operations
|
||||
* in *ops and the HW parameters in *params. Returns an error code
|
||||
* otherwise.
|
||||
*/
|
||||
int vgic_v2_probe(struct device_node *vgic_node,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params)
|
||||
int vgic_v2_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params)
|
||||
{
|
||||
int ret;
|
||||
struct resource vctrl_res;
|
||||
struct resource vcpu_res;
|
||||
struct vgic_params *vgic = &vgic_v2_params;
|
||||
const struct resource *vctrl_res = &gic_kvm_info->vctrl;
|
||||
const struct resource *vcpu_res = &gic_kvm_info->vcpu;
|
||||
|
||||
vgic->maint_irq = irq_of_parse_and_map(vgic_node, 0);
|
||||
if (!vgic->maint_irq) {
|
||||
kvm_err("error getting vgic maintenance irq from DT\n");
|
||||
if (!gic_kvm_info->maint_irq) {
|
||||
kvm_err("error getting vgic maintenance irq\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
vgic->maint_irq = gic_kvm_info->maint_irq;
|
||||
|
||||
if (!gic_kvm_info->vctrl.start) {
|
||||
kvm_err("GICH not present in the firmware table\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(vgic_node, 2, &vctrl_res);
|
||||
if (ret) {
|
||||
kvm_err("Cannot obtain GICH resource\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
vgic->vctrl_base = of_iomap(vgic_node, 2);
|
||||
vgic->vctrl_base = ioremap(gic_kvm_info->vctrl.start,
|
||||
resource_size(&gic_kvm_info->vctrl));
|
||||
if (!vgic->vctrl_base) {
|
||||
kvm_err("Cannot ioremap GICH\n");
|
||||
ret = -ENOMEM;
|
||||
|
@ -228,29 +226,23 @@ int vgic_v2_probe(struct device_node *vgic_node,
|
|||
vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1;
|
||||
|
||||
ret = create_hyp_io_mappings(vgic->vctrl_base,
|
||||
vgic->vctrl_base + resource_size(&vctrl_res),
|
||||
vctrl_res.start);
|
||||
vgic->vctrl_base + resource_size(vctrl_res),
|
||||
vctrl_res->start);
|
||||
if (ret) {
|
||||
kvm_err("Cannot map VCTRL into hyp\n");
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
if (of_address_to_resource(vgic_node, 3, &vcpu_res)) {
|
||||
kvm_err("Cannot obtain GICV resource\n");
|
||||
ret = -ENXIO;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(vcpu_res.start)) {
|
||||
if (!PAGE_ALIGNED(vcpu_res->start)) {
|
||||
kvm_err("GICV physical address 0x%llx not page aligned\n",
|
||||
(unsigned long long)vcpu_res.start);
|
||||
(unsigned long long)vcpu_res->start);
|
||||
ret = -ENXIO;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(resource_size(&vcpu_res))) {
|
||||
if (!PAGE_ALIGNED(resource_size(vcpu_res))) {
|
||||
kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n",
|
||||
(unsigned long long)resource_size(&vcpu_res),
|
||||
(unsigned long long)resource_size(vcpu_res),
|
||||
PAGE_SIZE);
|
||||
ret = -ENXIO;
|
||||
goto out_unmap;
|
||||
|
@ -259,10 +251,10 @@ int vgic_v2_probe(struct device_node *vgic_node,
|
|||
vgic->can_emulate_gicv2 = true;
|
||||
kvm_register_device_ops(&kvm_arm_vgic_v2_ops, KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
|
||||
vgic->vcpu_base = vcpu_res.start;
|
||||
vgic->vcpu_base = vcpu_res->start;
|
||||
|
||||
kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
|
||||
vctrl_res.start, vgic->maint_irq);
|
||||
kvm_info("GICH base=0x%llx, GICV base=0x%llx, IRQ=%d\n",
|
||||
gic_kvm_info->vctrl.start, vgic->vcpu_base, vgic->maint_irq);
|
||||
|
||||
vgic->type = VGIC_V2;
|
||||
vgic->max_gic_vcpus = VGIC_V2_MAX_CPUS;
|
||||
|
@ -276,6 +268,5 @@ int vgic_v2_probe(struct device_node *vgic_node,
|
|||
out_unmap:
|
||||
iounmap(vgic->vctrl_base);
|
||||
out:
|
||||
of_node_put(vgic_node);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -20,11 +20,9 @@
|
|||
#include <linux/kvm_host.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_arm.h>
|
||||
|
@ -222,30 +220,24 @@ static void vgic_cpu_init_lrs(void *params)
|
|||
}
|
||||
|
||||
/**
|
||||
* vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
|
||||
* @node: pointer to the DT node
|
||||
* @ops: address of a pointer to the GICv3 operations
|
||||
* @params: address of a pointer to HW-specific parameters
|
||||
* vgic_v3_probe - probe for a GICv3 compatible interrupt controller
|
||||
* @gic_kvm_info: pointer to the GIC description
|
||||
* @ops: address of a pointer to the GICv3 operations
|
||||
* @params: address of a pointer to HW-specific parameters
|
||||
*
|
||||
* Returns 0 if a GICv3 has been found, with the low level operations
|
||||
* in *ops and the HW parameters in *params. Returns an error code
|
||||
* otherwise.
|
||||
*/
|
||||
int vgic_v3_probe(struct device_node *vgic_node,
|
||||
int vgic_v3_probe(const struct gic_kvm_info *gic_kvm_info,
|
||||
const struct vgic_ops **ops,
|
||||
const struct vgic_params **params)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 gicv_idx;
|
||||
struct resource vcpu_res;
|
||||
struct vgic_params *vgic = &vgic_v3_params;
|
||||
const struct resource *vcpu_res = &gic_kvm_info->vcpu;
|
||||
|
||||
vgic->maint_irq = irq_of_parse_and_map(vgic_node, 0);
|
||||
if (!vgic->maint_irq) {
|
||||
kvm_err("error getting vgic maintenance irq from DT\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
vgic->maint_irq = gic_kvm_info->maint_irq;
|
||||
|
||||
ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
|
||||
|
||||
|
@ -256,24 +248,19 @@ int vgic_v3_probe(struct device_node *vgic_node,
|
|||
vgic->nr_lr = (ich_vtr_el2 & 0xf) + 1;
|
||||
vgic->can_emulate_gicv2 = false;
|
||||
|
||||
if (of_property_read_u32(vgic_node, "#redistributor-regions", &gicv_idx))
|
||||
gicv_idx = 1;
|
||||
|
||||
gicv_idx += 3; /* Also skip GICD, GICC, GICH */
|
||||
if (of_address_to_resource(vgic_node, gicv_idx, &vcpu_res)) {
|
||||
if (!vcpu_res->start) {
|
||||
kvm_info("GICv3: no GICV resource entry\n");
|
||||
vgic->vcpu_base = 0;
|
||||
} else if (!PAGE_ALIGNED(vcpu_res.start)) {
|
||||
} else if (!PAGE_ALIGNED(vcpu_res->start)) {
|
||||
pr_warn("GICV physical address 0x%llx not page aligned\n",
|
||||
(unsigned long long)vcpu_res.start);
|
||||
(unsigned long long)vcpu_res->start);
|
||||
vgic->vcpu_base = 0;
|
||||
} else if (!PAGE_ALIGNED(resource_size(&vcpu_res))) {
|
||||
} else if (!PAGE_ALIGNED(resource_size(vcpu_res))) {
|
||||
pr_warn("GICV size 0x%llx not a multiple of page size 0x%lx\n",
|
||||
(unsigned long long)resource_size(&vcpu_res),
|
||||
(unsigned long long)resource_size(vcpu_res),
|
||||
PAGE_SIZE);
|
||||
vgic->vcpu_base = 0;
|
||||
} else {
|
||||
vgic->vcpu_base = vcpu_res.start;
|
||||
vgic->vcpu_base = vcpu_res->start;
|
||||
vgic->can_emulate_gicv2 = true;
|
||||
kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
|
@ -286,15 +273,13 @@ int vgic_v3_probe(struct device_node *vgic_node,
|
|||
vgic->type = VGIC_V3;
|
||||
vgic->max_gic_vcpus = VGIC_V3_MAX_CPUS;
|
||||
|
||||
kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
|
||||
vcpu_res.start, vgic->maint_irq);
|
||||
kvm_info("GICV base=0x%llx, IRQ=%d\n",
|
||||
vgic->vcpu_base, vgic->maint_irq);
|
||||
|
||||
on_each_cpu(vgic_cpu_init_lrs, vgic, 1);
|
||||
|
||||
*ops = &vgic_v3_ops;
|
||||
*params = vgic;
|
||||
|
||||
out:
|
||||
of_node_put(vgic_node);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
#include <linux/kvm_host.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
|
@ -33,6 +31,7 @@
|
|||
#include <trace/events/kvm.h>
|
||||
#include <asm/kvm.h>
|
||||
#include <kvm/iodev.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
|
@ -2389,33 +2388,38 @@ static struct notifier_block vgic_cpu_nb = {
|
|||
.notifier_call = vgic_cpu_notify,
|
||||
};
|
||||
|
||||
static const struct of_device_id vgic_ids[] = {
|
||||
{ .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, },
|
||||
{ .compatible = "arm,cortex-a7-gic", .data = vgic_v2_probe, },
|
||||
{ .compatible = "arm,gic-400", .data = vgic_v2_probe, },
|
||||
{ .compatible = "arm,gic-v3", .data = vgic_v3_probe, },
|
||||
{},
|
||||
};
|
||||
static int kvm_vgic_probe(void)
|
||||
{
|
||||
const struct gic_kvm_info *gic_kvm_info;
|
||||
int ret;
|
||||
|
||||
gic_kvm_info = gic_get_kvm_info();
|
||||
if (!gic_kvm_info)
|
||||
return -ENODEV;
|
||||
|
||||
switch (gic_kvm_info->type) {
|
||||
case GIC_V2:
|
||||
ret = vgic_v2_probe(gic_kvm_info, &vgic_ops, &vgic);
|
||||
break;
|
||||
case GIC_V3:
|
||||
ret = vgic_v3_probe(gic_kvm_info, &vgic_ops, &vgic);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_vgic_hyp_init(void)
|
||||
{
|
||||
const struct of_device_id *matched_id;
|
||||
const int (*vgic_probe)(struct device_node *,const struct vgic_ops **,
|
||||
const struct vgic_params **);
|
||||
struct device_node *vgic_node;
|
||||
int ret;
|
||||
|
||||
vgic_node = of_find_matching_node_and_match(NULL,
|
||||
vgic_ids, &matched_id);
|
||||
if (!vgic_node) {
|
||||
kvm_err("error: no compatible GIC node found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vgic_probe = matched_id->data;
|
||||
ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
|
||||
if (ret)
|
||||
ret = kvm_vgic_probe();
|
||||
if (ret) {
|
||||
kvm_err("error: KVM vGIC probing failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler,
|
||||
"vgic", kvm_get_running_vcpus());
|
||||
|
|
|
@ -408,15 +408,17 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
|
|||
*/
|
||||
fdput(f);
|
||||
#ifdef CONFIG_HAVE_KVM_IRQ_BYPASS
|
||||
irqfd->consumer.token = (void *)irqfd->eventfd;
|
||||
irqfd->consumer.add_producer = kvm_arch_irq_bypass_add_producer;
|
||||
irqfd->consumer.del_producer = kvm_arch_irq_bypass_del_producer;
|
||||
irqfd->consumer.stop = kvm_arch_irq_bypass_stop;
|
||||
irqfd->consumer.start = kvm_arch_irq_bypass_start;
|
||||
ret = irq_bypass_register_consumer(&irqfd->consumer);
|
||||
if (ret)
|
||||
pr_info("irq bypass consumer (token %p) registration fails: %d\n",
|
||||
if (kvm_arch_has_irq_bypass()) {
|
||||
irqfd->consumer.token = (void *)irqfd->eventfd;
|
||||
irqfd->consumer.add_producer = kvm_arch_irq_bypass_add_producer;
|
||||
irqfd->consumer.del_producer = kvm_arch_irq_bypass_del_producer;
|
||||
irqfd->consumer.stop = kvm_arch_irq_bypass_stop;
|
||||
irqfd->consumer.start = kvm_arch_irq_bypass_start;
|
||||
ret = irq_bypass_register_consumer(&irqfd->consumer);
|
||||
if (ret)
|
||||
pr_info("irq bypass consumer (token %p) registration fails: %d\n",
|
||||
irqfd->consumer.token, ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2028,6 +2028,8 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
if (kvm_vcpu_check_block(vcpu) < 0) {
|
||||
++vcpu->stat.halt_successful_poll;
|
||||
if (!vcpu_valid_wakeup(vcpu))
|
||||
++vcpu->stat.halt_poll_invalid;
|
||||
goto out;
|
||||
}
|
||||
cur = ktime_get();
|
||||
|
@ -2053,7 +2055,9 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
|
|||
out:
|
||||
block_ns = ktime_to_ns(cur) - ktime_to_ns(start);
|
||||
|
||||
if (halt_poll_ns) {
|
||||
if (!vcpu_valid_wakeup(vcpu))
|
||||
shrink_halt_poll_ns(vcpu);
|
||||
else if (halt_poll_ns) {
|
||||
if (block_ns <= vcpu->halt_poll_ns)
|
||||
;
|
||||
/* we had a long block, shrink polling */
|
||||
|
@ -2066,18 +2070,14 @@ out:
|
|||
} else
|
||||
vcpu->halt_poll_ns = 0;
|
||||
|
||||
trace_kvm_vcpu_wakeup(block_ns, waited);
|
||||
trace_kvm_vcpu_wakeup(block_ns, waited, vcpu_valid_wakeup(vcpu));
|
||||
kvm_arch_vcpu_block_finish(vcpu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_vcpu_block);
|
||||
|
||||
#ifndef CONFIG_S390
|
||||
/*
|
||||
* Kick a sleeping VCPU, or a guest VCPU in guest mode, into host kernel mode.
|
||||
*/
|
||||
void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
|
||||
void kvm_vcpu_wake_up(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int me;
|
||||
int cpu = vcpu->cpu;
|
||||
struct swait_queue_head *wqp;
|
||||
|
||||
wqp = kvm_arch_vcpu_wq(vcpu);
|
||||
|
@ -2086,6 +2086,18 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
|
|||
++vcpu->stat.halt_wakeup;
|
||||
}
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_vcpu_wake_up);
|
||||
|
||||
/*
|
||||
* Kick a sleeping VCPU, or a guest VCPU in guest mode, into host kernel mode.
|
||||
*/
|
||||
void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int me;
|
||||
int cpu = vcpu->cpu;
|
||||
|
||||
kvm_vcpu_wake_up(vcpu);
|
||||
me = get_cpu();
|
||||
if (cpu != me && (unsigned)cpu < nr_cpu_ids && cpu_online(cpu))
|
||||
if (kvm_arch_vcpu_should_kick(vcpu))
|
||||
|
@ -2272,7 +2284,7 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)
|
|||
int r;
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
if (id >= KVM_MAX_VCPUS)
|
||||
if (id >= KVM_MAX_VCPU_ID)
|
||||
return -EINVAL;
|
||||
|
||||
vcpu = kvm_arch_vcpu_create(kvm, id);
|
||||
|
@ -2746,6 +2758,8 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
|
|||
case KVM_CAP_MULTI_ADDRESS_SPACE:
|
||||
return KVM_ADDRESS_SPACE_NUM;
|
||||
#endif
|
||||
case KVM_CAP_MAX_VCPU_ID:
|
||||
return KVM_MAX_VCPU_ID;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -89,6 +89,9 @@ int irq_bypass_register_producer(struct irq_bypass_producer *producer)
|
|||
struct irq_bypass_producer *tmp;
|
||||
struct irq_bypass_consumer *consumer;
|
||||
|
||||
if (!producer->token)
|
||||
return -EINVAL;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
|
@ -136,6 +139,9 @@ void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
|
|||
struct irq_bypass_producer *tmp;
|
||||
struct irq_bypass_consumer *consumer;
|
||||
|
||||
if (!producer->token)
|
||||
return;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
|
@ -177,7 +183,8 @@ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
|
|||
struct irq_bypass_consumer *tmp;
|
||||
struct irq_bypass_producer *producer;
|
||||
|
||||
if (!consumer->add_producer || !consumer->del_producer)
|
||||
if (!consumer->token ||
|
||||
!consumer->add_producer || !consumer->del_producer)
|
||||
return -EINVAL;
|
||||
|
||||
might_sleep();
|
||||
|
@ -227,6 +234,9 @@ void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
|
|||
struct irq_bypass_consumer *tmp;
|
||||
struct irq_bypass_producer *producer;
|
||||
|
||||
if (!consumer->token)
|
||||
return;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
|
|
Loading…
Reference in New Issue