mirror of
https://github.com/suyu-emu/horinux.git
synced 2026-09-21 14:15:21 -07:00
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM updates from Paolo Bonzini:
"4.11 is going to be a relatively large release for KVM, with a little
over 200 commits and noteworthy changes for most architectures.
ARM:
- GICv3 save/restore
- cache flushing fixes
- working MSI injection for GICv3 ITS
- physical timer emulation
MIPS:
- various improvements under the hood
- support for SMP guests
- a large rewrite of MMU emulation. KVM MIPS can now use MMU
notifiers to support copy-on-write, KSM, idle page tracking,
swapping, ballooning and everything else. KVM_CAP_READONLY_MEM is
also supported, so that writes to some memory regions can be
treated as MMIO. The new MMU also paves the way for hardware
virtualization support.
PPC:
- support for POWER9 using the radix-tree MMU for host and guest
- resizable hashed page table
- bugfixes.
s390:
- expose more features to the guest
- more SIMD extensions
- instruction execution protection
- ESOP2
x86:
- improved hashing in the MMU
- faster PageLRU tracking for Intel CPUs without EPT A/D bits
- some refactoring of nested VMX entry/exit code, preparing for live
migration support of nested hypervisors
- expose yet another AVX512 CPUID bit
- host-to-guest PTP support
- refactoring of interrupt injection, with some optimizations thrown
in and some duct tape removed.
- remove lazy FPU handling
- optimizations of user-mode exits
- optimizations of vcpu_is_preempted() for KVM guests
generic:
- alternative signaling mechanism that doesn't pound on
tsk->sighand->siglock"
* tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (195 commits)
x86/kvm: Provide optimized version of vcpu_is_preempted() for x86-64
x86/paravirt: Change vcp_is_preempted() arg type to long
KVM: VMX: use correct vmcs_read/write for guest segment selector/base
x86/kvm/vmx: Defer TR reload after VM exit
x86/asm/64: Drop __cacheline_aligned from struct x86_hw_tss
x86/kvm/vmx: Simplify segment_base()
x86/kvm/vmx: Get rid of segment_base() on 64-bit kernels
x86/kvm/vmx: Don't fetch the TSS base from the GDT
x86/asm: Define the kernel TSS limit in a macro
kvm: fix page struct leak in handle_vmon
KVM: PPC: Book3S HV: Disable HPT resizing on POWER9 for now
KVM: Return an error code only as a constant in kvm_get_dirty_log()
KVM: Return an error code only as a constant in kvm_get_dirty_log_protect()
KVM: Return directly after a failed copy_from_user() in kvm_vm_compat_ioctl()
KVM: x86: remove code for lazy FPU handling
KVM: race-free exit from KVM_RUN without POSIX signals
KVM: PPC: Book3S HV: Turn "KVM guest htab" message into a debug message
KVM: PPC: Book3S PR: Ratelimit copy data failure error messages
KVM: Support vCPU-based gfn->hva cache
KVM: use separate generations for each address space
...
This commit is contained in:
@@ -2061,6 +2061,8 @@ registers, find a list below:
|
||||
MIPS | KVM_REG_MIPS_LO | 64
|
||||
MIPS | KVM_REG_MIPS_PC | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_INDEX | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_ENTRYLO0 | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_ENTRYLO1 | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_CONTEXT | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_USERLOCAL | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_PAGEMASK | 32
|
||||
@@ -2071,9 +2073,11 @@ registers, find a list below:
|
||||
MIPS | KVM_REG_MIPS_CP0_ENTRYHI | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_COMPARE | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_STATUS | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_INTCTL | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_CAUSE | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_EPC | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_PRID | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_EBASE | 64
|
||||
MIPS | KVM_REG_MIPS_CP0_CONFIG | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_CONFIG1 | 32
|
||||
MIPS | KVM_REG_MIPS_CP0_CONFIG2 | 32
|
||||
@@ -2148,6 +2152,12 @@ patterns depending on whether they're 32-bit or 64-bit registers:
|
||||
0x7020 0000 0001 00 <reg:5> <sel:3> (32-bit)
|
||||
0x7030 0000 0001 00 <reg:5> <sel:3> (64-bit)
|
||||
|
||||
Note: KVM_REG_MIPS_CP0_ENTRYLO0 and KVM_REG_MIPS_CP0_ENTRYLO1 are the MIPS64
|
||||
versions of the EntryLo registers regardless of the word size of the host
|
||||
hardware, host kernel, guest, and whether XPA is present in the guest, i.e.
|
||||
with the RI and XI bits (if they exist) in bits 63 and 62 respectively, and
|
||||
the PFNX field starting at bit 30.
|
||||
|
||||
MIPS KVM control registers (see above) have the following id bit patterns:
|
||||
0x7030 0000 0002 <reg:16>
|
||||
|
||||
@@ -2443,18 +2453,20 @@ are, it will do nothing and return an EBUSY error.
|
||||
The parameter is a pointer to a 32-bit unsigned integer variable
|
||||
containing the order (log base 2) of the desired size of the hash
|
||||
table, which must be between 18 and 46. On successful return from the
|
||||
ioctl, it will have been updated with the order of the hash table that
|
||||
was allocated.
|
||||
ioctl, the value will not be changed by the kernel.
|
||||
|
||||
If no hash table has been allocated when any vcpu is asked to run
|
||||
(with the KVM_RUN ioctl), the host kernel will allocate a
|
||||
default-sized hash table (16 MB).
|
||||
|
||||
If this ioctl is called when a hash table has already been allocated,
|
||||
the kernel will clear out the existing hash table (zero all HPTEs) and
|
||||
return the hash table order in the parameter. (If the guest is using
|
||||
the virtualized real-mode area (VRMA) facility, the kernel will
|
||||
re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.)
|
||||
with a different order from the existing hash table, the existing hash
|
||||
table will be freed and a new one allocated. If this is ioctl is
|
||||
called when a hash table has already been allocated of the same order
|
||||
as specified, the kernel will clear out the existing hash table (zero
|
||||
all HPTEs). In either case, if the guest is using the virtualized
|
||||
real-mode area (VRMA) facility, the kernel will re-create the VMRA
|
||||
HPTEs on the next KVM_RUN of any vcpu.
|
||||
|
||||
4.77 KVM_S390_INTERRUPT
|
||||
|
||||
@@ -3177,7 +3189,7 @@ of IOMMU pages.
|
||||
|
||||
The rest of functionality is identical to KVM_CREATE_SPAPR_TCE.
|
||||
|
||||
4.98 KVM_REINJECT_CONTROL
|
||||
4.99 KVM_REINJECT_CONTROL
|
||||
|
||||
Capability: KVM_CAP_REINJECT_CONTROL
|
||||
Architectures: x86
|
||||
@@ -3201,7 +3213,7 @@ struct kvm_reinject_control {
|
||||
pit_reinject = 0 (!reinject mode) is recommended, unless running an old
|
||||
operating system that uses the PIT for timing (e.g. Linux 2.4.x).
|
||||
|
||||
4.99 KVM_PPC_CONFIGURE_V3_MMU
|
||||
4.100 KVM_PPC_CONFIGURE_V3_MMU
|
||||
|
||||
Capability: KVM_CAP_PPC_RADIX_MMU or KVM_CAP_PPC_HASH_MMU_V3
|
||||
Architectures: ppc
|
||||
@@ -3232,7 +3244,7 @@ process table, which is in the guest's space. This field is formatted
|
||||
as the second doubleword of the partition table entry, as defined in
|
||||
the Power ISA V3.00, Book III section 5.7.6.1.
|
||||
|
||||
4.100 KVM_PPC_GET_RMMU_INFO
|
||||
4.101 KVM_PPC_GET_RMMU_INFO
|
||||
|
||||
Capability: KVM_CAP_PPC_RADIX_MMU
|
||||
Architectures: ppc
|
||||
@@ -3266,6 +3278,101 @@ The ap_encodings gives the supported page sizes and their AP field
|
||||
encodings, encoded with the AP value in the top 3 bits and the log
|
||||
base 2 of the page size in the bottom 6 bits.
|
||||
|
||||
4.102 KVM_PPC_RESIZE_HPT_PREPARE
|
||||
|
||||
Capability: KVM_CAP_SPAPR_RESIZE_HPT
|
||||
Architectures: powerpc
|
||||
Type: vm ioctl
|
||||
Parameters: struct kvm_ppc_resize_hpt (in)
|
||||
Returns: 0 on successful completion,
|
||||
>0 if a new HPT is being prepared, the value is an estimated
|
||||
number of milliseconds until preparation is complete
|
||||
-EFAULT if struct kvm_reinject_control cannot be read,
|
||||
-EINVAL if the supplied shift or flags are invalid
|
||||
-ENOMEM if unable to allocate the new HPT
|
||||
-ENOSPC if there was a hash collision when moving existing
|
||||
HPT entries to the new HPT
|
||||
-EIO on other error conditions
|
||||
|
||||
Used to implement the PAPR extension for runtime resizing of a guest's
|
||||
Hashed Page Table (HPT). Specifically this starts, stops or monitors
|
||||
the preparation of a new potential HPT for the guest, essentially
|
||||
implementing the H_RESIZE_HPT_PREPARE hypercall.
|
||||
|
||||
If called with shift > 0 when there is no pending HPT for the guest,
|
||||
this begins preparation of a new pending HPT of size 2^(shift) bytes.
|
||||
It then returns a positive integer with the estimated number of
|
||||
milliseconds until preparation is complete.
|
||||
|
||||
If called when there is a pending HPT whose size does not match that
|
||||
requested in the parameters, discards the existing pending HPT and
|
||||
creates a new one as above.
|
||||
|
||||
If called when there is a pending HPT of the size requested, will:
|
||||
* If preparation of the pending HPT is already complete, return 0
|
||||
* If preparation of the pending HPT has failed, return an error
|
||||
code, then discard the pending HPT.
|
||||
* If preparation of the pending HPT is still in progress, return an
|
||||
estimated number of milliseconds until preparation is complete.
|
||||
|
||||
If called with shift == 0, discards any currently pending HPT and
|
||||
returns 0 (i.e. cancels any in-progress preparation).
|
||||
|
||||
flags is reserved for future expansion, currently setting any bits in
|
||||
flags will result in an -EINVAL.
|
||||
|
||||
Normally this will be called repeatedly with the same parameters until
|
||||
it returns <= 0. The first call will initiate preparation, subsequent
|
||||
ones will monitor preparation until it completes or fails.
|
||||
|
||||
struct kvm_ppc_resize_hpt {
|
||||
__u64 flags;
|
||||
__u32 shift;
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
4.103 KVM_PPC_RESIZE_HPT_COMMIT
|
||||
|
||||
Capability: KVM_CAP_SPAPR_RESIZE_HPT
|
||||
Architectures: powerpc
|
||||
Type: vm ioctl
|
||||
Parameters: struct kvm_ppc_resize_hpt (in)
|
||||
Returns: 0 on successful completion,
|
||||
-EFAULT if struct kvm_reinject_control cannot be read,
|
||||
-EINVAL if the supplied shift or flags are invalid
|
||||
-ENXIO is there is no pending HPT, or the pending HPT doesn't
|
||||
have the requested size
|
||||
-EBUSY if the pending HPT is not fully prepared
|
||||
-ENOSPC if there was a hash collision when moving existing
|
||||
HPT entries to the new HPT
|
||||
-EIO on other error conditions
|
||||
|
||||
Used to implement the PAPR extension for runtime resizing of a guest's
|
||||
Hashed Page Table (HPT). Specifically this requests that the guest be
|
||||
transferred to working with the new HPT, essentially implementing the
|
||||
H_RESIZE_HPT_COMMIT hypercall.
|
||||
|
||||
This should only be called after KVM_PPC_RESIZE_HPT_PREPARE has
|
||||
returned 0 with the same parameters. In other cases
|
||||
KVM_PPC_RESIZE_HPT_COMMIT will return an error (usually -ENXIO or
|
||||
-EBUSY, though others may be possible if the preparation was started,
|
||||
but failed).
|
||||
|
||||
This will have undefined effects on the guest if it has not already
|
||||
placed itself in a quiescent state where no vcpu will make MMU enabled
|
||||
memory accesses.
|
||||
|
||||
On succsful completion, the pending HPT will become the guest's active
|
||||
HPT and the previous HPT will be discarded.
|
||||
|
||||
On failure, the guest will still be operating on its previous HPT.
|
||||
|
||||
struct kvm_ppc_resize_hpt {
|
||||
__u64 flags;
|
||||
__u32 shift;
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
5. The kvm_run structure
|
||||
------------------------
|
||||
|
||||
@@ -3282,7 +3389,18 @@ struct kvm_run {
|
||||
Request that KVM_RUN return when it becomes possible to inject external
|
||||
interrupts into the guest. Useful in conjunction with KVM_INTERRUPT.
|
||||
|
||||
__u8 padding1[7];
|
||||
__u8 immediate_exit;
|
||||
|
||||
This field is polled once when KVM_RUN starts; if non-zero, KVM_RUN
|
||||
exits immediately, returning -EINTR. In the common scenario where a
|
||||
signal is used to "kick" a VCPU out of KVM_RUN, this field can be used
|
||||
to avoid usage of KVM_SET_SIGNAL_MASK, which has worse scalability.
|
||||
Rather than blocking the signal outside KVM_RUN, userspace can set up
|
||||
a signal handler that sets run->immediate_exit to a non-zero value.
|
||||
|
||||
This field is ignored if KVM_CAP_IMMEDIATE_EXIT is not available.
|
||||
|
||||
__u8 padding1[6];
|
||||
|
||||
/* out */
|
||||
__u32 exit_reason;
|
||||
|
||||
@@ -118,7 +118,7 @@ Groups:
|
||||
-EBUSY: One or more VCPUs are running
|
||||
|
||||
|
||||
KVM_DEV_ARM_VGIC_CPU_SYSREGS
|
||||
KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS
|
||||
Attributes:
|
||||
The attr field of kvm_device_attr encodes two values:
|
||||
bits: | 63 .... 32 | 31 .... 16 | 15 .... 0 |
|
||||
@@ -139,13 +139,15 @@ Groups:
|
||||
All system regs accessed through this API are (rw, 64-bit) and
|
||||
kvm_device_attr.addr points to a __u64 value.
|
||||
|
||||
KVM_DEV_ARM_VGIC_CPU_SYSREGS accesses the CPU interface registers for the
|
||||
KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS accesses the CPU interface registers for the
|
||||
CPU specified by the mpidr field.
|
||||
|
||||
CPU interface registers access is not implemented for AArch32 mode.
|
||||
Error -ENXIO is returned when accessed in AArch32 mode.
|
||||
Errors:
|
||||
-ENXIO: Getting or setting this register is not yet supported
|
||||
-EBUSY: VCPU is running
|
||||
-EINVAL: Invalid mpidr supplied
|
||||
-EINVAL: Invalid mpidr or register value supplied
|
||||
|
||||
|
||||
KVM_DEV_ARM_VGIC_GRP_NR_IRQS
|
||||
@@ -204,3 +206,6 @@ Groups:
|
||||
architecture defined MPIDR, and the field is encoded as follows:
|
||||
| 63 .... 56 | 55 .... 48 | 47 .... 40 | 39 .... 32 |
|
||||
| Aff3 | Aff2 | Aff1 | Aff0 |
|
||||
Errors:
|
||||
-EINVAL: vINTID is not multiple of 32 or
|
||||
info field is not VGIC_LEVEL_INFO_LINE_LEVEL
|
||||
|
||||
@@ -81,3 +81,38 @@ the vcpu to sleep until occurrence of an appropriate event. Another vcpu of the
|
||||
same guest can wakeup the sleeping vcpu by issuing KVM_HC_KICK_CPU hypercall,
|
||||
specifying APIC ID (a1) of the vcpu to be woken up. An additional argument (a0)
|
||||
is used in the hypercall for future use.
|
||||
|
||||
|
||||
6. KVM_HC_CLOCK_PAIRING
|
||||
------------------------
|
||||
Architecture: x86
|
||||
Status: active
|
||||
Purpose: Hypercall used to synchronize host and guest clocks.
|
||||
Usage:
|
||||
|
||||
a0: guest physical address where host copies
|
||||
"struct kvm_clock_offset" structure.
|
||||
|
||||
a1: clock_type, ATM only KVM_CLOCK_PAIRING_WALLCLOCK (0)
|
||||
is supported (corresponding to the host's CLOCK_REALTIME clock).
|
||||
|
||||
struct kvm_clock_pairing {
|
||||
__s64 sec;
|
||||
__s64 nsec;
|
||||
__u64 tsc;
|
||||
__u32 flags;
|
||||
__u32 pad[9];
|
||||
};
|
||||
|
||||
Where:
|
||||
* sec: seconds from clock_type clock.
|
||||
* nsec: nanoseconds from clock_type clock.
|
||||
* tsc: guest TSC value used to calculate sec/nsec pair
|
||||
* flags: flags, unused (0) at the moment.
|
||||
|
||||
The hypercall lets a guest compute a precise timestamp across
|
||||
host and guest. The guest can use the returned TSC value to
|
||||
compute the CLOCK_REALTIME for its clock, at the same instant.
|
||||
|
||||
Returns KVM_EOPNOTSUPP if the host does not use TSC clocksource,
|
||||
or if clock type is different than KVM_CLOCK_PAIRING_WALLCLOCK.
|
||||
|
||||
@@ -26,9 +26,16 @@ sections.
|
||||
Fast page fault:
|
||||
|
||||
Fast page fault is the fast path which fixes the guest page fault out of
|
||||
the mmu-lock on x86. Currently, the page fault can be fast only if the
|
||||
shadow page table is present and it is caused by write-protect, that means
|
||||
we just need change the W bit of the spte.
|
||||
the mmu-lock on x86. Currently, the page fault can be fast in one of the
|
||||
following two cases:
|
||||
|
||||
1. Access Tracking: The SPTE is not present, but it is marked for access
|
||||
tracking i.e. the SPTE_SPECIAL_MASK is set. That means we need to
|
||||
restore the saved R/X bits. This is described in more detail later below.
|
||||
|
||||
2. Write-Protection: The SPTE is present and the fault is
|
||||
caused by write-protect. That means we just need to change the W bit of the
|
||||
spte.
|
||||
|
||||
What we use to avoid all the race is the SPTE_HOST_WRITEABLE bit and
|
||||
SPTE_MMU_WRITEABLE bit on the spte:
|
||||
@@ -38,7 +45,8 @@ SPTE_MMU_WRITEABLE bit on the spte:
|
||||
page write-protection.
|
||||
|
||||
On fast page fault path, we will use cmpxchg to atomically set the spte W
|
||||
bit if spte.SPTE_HOST_WRITEABLE = 1 and spte.SPTE_WRITE_PROTECT = 1, this
|
||||
bit if spte.SPTE_HOST_WRITEABLE = 1 and spte.SPTE_WRITE_PROTECT = 1, or
|
||||
restore the saved R/X bits if VMX_EPT_TRACK_ACCESS mask is set, or both. This
|
||||
is safe because whenever changing these bits can be detected by cmpxchg.
|
||||
|
||||
But we need carefully check these cases:
|
||||
@@ -142,6 +150,21 @@ Since the spte is "volatile" if it can be updated out of mmu-lock, we always
|
||||
atomically update the spte, the race caused by fast page fault can be avoided,
|
||||
See the comments in spte_has_volatile_bits() and mmu_spte_update().
|
||||
|
||||
Lockless Access Tracking:
|
||||
|
||||
This is used for Intel CPUs that are using EPT but do not support the EPT A/D
|
||||
bits. In this case, when the KVM MMU notifier is called to track accesses to a
|
||||
page (via kvm_mmu_notifier_clear_flush_young), it marks the PTE as not-present
|
||||
by clearing the RWX bits in the PTE and storing the original R & X bits in
|
||||
some unused/ignored bits. In addition, the SPTE_SPECIAL_MASK is also set on the
|
||||
PTE (using the ignored bit 62). When the VM tries to access the page later on,
|
||||
a fault is generated and the fast page fault mechanism described above is used
|
||||
to atomically restore the PTE to a Present state. The W bit is not saved when
|
||||
the PTE is marked for access tracking and during restoration to the Present
|
||||
state, the W bit is set depending on whether or not it was a write access. If
|
||||
it wasn't, then the W bit will remain clear until a write access happens, at
|
||||
which time it will be set using the Dirty tracking mechanism described above.
|
||||
|
||||
3. Reference
|
||||
------------
|
||||
|
||||
|
||||
@@ -60,9 +60,6 @@ struct kvm_arch {
|
||||
/* The last vcpu id that ran on each physical CPU */
|
||||
int __percpu *last_vcpu_ran;
|
||||
|
||||
/* Timer */
|
||||
struct arch_timer_kvm timer;
|
||||
|
||||
/*
|
||||
* Anything that is not used directly from assembly code goes
|
||||
* here.
|
||||
|
||||
@@ -129,8 +129,7 @@ static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
|
||||
|
||||
static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu,
|
||||
kvm_pfn_t pfn,
|
||||
unsigned long size,
|
||||
bool ipa_uncached)
|
||||
unsigned long size)
|
||||
{
|
||||
/*
|
||||
* If we are going to insert an instruction page and the icache is
|
||||
@@ -150,18 +149,12 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu,
|
||||
* and iterate over the range.
|
||||
*/
|
||||
|
||||
bool need_flush = !vcpu_has_cache_enabled(vcpu) || ipa_uncached;
|
||||
|
||||
VM_BUG_ON(size & ~PAGE_MASK);
|
||||
|
||||
if (!need_flush && !icache_is_pipt())
|
||||
goto vipt_cache;
|
||||
|
||||
while (size) {
|
||||
void *va = kmap_atomic_pfn(pfn);
|
||||
|
||||
if (need_flush)
|
||||
kvm_flush_dcache_to_poc(va, PAGE_SIZE);
|
||||
kvm_flush_dcache_to_poc(va, PAGE_SIZE);
|
||||
|
||||
if (icache_is_pipt())
|
||||
__cpuc_coherent_user_range((unsigned long)va,
|
||||
@@ -173,7 +166,6 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu,
|
||||
kunmap_atomic(va);
|
||||
}
|
||||
|
||||
vipt_cache:
|
||||
if (!icache_is_pipt() && !icache_is_vivt_asid_tagged()) {
|
||||
/* any kind of VIPT cache */
|
||||
__flush_icache_all();
|
||||
|
||||
@@ -181,10 +181,23 @@ struct kvm_arch_memory_slot {
|
||||
#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2
|
||||
#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32
|
||||
#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT 32
|
||||
#define KVM_DEV_ARM_VGIC_V3_MPIDR_MASK \
|
||||
(0xffffffffULL << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0
|
||||
#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK (0xffff)
|
||||
#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3
|
||||
#define KVM_DEV_ARM_VGIC_GRP_CTRL 4
|
||||
#define KVM_DEV_ARM_VGIC_GRP_REDIST_REGS 5
|
||||
#define KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 6
|
||||
#define KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO 7
|
||||
#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT 10
|
||||
#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK \
|
||||
(0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK 0x3ff
|
||||
#define VGIC_LEVEL_INFO_LINE_LEVEL 0
|
||||
|
||||
#define KVM_DEV_ARM_VGIC_CTRL_INIT 0
|
||||
|
||||
/* KVM_IRQ_LINE irq field index values */
|
||||
|
||||
@@ -7,7 +7,7 @@ ifeq ($(plus_virt),+virt)
|
||||
plus_virt_def := -DREQUIRES_VIRT=1
|
||||
endif
|
||||
|
||||
ccflags-y += -Iarch/arm/kvm
|
||||
ccflags-y += -Iarch/arm/kvm -Ivirt/kvm/arm/vgic
|
||||
CFLAGS_arm.o := -I. $(plus_virt_def)
|
||||
CFLAGS_mmu.o := -I.
|
||||
|
||||
@@ -20,7 +20,7 @@ kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o $(KVM)/vf
|
||||
obj-$(CONFIG_KVM_ARM_HOST) += hyp/
|
||||
obj-y += kvm-arm.o init.o interrupts.o
|
||||
obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
|
||||
obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o
|
||||
obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o vgic-v3-coproc.o
|
||||
obj-y += $(KVM)/arm/aarch32.o
|
||||
|
||||
obj-y += $(KVM)/arm/vgic/vgic.o
|
||||
@@ -33,5 +33,6 @@ obj-y += $(KVM)/arm/vgic/vgic-mmio-v2.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-mmio-v3.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-kvm-device.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-its.o
|
||||
obj-y += $(KVM)/arm/vgic/vgic-debug.o
|
||||
obj-y += $(KVM)/irqchip.o
|
||||
obj-y += $(KVM)/arm/arch_timer.o
|
||||
|
||||
+6
-2
@@ -135,7 +135,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||
goto out_free_stage2_pgd;
|
||||
|
||||
kvm_vgic_early_init(kvm);
|
||||
kvm_timer_init(kvm);
|
||||
|
||||
/* Mark the initial VMID generation invalid */
|
||||
kvm->arch.vmid_gen = 0;
|
||||
@@ -207,6 +206,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_ARM_PSCI_0_2:
|
||||
case KVM_CAP_READONLY_MEM:
|
||||
case KVM_CAP_MP_STATE:
|
||||
case KVM_CAP_IMMEDIATE_EXIT:
|
||||
r = 1;
|
||||
break;
|
||||
case KVM_CAP_COALESCED_MMIO:
|
||||
@@ -301,7 +301,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
||||
|
||||
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return kvm_timer_should_fire(vcpu);
|
||||
return kvm_timer_should_fire(vcpu_vtimer(vcpu)) ||
|
||||
kvm_timer_should_fire(vcpu_ptimer(vcpu));
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu)
|
||||
@@ -604,6 +605,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (run->immediate_exit)
|
||||
return -EINTR;
|
||||
|
||||
if (vcpu->sigset_active)
|
||||
sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
|
||||
|
||||
|
||||
+4
-16
@@ -1232,9 +1232,9 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
|
||||
}
|
||||
|
||||
static void coherent_cache_guest_page(struct kvm_vcpu *vcpu, kvm_pfn_t pfn,
|
||||
unsigned long size, bool uncached)
|
||||
unsigned long size)
|
||||
{
|
||||
__coherent_cache_guest_page(vcpu, pfn, size, uncached);
|
||||
__coherent_cache_guest_page(vcpu, pfn, size);
|
||||
}
|
||||
|
||||
static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
@@ -1250,7 +1250,6 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
struct vm_area_struct *vma;
|
||||
kvm_pfn_t pfn;
|
||||
pgprot_t mem_type = PAGE_S2;
|
||||
bool fault_ipa_uncached;
|
||||
bool logging_active = memslot_is_logging(memslot);
|
||||
unsigned long flags = 0;
|
||||
|
||||
@@ -1337,8 +1336,6 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
if (!hugetlb && !force_pte)
|
||||
hugetlb = transparent_hugepage_adjust(&pfn, &fault_ipa);
|
||||
|
||||
fault_ipa_uncached = memslot->flags & KVM_MEMSLOT_INCOHERENT;
|
||||
|
||||
if (hugetlb) {
|
||||
pmd_t new_pmd = pfn_pmd(pfn, mem_type);
|
||||
new_pmd = pmd_mkhuge(new_pmd);
|
||||
@@ -1346,7 +1343,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
new_pmd = kvm_s2pmd_mkwrite(new_pmd);
|
||||
kvm_set_pfn_dirty(pfn);
|
||||
}
|
||||
coherent_cache_guest_page(vcpu, pfn, PMD_SIZE, fault_ipa_uncached);
|
||||
coherent_cache_guest_page(vcpu, pfn, PMD_SIZE);
|
||||
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
|
||||
} else {
|
||||
pte_t new_pte = pfn_pte(pfn, mem_type);
|
||||
@@ -1356,7 +1353,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
kvm_set_pfn_dirty(pfn);
|
||||
mark_page_dirty(kvm, gfn);
|
||||
}
|
||||
coherent_cache_guest_page(vcpu, pfn, PAGE_SIZE, fault_ipa_uncached);
|
||||
coherent_cache_guest_page(vcpu, pfn, PAGE_SIZE);
|
||||
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, flags);
|
||||
}
|
||||
|
||||
@@ -1879,15 +1876,6 @@ void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
|
||||
int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
|
||||
unsigned long npages)
|
||||
{
|
||||
/*
|
||||
* Readonly memslots are not incoherent with the caches by definition,
|
||||
* but in practice, they are used mostly to emulate ROMs or NOR flashes
|
||||
* that the guest may consider devices and hence map as uncached.
|
||||
* To prevent incoherency issues in these cases, tag all readonly
|
||||
* regions as incoherent.
|
||||
*/
|
||||
if (slot->flags & KVM_MEM_READONLY)
|
||||
slot->flags |= KVM_MEMSLOT_INCOHERENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ static struct kvm_regs cortexa_regs_reset = {
|
||||
.usr_regs.ARM_cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT,
|
||||
};
|
||||
|
||||
static const struct kvm_irq_level cortexa_ptimer_irq = {
|
||||
{ .irq = 30 },
|
||||
.level = 1,
|
||||
};
|
||||
|
||||
static const struct kvm_irq_level cortexa_vtimer_irq = {
|
||||
{ .irq = 27 },
|
||||
.level = 1,
|
||||
@@ -58,6 +63,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_regs *reset_regs;
|
||||
const struct kvm_irq_level *cpu_vtimer_irq;
|
||||
const struct kvm_irq_level *cpu_ptimer_irq;
|
||||
|
||||
switch (vcpu->arch.target) {
|
||||
case KVM_ARM_TARGET_CORTEX_A7:
|
||||
@@ -65,6 +71,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||
reset_regs = &cortexa_regs_reset;
|
||||
vcpu->arch.midr = read_cpuid_id();
|
||||
cpu_vtimer_irq = &cortexa_vtimer_irq;
|
||||
cpu_ptimer_irq = &cortexa_ptimer_irq;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
@@ -77,5 +84,5 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||
kvm_reset_coprocs(vcpu);
|
||||
|
||||
/* Reset arch_timer context */
|
||||
return kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq);
|
||||
return kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq, cpu_ptimer_irq);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* VGIC system registers handling functions for AArch32 mode
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include "vgic.h"
|
||||
|
||||
int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
|
||||
u64 *reg)
|
||||
{
|
||||
/*
|
||||
* TODO: Implement for AArch32
|
||||
*/
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id,
|
||||
u64 *reg)
|
||||
{
|
||||
/*
|
||||
* TODO: Implement for AArch32
|
||||
*/
|
||||
return -ENXIO;
|
||||
}
|
||||
@@ -70,9 +70,6 @@ struct kvm_arch {
|
||||
|
||||
/* Interrupt controller */
|
||||
struct vgic_dist vgic;
|
||||
|
||||
/* Timer */
|
||||
struct arch_timer_kvm timer;
|
||||
};
|
||||
|
||||
#define KVM_NR_MEM_OBJS 40
|
||||
|
||||
@@ -236,13 +236,11 @@ static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
|
||||
|
||||
static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu,
|
||||
kvm_pfn_t pfn,
|
||||
unsigned long size,
|
||||
bool ipa_uncached)
|
||||
unsigned long size)
|
||||
{
|
||||
void *va = page_address(pfn_to_page(pfn));
|
||||
|
||||
if (!vcpu_has_cache_enabled(vcpu) || ipa_uncached)
|
||||
kvm_flush_dcache_to_poc(va, size);
|
||||
kvm_flush_dcache_to_poc(va, size);
|
||||
|
||||
if (!icache_is_aliasing()) { /* PIPT */
|
||||
flush_icache_range((unsigned long)va,
|
||||
|
||||
@@ -201,10 +201,23 @@ struct kvm_arch_memory_slot {
|
||||
#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2
|
||||
#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32
|
||||
#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT 32
|
||||
#define KVM_DEV_ARM_VGIC_V3_MPIDR_MASK \
|
||||
(0xffffffffULL << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0
|
||||
#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK (0xffff)
|
||||
#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3
|
||||
#define KVM_DEV_ARM_VGIC_GRP_CTRL 4
|
||||
#define KVM_DEV_ARM_VGIC_GRP_REDIST_REGS 5
|
||||
#define KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 6
|
||||
#define KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO 7
|
||||
#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT 10
|
||||
#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK \
|
||||
(0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT)
|
||||
#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK 0x3ff
|
||||
#define VGIC_LEVEL_INFO_LINE_LEVEL 0
|
||||
|
||||
#define KVM_DEV_ARM_VGIC_CTRL_INIT 0
|
||||
|
||||
/* Device Control API on vcpu fd */
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Makefile for Kernel-based Virtual Machine module
|
||||
#
|
||||
|
||||
ccflags-y += -Iarch/arm64/kvm
|
||||
ccflags-y += -Iarch/arm64/kvm -Ivirt/kvm/arm/vgic
|
||||
CFLAGS_arm.o := -I.
|
||||
CFLAGS_mmu.o := -I.
|
||||
|
||||
@@ -19,6 +19,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += guest.o debug.o reset.o sys_regs.o sys_regs_generic_v8.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += vgic-sys-reg-v3.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/aarch32.o
|
||||
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic.o
|
||||
@@ -31,6 +32,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v2.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v3.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-kvm-device.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-its.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-debug.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/irqchip.o
|
||||
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer.o
|
||||
kvm-$(CONFIG_KVM_ARM_PMU) += $(KVM)/arm/pmu.o
|
||||
|
||||
@@ -46,6 +46,11 @@ static const struct kvm_regs default_regs_reset32 = {
|
||||
COMPAT_PSR_I_BIT | COMPAT_PSR_F_BIT),
|
||||
};
|
||||
|
||||
static const struct kvm_irq_level default_ptimer_irq = {
|
||||
.irq = 30,
|
||||
.level = 1,
|
||||
};
|
||||
|
||||
static const struct kvm_irq_level default_vtimer_irq = {
|
||||
.irq = 27,
|
||||
.level = 1,
|
||||
@@ -104,6 +109,7 @@ int kvm_arch_dev_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
const struct kvm_irq_level *cpu_vtimer_irq;
|
||||
const struct kvm_irq_level *cpu_ptimer_irq;
|
||||
const struct kvm_regs *cpu_reset;
|
||||
|
||||
switch (vcpu->arch.target) {
|
||||
@@ -117,6 +123,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
|
||||
cpu_vtimer_irq = &default_vtimer_irq;
|
||||
cpu_ptimer_irq = &default_ptimer_irq;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -130,5 +137,5 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
|
||||
kvm_pmu_vcpu_reset(vcpu);
|
||||
|
||||
/* Reset timer */
|
||||
return kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq);
|
||||
return kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq, cpu_ptimer_irq);
|
||||
}
|
||||
|
||||
+81
-11
@@ -820,6 +820,61 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
CRm((0b1100 | (((n) >> 3) & 0x3))), Op2(((n) & 0x7)), \
|
||||
access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), }
|
||||
|
||||
static bool access_cntp_tval(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
|
||||
u64 now = kvm_phys_timer_read();
|
||||
|
||||
if (p->is_write)
|
||||
ptimer->cnt_cval = p->regval + now;
|
||||
else
|
||||
p->regval = ptimer->cnt_cval - now;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool access_cntp_ctl(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
|
||||
|
||||
if (p->is_write) {
|
||||
/* ISTATUS bit is read-only */
|
||||
ptimer->cnt_ctl = p->regval & ~ARCH_TIMER_CTRL_IT_STAT;
|
||||
} else {
|
||||
u64 now = kvm_phys_timer_read();
|
||||
|
||||
p->regval = ptimer->cnt_ctl;
|
||||
/*
|
||||
* Set ISTATUS bit if it's expired.
|
||||
* Note that according to ARMv8 ARM Issue A.k, ISTATUS bit is
|
||||
* UNKNOWN when ENABLE bit is 0, so we chose to set ISTATUS bit
|
||||
* regardless of ENABLE bit for our implementation convenience.
|
||||
*/
|
||||
if (ptimer->cnt_cval <= now)
|
||||
p->regval |= ARCH_TIMER_CTRL_IT_STAT;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool access_cntp_cval(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
|
||||
|
||||
if (p->is_write)
|
||||
ptimer->cnt_cval = p->regval;
|
||||
else
|
||||
p->regval = ptimer->cnt_cval;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Architected system registers.
|
||||
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
|
||||
@@ -1029,6 +1084,16 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
{ Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b011),
|
||||
NULL, reset_unknown, TPIDRRO_EL0 },
|
||||
|
||||
/* CNTP_TVAL_EL0 */
|
||||
{ Op0(0b11), Op1(0b011), CRn(0b1110), CRm(0b0010), Op2(0b000),
|
||||
access_cntp_tval },
|
||||
/* CNTP_CTL_EL0 */
|
||||
{ Op0(0b11), Op1(0b011), CRn(0b1110), CRm(0b0010), Op2(0b001),
|
||||
access_cntp_ctl },
|
||||
/* CNTP_CVAL_EL0 */
|
||||
{ Op0(0b11), Op1(0b011), CRn(0b1110), CRm(0b0010), Op2(0b010),
|
||||
access_cntp_cval },
|
||||
|
||||
/* PMEVCNTRn_EL0 */
|
||||
PMU_PMEVCNTR_EL0(0),
|
||||
PMU_PMEVCNTR_EL0(1),
|
||||
@@ -1795,6 +1860,17 @@ static bool index_to_params(u64 id, struct sys_reg_params *params)
|
||||
}
|
||||
}
|
||||
|
||||
const struct sys_reg_desc *find_reg_by_id(u64 id,
|
||||
struct sys_reg_params *params,
|
||||
const struct sys_reg_desc table[],
|
||||
unsigned int num)
|
||||
{
|
||||
if (!index_to_params(id, params))
|
||||
return NULL;
|
||||
|
||||
return find_reg(params, table, num);
|
||||
}
|
||||
|
||||
/* Decode an index value, and find the sys_reg_desc entry. */
|
||||
static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu,
|
||||
u64 id)
|
||||
@@ -1807,11 +1883,8 @@ static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu,
|
||||
if ((id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM64_SYSREG)
|
||||
return NULL;
|
||||
|
||||
if (!index_to_params(id, ¶ms))
|
||||
return NULL;
|
||||
|
||||
table = get_target_table(vcpu->arch.target, true, &num);
|
||||
r = find_reg(¶ms, table, num);
|
||||
r = find_reg_by_id(id, ¶ms, table, num);
|
||||
if (!r)
|
||||
r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
|
||||
@@ -1918,10 +1991,8 @@ static int get_invariant_sys_reg(u64 id, void __user *uaddr)
|
||||
struct sys_reg_params params;
|
||||
const struct sys_reg_desc *r;
|
||||
|
||||
if (!index_to_params(id, ¶ms))
|
||||
return -ENOENT;
|
||||
|
||||
r = find_reg(¶ms, invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs));
|
||||
r = find_reg_by_id(id, ¶ms, invariant_sys_regs,
|
||||
ARRAY_SIZE(invariant_sys_regs));
|
||||
if (!r)
|
||||
return -ENOENT;
|
||||
|
||||
@@ -1935,9 +2006,8 @@ static int set_invariant_sys_reg(u64 id, void __user *uaddr)
|
||||
int err;
|
||||
u64 val = 0; /* Make sure high bits are 0 for 32-bit regs */
|
||||
|
||||
if (!index_to_params(id, ¶ms))
|
||||
return -ENOENT;
|
||||
r = find_reg(¶ms, invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs));
|
||||
r = find_reg_by_id(id, ¶ms, invariant_sys_regs,
|
||||
ARRAY_SIZE(invariant_sys_regs));
|
||||
if (!r)
|
||||
return -ENOENT;
|
||||
|
||||
|
||||
@@ -136,6 +136,10 @@ static inline int cmp_sys_reg(const struct sys_reg_desc *i1,
|
||||
return i1->Op2 - i2->Op2;
|
||||
}
|
||||
|
||||
const struct sys_reg_desc *find_reg_by_id(u64 id,
|
||||
struct sys_reg_params *params,
|
||||
const struct sys_reg_desc table[],
|
||||
unsigned int num);
|
||||
|
||||
#define Op0(_x) .Op0 = _x
|
||||
#define Op1(_x) .Op1 = _x
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* VGIC system registers handling functions for AArch64 mode
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include "vgic.h"
|
||||
#include "sys_regs.h"
|
||||
|
||||
static bool access_gic_ctlr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
u32 host_pri_bits, host_id_bits, host_seis, host_a3v, seis, a3v;
|
||||
struct vgic_cpu *vgic_v3_cpu = &vcpu->arch.vgic_cpu;
|
||||
struct vgic_vmcr vmcr;
|
||||
u64 val;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
if (p->is_write) {
|
||||
val = p->regval;
|
||||
|
||||
/*
|
||||
* Disallow restoring VM state if not supported by this
|
||||
* hardware.
|
||||
*/
|
||||
host_pri_bits = ((val & ICC_CTLR_EL1_PRI_BITS_MASK) >>
|
||||
ICC_CTLR_EL1_PRI_BITS_SHIFT) + 1;
|
||||
if (host_pri_bits > vgic_v3_cpu->num_pri_bits)
|
||||
return false;
|
||||
|
||||
vgic_v3_cpu->num_pri_bits = host_pri_bits;
|
||||
|
||||
host_id_bits = (val & ICC_CTLR_EL1_ID_BITS_MASK) >>
|
||||
ICC_CTLR_EL1_ID_BITS_SHIFT;
|
||||
if (host_id_bits > vgic_v3_cpu->num_id_bits)
|
||||
return false;
|
||||
|
||||
vgic_v3_cpu->num_id_bits = host_id_bits;
|
||||
|
||||
host_seis = ((kvm_vgic_global_state.ich_vtr_el2 &
|
||||
ICH_VTR_SEIS_MASK) >> ICH_VTR_SEIS_SHIFT);
|
||||
seis = (val & ICC_CTLR_EL1_SEIS_MASK) >>
|
||||
ICC_CTLR_EL1_SEIS_SHIFT;
|
||||
if (host_seis != seis)
|
||||
return false;
|
||||
|
||||
host_a3v = ((kvm_vgic_global_state.ich_vtr_el2 &
|
||||
ICH_VTR_A3V_MASK) >> ICH_VTR_A3V_SHIFT);
|
||||
a3v = (val & ICC_CTLR_EL1_A3V_MASK) >> ICC_CTLR_EL1_A3V_SHIFT;
|
||||
if (host_a3v != a3v)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Here set VMCR.CTLR in ICC_CTLR_EL1 layout.
|
||||
* The vgic_set_vmcr() will convert to ICH_VMCR layout.
|
||||
*/
|
||||
vmcr.ctlr = val & ICC_CTLR_EL1_CBPR_MASK;
|
||||
vmcr.ctlr |= val & ICC_CTLR_EL1_EOImode_MASK;
|
||||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
} else {
|
||||
val = 0;
|
||||
val |= (vgic_v3_cpu->num_pri_bits - 1) <<
|
||||
ICC_CTLR_EL1_PRI_BITS_SHIFT;
|
||||
val |= vgic_v3_cpu->num_id_bits << ICC_CTLR_EL1_ID_BITS_SHIFT;
|
||||
val |= ((kvm_vgic_global_state.ich_vtr_el2 &
|
||||
ICH_VTR_SEIS_MASK) >> ICH_VTR_SEIS_SHIFT) <<
|
||||
ICC_CTLR_EL1_SEIS_SHIFT;
|
||||
val |= ((kvm_vgic_global_state.ich_vtr_el2 &
|
||||
ICH_VTR_A3V_MASK) >> ICH_VTR_A3V_SHIFT) <<
|
||||
ICC_CTLR_EL1_A3V_SHIFT;
|
||||
/*
|
||||
* The VMCR.CTLR value is in ICC_CTLR_EL1 layout.
|
||||
* Extract it directly using ICC_CTLR_EL1 reg definitions.
|
||||
*/
|
||||
val |= vmcr.ctlr & ICC_CTLR_EL1_CBPR_MASK;
|
||||
val |= vmcr.ctlr & ICC_CTLR_EL1_EOImode_MASK;
|
||||
|
||||
p->regval = val;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool access_gic_pmr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct vgic_vmcr vmcr;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
if (p->is_write) {
|
||||
vmcr.pmr = (p->regval & ICC_PMR_EL1_MASK) >> ICC_PMR_EL1_SHIFT;
|
||||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
} else {
|
||||
p->regval = (vmcr.pmr << ICC_PMR_EL1_SHIFT) & ICC_PMR_EL1_MASK;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool access_gic_bpr0(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct vgic_vmcr vmcr;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
if (p->is_write) {
|
||||
vmcr.bpr = (p->regval & ICC_BPR0_EL1_MASK) >>
|
||||
ICC_BPR0_EL1_SHIFT;
|
||||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
} else {
|
||||
p->regval = (vmcr.bpr << ICC_BPR0_EL1_SHIFT) &
|
||||
ICC_BPR0_EL1_MASK;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool access_gic_bpr1(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct vgic_vmcr vmcr;
|
||||
|
||||
if (!p->is_write)
|
||||
p->regval = 0;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
if (!((vmcr.ctlr & ICH_VMCR_CBPR_MASK) >> ICH_VMCR_CBPR_SHIFT)) {
|
||||
if (p->is_write) {
|
||||
vmcr.abpr = (p->regval & ICC_BPR1_EL1_MASK) >>
|
||||
ICC_BPR1_EL1_SHIFT;
|
||||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
} else {
|
||||
p->regval = (vmcr.abpr << ICC_BPR1_EL1_SHIFT) &
|
||||
ICC_BPR1_EL1_MASK;
|
||||
}
|
||||
} else {
|
||||
if (!p->is_write)
|
||||
p->regval = min((vmcr.bpr + 1), 7U);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool access_gic_grpen0(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct vgic_vmcr vmcr;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
if (p->is_write) {
|
||||
vmcr.grpen0 = (p->regval & ICC_IGRPEN0_EL1_MASK) >>
|
||||
ICC_IGRPEN0_EL1_SHIFT;
|
||||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
} else {
|
||||
p->regval = (vmcr.grpen0 << ICC_IGRPEN0_EL1_SHIFT) &
|
||||
ICC_IGRPEN0_EL1_MASK;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool access_gic_grpen1(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct vgic_vmcr vmcr;
|
||||
|
||||
vgic_get_vmcr(vcpu, &vmcr);
|
||||
if (p->is_write) {
|
||||
vmcr.grpen1 = (p->regval & ICC_IGRPEN1_EL1_MASK) >>
|
||||
ICC_IGRPEN1_EL1_SHIFT;
|
||||
vgic_set_vmcr(vcpu, &vmcr);
|
||||
} else {
|
||||
p->regval = (vmcr.grpen1 << ICC_IGRPEN1_EL1_SHIFT) &
|
||||
ICC_IGRPEN1_EL1_MASK;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void vgic_v3_access_apr_reg(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p, u8 apr, u8 idx)
|
||||
{
|
||||
struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
uint32_t *ap_reg;
|
||||
|
||||
if (apr)
|
||||
ap_reg = &vgicv3->vgic_ap1r[idx];
|
||||
else
|
||||
ap_reg = &vgicv3->vgic_ap0r[idx];
|
||||
|
||||
if (p->is_write)
|
||||
*ap_reg = p->regval;
|
||||
else
|
||||
p->regval = *ap_reg;
|
||||
}
|
||||
|
||||
static bool access_gic_aprn(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r, u8 apr)
|
||||
{
|
||||
struct vgic_cpu *vgic_v3_cpu = &vcpu->arch.vgic_cpu;
|
||||
u8 idx = r->Op2 & 3;
|
||||
|
||||
/*
|
||||
* num_pri_bits are initialized with HW supported values.
|
||||
* We can rely safely on num_pri_bits even if VM has not
|
||||
* restored ICC_CTLR_EL1 before restoring APnR registers.
|
||||
*/
|
||||
switch (vgic_v3_cpu->num_pri_bits) {
|
||||
case 7:
|
||||
vgic_v3_access_apr_reg(vcpu, p, apr, idx);
|
||||
break;
|
||||
case 6:
|
||||
if (idx > 1)
|
||||
goto err;
|
||||
vgic_v3_access_apr_reg(vcpu, p, apr, idx);
|
||||
break;
|
||||
default:
|
||||
if (idx > 0)
|
||||
goto err;
|
||||
vgic_v3_access_apr_reg(vcpu, p, apr, idx);
|
||||
}
|
||||
|
||||
return true;
|
||||
err:
|
||||
if (!p->is_write)
|
||||
p->regval = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool access_gic_ap0r(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
|
||||
{
|
||||
return access_gic_aprn(vcpu, p, r, 0);
|
||||
}
|
||||
|
||||
static bool access_gic_ap1r(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
return access_gic_aprn(vcpu, p, r, 1);
|
||||
}
|
||||
|
||||
static bool access_gic_sre(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
/* Validate SRE bit */
|
||||
if (p->is_write) {
|
||||
if (!(p->regval & ICC_SRE_EL1_SRE))
|
||||
return false;
|
||||
} else {
|
||||
p->regval = vgicv3->vgic_sre;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
static const struct sys_reg_desc gic_v3_icc_reg_descs[] = {
|
||||
/* ICC_PMR_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(4), CRm(6), Op2(0), access_gic_pmr },
|
||||
/* ICC_BPR0_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(8), Op2(3), access_gic_bpr0 },
|
||||
/* ICC_AP0R0_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(8), Op2(4), access_gic_ap0r },
|
||||
/* ICC_AP0R1_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(8), Op2(5), access_gic_ap0r },
|
||||
/* ICC_AP0R2_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(8), Op2(6), access_gic_ap0r },
|
||||
/* ICC_AP0R3_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(8), Op2(7), access_gic_ap0r },
|
||||
/* ICC_AP1R0_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(9), Op2(0), access_gic_ap1r },
|
||||
/* ICC_AP1R1_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(9), Op2(1), access_gic_ap1r },
|
||||
/* ICC_AP1R2_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(9), Op2(2), access_gic_ap1r },
|
||||
/* ICC_AP1R3_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(9), Op2(3), access_gic_ap1r },
|
||||
/* ICC_BPR1_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(12), Op2(3), access_gic_bpr1 },
|
||||
/* ICC_CTLR_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(12), Op2(4), access_gic_ctlr },
|
||||
/* ICC_SRE_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(12), Op2(5), access_gic_sre },
|
||||
/* ICC_IGRPEN0_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(12), Op2(6), access_gic_grpen0 },
|
||||
/* ICC_GRPEN1_EL1 */
|
||||
{ Op0(3), Op1(0), CRn(12), CRm(12), Op2(7), access_gic_grpen1 },
|
||||
};
|
||||
|
||||
int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
|
||||
u64 *reg)
|
||||
{
|
||||
struct sys_reg_params params;
|
||||
u64 sysreg = (id & KVM_DEV_ARM_VGIC_SYSREG_MASK) | KVM_REG_SIZE_U64;
|
||||
|
||||
params.regval = *reg;
|
||||
params.is_write = is_write;
|
||||
params.is_aarch32 = false;
|
||||
params.is_32bit = false;
|
||||
|
||||
if (find_reg_by_id(sysreg, ¶ms, gic_v3_icc_reg_descs,
|
||||
ARRAY_SIZE(gic_v3_icc_reg_descs)))
|
||||
return 0;
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id,
|
||||
u64 *reg)
|
||||
{
|
||||
struct sys_reg_params params;
|
||||
const struct sys_reg_desc *r;
|
||||
u64 sysreg = (id & KVM_DEV_ARM_VGIC_SYSREG_MASK) | KVM_REG_SIZE_U64;
|
||||
|
||||
if (is_write)
|
||||
params.regval = *reg;
|
||||
params.is_write = is_write;
|
||||
params.is_aarch32 = false;
|
||||
params.is_32bit = false;
|
||||
|
||||
r = find_reg_by_id(sysreg, ¶ms, gic_v3_icc_reg_descs,
|
||||
ARRAY_SIZE(gic_v3_icc_reg_descs));
|
||||
if (!r)
|
||||
return -ENXIO;
|
||||
|
||||
if (!r->access(vcpu, ¶ms, r))
|
||||
return -EINVAL;
|
||||
|
||||
if (!is_write)
|
||||
*reg = params.regval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user