mirror of https://gitee.com/openkylin/linux.git
KVM/ARM New features for 3.17 include:
- Fixes and code refactoring for stage2 kvm MMU unmap_range - Support unmapping IPAs on deleting memslots for arm and arm64 - Support MMIO mappings in stage2 faults - KVM VGIC v2 emulation on GICv3 hardware - Big-Endian support for arm/arm64 (guest and host) - Debug Architecture support for arm64 (arm32 is on Christoffer's todo list) - Detect non page-aligned GICV regions and bail out (plugs guest-can-crash host bug) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJT3oZTAAoJEEtpOizt6ddyKQIH/1Bj/cZYSkkSf3IJfQhHRbWN jS37IBsvcHwjHkRJxCNmQuKP/Ho5XEusluPGrVY25PAgBMl+ouPqAuKzUk+GEab6 snjJjDFqw0zs0x0h3tg6UwfZdF+eyyIkmFGn8/IATD5P3PPd8kWBVtYnSnZmYK+R KJNVcp6RPDrt9kvUDY8Ln9fW99Jl+7CdgQAnc3QkHcXUlanLyrfq+fE1lSzyrbhZ ETzyMFAX4kCdc8tflgyyBS4A7+RvfQ6ZIQummxoAMFHIoSk90dtK7ovX68rd9U3e yL+mpe130+dTIFpUMbxCnIdE7C0eud3vcgXC6MuWtFjUrxQoaEgsVE+ffGC5tX0= =axkp -----END PGP SIGNATURE----- Merge tag 'kvm-arm-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into kvm KVM/ARM New features for 3.17 include: - Fixes and code refactoring for stage2 kvm MMU unmap_range - Support unmapping IPAs on deleting memslots for arm and arm64 - Support MMIO mappings in stage2 faults - KVM VGIC v2 emulation on GICv3 hardware - Big-Endian support for arm/arm64 (guest and host) - Debug Architecture support for arm64 (arm32 is on Christoffer's todo list) Conflicts: virt/kvm/arm/vgic.c [last minute cherry-pick from 3.17 to 3.16]
This commit is contained in:
commit
5d57686605
|
@ -168,6 +168,14 @@ Before jumping into the kernel, the following conditions must be met:
|
||||||
the kernel image will be entered must be initialised by software at a
|
the kernel image will be entered must be initialised by software at a
|
||||||
higher exception level to prevent execution in an UNKNOWN state.
|
higher exception level to prevent execution in an UNKNOWN state.
|
||||||
|
|
||||||
|
For systems with a GICv3 interrupt controller:
|
||||||
|
- If EL3 is present:
|
||||||
|
ICC_SRE_EL3.Enable (bit 3) must be initialiased to 0b1.
|
||||||
|
ICC_SRE_EL3.SRE (bit 0) must be initialised to 0b1.
|
||||||
|
- If the kernel is entered at EL1:
|
||||||
|
ICC.SRE_EL2.Enable (bit 3) must be initialised to 0b1
|
||||||
|
ICC_SRE_EL2.SRE (bit 0) must be initialised to 0b1.
|
||||||
|
|
||||||
The requirements described above for CPU mode, caches, MMUs, architected
|
The requirements described above for CPU mode, caches, MMUs, architected
|
||||||
timers, coherency and system registers apply to all CPUs. All CPUs must
|
timers, coherency and system registers apply to all CPUs. All CPUs must
|
||||||
enter the kernel in the same exception level.
|
enter the kernel in the same exception level.
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
* ARM Generic Interrupt Controller, version 3
|
||||||
|
|
||||||
|
AArch64 SMP cores are often associated with a GICv3, providing Private
|
||||||
|
Peripheral Interrupts (PPI), Shared Peripheral Interrupts (SPI),
|
||||||
|
Software Generated Interrupts (SGI), and Locality-specific Peripheral
|
||||||
|
Interrupts (LPI).
|
||||||
|
|
||||||
|
Main node required properties:
|
||||||
|
|
||||||
|
- compatible : should at least contain "arm,gic-v3".
|
||||||
|
- interrupt-controller : Identifies the node as an interrupt controller
|
||||||
|
- #interrupt-cells : Specifies the number of cells needed to encode an
|
||||||
|
interrupt source. Must be a single cell with a value of at least 3.
|
||||||
|
|
||||||
|
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
|
||||||
|
interrupts. Other values are reserved for future use.
|
||||||
|
|
||||||
|
The 2nd cell contains the interrupt number for the interrupt type.
|
||||||
|
SPI interrupts are in the range [0-987]. PPI interrupts are in the
|
||||||
|
range [0-15].
|
||||||
|
|
||||||
|
The 3rd cell is the flags, encoded as follows:
|
||||||
|
bits[3:0] trigger type and level flags.
|
||||||
|
1 = edge triggered
|
||||||
|
4 = level triggered
|
||||||
|
|
||||||
|
Cells 4 and beyond are reserved for future use. When the 1st cell
|
||||||
|
has a value of 0 or 1, cells 4 and beyond act as padding, and may be
|
||||||
|
ignored. It is recommended that padding cells have a value of 0.
|
||||||
|
|
||||||
|
- reg : Specifies base physical address(s) and size of the GIC
|
||||||
|
registers, in the following order:
|
||||||
|
- GIC Distributor interface (GICD)
|
||||||
|
- GIC Redistributors (GICR), one range per redistributor region
|
||||||
|
- GIC CPU interface (GICC)
|
||||||
|
- GIC Hypervisor interface (GICH)
|
||||||
|
- GIC Virtual CPU interface (GICV)
|
||||||
|
|
||||||
|
GICC, GICH and GICV are optional.
|
||||||
|
|
||||||
|
- interrupts : Interrupt source of the VGIC maintenance interrupt.
|
||||||
|
|
||||||
|
Optional
|
||||||
|
|
||||||
|
- redistributor-stride : If using padding pages, specifies the stride
|
||||||
|
of consecutive redistributors. Must be a multiple of 64kB.
|
||||||
|
|
||||||
|
- #redistributor-regions: The number of independent contiguous regions
|
||||||
|
occupied by the redistributors. Required if more than one such
|
||||||
|
region is present.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
gic: interrupt-controller@2cf00000 {
|
||||||
|
compatible = "arm,gic-v3";
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-controller;
|
||||||
|
reg = <0x0 0x2f000000 0 0x10000>, // GICD
|
||||||
|
<0x0 0x2f100000 0 0x200000>, // GICR
|
||||||
|
<0x0 0x2c000000 0 0x2000>, // GICC
|
||||||
|
<0x0 0x2c010000 0 0x2000>, // GICH
|
||||||
|
<0x0 0x2c020000 0 0x2000>; // GICV
|
||||||
|
interrupts = <1 9 4>;
|
||||||
|
};
|
||||||
|
|
||||||
|
gic: interrupt-controller@2c010000 {
|
||||||
|
compatible = "arm,gic-v3";
|
||||||
|
#interrupt-cells = <3>;
|
||||||
|
interrupt-controller;
|
||||||
|
redistributor-stride = <0x0 0x40000>; // 256kB stride
|
||||||
|
#redistributor-regions = <2>;
|
||||||
|
reg = <0x0 0x2c010000 0 0x10000>, // GICD
|
||||||
|
<0x0 0x2d000000 0 0x800000>, // GICR 1: CPUs 0-31
|
||||||
|
<0x0 0x2e000000 0 0x800000>; // GICR 2: CPUs 32-63
|
||||||
|
<0x0 0x2c040000 0 0x2000>, // GICC
|
||||||
|
<0x0 0x2c060000 0 0x2000>, // GICH
|
||||||
|
<0x0 0x2c080000 0 0x2000>; // GICV
|
||||||
|
interrupts = <1 9 4>;
|
||||||
|
};
|
|
@ -61,6 +61,24 @@
|
||||||
#define ARM_EXCEPTION_FIQ 6
|
#define ARM_EXCEPTION_FIQ 6
|
||||||
#define ARM_EXCEPTION_HVC 7
|
#define ARM_EXCEPTION_HVC 7
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The rr_lo_hi macro swaps a pair of registers depending on
|
||||||
|
* current endianness. It is used in conjunction with ldrd and strd
|
||||||
|
* instructions that load/store a 64-bit value from/to memory to/from
|
||||||
|
* a pair of registers which are used with the mrrc and mcrr instructions.
|
||||||
|
* If used with the ldrd/strd instructions, the a1 parameter is the first
|
||||||
|
* source/destination register and the a2 parameter is the second
|
||||||
|
* source/destination register. Note that the ldrd/strd instructions
|
||||||
|
* already swap the bytes within the words correctly according to the
|
||||||
|
* endianness setting, but the order of the registers need to be effectively
|
||||||
|
* swapped when used with the mrrc/mcrr instructions.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_CPU_ENDIAN_BE8
|
||||||
|
#define rr_lo_hi(a1, a2) a2, a1
|
||||||
|
#else
|
||||||
|
#define rr_lo_hi(a1, a2) a1, a2
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
struct kvm;
|
struct kvm;
|
||||||
struct kvm_vcpu;
|
struct kvm_vcpu;
|
||||||
|
|
|
@ -185,9 +185,16 @@ static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
|
||||||
default:
|
default:
|
||||||
return be32_to_cpu(data);
|
return be32_to_cpu(data);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
switch (len) {
|
||||||
|
case 1:
|
||||||
|
return data & 0xff;
|
||||||
|
case 2:
|
||||||
|
return le16_to_cpu(data & 0xffff);
|
||||||
|
default:
|
||||||
|
return le32_to_cpu(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data; /* Leave LE untouched */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
|
static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
|
||||||
|
@ -203,9 +210,16 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
|
||||||
default:
|
default:
|
||||||
return cpu_to_be32(data);
|
return cpu_to_be32(data);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
switch (len) {
|
||||||
|
case 1:
|
||||||
|
return data & 0xff;
|
||||||
|
case 2:
|
||||||
|
return cpu_to_le16(data & 0xffff);
|
||||||
|
default:
|
||||||
|
return cpu_to_le32(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data; /* Leave LE untouched */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __ARM_KVM_EMULATE_H__ */
|
#endif /* __ARM_KVM_EMULATE_H__ */
|
||||||
|
|
|
@ -225,10 +225,12 @@ static inline int kvm_arch_dev_ioctl_check_extension(long ext)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void vgic_arch_setup(const struct vgic_params *vgic)
|
||||||
|
{
|
||||||
|
BUG_ON(vgic->type != VGIC_V2);
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_perf_init(void);
|
int kvm_perf_init(void);
|
||||||
int kvm_perf_teardown(void);
|
int kvm_perf_teardown(void);
|
||||||
|
|
||||||
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);
|
|
||||||
int kvm_arm_timer_set_reg(struct kvm_vcpu *, u64 regid, u64 value);
|
|
||||||
|
|
||||||
#endif /* __ARM_KVM_HOST_H__ */
|
#endif /* __ARM_KVM_HOST_H__ */
|
||||||
|
|
|
@ -127,6 +127,18 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
|
||||||
(__boundary - 1 < (end) - 1)? __boundary: (end); \
|
(__boundary - 1 < (end) - 1)? __boundary: (end); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
static inline bool kvm_page_empty(void *ptr)
|
||||||
|
{
|
||||||
|
struct page *ptr_page = virt_to_page(ptr);
|
||||||
|
return page_count(ptr_page) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define kvm_pte_table_empty(ptep) kvm_page_empty(ptep)
|
||||||
|
#define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
|
||||||
|
#define kvm_pud_table_empty(pudp) (0)
|
||||||
|
|
||||||
|
|
||||||
struct kvm;
|
struct kvm;
|
||||||
|
|
||||||
#define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l))
|
#define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l))
|
||||||
|
|
|
@ -182,13 +182,13 @@ int main(void)
|
||||||
DEFINE(VCPU_HYP_PC, offsetof(struct kvm_vcpu, arch.fault.hyp_pc));
|
DEFINE(VCPU_HYP_PC, offsetof(struct kvm_vcpu, arch.fault.hyp_pc));
|
||||||
#ifdef CONFIG_KVM_ARM_VGIC
|
#ifdef CONFIG_KVM_ARM_VGIC
|
||||||
DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu));
|
DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu));
|
||||||
DEFINE(VGIC_CPU_HCR, offsetof(struct vgic_cpu, vgic_hcr));
|
DEFINE(VGIC_V2_CPU_HCR, offsetof(struct vgic_cpu, vgic_v2.vgic_hcr));
|
||||||
DEFINE(VGIC_CPU_VMCR, offsetof(struct vgic_cpu, vgic_vmcr));
|
DEFINE(VGIC_V2_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr));
|
||||||
DEFINE(VGIC_CPU_MISR, offsetof(struct vgic_cpu, vgic_misr));
|
DEFINE(VGIC_V2_CPU_MISR, offsetof(struct vgic_cpu, vgic_v2.vgic_misr));
|
||||||
DEFINE(VGIC_CPU_EISR, offsetof(struct vgic_cpu, vgic_eisr));
|
DEFINE(VGIC_V2_CPU_EISR, offsetof(struct vgic_cpu, vgic_v2.vgic_eisr));
|
||||||
DEFINE(VGIC_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_elrsr));
|
DEFINE(VGIC_V2_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr));
|
||||||
DEFINE(VGIC_CPU_APR, offsetof(struct vgic_cpu, vgic_apr));
|
DEFINE(VGIC_V2_CPU_APR, offsetof(struct vgic_cpu, vgic_v2.vgic_apr));
|
||||||
DEFINE(VGIC_CPU_LR, offsetof(struct vgic_cpu, vgic_lr));
|
DEFINE(VGIC_V2_CPU_LR, offsetof(struct vgic_cpu, vgic_v2.vgic_lr));
|
||||||
DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
|
DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
|
||||||
#ifdef CONFIG_KVM_ARM_TIMER
|
#ifdef CONFIG_KVM_ARM_TIMER
|
||||||
DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
|
DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
|
||||||
|
|
|
@ -134,9 +134,7 @@ ENTRY(__hyp_stub_install_secondary)
|
||||||
mcr p15, 4, r7, c1, c1, 3 @ HSTR
|
mcr p15, 4, r7, c1, c1, 3 @ HSTR
|
||||||
|
|
||||||
THUMB( orr r7, #(1 << 30) ) @ HSCTLR.TE
|
THUMB( orr r7, #(1 << 30) ) @ HSCTLR.TE
|
||||||
#ifdef CONFIG_CPU_BIG_ENDIAN
|
ARM_BE8(orr r7, r7, #(1 << 25)) @ HSCTLR.EE
|
||||||
orr r7, #(1 << 9) @ HSCTLR.EE
|
|
||||||
#endif
|
|
||||||
mcr p15, 4, r7, c1, c0, 0 @ HSCTLR
|
mcr p15, 4, r7, c1, c0, 0 @ HSCTLR
|
||||||
|
|
||||||
mrc p15, 4, r7, c1, c1, 1 @ HDCR
|
mrc p15, 4, r7, c1, c1, 1 @ HDCR
|
||||||
|
|
|
@ -23,7 +23,7 @@ config KVM
|
||||||
select HAVE_KVM_CPU_RELAX_INTERCEPT
|
select HAVE_KVM_CPU_RELAX_INTERCEPT
|
||||||
select KVM_MMIO
|
select KVM_MMIO
|
||||||
select KVM_ARM_HOST
|
select KVM_ARM_HOST
|
||||||
depends on ARM_VIRT_EXT && ARM_LPAE && !CPU_BIG_ENDIAN
|
depends on ARM_VIRT_EXT && ARM_LPAE
|
||||||
---help---
|
---help---
|
||||||
Support hosting virtualized guest machines. You will also
|
Support hosting virtualized guest machines. You will also
|
||||||
need to select one or more of the processor modules below.
|
need to select one or more of the processor modules below.
|
||||||
|
|
|
@ -21,4 +21,5 @@ 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 += 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
|
||||||
obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
|
obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
|
||||||
|
obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v2.o
|
||||||
obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
|
obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
|
||||||
|
|
|
@ -155,16 +155,6 @@ int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
|
||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
|
|
||||||
struct kvm_memory_slot *dont)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
|
|
||||||
unsigned long npages)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_arch_destroy_vm - destroy the VM data structure
|
* kvm_arch_destroy_vm - destroy the VM data structure
|
||||||
|
@ -225,33 +215,6 @@ long kvm_arch_dev_ioctl(struct file *filp,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_arch_memslots_updated(struct kvm *kvm)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
|
||||||
struct kvm_memory_slot *memslot,
|
|
||||||
struct kvm_userspace_memory_region *mem,
|
|
||||||
enum kvm_mr_change change)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_arch_commit_memory_region(struct kvm *kvm,
|
|
||||||
struct kvm_userspace_memory_region *mem,
|
|
||||||
const struct kvm_memory_slot *old,
|
|
||||||
enum kvm_mr_change change)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
|
||||||
struct kvm_memory_slot *slot)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
|
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,6 +44,31 @@ static u32 cache_levels;
|
||||||
/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */
|
/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */
|
||||||
#define CSSELR_MAX 12
|
#define CSSELR_MAX 12
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kvm_vcpu_arch.cp15 holds cp15 registers as an array of u32, but some
|
||||||
|
* of cp15 registers can be viewed either as couple of two u32 registers
|
||||||
|
* or one u64 register. Current u64 register encoding is that least
|
||||||
|
* significant u32 word is followed by most significant u32 word.
|
||||||
|
*/
|
||||||
|
static inline void vcpu_cp15_reg64_set(struct kvm_vcpu *vcpu,
|
||||||
|
const struct coproc_reg *r,
|
||||||
|
u64 val)
|
||||||
|
{
|
||||||
|
vcpu->arch.cp15[r->reg] = val & 0xffffffff;
|
||||||
|
vcpu->arch.cp15[r->reg + 1] = val >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 vcpu_cp15_reg64_get(struct kvm_vcpu *vcpu,
|
||||||
|
const struct coproc_reg *r)
|
||||||
|
{
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
val = vcpu->arch.cp15[r->reg + 1];
|
||||||
|
val = val << 32;
|
||||||
|
val = val | vcpu->arch.cp15[r->reg];
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
{
|
{
|
||||||
kvm_inject_undefined(vcpu);
|
kvm_inject_undefined(vcpu);
|
||||||
|
@ -682,17 +707,23 @@ static struct coproc_reg invariant_cp15[] = {
|
||||||
{ CRn( 0), CRm( 0), Op1( 1), Op2( 7), is32, NULL, get_AIDR },
|
{ CRn( 0), CRm( 0), Op1( 1), Op2( 7), is32, NULL, get_AIDR },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads a register value from a userspace address to a kernel
|
||||||
|
* variable. Make sure that register size matches sizeof(*__val).
|
||||||
|
*/
|
||||||
static int reg_from_user(void *val, const void __user *uaddr, u64 id)
|
static int reg_from_user(void *val, const void __user *uaddr, u64 id)
|
||||||
{
|
{
|
||||||
/* This Just Works because we are little endian. */
|
|
||||||
if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0)
|
if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes a register value to a userspace address from a kernel variable.
|
||||||
|
* Make sure that register size matches sizeof(*__val).
|
||||||
|
*/
|
||||||
static int reg_to_user(void __user *uaddr, const void *val, u64 id)
|
static int reg_to_user(void __user *uaddr, const void *val, u64 id)
|
||||||
{
|
{
|
||||||
/* This Just Works because we are little endian. */
|
|
||||||
if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0)
|
if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -702,6 +733,7 @@ static int get_invariant_cp15(u64 id, void __user *uaddr)
|
||||||
{
|
{
|
||||||
struct coproc_params params;
|
struct coproc_params params;
|
||||||
const struct coproc_reg *r;
|
const struct coproc_reg *r;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!index_to_params(id, ¶ms))
|
if (!index_to_params(id, ¶ms))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
@ -710,7 +742,15 @@ static int get_invariant_cp15(u64 id, void __user *uaddr)
|
||||||
if (!r)
|
if (!r)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
return reg_to_user(uaddr, &r->val, id);
|
ret = -ENOENT;
|
||||||
|
if (KVM_REG_SIZE(id) == 4) {
|
||||||
|
u32 val = r->val;
|
||||||
|
|
||||||
|
ret = reg_to_user(uaddr, &val, id);
|
||||||
|
} else if (KVM_REG_SIZE(id) == 8) {
|
||||||
|
ret = reg_to_user(uaddr, &r->val, id);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_invariant_cp15(u64 id, void __user *uaddr)
|
static int set_invariant_cp15(u64 id, void __user *uaddr)
|
||||||
|
@ -718,7 +758,7 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
|
||||||
struct coproc_params params;
|
struct coproc_params params;
|
||||||
const struct coproc_reg *r;
|
const struct coproc_reg *r;
|
||||||
int err;
|
int err;
|
||||||
u64 val = 0; /* Make sure high bits are 0 for 32-bit regs */
|
u64 val;
|
||||||
|
|
||||||
if (!index_to_params(id, ¶ms))
|
if (!index_to_params(id, ¶ms))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
@ -726,7 +766,16 @@ static int set_invariant_cp15(u64 id, void __user *uaddr)
|
||||||
if (!r)
|
if (!r)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
err = reg_from_user(&val, uaddr, id);
|
err = -ENOENT;
|
||||||
|
if (KVM_REG_SIZE(id) == 4) {
|
||||||
|
u32 val32;
|
||||||
|
|
||||||
|
err = reg_from_user(&val32, uaddr, id);
|
||||||
|
if (!err)
|
||||||
|
val = val32;
|
||||||
|
} else if (KVM_REG_SIZE(id) == 8) {
|
||||||
|
err = reg_from_user(&val, uaddr, id);
|
||||||
|
}
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -1004,6 +1053,7 @@ int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
{
|
{
|
||||||
const struct coproc_reg *r;
|
const struct coproc_reg *r;
|
||||||
void __user *uaddr = (void __user *)(long)reg->addr;
|
void __user *uaddr = (void __user *)(long)reg->addr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
|
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
|
||||||
return demux_c15_get(reg->id, uaddr);
|
return demux_c15_get(reg->id, uaddr);
|
||||||
|
@ -1015,14 +1065,24 @@ int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
if (!r)
|
if (!r)
|
||||||
return get_invariant_cp15(reg->id, uaddr);
|
return get_invariant_cp15(reg->id, uaddr);
|
||||||
|
|
||||||
/* Note: copies two regs if size is 64 bit. */
|
ret = -ENOENT;
|
||||||
return reg_to_user(uaddr, &vcpu->arch.cp15[r->reg], reg->id);
|
if (KVM_REG_SIZE(reg->id) == 8) {
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
val = vcpu_cp15_reg64_get(vcpu, r);
|
||||||
|
ret = reg_to_user(uaddr, &val, reg->id);
|
||||||
|
} else if (KVM_REG_SIZE(reg->id) == 4) {
|
||||||
|
ret = reg_to_user(uaddr, &vcpu->arch.cp15[r->reg], reg->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
{
|
{
|
||||||
const struct coproc_reg *r;
|
const struct coproc_reg *r;
|
||||||
void __user *uaddr = (void __user *)(long)reg->addr;
|
void __user *uaddr = (void __user *)(long)reg->addr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
|
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
|
||||||
return demux_c15_set(reg->id, uaddr);
|
return demux_c15_set(reg->id, uaddr);
|
||||||
|
@ -1034,8 +1094,18 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
if (!r)
|
if (!r)
|
||||||
return set_invariant_cp15(reg->id, uaddr);
|
return set_invariant_cp15(reg->id, uaddr);
|
||||||
|
|
||||||
/* Note: copies two regs if size is 64 bit */
|
ret = -ENOENT;
|
||||||
return reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id);
|
if (KVM_REG_SIZE(reg->id) == 8) {
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
ret = reg_from_user(&val, uaddr, reg->id);
|
||||||
|
if (!ret)
|
||||||
|
vcpu_cp15_reg64_set(vcpu, r, val);
|
||||||
|
} else if (KVM_REG_SIZE(reg->id) == 4) {
|
||||||
|
ret = reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int num_demux_regs(void)
|
static unsigned int num_demux_regs(void)
|
||||||
|
|
|
@ -124,16 +124,6 @@ static bool is_timer_reg(u64 index)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define NUM_TIMER_REGS 3
|
#define NUM_TIMER_REGS 3
|
||||||
|
|
|
@ -71,7 +71,7 @@ __do_hyp_init:
|
||||||
bne phase2 @ Yes, second stage init
|
bne phase2 @ Yes, second stage init
|
||||||
|
|
||||||
@ Set the HTTBR to point to the hypervisor PGD pointer passed
|
@ Set the HTTBR to point to the hypervisor PGD pointer passed
|
||||||
mcrr p15, 4, r2, r3, c2
|
mcrr p15, 4, rr_lo_hi(r2, r3), c2
|
||||||
|
|
||||||
@ Set the HTCR and VTCR to the same shareability and cacheability
|
@ Set the HTCR and VTCR to the same shareability and cacheability
|
||||||
@ settings as the non-secure TTBCR and with T0SZ == 0.
|
@ settings as the non-secure TTBCR and with T0SZ == 0.
|
||||||
|
@ -137,7 +137,7 @@ phase2:
|
||||||
mov pc, r0
|
mov pc, r0
|
||||||
|
|
||||||
target: @ We're now in the trampoline code, switch page tables
|
target: @ We're now in the trampoline code, switch page tables
|
||||||
mcrr p15, 4, r2, r3, c2
|
mcrr p15, 4, rr_lo_hi(r2, r3), c2
|
||||||
isb
|
isb
|
||||||
|
|
||||||
@ Invalidate the old TLBs
|
@ Invalidate the old TLBs
|
||||||
|
|
|
@ -52,7 +52,7 @@ ENTRY(__kvm_tlb_flush_vmid_ipa)
|
||||||
dsb ishst
|
dsb ishst
|
||||||
add r0, r0, #KVM_VTTBR
|
add r0, r0, #KVM_VTTBR
|
||||||
ldrd r2, r3, [r0]
|
ldrd r2, r3, [r0]
|
||||||
mcrr p15, 6, r2, r3, c2 @ Write VTTBR
|
mcrr p15, 6, rr_lo_hi(r2, r3), c2 @ Write VTTBR
|
||||||
isb
|
isb
|
||||||
mcr p15, 0, r0, c8, c3, 0 @ TLBIALLIS (rt ignored)
|
mcr p15, 0, r0, c8, c3, 0 @ TLBIALLIS (rt ignored)
|
||||||
dsb ish
|
dsb ish
|
||||||
|
@ -135,7 +135,7 @@ ENTRY(__kvm_vcpu_run)
|
||||||
ldr r1, [vcpu, #VCPU_KVM]
|
ldr r1, [vcpu, #VCPU_KVM]
|
||||||
add r1, r1, #KVM_VTTBR
|
add r1, r1, #KVM_VTTBR
|
||||||
ldrd r2, r3, [r1]
|
ldrd r2, r3, [r1]
|
||||||
mcrr p15, 6, r2, r3, c2 @ Write VTTBR
|
mcrr p15, 6, rr_lo_hi(r2, r3), c2 @ Write VTTBR
|
||||||
|
|
||||||
@ We're all done, just restore the GPRs and go to the guest
|
@ We're all done, just restore the GPRs and go to the guest
|
||||||
restore_guest_regs
|
restore_guest_regs
|
||||||
|
@ -199,8 +199,13 @@ after_vfp_restore:
|
||||||
|
|
||||||
restore_host_regs
|
restore_host_regs
|
||||||
clrex @ Clear exclusive monitor
|
clrex @ Clear exclusive monitor
|
||||||
|
#ifndef CONFIG_CPU_ENDIAN_BE8
|
||||||
mov r0, r1 @ Return the return code
|
mov r0, r1 @ Return the return code
|
||||||
mov r1, #0 @ Clear upper bits in return value
|
mov r1, #0 @ Clear upper bits in return value
|
||||||
|
#else
|
||||||
|
@ r1 already has return code
|
||||||
|
mov r0, #0 @ Clear upper bits in return value
|
||||||
|
#endif /* CONFIG_CPU_ENDIAN_BE8 */
|
||||||
bx lr @ return to IOCTL
|
bx lr @ return to IOCTL
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <linux/irqchip/arm-gic.h>
|
#include <linux/irqchip/arm-gic.h>
|
||||||
|
#include <asm/assembler.h>
|
||||||
|
|
||||||
#define VCPU_USR_REG(_reg_nr) (VCPU_USR_REGS + (_reg_nr * 4))
|
#define VCPU_USR_REG(_reg_nr) (VCPU_USR_REGS + (_reg_nr * 4))
|
||||||
#define VCPU_USR_SP (VCPU_USR_REG(13))
|
#define VCPU_USR_SP (VCPU_USR_REG(13))
|
||||||
|
@ -420,15 +421,23 @@ vcpu .req r0 @ vcpu pointer always in r0
|
||||||
ldr r8, [r2, #GICH_ELRSR0]
|
ldr r8, [r2, #GICH_ELRSR0]
|
||||||
ldr r9, [r2, #GICH_ELRSR1]
|
ldr r9, [r2, #GICH_ELRSR1]
|
||||||
ldr r10, [r2, #GICH_APR]
|
ldr r10, [r2, #GICH_APR]
|
||||||
|
ARM_BE8(rev r3, r3 )
|
||||||
|
ARM_BE8(rev r4, r4 )
|
||||||
|
ARM_BE8(rev r5, r5 )
|
||||||
|
ARM_BE8(rev r6, r6 )
|
||||||
|
ARM_BE8(rev r7, r7 )
|
||||||
|
ARM_BE8(rev r8, r8 )
|
||||||
|
ARM_BE8(rev r9, r9 )
|
||||||
|
ARM_BE8(rev r10, r10 )
|
||||||
|
|
||||||
str r3, [r11, #VGIC_CPU_HCR]
|
str r3, [r11, #VGIC_V2_CPU_HCR]
|
||||||
str r4, [r11, #VGIC_CPU_VMCR]
|
str r4, [r11, #VGIC_V2_CPU_VMCR]
|
||||||
str r5, [r11, #VGIC_CPU_MISR]
|
str r5, [r11, #VGIC_V2_CPU_MISR]
|
||||||
str r6, [r11, #VGIC_CPU_EISR]
|
str r6, [r11, #VGIC_V2_CPU_EISR]
|
||||||
str r7, [r11, #(VGIC_CPU_EISR + 4)]
|
str r7, [r11, #(VGIC_V2_CPU_EISR + 4)]
|
||||||
str r8, [r11, #VGIC_CPU_ELRSR]
|
str r8, [r11, #VGIC_V2_CPU_ELRSR]
|
||||||
str r9, [r11, #(VGIC_CPU_ELRSR + 4)]
|
str r9, [r11, #(VGIC_V2_CPU_ELRSR + 4)]
|
||||||
str r10, [r11, #VGIC_CPU_APR]
|
str r10, [r11, #VGIC_V2_CPU_APR]
|
||||||
|
|
||||||
/* Clear GICH_HCR */
|
/* Clear GICH_HCR */
|
||||||
mov r5, #0
|
mov r5, #0
|
||||||
|
@ -436,9 +445,10 @@ vcpu .req r0 @ vcpu pointer always in r0
|
||||||
|
|
||||||
/* Save list registers */
|
/* Save list registers */
|
||||||
add r2, r2, #GICH_LR0
|
add r2, r2, #GICH_LR0
|
||||||
add r3, r11, #VGIC_CPU_LR
|
add r3, r11, #VGIC_V2_CPU_LR
|
||||||
ldr r4, [r11, #VGIC_CPU_NR_LR]
|
ldr r4, [r11, #VGIC_CPU_NR_LR]
|
||||||
1: ldr r6, [r2], #4
|
1: ldr r6, [r2], #4
|
||||||
|
ARM_BE8(rev r6, r6 )
|
||||||
str r6, [r3], #4
|
str r6, [r3], #4
|
||||||
subs r4, r4, #1
|
subs r4, r4, #1
|
||||||
bne 1b
|
bne 1b
|
||||||
|
@ -463,9 +473,12 @@ vcpu .req r0 @ vcpu pointer always in r0
|
||||||
add r11, vcpu, #VCPU_VGIC_CPU
|
add r11, vcpu, #VCPU_VGIC_CPU
|
||||||
|
|
||||||
/* We only restore a minimal set of registers */
|
/* We only restore a minimal set of registers */
|
||||||
ldr r3, [r11, #VGIC_CPU_HCR]
|
ldr r3, [r11, #VGIC_V2_CPU_HCR]
|
||||||
ldr r4, [r11, #VGIC_CPU_VMCR]
|
ldr r4, [r11, #VGIC_V2_CPU_VMCR]
|
||||||
ldr r8, [r11, #VGIC_CPU_APR]
|
ldr r8, [r11, #VGIC_V2_CPU_APR]
|
||||||
|
ARM_BE8(rev r3, r3 )
|
||||||
|
ARM_BE8(rev r4, r4 )
|
||||||
|
ARM_BE8(rev r8, r8 )
|
||||||
|
|
||||||
str r3, [r2, #GICH_HCR]
|
str r3, [r2, #GICH_HCR]
|
||||||
str r4, [r2, #GICH_VMCR]
|
str r4, [r2, #GICH_VMCR]
|
||||||
|
@ -473,9 +486,10 @@ vcpu .req r0 @ vcpu pointer always in r0
|
||||||
|
|
||||||
/* Restore list registers */
|
/* Restore list registers */
|
||||||
add r2, r2, #GICH_LR0
|
add r2, r2, #GICH_LR0
|
||||||
add r3, r11, #VGIC_CPU_LR
|
add r3, r11, #VGIC_V2_CPU_LR
|
||||||
ldr r4, [r11, #VGIC_CPU_NR_LR]
|
ldr r4, [r11, #VGIC_CPU_NR_LR]
|
||||||
1: ldr r6, [r3], #4
|
1: ldr r6, [r3], #4
|
||||||
|
ARM_BE8(rev r6, r6 )
|
||||||
str r6, [r2], #4
|
str r6, [r2], #4
|
||||||
subs r4, r4, #1
|
subs r4, r4, #1
|
||||||
bne 1b
|
bne 1b
|
||||||
|
@ -506,7 +520,7 @@ vcpu .req r0 @ vcpu pointer always in r0
|
||||||
mcr p15, 0, r2, c14, c3, 1 @ CNTV_CTL
|
mcr p15, 0, r2, c14, c3, 1 @ CNTV_CTL
|
||||||
isb
|
isb
|
||||||
|
|
||||||
mrrc p15, 3, r2, r3, c14 @ CNTV_CVAL
|
mrrc p15, 3, rr_lo_hi(r2, r3), c14 @ CNTV_CVAL
|
||||||
ldr r4, =VCPU_TIMER_CNTV_CVAL
|
ldr r4, =VCPU_TIMER_CNTV_CVAL
|
||||||
add r5, vcpu, r4
|
add r5, vcpu, r4
|
||||||
strd r2, r3, [r5]
|
strd r2, r3, [r5]
|
||||||
|
@ -546,12 +560,12 @@ vcpu .req r0 @ vcpu pointer always in r0
|
||||||
|
|
||||||
ldr r2, [r4, #KVM_TIMER_CNTVOFF]
|
ldr r2, [r4, #KVM_TIMER_CNTVOFF]
|
||||||
ldr r3, [r4, #(KVM_TIMER_CNTVOFF + 4)]
|
ldr r3, [r4, #(KVM_TIMER_CNTVOFF + 4)]
|
||||||
mcrr p15, 4, r2, r3, c14 @ CNTVOFF
|
mcrr p15, 4, rr_lo_hi(r2, r3), c14 @ CNTVOFF
|
||||||
|
|
||||||
ldr r4, =VCPU_TIMER_CNTV_CVAL
|
ldr r4, =VCPU_TIMER_CNTV_CVAL
|
||||||
add r5, vcpu, r4
|
add r5, vcpu, r4
|
||||||
ldrd r2, r3, [r5]
|
ldrd r2, r3, [r5]
|
||||||
mcrr p15, 3, r2, r3, c14 @ CNTV_CVAL
|
mcrr p15, 3, rr_lo_hi(r2, r3), c14 @ CNTV_CVAL
|
||||||
isb
|
isb
|
||||||
|
|
||||||
ldr r2, [vcpu, #VCPU_TIMER_CNTV_CTL]
|
ldr r2, [vcpu, #VCPU_TIMER_CNTV_CTL]
|
||||||
|
|
|
@ -90,104 +90,115 @@ static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc)
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool page_empty(void *ptr)
|
static void clear_pgd_entry(struct kvm *kvm, pgd_t *pgd, phys_addr_t addr)
|
||||||
{
|
{
|
||||||
struct page *ptr_page = virt_to_page(ptr);
|
pud_t *pud_table __maybe_unused = pud_offset(pgd, 0);
|
||||||
return page_count(ptr_page) == 1;
|
pgd_clear(pgd);
|
||||||
|
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||||
|
pud_free(NULL, 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_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
|
||||||
{
|
{
|
||||||
if (pud_huge(*pud)) {
|
pmd_t *pmd_table = pmd_offset(pud, 0);
|
||||||
pud_clear(pud);
|
VM_BUG_ON(pud_huge(*pud));
|
||||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
pud_clear(pud);
|
||||||
} else {
|
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||||
pmd_t *pmd_table = pmd_offset(pud, 0);
|
pmd_free(NULL, pmd_table);
|
||||||
pud_clear(pud);
|
|
||||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
|
||||||
pmd_free(NULL, pmd_table);
|
|
||||||
}
|
|
||||||
put_page(virt_to_page(pud));
|
put_page(virt_to_page(pud));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
|
static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
|
||||||
{
|
{
|
||||||
if (kvm_pmd_huge(*pmd)) {
|
pte_t *pte_table = pte_offset_kernel(pmd, 0);
|
||||||
pmd_clear(pmd);
|
VM_BUG_ON(kvm_pmd_huge(*pmd));
|
||||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
pmd_clear(pmd);
|
||||||
} else {
|
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||||
pte_t *pte_table = pte_offset_kernel(pmd, 0);
|
pte_free_kernel(NULL, pte_table);
|
||||||
pmd_clear(pmd);
|
|
||||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
|
||||||
pte_free_kernel(NULL, pte_table);
|
|
||||||
}
|
|
||||||
put_page(virt_to_page(pmd));
|
put_page(virt_to_page(pmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr)
|
static void unmap_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||||
|
phys_addr_t addr, phys_addr_t end)
|
||||||
{
|
{
|
||||||
if (pte_present(*pte)) {
|
phys_addr_t start_addr = addr;
|
||||||
kvm_set_pte(pte, __pte(0));
|
pte_t *pte, *start_pte;
|
||||||
put_page(virt_to_page(pte));
|
|
||||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
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));
|
||||||
|
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||||
|
}
|
||||||
|
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||||
|
|
||||||
|
if (kvm_pte_table_empty(start_pte))
|
||||||
|
clear_pmd_entry(kvm, pmd, start_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
|
static void unmap_pmds(struct kvm *kvm, pud_t *pud,
|
||||||
unsigned long long start, u64 size)
|
phys_addr_t addr, phys_addr_t end)
|
||||||
{
|
{
|
||||||
pgd_t *pgd;
|
phys_addr_t next, start_addr = addr;
|
||||||
pud_t *pud;
|
pmd_t *pmd, *start_pmd;
|
||||||
pmd_t *pmd;
|
|
||||||
pte_t *pte;
|
|
||||||
unsigned long long addr = start, end = start + size;
|
|
||||||
u64 next;
|
|
||||||
|
|
||||||
while (addr < end) {
|
start_pmd = pmd = pmd_offset(pud, addr);
|
||||||
pgd = pgdp + pgd_index(addr);
|
do {
|
||||||
pud = pud_offset(pgd, addr);
|
next = kvm_pmd_addr_end(addr, end);
|
||||||
pte = NULL;
|
if (!pmd_none(*pmd)) {
|
||||||
if (pud_none(*pud)) {
|
if (kvm_pmd_huge(*pmd)) {
|
||||||
addr = kvm_pud_addr_end(addr, end);
|
pmd_clear(pmd);
|
||||||
continue;
|
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||||
}
|
put_page(virt_to_page(pmd));
|
||||||
|
} else {
|
||||||
if (pud_huge(*pud)) {
|
unmap_ptes(kvm, pmd, addr, next);
|
||||||
/*
|
|
||||||
* If we are dealing with a huge pud, just clear it and
|
|
||||||
* move on.
|
|
||||||
*/
|
|
||||||
clear_pud_entry(kvm, pud, addr);
|
|
||||||
addr = kvm_pud_addr_end(addr, end);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pmd = pmd_offset(pud, addr);
|
|
||||||
if (pmd_none(*pmd)) {
|
|
||||||
addr = kvm_pmd_addr_end(addr, end);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!kvm_pmd_huge(*pmd)) {
|
|
||||||
pte = pte_offset_kernel(pmd, addr);
|
|
||||||
clear_pte_entry(kvm, pte, addr);
|
|
||||||
next = addr + PAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the pmd entry is to be cleared, walk back up the ladder
|
|
||||||
*/
|
|
||||||
if (kvm_pmd_huge(*pmd) || (pte && page_empty(pte))) {
|
|
||||||
clear_pmd_entry(kvm, pmd, addr);
|
|
||||||
next = kvm_pmd_addr_end(addr, end);
|
|
||||||
if (page_empty(pmd) && !page_empty(pud)) {
|
|
||||||
clear_pud_entry(kvm, pud, addr);
|
|
||||||
next = kvm_pud_addr_end(addr, end);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} while (pmd++, addr = next, addr != end);
|
||||||
|
|
||||||
addr = next;
|
if (kvm_pmd_table_empty(start_pmd))
|
||||||
}
|
clear_pud_entry(kvm, pud, start_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unmap_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);
|
||||||
|
do {
|
||||||
|
next = kvm_pud_addr_end(addr, end);
|
||||||
|
if (!pud_none(*pud)) {
|
||||||
|
if (pud_huge(*pud)) {
|
||||||
|
pud_clear(pud);
|
||||||
|
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||||
|
put_page(virt_to_page(pud));
|
||||||
|
} else {
|
||||||
|
unmap_pmds(kvm, pud, addr, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (pud++, addr = next, addr != end);
|
||||||
|
|
||||||
|
if (kvm_pud_table_empty(start_pud))
|
||||||
|
clear_pgd_entry(kvm, pgd, start_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
|
||||||
|
phys_addr_t start, u64 size)
|
||||||
|
{
|
||||||
|
pgd_t *pgd;
|
||||||
|
phys_addr_t addr = start, end = start + size;
|
||||||
|
phys_addr_t next;
|
||||||
|
|
||||||
|
pgd = pgdp + pgd_index(addr);
|
||||||
|
do {
|
||||||
|
next = kvm_pgd_addr_end(addr, end);
|
||||||
|
unmap_puds(kvm, pgd, addr, next);
|
||||||
|
} while (pgd++, addr = next, addr != end);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
|
static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||||
|
@ -748,6 +759,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||||
struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
|
struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
|
pgprot_t mem_type = PAGE_S2;
|
||||||
|
|
||||||
write_fault = kvm_is_write_fault(kvm_vcpu_get_hsr(vcpu));
|
write_fault = kvm_is_write_fault(kvm_vcpu_get_hsr(vcpu));
|
||||||
if (fault_status == FSC_PERM && !write_fault) {
|
if (fault_status == FSC_PERM && !write_fault) {
|
||||||
|
@ -798,6 +810,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||||
if (is_error_pfn(pfn))
|
if (is_error_pfn(pfn))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (kvm_is_mmio_pfn(pfn))
|
||||||
|
mem_type = PAGE_S2_DEVICE;
|
||||||
|
|
||||||
spin_lock(&kvm->mmu_lock);
|
spin_lock(&kvm->mmu_lock);
|
||||||
if (mmu_notifier_retry(kvm, mmu_seq))
|
if (mmu_notifier_retry(kvm, mmu_seq))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
@ -805,7 +820,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||||
hugetlb = transparent_hugepage_adjust(&pfn, &fault_ipa);
|
hugetlb = transparent_hugepage_adjust(&pfn, &fault_ipa);
|
||||||
|
|
||||||
if (hugetlb) {
|
if (hugetlb) {
|
||||||
pmd_t new_pmd = pfn_pmd(pfn, PAGE_S2);
|
pmd_t new_pmd = pfn_pmd(pfn, mem_type);
|
||||||
new_pmd = pmd_mkhuge(new_pmd);
|
new_pmd = pmd_mkhuge(new_pmd);
|
||||||
if (writable) {
|
if (writable) {
|
||||||
kvm_set_s2pmd_writable(&new_pmd);
|
kvm_set_s2pmd_writable(&new_pmd);
|
||||||
|
@ -814,13 +829,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||||
coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE);
|
coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE);
|
||||||
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
|
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
|
||||||
} else {
|
} else {
|
||||||
pte_t new_pte = pfn_pte(pfn, PAGE_S2);
|
pte_t new_pte = pfn_pte(pfn, mem_type);
|
||||||
if (writable) {
|
if (writable) {
|
||||||
kvm_set_s2pte_writable(&new_pte);
|
kvm_set_s2pte_writable(&new_pte);
|
||||||
kvm_set_pfn_dirty(pfn);
|
kvm_set_pfn_dirty(pfn);
|
||||||
}
|
}
|
||||||
coherent_cache_guest_page(vcpu, hva, PAGE_SIZE);
|
coherent_cache_guest_page(vcpu, hva, PAGE_SIZE);
|
||||||
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, false);
|
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte,
|
||||||
|
mem_type == PAGE_S2_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1100,3 +1116,49 @@ int kvm_mmu_init(void)
|
||||||
free_hyp_pgds();
|
free_hyp_pgds();
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_arch_commit_memory_region(struct kvm *kvm,
|
||||||
|
struct kvm_userspace_memory_region *mem,
|
||||||
|
const struct kvm_memory_slot *old,
|
||||||
|
enum kvm_mr_change change)
|
||||||
|
{
|
||||||
|
gpa_t gpa = old->base_gfn << PAGE_SHIFT;
|
||||||
|
phys_addr_t size = old->npages << PAGE_SHIFT;
|
||||||
|
if (change == KVM_MR_DELETE || change == KVM_MR_MOVE) {
|
||||||
|
spin_lock(&kvm->mmu_lock);
|
||||||
|
unmap_stage2_range(kvm, gpa, size);
|
||||||
|
spin_unlock(&kvm->mmu_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
||||||
|
struct kvm_memory_slot *memslot,
|
||||||
|
struct kvm_userspace_memory_region *mem,
|
||||||
|
enum kvm_mr_change change)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
|
||||||
|
struct kvm_memory_slot *dont)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
|
||||||
|
unsigned long npages)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_arch_memslots_updated(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_arch_flush_shadow_all(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
||||||
|
struct kvm_memory_slot *slot)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,15 @@
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
/* Low-level stepping controls. */
|
||||||
|
#define DBG_MDSCR_SS (1 << 0)
|
||||||
|
#define DBG_SPSR_SS (1 << 21)
|
||||||
|
|
||||||
|
/* MDSCR_EL1 enabling bits */
|
||||||
|
#define DBG_MDSCR_KDE (1 << 13)
|
||||||
|
#define DBG_MDSCR_MDE (1 << 15)
|
||||||
|
#define DBG_MDSCR_MASK ~(DBG_MDSCR_KDE | DBG_MDSCR_MDE)
|
||||||
|
|
||||||
#define DBG_ESR_EVT(x) (((x) >> 27) & 0x7)
|
#define DBG_ESR_EVT(x) (((x) >> 27) & 0x7)
|
||||||
|
|
||||||
/* AArch64 */
|
/* AArch64 */
|
||||||
|
@ -73,11 +82,6 @@
|
||||||
|
|
||||||
#define CACHE_FLUSH_IS_SAFE 1
|
#define CACHE_FLUSH_IS_SAFE 1
|
||||||
|
|
||||||
enum debug_el {
|
|
||||||
DBG_ACTIVE_EL0 = 0,
|
|
||||||
DBG_ACTIVE_EL1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* AArch32 */
|
/* AArch32 */
|
||||||
#define DBG_ESR_EVT_BKPT 0x4
|
#define DBG_ESR_EVT_BKPT 0x4
|
||||||
#define DBG_ESR_EVT_VECC 0x5
|
#define DBG_ESR_EVT_VECC 0x5
|
||||||
|
@ -115,6 +119,11 @@ void unregister_break_hook(struct break_hook *hook);
|
||||||
|
|
||||||
u8 debug_monitors_arch(void);
|
u8 debug_monitors_arch(void);
|
||||||
|
|
||||||
|
enum debug_el {
|
||||||
|
DBG_ACTIVE_EL0 = 0,
|
||||||
|
DBG_ACTIVE_EL1,
|
||||||
|
};
|
||||||
|
|
||||||
void enable_debug_monitors(enum debug_el el);
|
void enable_debug_monitors(enum debug_el el);
|
||||||
void disable_debug_monitors(enum debug_el el);
|
void disable_debug_monitors(enum debug_el el);
|
||||||
|
|
||||||
|
|
|
@ -76,9 +76,10 @@
|
||||||
*/
|
*/
|
||||||
#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
|
#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
|
||||||
HCR_TVM | HCR_BSU_IS | HCR_FB | HCR_TAC | \
|
HCR_TVM | HCR_BSU_IS | HCR_FB | HCR_TAC | \
|
||||||
HCR_AMO | HCR_IMO | HCR_FMO | \
|
HCR_AMO | HCR_SWIO | HCR_TIDCP | HCR_RW)
|
||||||
HCR_SWIO | HCR_TIDCP | HCR_RW)
|
|
||||||
#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
|
#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
|
||||||
|
#define HCR_INT_OVERRIDE (HCR_FMO | HCR_IMO)
|
||||||
|
|
||||||
|
|
||||||
/* Hyp System Control Register (SCTLR_EL2) bits */
|
/* Hyp System Control Register (SCTLR_EL2) bits */
|
||||||
#define SCTLR_EL2_EE (1 << 25)
|
#define SCTLR_EL2_EE (1 << 25)
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#ifndef __ARM_KVM_ASM_H__
|
#ifndef __ARM_KVM_ASM_H__
|
||||||
#define __ARM_KVM_ASM_H__
|
#define __ARM_KVM_ASM_H__
|
||||||
|
|
||||||
|
#include <asm/virt.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 0 is reserved as an invalid value.
|
* 0 is reserved as an invalid value.
|
||||||
* Order *must* be kept in sync with the hyp switch code.
|
* Order *must* be kept in sync with the hyp switch code.
|
||||||
|
@ -43,14 +45,25 @@
|
||||||
#define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */
|
#define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */
|
||||||
#define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */
|
#define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */
|
||||||
#define PAR_EL1 21 /* Physical Address Register */
|
#define PAR_EL1 21 /* Physical Address Register */
|
||||||
|
#define MDSCR_EL1 22 /* Monitor Debug System Control Register */
|
||||||
|
#define DBGBCR0_EL1 23 /* Debug Breakpoint Control Registers (0-15) */
|
||||||
|
#define DBGBCR15_EL1 38
|
||||||
|
#define DBGBVR0_EL1 39 /* Debug Breakpoint Value Registers (0-15) */
|
||||||
|
#define DBGBVR15_EL1 54
|
||||||
|
#define DBGWCR0_EL1 55 /* Debug Watchpoint Control Registers (0-15) */
|
||||||
|
#define DBGWCR15_EL1 70
|
||||||
|
#define DBGWVR0_EL1 71 /* Debug Watchpoint Value Registers (0-15) */
|
||||||
|
#define DBGWVR15_EL1 86
|
||||||
|
#define MDCCINT_EL1 87 /* Monitor Debug Comms Channel Interrupt Enable Reg */
|
||||||
|
|
||||||
/* 32bit specific registers. Keep them at the end of the range */
|
/* 32bit specific registers. Keep them at the end of the range */
|
||||||
#define DACR32_EL2 22 /* Domain Access Control Register */
|
#define DACR32_EL2 88 /* Domain Access Control Register */
|
||||||
#define IFSR32_EL2 23 /* Instruction Fault Status Register */
|
#define IFSR32_EL2 89 /* Instruction Fault Status Register */
|
||||||
#define FPEXC32_EL2 24 /* Floating-Point Exception Control Register */
|
#define FPEXC32_EL2 90 /* Floating-Point Exception Control Register */
|
||||||
#define DBGVCR32_EL2 25 /* Debug Vector Catch Register */
|
#define DBGVCR32_EL2 91 /* Debug Vector Catch Register */
|
||||||
#define TEECR32_EL1 26 /* ThumbEE Configuration Register */
|
#define TEECR32_EL1 92 /* ThumbEE Configuration Register */
|
||||||
#define TEEHBR32_EL1 27 /* ThumbEE Handler Base Register */
|
#define TEEHBR32_EL1 93 /* ThumbEE Handler Base Register */
|
||||||
#define NR_SYS_REGS 28
|
#define NR_SYS_REGS 94
|
||||||
|
|
||||||
/* 32bit mapping */
|
/* 32bit mapping */
|
||||||
#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
|
#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
|
||||||
|
@ -82,11 +95,23 @@
|
||||||
#define c10_AMAIR0 (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
|
#define c10_AMAIR0 (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
|
||||||
#define c10_AMAIR1 (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
|
#define c10_AMAIR1 (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
|
||||||
#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
|
#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
|
||||||
#define NR_CP15_REGS (NR_SYS_REGS * 2)
|
|
||||||
|
#define cp14_DBGDSCRext (MDSCR_EL1 * 2)
|
||||||
|
#define cp14_DBGBCR0 (DBGBCR0_EL1 * 2)
|
||||||
|
#define cp14_DBGBVR0 (DBGBVR0_EL1 * 2)
|
||||||
|
#define cp14_DBGBXVR0 (cp14_DBGBVR0 + 1)
|
||||||
|
#define cp14_DBGWCR0 (DBGWCR0_EL1 * 2)
|
||||||
|
#define cp14_DBGWVR0 (DBGWVR0_EL1 * 2)
|
||||||
|
#define cp14_DBGDCCINT (MDCCINT_EL1 * 2)
|
||||||
|
|
||||||
|
#define NR_COPRO_REGS (NR_SYS_REGS * 2)
|
||||||
|
|
||||||
#define ARM_EXCEPTION_IRQ 0
|
#define ARM_EXCEPTION_IRQ 0
|
||||||
#define ARM_EXCEPTION_TRAP 1
|
#define ARM_EXCEPTION_TRAP 1
|
||||||
|
|
||||||
|
#define KVM_ARM64_DEBUG_DIRTY_SHIFT 0
|
||||||
|
#define KVM_ARM64_DEBUG_DIRTY (1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
struct kvm;
|
struct kvm;
|
||||||
struct kvm_vcpu;
|
struct kvm_vcpu;
|
||||||
|
@ -96,13 +121,21 @@ extern char __kvm_hyp_init_end[];
|
||||||
|
|
||||||
extern char __kvm_hyp_vector[];
|
extern char __kvm_hyp_vector[];
|
||||||
|
|
||||||
extern char __kvm_hyp_code_start[];
|
#define __kvm_hyp_code_start __hyp_text_start
|
||||||
extern char __kvm_hyp_code_end[];
|
#define __kvm_hyp_code_end __hyp_text_end
|
||||||
|
|
||||||
extern void __kvm_flush_vm_context(void);
|
extern void __kvm_flush_vm_context(void);
|
||||||
extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
|
extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
|
||||||
|
|
||||||
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
|
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
|
extern u64 __vgic_v3_get_ich_vtr_el2(void);
|
||||||
|
|
||||||
|
extern char __save_vgic_v2_state[];
|
||||||
|
extern char __restore_vgic_v2_state[];
|
||||||
|
extern char __save_vgic_v3_state[];
|
||||||
|
extern char __restore_vgic_v3_state[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __ARM_KVM_ASM_H__ */
|
#endif /* __ARM_KVM_ASM_H__ */
|
||||||
|
|
|
@ -39,7 +39,8 @@ void kvm_register_target_sys_reg_table(unsigned int target,
|
||||||
struct kvm_sys_reg_target_table *table);
|
struct kvm_sys_reg_target_table *table);
|
||||||
|
|
||||||
int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
||||||
int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
||||||
|
int kvm_handle_cp14_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
||||||
int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
||||||
int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
||||||
int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run);
|
||||||
|
|
|
@ -213,6 +213,17 @@ static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
|
||||||
default:
|
default:
|
||||||
return be64_to_cpu(data);
|
return be64_to_cpu(data);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
switch (len) {
|
||||||
|
case 1:
|
||||||
|
return data & 0xff;
|
||||||
|
case 2:
|
||||||
|
return le16_to_cpu(data & 0xffff);
|
||||||
|
case 4:
|
||||||
|
return le32_to_cpu(data & 0xffffffff);
|
||||||
|
default:
|
||||||
|
return le64_to_cpu(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data; /* Leave LE untouched */
|
return data; /* Leave LE untouched */
|
||||||
|
@ -233,6 +244,17 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
|
||||||
default:
|
default:
|
||||||
return cpu_to_be64(data);
|
return cpu_to_be64(data);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
switch (len) {
|
||||||
|
case 1:
|
||||||
|
return data & 0xff;
|
||||||
|
case 2:
|
||||||
|
return cpu_to_le16(data & 0xffff);
|
||||||
|
case 4:
|
||||||
|
return cpu_to_le32(data & 0xffffffff);
|
||||||
|
default:
|
||||||
|
return cpu_to_le64(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data; /* Leave LE untouched */
|
return data; /* Leave LE untouched */
|
||||||
|
|
|
@ -86,7 +86,7 @@ struct kvm_cpu_context {
|
||||||
struct kvm_regs gp_regs;
|
struct kvm_regs gp_regs;
|
||||||
union {
|
union {
|
||||||
u64 sys_regs[NR_SYS_REGS];
|
u64 sys_regs[NR_SYS_REGS];
|
||||||
u32 cp15[NR_CP15_REGS];
|
u32 copro[NR_COPRO_REGS];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,6 +101,9 @@ struct kvm_vcpu_arch {
|
||||||
/* Exception Information */
|
/* Exception Information */
|
||||||
struct kvm_vcpu_fault_info fault;
|
struct kvm_vcpu_fault_info fault;
|
||||||
|
|
||||||
|
/* Debug state */
|
||||||
|
u64 debug_flags;
|
||||||
|
|
||||||
/* Pointer to host CPU context */
|
/* Pointer to host CPU context */
|
||||||
kvm_cpu_context_t *host_cpu_context;
|
kvm_cpu_context_t *host_cpu_context;
|
||||||
|
|
||||||
|
@ -138,7 +141,20 @@ struct kvm_vcpu_arch {
|
||||||
|
|
||||||
#define vcpu_gp_regs(v) (&(v)->arch.ctxt.gp_regs)
|
#define vcpu_gp_regs(v) (&(v)->arch.ctxt.gp_regs)
|
||||||
#define vcpu_sys_reg(v,r) ((v)->arch.ctxt.sys_regs[(r)])
|
#define vcpu_sys_reg(v,r) ((v)->arch.ctxt.sys_regs[(r)])
|
||||||
#define vcpu_cp15(v,r) ((v)->arch.ctxt.cp15[(r)])
|
/*
|
||||||
|
* CP14 and CP15 live in the same array, as they are backed by the
|
||||||
|
* same system registers.
|
||||||
|
*/
|
||||||
|
#define vcpu_cp14(v,r) ((v)->arch.ctxt.copro[(r)])
|
||||||
|
#define vcpu_cp15(v,r) ((v)->arch.ctxt.copro[(r)])
|
||||||
|
|
||||||
|
#ifdef CONFIG_CPU_BIG_ENDIAN
|
||||||
|
#define vcpu_cp15_64_high(v,r) vcpu_cp15((v),(r))
|
||||||
|
#define vcpu_cp15_64_low(v,r) vcpu_cp15((v),(r) + 1)
|
||||||
|
#else
|
||||||
|
#define vcpu_cp15_64_high(v,r) vcpu_cp15((v),(r) + 1)
|
||||||
|
#define vcpu_cp15_64_low(v,r) vcpu_cp15((v),(r))
|
||||||
|
#endif
|
||||||
|
|
||||||
struct kvm_vm_stat {
|
struct kvm_vm_stat {
|
||||||
u32 remote_tlb_flush;
|
u32 remote_tlb_flush;
|
||||||
|
@ -200,4 +216,32 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
|
||||||
hyp_stack_ptr, vector_ptr);
|
hyp_stack_ptr, vector_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct vgic_sr_vectors {
|
||||||
|
void *save_vgic;
|
||||||
|
void *restore_vgic;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void vgic_arch_setup(const struct vgic_params *vgic)
|
||||||
|
{
|
||||||
|
extern struct vgic_sr_vectors __vgic_sr_vectors;
|
||||||
|
|
||||||
|
switch(vgic->type)
|
||||||
|
{
|
||||||
|
case VGIC_V2:
|
||||||
|
__vgic_sr_vectors.save_vgic = __save_vgic_v2_state;
|
||||||
|
__vgic_sr_vectors.restore_vgic = __restore_vgic_v2_state;
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef CONFIG_ARM_GIC_V3
|
||||||
|
case VGIC_V3:
|
||||||
|
__vgic_sr_vectors.save_vgic = __save_vgic_v3_state;
|
||||||
|
__vgic_sr_vectors.restore_vgic = __restore_vgic_v3_state;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __ARM64_KVM_HOST_H__ */
|
#endif /* __ARM64_KVM_HOST_H__ */
|
||||||
|
|
|
@ -125,6 +125,21 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
|
||||||
#define kvm_pud_addr_end(addr, end) pud_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)
|
#define kvm_pmd_addr_end(addr, end) pmd_addr_end(addr, end)
|
||||||
|
|
||||||
|
static inline bool kvm_page_empty(void *ptr)
|
||||||
|
{
|
||||||
|
struct page *ptr_page = virt_to_page(ptr);
|
||||||
|
return page_count(ptr_page) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define kvm_pte_table_empty(ptep) kvm_page_empty(ptep)
|
||||||
|
#ifndef CONFIG_ARM64_64K_PAGES
|
||||||
|
#define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp)
|
||||||
|
#else
|
||||||
|
#define kvm_pmd_table_empty(pmdp) (0)
|
||||||
|
#endif
|
||||||
|
#define kvm_pud_table_empty(pudp) (0)
|
||||||
|
|
||||||
|
|
||||||
struct kvm;
|
struct kvm;
|
||||||
|
|
||||||
#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
|
#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
|
||||||
|
|
|
@ -50,6 +50,10 @@ static inline bool is_hyp_mode_mismatched(void)
|
||||||
return __boot_cpu_mode[0] != __boot_cpu_mode[1];
|
return __boot_cpu_mode[0] != __boot_cpu_mode[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The section containing the hypervisor text */
|
||||||
|
extern char __hyp_text_start[];
|
||||||
|
extern char __hyp_text_end[];
|
||||||
|
|
||||||
#endif /* __ASSEMBLY__ */
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
#endif /* ! __ASM__VIRT_H */
|
#endif /* ! __ASM__VIRT_H */
|
||||||
|
|
|
@ -120,6 +120,7 @@ int main(void)
|
||||||
DEFINE(VCPU_ESR_EL2, offsetof(struct kvm_vcpu, arch.fault.esr_el2));
|
DEFINE(VCPU_ESR_EL2, offsetof(struct kvm_vcpu, arch.fault.esr_el2));
|
||||||
DEFINE(VCPU_FAR_EL2, offsetof(struct kvm_vcpu, arch.fault.far_el2));
|
DEFINE(VCPU_FAR_EL2, offsetof(struct kvm_vcpu, arch.fault.far_el2));
|
||||||
DEFINE(VCPU_HPFAR_EL2, offsetof(struct kvm_vcpu, arch.fault.hpfar_el2));
|
DEFINE(VCPU_HPFAR_EL2, offsetof(struct kvm_vcpu, arch.fault.hpfar_el2));
|
||||||
|
DEFINE(VCPU_DEBUG_FLAGS, offsetof(struct kvm_vcpu, arch.debug_flags));
|
||||||
DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2));
|
DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2));
|
||||||
DEFINE(VCPU_IRQ_LINES, offsetof(struct kvm_vcpu, arch.irq_lines));
|
DEFINE(VCPU_IRQ_LINES, offsetof(struct kvm_vcpu, arch.irq_lines));
|
||||||
DEFINE(VCPU_HOST_CONTEXT, offsetof(struct kvm_vcpu, arch.host_cpu_context));
|
DEFINE(VCPU_HOST_CONTEXT, offsetof(struct kvm_vcpu, arch.host_cpu_context));
|
||||||
|
@ -129,13 +130,24 @@ int main(void)
|
||||||
DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled));
|
DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled));
|
||||||
DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm));
|
DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm));
|
||||||
DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu));
|
DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu));
|
||||||
DEFINE(VGIC_CPU_HCR, offsetof(struct vgic_cpu, vgic_hcr));
|
DEFINE(VGIC_SAVE_FN, offsetof(struct vgic_sr_vectors, save_vgic));
|
||||||
DEFINE(VGIC_CPU_VMCR, offsetof(struct vgic_cpu, vgic_vmcr));
|
DEFINE(VGIC_RESTORE_FN, offsetof(struct vgic_sr_vectors, restore_vgic));
|
||||||
DEFINE(VGIC_CPU_MISR, offsetof(struct vgic_cpu, vgic_misr));
|
DEFINE(VGIC_SR_VECTOR_SZ, sizeof(struct vgic_sr_vectors));
|
||||||
DEFINE(VGIC_CPU_EISR, offsetof(struct vgic_cpu, vgic_eisr));
|
DEFINE(VGIC_V2_CPU_HCR, offsetof(struct vgic_cpu, vgic_v2.vgic_hcr));
|
||||||
DEFINE(VGIC_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_elrsr));
|
DEFINE(VGIC_V2_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr));
|
||||||
DEFINE(VGIC_CPU_APR, offsetof(struct vgic_cpu, vgic_apr));
|
DEFINE(VGIC_V2_CPU_MISR, offsetof(struct vgic_cpu, vgic_v2.vgic_misr));
|
||||||
DEFINE(VGIC_CPU_LR, offsetof(struct vgic_cpu, vgic_lr));
|
DEFINE(VGIC_V2_CPU_EISR, offsetof(struct vgic_cpu, vgic_v2.vgic_eisr));
|
||||||
|
DEFINE(VGIC_V2_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr));
|
||||||
|
DEFINE(VGIC_V2_CPU_APR, offsetof(struct vgic_cpu, vgic_v2.vgic_apr));
|
||||||
|
DEFINE(VGIC_V2_CPU_LR, offsetof(struct vgic_cpu, vgic_v2.vgic_lr));
|
||||||
|
DEFINE(VGIC_V3_CPU_HCR, offsetof(struct vgic_cpu, vgic_v3.vgic_hcr));
|
||||||
|
DEFINE(VGIC_V3_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v3.vgic_vmcr));
|
||||||
|
DEFINE(VGIC_V3_CPU_MISR, offsetof(struct vgic_cpu, vgic_v3.vgic_misr));
|
||||||
|
DEFINE(VGIC_V3_CPU_EISR, offsetof(struct vgic_cpu, vgic_v3.vgic_eisr));
|
||||||
|
DEFINE(VGIC_V3_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v3.vgic_elrsr));
|
||||||
|
DEFINE(VGIC_V3_CPU_AP0R, offsetof(struct vgic_cpu, vgic_v3.vgic_ap0r));
|
||||||
|
DEFINE(VGIC_V3_CPU_AP1R, offsetof(struct vgic_cpu, vgic_v3.vgic_ap1r));
|
||||||
|
DEFINE(VGIC_V3_CPU_LR, offsetof(struct vgic_cpu, vgic_v3.vgic_lr));
|
||||||
DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
|
DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
|
||||||
DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
|
DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
|
||||||
DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
|
DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
|
||||||
|
|
|
@ -30,15 +30,6 @@
|
||||||
#include <asm/cputype.h>
|
#include <asm/cputype.h>
|
||||||
#include <asm/system_misc.h>
|
#include <asm/system_misc.h>
|
||||||
|
|
||||||
/* Low-level stepping controls. */
|
|
||||||
#define DBG_MDSCR_SS (1 << 0)
|
|
||||||
#define DBG_SPSR_SS (1 << 21)
|
|
||||||
|
|
||||||
/* MDSCR_EL1 enabling bits */
|
|
||||||
#define DBG_MDSCR_KDE (1 << 13)
|
|
||||||
#define DBG_MDSCR_MDE (1 << 15)
|
|
||||||
#define DBG_MDSCR_MASK ~(DBG_MDSCR_KDE | DBG_MDSCR_MDE)
|
|
||||||
|
|
||||||
/* Determine debug architecture. */
|
/* Determine debug architecture. */
|
||||||
u8 debug_monitors_arch(void)
|
u8 debug_monitors_arch(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,4 +20,8 @@ kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o
|
||||||
kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o
|
kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o
|
||||||
|
|
||||||
kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
|
kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
|
||||||
|
kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v2.o
|
||||||
|
kvm-$(CONFIG_KVM_ARM_VGIC) += vgic-v2-switch.o
|
||||||
|
kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v3.o
|
||||||
|
kvm-$(CONFIG_KVM_ARM_VGIC) += vgic-v3-switch.o
|
||||||
kvm-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
|
kvm-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o
|
||||||
|
|
|
@ -135,6 +135,59 @@ static unsigned long num_core_regs(void)
|
||||||
return sizeof(struct kvm_regs) / sizeof(__u32);
|
return sizeof(struct kvm_regs) / sizeof(__u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ARM64 versions of the TIMER registers, always available on arm64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NUM_TIMER_REGS 3
|
||||||
|
|
||||||
|
static bool is_timer_reg(u64 index)
|
||||||
|
{
|
||||||
|
switch (index) {
|
||||||
|
case KVM_REG_ARM_TIMER_CTL:
|
||||||
|
case KVM_REG_ARM_TIMER_CNT:
|
||||||
|
case KVM_REG_ARM_TIMER_CVAL:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_timer_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
||||||
|
{
|
||||||
|
if (put_user(KVM_REG_ARM_TIMER_CTL, uindices))
|
||||||
|
return -EFAULT;
|
||||||
|
uindices++;
|
||||||
|
if (put_user(KVM_REG_ARM_TIMER_CNT, uindices))
|
||||||
|
return -EFAULT;
|
||||||
|
uindices++;
|
||||||
|
if (put_user(KVM_REG_ARM_TIMER_CVAL, uindices))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
|
{
|
||||||
|
void __user *uaddr = (void __user *)(long)reg->addr;
|
||||||
|
u64 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return kvm_arm_timer_set_reg(vcpu, reg->id, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
|
{
|
||||||
|
void __user *uaddr = (void __user *)(long)reg->addr;
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
val = kvm_arm_timer_get_reg(vcpu, reg->id);
|
||||||
|
return copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_arm_num_regs - how many registers do we present via KVM_GET_ONE_REG
|
* kvm_arm_num_regs - how many registers do we present via KVM_GET_ONE_REG
|
||||||
*
|
*
|
||||||
|
@ -142,7 +195,8 @@ static unsigned long num_core_regs(void)
|
||||||
*/
|
*/
|
||||||
unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
|
unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu);
|
return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu)
|
||||||
|
+ NUM_TIMER_REGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,6 +208,7 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
const u64 core_reg = KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE;
|
const u64 core_reg = KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE;
|
||||||
|
int ret;
|
||||||
|
|
||||||
for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) {
|
for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) {
|
||||||
if (put_user(core_reg | i, uindices))
|
if (put_user(core_reg | i, uindices))
|
||||||
|
@ -161,6 +216,11 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
||||||
uindices++;
|
uindices++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = copy_timer_indices(vcpu, uindices);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
uindices += NUM_TIMER_REGS;
|
||||||
|
|
||||||
return kvm_arm_copy_sys_reg_indices(vcpu, uindices);
|
return kvm_arm_copy_sys_reg_indices(vcpu, uindices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +234,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
|
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
|
||||||
return get_core_reg(vcpu, reg);
|
return get_core_reg(vcpu, reg);
|
||||||
|
|
||||||
|
if (is_timer_reg(reg->id))
|
||||||
|
return get_timer_reg(vcpu, reg);
|
||||||
|
|
||||||
return kvm_arm_sys_reg_get_reg(vcpu, reg);
|
return kvm_arm_sys_reg_get_reg(vcpu, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +250,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||||
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
|
if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
|
||||||
return set_core_reg(vcpu, reg);
|
return set_core_reg(vcpu, reg);
|
||||||
|
|
||||||
|
if (is_timer_reg(reg->id))
|
||||||
|
return set_timer_reg(vcpu, reg);
|
||||||
|
|
||||||
return kvm_arm_sys_reg_set_reg(vcpu, reg);
|
return kvm_arm_sys_reg_set_reg(vcpu, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,9 +73,9 @@ static exit_handle_fn arm_exit_handlers[] = {
|
||||||
[ESR_EL2_EC_WFI] = kvm_handle_wfx,
|
[ESR_EL2_EC_WFI] = kvm_handle_wfx,
|
||||||
[ESR_EL2_EC_CP15_32] = kvm_handle_cp15_32,
|
[ESR_EL2_EC_CP15_32] = kvm_handle_cp15_32,
|
||||||
[ESR_EL2_EC_CP15_64] = kvm_handle_cp15_64,
|
[ESR_EL2_EC_CP15_64] = kvm_handle_cp15_64,
|
||||||
[ESR_EL2_EC_CP14_MR] = kvm_handle_cp14_access,
|
[ESR_EL2_EC_CP14_MR] = kvm_handle_cp14_32,
|
||||||
[ESR_EL2_EC_CP14_LS] = kvm_handle_cp14_load_store,
|
[ESR_EL2_EC_CP14_LS] = kvm_handle_cp14_load_store,
|
||||||
[ESR_EL2_EC_CP14_64] = kvm_handle_cp14_access,
|
[ESR_EL2_EC_CP14_64] = kvm_handle_cp14_64,
|
||||||
[ESR_EL2_EC_HVC32] = handle_hvc,
|
[ESR_EL2_EC_HVC32] = handle_hvc,
|
||||||
[ESR_EL2_EC_SMC32] = handle_smc,
|
[ESR_EL2_EC_SMC32] = handle_smc,
|
||||||
[ESR_EL2_EC_HVC64] = handle_hvc,
|
[ESR_EL2_EC_HVC64] = handle_hvc,
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/linkage.h>
|
#include <linux/linkage.h>
|
||||||
#include <linux/irqchip/arm-gic.h>
|
|
||||||
|
|
||||||
#include <asm/assembler.h>
|
#include <asm/assembler.h>
|
||||||
#include <asm/memory.h>
|
#include <asm/memory.h>
|
||||||
#include <asm/asm-offsets.h>
|
#include <asm/asm-offsets.h>
|
||||||
|
#include <asm/debug-monitors.h>
|
||||||
#include <asm/fpsimdmacros.h>
|
#include <asm/fpsimdmacros.h>
|
||||||
#include <asm/kvm.h>
|
#include <asm/kvm.h>
|
||||||
#include <asm/kvm_asm.h>
|
#include <asm/kvm_asm.h>
|
||||||
|
@ -36,9 +36,6 @@
|
||||||
.pushsection .hyp.text, "ax"
|
.pushsection .hyp.text, "ax"
|
||||||
.align PAGE_SHIFT
|
.align PAGE_SHIFT
|
||||||
|
|
||||||
__kvm_hyp_code_start:
|
|
||||||
.globl __kvm_hyp_code_start
|
|
||||||
|
|
||||||
.macro save_common_regs
|
.macro save_common_regs
|
||||||
// x2: base address for cpu context
|
// x2: base address for cpu context
|
||||||
// x3: tmp register
|
// x3: tmp register
|
||||||
|
@ -215,6 +212,7 @@ __kvm_hyp_code_start:
|
||||||
mrs x22, amair_el1
|
mrs x22, amair_el1
|
||||||
mrs x23, cntkctl_el1
|
mrs x23, cntkctl_el1
|
||||||
mrs x24, par_el1
|
mrs x24, par_el1
|
||||||
|
mrs x25, mdscr_el1
|
||||||
|
|
||||||
stp x4, x5, [x3]
|
stp x4, x5, [x3]
|
||||||
stp x6, x7, [x3, #16]
|
stp x6, x7, [x3, #16]
|
||||||
|
@ -226,7 +224,202 @@ __kvm_hyp_code_start:
|
||||||
stp x18, x19, [x3, #112]
|
stp x18, x19, [x3, #112]
|
||||||
stp x20, x21, [x3, #128]
|
stp x20, x21, [x3, #128]
|
||||||
stp x22, x23, [x3, #144]
|
stp x22, x23, [x3, #144]
|
||||||
str x24, [x3, #160]
|
stp x24, x25, [x3, #160]
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro save_debug
|
||||||
|
// x2: base address for cpu context
|
||||||
|
// x3: tmp register
|
||||||
|
|
||||||
|
mrs x26, id_aa64dfr0_el1
|
||||||
|
ubfx x24, x26, #12, #4 // Extract BRPs
|
||||||
|
ubfx x25, x26, #20, #4 // Extract WRPs
|
||||||
|
mov w26, #15
|
||||||
|
sub w24, w26, w24 // How many BPs to skip
|
||||||
|
sub w25, w26, w25 // How many WPs to skip
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBCR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
mrs x20, dbgbcr15_el1
|
||||||
|
mrs x19, dbgbcr14_el1
|
||||||
|
mrs x18, dbgbcr13_el1
|
||||||
|
mrs x17, dbgbcr12_el1
|
||||||
|
mrs x16, dbgbcr11_el1
|
||||||
|
mrs x15, dbgbcr10_el1
|
||||||
|
mrs x14, dbgbcr9_el1
|
||||||
|
mrs x13, dbgbcr8_el1
|
||||||
|
mrs x12, dbgbcr7_el1
|
||||||
|
mrs x11, dbgbcr6_el1
|
||||||
|
mrs x10, dbgbcr5_el1
|
||||||
|
mrs x9, dbgbcr4_el1
|
||||||
|
mrs x8, dbgbcr3_el1
|
||||||
|
mrs x7, dbgbcr2_el1
|
||||||
|
mrs x6, dbgbcr1_el1
|
||||||
|
mrs x5, dbgbcr0_el1
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
|
||||||
|
1:
|
||||||
|
str x20, [x3, #(15 * 8)]
|
||||||
|
str x19, [x3, #(14 * 8)]
|
||||||
|
str x18, [x3, #(13 * 8)]
|
||||||
|
str x17, [x3, #(12 * 8)]
|
||||||
|
str x16, [x3, #(11 * 8)]
|
||||||
|
str x15, [x3, #(10 * 8)]
|
||||||
|
str x14, [x3, #(9 * 8)]
|
||||||
|
str x13, [x3, #(8 * 8)]
|
||||||
|
str x12, [x3, #(7 * 8)]
|
||||||
|
str x11, [x3, #(6 * 8)]
|
||||||
|
str x10, [x3, #(5 * 8)]
|
||||||
|
str x9, [x3, #(4 * 8)]
|
||||||
|
str x8, [x3, #(3 * 8)]
|
||||||
|
str x7, [x3, #(2 * 8)]
|
||||||
|
str x6, [x3, #(1 * 8)]
|
||||||
|
str x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBVR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
mrs x20, dbgbvr15_el1
|
||||||
|
mrs x19, dbgbvr14_el1
|
||||||
|
mrs x18, dbgbvr13_el1
|
||||||
|
mrs x17, dbgbvr12_el1
|
||||||
|
mrs x16, dbgbvr11_el1
|
||||||
|
mrs x15, dbgbvr10_el1
|
||||||
|
mrs x14, dbgbvr9_el1
|
||||||
|
mrs x13, dbgbvr8_el1
|
||||||
|
mrs x12, dbgbvr7_el1
|
||||||
|
mrs x11, dbgbvr6_el1
|
||||||
|
mrs x10, dbgbvr5_el1
|
||||||
|
mrs x9, dbgbvr4_el1
|
||||||
|
mrs x8, dbgbvr3_el1
|
||||||
|
mrs x7, dbgbvr2_el1
|
||||||
|
mrs x6, dbgbvr1_el1
|
||||||
|
mrs x5, dbgbvr0_el1
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
|
||||||
|
1:
|
||||||
|
str x20, [x3, #(15 * 8)]
|
||||||
|
str x19, [x3, #(14 * 8)]
|
||||||
|
str x18, [x3, #(13 * 8)]
|
||||||
|
str x17, [x3, #(12 * 8)]
|
||||||
|
str x16, [x3, #(11 * 8)]
|
||||||
|
str x15, [x3, #(10 * 8)]
|
||||||
|
str x14, [x3, #(9 * 8)]
|
||||||
|
str x13, [x3, #(8 * 8)]
|
||||||
|
str x12, [x3, #(7 * 8)]
|
||||||
|
str x11, [x3, #(6 * 8)]
|
||||||
|
str x10, [x3, #(5 * 8)]
|
||||||
|
str x9, [x3, #(4 * 8)]
|
||||||
|
str x8, [x3, #(3 * 8)]
|
||||||
|
str x7, [x3, #(2 * 8)]
|
||||||
|
str x6, [x3, #(1 * 8)]
|
||||||
|
str x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWCR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
mrs x20, dbgwcr15_el1
|
||||||
|
mrs x19, dbgwcr14_el1
|
||||||
|
mrs x18, dbgwcr13_el1
|
||||||
|
mrs x17, dbgwcr12_el1
|
||||||
|
mrs x16, dbgwcr11_el1
|
||||||
|
mrs x15, dbgwcr10_el1
|
||||||
|
mrs x14, dbgwcr9_el1
|
||||||
|
mrs x13, dbgwcr8_el1
|
||||||
|
mrs x12, dbgwcr7_el1
|
||||||
|
mrs x11, dbgwcr6_el1
|
||||||
|
mrs x10, dbgwcr5_el1
|
||||||
|
mrs x9, dbgwcr4_el1
|
||||||
|
mrs x8, dbgwcr3_el1
|
||||||
|
mrs x7, dbgwcr2_el1
|
||||||
|
mrs x6, dbgwcr1_el1
|
||||||
|
mrs x5, dbgwcr0_el1
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
|
||||||
|
1:
|
||||||
|
str x20, [x3, #(15 * 8)]
|
||||||
|
str x19, [x3, #(14 * 8)]
|
||||||
|
str x18, [x3, #(13 * 8)]
|
||||||
|
str x17, [x3, #(12 * 8)]
|
||||||
|
str x16, [x3, #(11 * 8)]
|
||||||
|
str x15, [x3, #(10 * 8)]
|
||||||
|
str x14, [x3, #(9 * 8)]
|
||||||
|
str x13, [x3, #(8 * 8)]
|
||||||
|
str x12, [x3, #(7 * 8)]
|
||||||
|
str x11, [x3, #(6 * 8)]
|
||||||
|
str x10, [x3, #(5 * 8)]
|
||||||
|
str x9, [x3, #(4 * 8)]
|
||||||
|
str x8, [x3, #(3 * 8)]
|
||||||
|
str x7, [x3, #(2 * 8)]
|
||||||
|
str x6, [x3, #(1 * 8)]
|
||||||
|
str x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWVR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
mrs x20, dbgwvr15_el1
|
||||||
|
mrs x19, dbgwvr14_el1
|
||||||
|
mrs x18, dbgwvr13_el1
|
||||||
|
mrs x17, dbgwvr12_el1
|
||||||
|
mrs x16, dbgwvr11_el1
|
||||||
|
mrs x15, dbgwvr10_el1
|
||||||
|
mrs x14, dbgwvr9_el1
|
||||||
|
mrs x13, dbgwvr8_el1
|
||||||
|
mrs x12, dbgwvr7_el1
|
||||||
|
mrs x11, dbgwvr6_el1
|
||||||
|
mrs x10, dbgwvr5_el1
|
||||||
|
mrs x9, dbgwvr4_el1
|
||||||
|
mrs x8, dbgwvr3_el1
|
||||||
|
mrs x7, dbgwvr2_el1
|
||||||
|
mrs x6, dbgwvr1_el1
|
||||||
|
mrs x5, dbgwvr0_el1
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
|
||||||
|
1:
|
||||||
|
str x20, [x3, #(15 * 8)]
|
||||||
|
str x19, [x3, #(14 * 8)]
|
||||||
|
str x18, [x3, #(13 * 8)]
|
||||||
|
str x17, [x3, #(12 * 8)]
|
||||||
|
str x16, [x3, #(11 * 8)]
|
||||||
|
str x15, [x3, #(10 * 8)]
|
||||||
|
str x14, [x3, #(9 * 8)]
|
||||||
|
str x13, [x3, #(8 * 8)]
|
||||||
|
str x12, [x3, #(7 * 8)]
|
||||||
|
str x11, [x3, #(6 * 8)]
|
||||||
|
str x10, [x3, #(5 * 8)]
|
||||||
|
str x9, [x3, #(4 * 8)]
|
||||||
|
str x8, [x3, #(3 * 8)]
|
||||||
|
str x7, [x3, #(2 * 8)]
|
||||||
|
str x6, [x3, #(1 * 8)]
|
||||||
|
str x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
mrs x21, mdccint_el1
|
||||||
|
str x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro restore_sysregs
|
.macro restore_sysregs
|
||||||
|
@ -245,7 +438,7 @@ __kvm_hyp_code_start:
|
||||||
ldp x18, x19, [x3, #112]
|
ldp x18, x19, [x3, #112]
|
||||||
ldp x20, x21, [x3, #128]
|
ldp x20, x21, [x3, #128]
|
||||||
ldp x22, x23, [x3, #144]
|
ldp x22, x23, [x3, #144]
|
||||||
ldr x24, [x3, #160]
|
ldp x24, x25, [x3, #160]
|
||||||
|
|
||||||
msr vmpidr_el2, x4
|
msr vmpidr_el2, x4
|
||||||
msr csselr_el1, x5
|
msr csselr_el1, x5
|
||||||
|
@ -268,6 +461,198 @@ __kvm_hyp_code_start:
|
||||||
msr amair_el1, x22
|
msr amair_el1, x22
|
||||||
msr cntkctl_el1, x23
|
msr cntkctl_el1, x23
|
||||||
msr par_el1, x24
|
msr par_el1, x24
|
||||||
|
msr mdscr_el1, x25
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro restore_debug
|
||||||
|
// x2: base address for cpu context
|
||||||
|
// x3: tmp register
|
||||||
|
|
||||||
|
mrs x26, id_aa64dfr0_el1
|
||||||
|
ubfx x24, x26, #12, #4 // Extract BRPs
|
||||||
|
ubfx x25, x26, #20, #4 // Extract WRPs
|
||||||
|
mov w26, #15
|
||||||
|
sub w24, w26, w24 // How many BPs to skip
|
||||||
|
sub w25, w26, w25 // How many WPs to skip
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBCR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
ldr x20, [x3, #(15 * 8)]
|
||||||
|
ldr x19, [x3, #(14 * 8)]
|
||||||
|
ldr x18, [x3, #(13 * 8)]
|
||||||
|
ldr x17, [x3, #(12 * 8)]
|
||||||
|
ldr x16, [x3, #(11 * 8)]
|
||||||
|
ldr x15, [x3, #(10 * 8)]
|
||||||
|
ldr x14, [x3, #(9 * 8)]
|
||||||
|
ldr x13, [x3, #(8 * 8)]
|
||||||
|
ldr x12, [x3, #(7 * 8)]
|
||||||
|
ldr x11, [x3, #(6 * 8)]
|
||||||
|
ldr x10, [x3, #(5 * 8)]
|
||||||
|
ldr x9, [x3, #(4 * 8)]
|
||||||
|
ldr x8, [x3, #(3 * 8)]
|
||||||
|
ldr x7, [x3, #(2 * 8)]
|
||||||
|
ldr x6, [x3, #(1 * 8)]
|
||||||
|
ldr x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
msr dbgbcr15_el1, x20
|
||||||
|
msr dbgbcr14_el1, x19
|
||||||
|
msr dbgbcr13_el1, x18
|
||||||
|
msr dbgbcr12_el1, x17
|
||||||
|
msr dbgbcr11_el1, x16
|
||||||
|
msr dbgbcr10_el1, x15
|
||||||
|
msr dbgbcr9_el1, x14
|
||||||
|
msr dbgbcr8_el1, x13
|
||||||
|
msr dbgbcr7_el1, x12
|
||||||
|
msr dbgbcr6_el1, x11
|
||||||
|
msr dbgbcr5_el1, x10
|
||||||
|
msr dbgbcr4_el1, x9
|
||||||
|
msr dbgbcr3_el1, x8
|
||||||
|
msr dbgbcr2_el1, x7
|
||||||
|
msr dbgbcr1_el1, x6
|
||||||
|
msr dbgbcr0_el1, x5
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGBVR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
ldr x20, [x3, #(15 * 8)]
|
||||||
|
ldr x19, [x3, #(14 * 8)]
|
||||||
|
ldr x18, [x3, #(13 * 8)]
|
||||||
|
ldr x17, [x3, #(12 * 8)]
|
||||||
|
ldr x16, [x3, #(11 * 8)]
|
||||||
|
ldr x15, [x3, #(10 * 8)]
|
||||||
|
ldr x14, [x3, #(9 * 8)]
|
||||||
|
ldr x13, [x3, #(8 * 8)]
|
||||||
|
ldr x12, [x3, #(7 * 8)]
|
||||||
|
ldr x11, [x3, #(6 * 8)]
|
||||||
|
ldr x10, [x3, #(5 * 8)]
|
||||||
|
ldr x9, [x3, #(4 * 8)]
|
||||||
|
ldr x8, [x3, #(3 * 8)]
|
||||||
|
ldr x7, [x3, #(2 * 8)]
|
||||||
|
ldr x6, [x3, #(1 * 8)]
|
||||||
|
ldr x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x24, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
msr dbgbvr15_el1, x20
|
||||||
|
msr dbgbvr14_el1, x19
|
||||||
|
msr dbgbvr13_el1, x18
|
||||||
|
msr dbgbvr12_el1, x17
|
||||||
|
msr dbgbvr11_el1, x16
|
||||||
|
msr dbgbvr10_el1, x15
|
||||||
|
msr dbgbvr9_el1, x14
|
||||||
|
msr dbgbvr8_el1, x13
|
||||||
|
msr dbgbvr7_el1, x12
|
||||||
|
msr dbgbvr6_el1, x11
|
||||||
|
msr dbgbvr5_el1, x10
|
||||||
|
msr dbgbvr4_el1, x9
|
||||||
|
msr dbgbvr3_el1, x8
|
||||||
|
msr dbgbvr2_el1, x7
|
||||||
|
msr dbgbvr1_el1, x6
|
||||||
|
msr dbgbvr0_el1, x5
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWCR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
ldr x20, [x3, #(15 * 8)]
|
||||||
|
ldr x19, [x3, #(14 * 8)]
|
||||||
|
ldr x18, [x3, #(13 * 8)]
|
||||||
|
ldr x17, [x3, #(12 * 8)]
|
||||||
|
ldr x16, [x3, #(11 * 8)]
|
||||||
|
ldr x15, [x3, #(10 * 8)]
|
||||||
|
ldr x14, [x3, #(9 * 8)]
|
||||||
|
ldr x13, [x3, #(8 * 8)]
|
||||||
|
ldr x12, [x3, #(7 * 8)]
|
||||||
|
ldr x11, [x3, #(6 * 8)]
|
||||||
|
ldr x10, [x3, #(5 * 8)]
|
||||||
|
ldr x9, [x3, #(4 * 8)]
|
||||||
|
ldr x8, [x3, #(3 * 8)]
|
||||||
|
ldr x7, [x3, #(2 * 8)]
|
||||||
|
ldr x6, [x3, #(1 * 8)]
|
||||||
|
ldr x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
msr dbgwcr15_el1, x20
|
||||||
|
msr dbgwcr14_el1, x19
|
||||||
|
msr dbgwcr13_el1, x18
|
||||||
|
msr dbgwcr12_el1, x17
|
||||||
|
msr dbgwcr11_el1, x16
|
||||||
|
msr dbgwcr10_el1, x15
|
||||||
|
msr dbgwcr9_el1, x14
|
||||||
|
msr dbgwcr8_el1, x13
|
||||||
|
msr dbgwcr7_el1, x12
|
||||||
|
msr dbgwcr6_el1, x11
|
||||||
|
msr dbgwcr5_el1, x10
|
||||||
|
msr dbgwcr4_el1, x9
|
||||||
|
msr dbgwcr3_el1, x8
|
||||||
|
msr dbgwcr2_el1, x7
|
||||||
|
msr dbgwcr1_el1, x6
|
||||||
|
msr dbgwcr0_el1, x5
|
||||||
|
|
||||||
|
add x3, x2, #CPU_SYSREG_OFFSET(DBGWVR0_EL1)
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
ldr x20, [x3, #(15 * 8)]
|
||||||
|
ldr x19, [x3, #(14 * 8)]
|
||||||
|
ldr x18, [x3, #(13 * 8)]
|
||||||
|
ldr x17, [x3, #(12 * 8)]
|
||||||
|
ldr x16, [x3, #(11 * 8)]
|
||||||
|
ldr x15, [x3, #(10 * 8)]
|
||||||
|
ldr x14, [x3, #(9 * 8)]
|
||||||
|
ldr x13, [x3, #(8 * 8)]
|
||||||
|
ldr x12, [x3, #(7 * 8)]
|
||||||
|
ldr x11, [x3, #(6 * 8)]
|
||||||
|
ldr x10, [x3, #(5 * 8)]
|
||||||
|
ldr x9, [x3, #(4 * 8)]
|
||||||
|
ldr x8, [x3, #(3 * 8)]
|
||||||
|
ldr x7, [x3, #(2 * 8)]
|
||||||
|
ldr x6, [x3, #(1 * 8)]
|
||||||
|
ldr x5, [x3, #(0 * 8)]
|
||||||
|
|
||||||
|
adr x26, 1f
|
||||||
|
add x26, x26, x25, lsl #2
|
||||||
|
br x26
|
||||||
|
1:
|
||||||
|
msr dbgwvr15_el1, x20
|
||||||
|
msr dbgwvr14_el1, x19
|
||||||
|
msr dbgwvr13_el1, x18
|
||||||
|
msr dbgwvr12_el1, x17
|
||||||
|
msr dbgwvr11_el1, x16
|
||||||
|
msr dbgwvr10_el1, x15
|
||||||
|
msr dbgwvr9_el1, x14
|
||||||
|
msr dbgwvr8_el1, x13
|
||||||
|
msr dbgwvr7_el1, x12
|
||||||
|
msr dbgwvr6_el1, x11
|
||||||
|
msr dbgwvr5_el1, x10
|
||||||
|
msr dbgwvr4_el1, x9
|
||||||
|
msr dbgwvr3_el1, x8
|
||||||
|
msr dbgwvr2_el1, x7
|
||||||
|
msr dbgwvr1_el1, x6
|
||||||
|
msr dbgwvr0_el1, x5
|
||||||
|
|
||||||
|
ldr x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
|
||||||
|
msr mdccint_el1, x21
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro skip_32bit_state tmp, target
|
.macro skip_32bit_state tmp, target
|
||||||
|
@ -282,6 +667,35 @@ __kvm_hyp_code_start:
|
||||||
tbz \tmp, #12, \target
|
tbz \tmp, #12, \target
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
.macro skip_debug_state tmp, target
|
||||||
|
ldr \tmp, [x0, #VCPU_DEBUG_FLAGS]
|
||||||
|
tbz \tmp, #KVM_ARM64_DEBUG_DIRTY_SHIFT, \target
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro compute_debug_state target
|
||||||
|
// Compute debug state: If any of KDE, MDE or KVM_ARM64_DEBUG_DIRTY
|
||||||
|
// is set, we do a full save/restore cycle and disable trapping.
|
||||||
|
add x25, x0, #VCPU_CONTEXT
|
||||||
|
|
||||||
|
// Check the state of MDSCR_EL1
|
||||||
|
ldr x25, [x25, #CPU_SYSREG_OFFSET(MDSCR_EL1)]
|
||||||
|
and x26, x25, #DBG_MDSCR_KDE
|
||||||
|
and x25, x25, #DBG_MDSCR_MDE
|
||||||
|
adds xzr, x25, x26
|
||||||
|
b.eq 9998f // Nothing to see there
|
||||||
|
|
||||||
|
// If any interesting bits was set, we must set the flag
|
||||||
|
mov x26, #KVM_ARM64_DEBUG_DIRTY
|
||||||
|
str x26, [x0, #VCPU_DEBUG_FLAGS]
|
||||||
|
b 9999f // Don't skip restore
|
||||||
|
|
||||||
|
9998:
|
||||||
|
// Otherwise load the flags from memory in case we recently
|
||||||
|
// trapped
|
||||||
|
skip_debug_state x25, \target
|
||||||
|
9999:
|
||||||
|
.endm
|
||||||
|
|
||||||
.macro save_guest_32bit_state
|
.macro save_guest_32bit_state
|
||||||
skip_32bit_state x3, 1f
|
skip_32bit_state x3, 1f
|
||||||
|
|
||||||
|
@ -297,10 +711,13 @@ __kvm_hyp_code_start:
|
||||||
mrs x4, dacr32_el2
|
mrs x4, dacr32_el2
|
||||||
mrs x5, ifsr32_el2
|
mrs x5, ifsr32_el2
|
||||||
mrs x6, fpexc32_el2
|
mrs x6, fpexc32_el2
|
||||||
mrs x7, dbgvcr32_el2
|
|
||||||
stp x4, x5, [x3]
|
stp x4, x5, [x3]
|
||||||
stp x6, x7, [x3, #16]
|
str x6, [x3, #16]
|
||||||
|
|
||||||
|
skip_debug_state x8, 2f
|
||||||
|
mrs x7, dbgvcr32_el2
|
||||||
|
str x7, [x3, #24]
|
||||||
|
2:
|
||||||
skip_tee_state x8, 1f
|
skip_tee_state x8, 1f
|
||||||
|
|
||||||
add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
|
add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
|
||||||
|
@ -323,12 +740,15 @@ __kvm_hyp_code_start:
|
||||||
|
|
||||||
add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
|
add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
|
||||||
ldp x4, x5, [x3]
|
ldp x4, x5, [x3]
|
||||||
ldp x6, x7, [x3, #16]
|
ldr x6, [x3, #16]
|
||||||
msr dacr32_el2, x4
|
msr dacr32_el2, x4
|
||||||
msr ifsr32_el2, x5
|
msr ifsr32_el2, x5
|
||||||
msr fpexc32_el2, x6
|
msr fpexc32_el2, x6
|
||||||
msr dbgvcr32_el2, x7
|
|
||||||
|
|
||||||
|
skip_debug_state x8, 2f
|
||||||
|
ldr x7, [x3, #24]
|
||||||
|
msr dbgvcr32_el2, x7
|
||||||
|
2:
|
||||||
skip_tee_state x8, 1f
|
skip_tee_state x8, 1f
|
||||||
|
|
||||||
add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
|
add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1)
|
||||||
|
@ -339,11 +759,8 @@ __kvm_hyp_code_start:
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro activate_traps
|
.macro activate_traps
|
||||||
ldr x2, [x0, #VCPU_IRQ_LINES]
|
ldr x2, [x0, #VCPU_HCR_EL2]
|
||||||
ldr x1, [x0, #VCPU_HCR_EL2]
|
msr hcr_el2, x2
|
||||||
orr x2, x2, x1
|
|
||||||
msr hcr_el2, x2
|
|
||||||
|
|
||||||
ldr x2, =(CPTR_EL2_TTA)
|
ldr x2, =(CPTR_EL2_TTA)
|
||||||
msr cptr_el2, x2
|
msr cptr_el2, x2
|
||||||
|
|
||||||
|
@ -353,6 +770,14 @@ __kvm_hyp_code_start:
|
||||||
mrs x2, mdcr_el2
|
mrs x2, mdcr_el2
|
||||||
and x2, x2, #MDCR_EL2_HPMN_MASK
|
and x2, x2, #MDCR_EL2_HPMN_MASK
|
||||||
orr x2, x2, #(MDCR_EL2_TPM | MDCR_EL2_TPMCR)
|
orr x2, x2, #(MDCR_EL2_TPM | MDCR_EL2_TPMCR)
|
||||||
|
orr x2, x2, #(MDCR_EL2_TDRA | MDCR_EL2_TDOSA)
|
||||||
|
|
||||||
|
// Check for KVM_ARM64_DEBUG_DIRTY, and set debug to trap
|
||||||
|
// if not dirty.
|
||||||
|
ldr x3, [x0, #VCPU_DEBUG_FLAGS]
|
||||||
|
tbnz x3, #KVM_ARM64_DEBUG_DIRTY_SHIFT, 1f
|
||||||
|
orr x2, x2, #MDCR_EL2_TDA
|
||||||
|
1:
|
||||||
msr mdcr_el2, x2
|
msr mdcr_el2, x2
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
@ -379,100 +804,33 @@ __kvm_hyp_code_start:
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save the VGIC CPU state into memory
|
* Call into the vgic backend for state saving
|
||||||
* x0: Register pointing to VCPU struct
|
|
||||||
* Do not corrupt x1!!!
|
|
||||||
*/
|
*/
|
||||||
.macro save_vgic_state
|
.macro save_vgic_state
|
||||||
/* Get VGIC VCTRL base into x2 */
|
adr x24, __vgic_sr_vectors
|
||||||
ldr x2, [x0, #VCPU_KVM]
|
ldr x24, [x24, VGIC_SAVE_FN]
|
||||||
kern_hyp_va x2
|
kern_hyp_va x24
|
||||||
ldr x2, [x2, #KVM_VGIC_VCTRL]
|
blr x24
|
||||||
kern_hyp_va x2
|
mrs x24, hcr_el2
|
||||||
cbz x2, 2f // disabled
|
mov x25, #HCR_INT_OVERRIDE
|
||||||
|
neg x25, x25
|
||||||
/* Compute the address of struct vgic_cpu */
|
and x24, x24, x25
|
||||||
add x3, x0, #VCPU_VGIC_CPU
|
msr hcr_el2, x24
|
||||||
|
|
||||||
/* Save all interesting registers */
|
|
||||||
ldr w4, [x2, #GICH_HCR]
|
|
||||||
ldr w5, [x2, #GICH_VMCR]
|
|
||||||
ldr w6, [x2, #GICH_MISR]
|
|
||||||
ldr w7, [x2, #GICH_EISR0]
|
|
||||||
ldr w8, [x2, #GICH_EISR1]
|
|
||||||
ldr w9, [x2, #GICH_ELRSR0]
|
|
||||||
ldr w10, [x2, #GICH_ELRSR1]
|
|
||||||
ldr w11, [x2, #GICH_APR]
|
|
||||||
CPU_BE( rev w4, w4 )
|
|
||||||
CPU_BE( rev w5, w5 )
|
|
||||||
CPU_BE( rev w6, w6 )
|
|
||||||
CPU_BE( rev w7, w7 )
|
|
||||||
CPU_BE( rev w8, w8 )
|
|
||||||
CPU_BE( rev w9, w9 )
|
|
||||||
CPU_BE( rev w10, w10 )
|
|
||||||
CPU_BE( rev w11, w11 )
|
|
||||||
|
|
||||||
str w4, [x3, #VGIC_CPU_HCR]
|
|
||||||
str w5, [x3, #VGIC_CPU_VMCR]
|
|
||||||
str w6, [x3, #VGIC_CPU_MISR]
|
|
||||||
str w7, [x3, #VGIC_CPU_EISR]
|
|
||||||
str w8, [x3, #(VGIC_CPU_EISR + 4)]
|
|
||||||
str w9, [x3, #VGIC_CPU_ELRSR]
|
|
||||||
str w10, [x3, #(VGIC_CPU_ELRSR + 4)]
|
|
||||||
str w11, [x3, #VGIC_CPU_APR]
|
|
||||||
|
|
||||||
/* Clear GICH_HCR */
|
|
||||||
str wzr, [x2, #GICH_HCR]
|
|
||||||
|
|
||||||
/* Save list registers */
|
|
||||||
add x2, x2, #GICH_LR0
|
|
||||||
ldr w4, [x3, #VGIC_CPU_NR_LR]
|
|
||||||
add x3, x3, #VGIC_CPU_LR
|
|
||||||
1: ldr w5, [x2], #4
|
|
||||||
CPU_BE( rev w5, w5 )
|
|
||||||
str w5, [x3], #4
|
|
||||||
sub w4, w4, #1
|
|
||||||
cbnz w4, 1b
|
|
||||||
2:
|
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore the VGIC CPU state from memory
|
* Call into the vgic backend for state restoring
|
||||||
* x0: Register pointing to VCPU struct
|
|
||||||
*/
|
*/
|
||||||
.macro restore_vgic_state
|
.macro restore_vgic_state
|
||||||
/* Get VGIC VCTRL base into x2 */
|
mrs x24, hcr_el2
|
||||||
ldr x2, [x0, #VCPU_KVM]
|
ldr x25, [x0, #VCPU_IRQ_LINES]
|
||||||
kern_hyp_va x2
|
orr x24, x24, #HCR_INT_OVERRIDE
|
||||||
ldr x2, [x2, #KVM_VGIC_VCTRL]
|
orr x24, x24, x25
|
||||||
kern_hyp_va x2
|
msr hcr_el2, x24
|
||||||
cbz x2, 2f // disabled
|
adr x24, __vgic_sr_vectors
|
||||||
|
ldr x24, [x24, #VGIC_RESTORE_FN]
|
||||||
/* Compute the address of struct vgic_cpu */
|
kern_hyp_va x24
|
||||||
add x3, x0, #VCPU_VGIC_CPU
|
blr x24
|
||||||
|
|
||||||
/* We only restore a minimal set of registers */
|
|
||||||
ldr w4, [x3, #VGIC_CPU_HCR]
|
|
||||||
ldr w5, [x3, #VGIC_CPU_VMCR]
|
|
||||||
ldr w6, [x3, #VGIC_CPU_APR]
|
|
||||||
CPU_BE( rev w4, w4 )
|
|
||||||
CPU_BE( rev w5, w5 )
|
|
||||||
CPU_BE( rev w6, w6 )
|
|
||||||
|
|
||||||
str w4, [x2, #GICH_HCR]
|
|
||||||
str w5, [x2, #GICH_VMCR]
|
|
||||||
str w6, [x2, #GICH_APR]
|
|
||||||
|
|
||||||
/* Restore list registers */
|
|
||||||
add x2, x2, #GICH_LR0
|
|
||||||
ldr w4, [x3, #VGIC_CPU_NR_LR]
|
|
||||||
add x3, x3, #VGIC_CPU_LR
|
|
||||||
1: ldr w5, [x3], #4
|
|
||||||
CPU_BE( rev w5, w5 )
|
|
||||||
str w5, [x2], #4
|
|
||||||
sub w4, w4, #1
|
|
||||||
cbnz w4, 1b
|
|
||||||
2:
|
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro save_timer_state
|
.macro save_timer_state
|
||||||
|
@ -537,6 +895,14 @@ __restore_sysregs:
|
||||||
restore_sysregs
|
restore_sysregs
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
__save_debug:
|
||||||
|
save_debug
|
||||||
|
ret
|
||||||
|
|
||||||
|
__restore_debug:
|
||||||
|
restore_debug
|
||||||
|
ret
|
||||||
|
|
||||||
__save_fpsimd:
|
__save_fpsimd:
|
||||||
save_fpsimd
|
save_fpsimd
|
||||||
ret
|
ret
|
||||||
|
@ -568,6 +934,9 @@ ENTRY(__kvm_vcpu_run)
|
||||||
bl __save_fpsimd
|
bl __save_fpsimd
|
||||||
bl __save_sysregs
|
bl __save_sysregs
|
||||||
|
|
||||||
|
compute_debug_state 1f
|
||||||
|
bl __save_debug
|
||||||
|
1:
|
||||||
activate_traps
|
activate_traps
|
||||||
activate_vm
|
activate_vm
|
||||||
|
|
||||||
|
@ -579,6 +948,10 @@ ENTRY(__kvm_vcpu_run)
|
||||||
|
|
||||||
bl __restore_sysregs
|
bl __restore_sysregs
|
||||||
bl __restore_fpsimd
|
bl __restore_fpsimd
|
||||||
|
|
||||||
|
skip_debug_state x3, 1f
|
||||||
|
bl __restore_debug
|
||||||
|
1:
|
||||||
restore_guest_32bit_state
|
restore_guest_32bit_state
|
||||||
restore_guest_regs
|
restore_guest_regs
|
||||||
|
|
||||||
|
@ -595,6 +968,10 @@ __kvm_vcpu_return:
|
||||||
save_guest_regs
|
save_guest_regs
|
||||||
bl __save_fpsimd
|
bl __save_fpsimd
|
||||||
bl __save_sysregs
|
bl __save_sysregs
|
||||||
|
|
||||||
|
skip_debug_state x3, 1f
|
||||||
|
bl __save_debug
|
||||||
|
1:
|
||||||
save_guest_32bit_state
|
save_guest_32bit_state
|
||||||
|
|
||||||
save_timer_state
|
save_timer_state
|
||||||
|
@ -609,6 +986,14 @@ __kvm_vcpu_return:
|
||||||
|
|
||||||
bl __restore_sysregs
|
bl __restore_sysregs
|
||||||
bl __restore_fpsimd
|
bl __restore_fpsimd
|
||||||
|
|
||||||
|
skip_debug_state x3, 1f
|
||||||
|
// Clear the dirty flag for the next run, as all the state has
|
||||||
|
// already been saved. Note that we nuke the whole 64bit word.
|
||||||
|
// If we ever add more flags, we'll have to be more careful...
|
||||||
|
str xzr, [x0, #VCPU_DEBUG_FLAGS]
|
||||||
|
bl __restore_debug
|
||||||
|
1:
|
||||||
restore_host_regs
|
restore_host_regs
|
||||||
|
|
||||||
mov x0, x1
|
mov x0, x1
|
||||||
|
@ -653,6 +1038,12 @@ ENTRY(__kvm_flush_vm_context)
|
||||||
ret
|
ret
|
||||||
ENDPROC(__kvm_flush_vm_context)
|
ENDPROC(__kvm_flush_vm_context)
|
||||||
|
|
||||||
|
// struct vgic_sr_vectors __vgi_sr_vectors;
|
||||||
|
.align 3
|
||||||
|
ENTRY(__vgic_sr_vectors)
|
||||||
|
.skip VGIC_SR_VECTOR_SZ
|
||||||
|
ENDPROC(__vgic_sr_vectors)
|
||||||
|
|
||||||
__kvm_hyp_panic:
|
__kvm_hyp_panic:
|
||||||
// Guess the context by looking at VTTBR:
|
// Guess the context by looking at VTTBR:
|
||||||
// If zero, then we're already a host.
|
// If zero, then we're already a host.
|
||||||
|
@ -830,7 +1221,7 @@ el1_trap:
|
||||||
mrs x2, far_el2
|
mrs x2, far_el2
|
||||||
|
|
||||||
2: mrs x0, tpidr_el2
|
2: mrs x0, tpidr_el2
|
||||||
str x1, [x0, #VCPU_ESR_EL2]
|
str w1, [x0, #VCPU_ESR_EL2]
|
||||||
str x2, [x0, #VCPU_FAR_EL2]
|
str x2, [x0, #VCPU_FAR_EL2]
|
||||||
str x3, [x0, #VCPU_HPFAR_EL2]
|
str x3, [x0, #VCPU_HPFAR_EL2]
|
||||||
|
|
||||||
|
@ -880,7 +1271,4 @@ ENTRY(__kvm_hyp_vector)
|
||||||
ventry el1_error_invalid // Error 32-bit EL1
|
ventry el1_error_invalid // Error 32-bit EL1
|
||||||
ENDPROC(__kvm_hyp_vector)
|
ENDPROC(__kvm_hyp_vector)
|
||||||
|
|
||||||
__kvm_hyp_code_end:
|
|
||||||
.globl __kvm_hyp_code_end
|
|
||||||
|
|
||||||
.popsection
|
.popsection
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <asm/kvm_mmu.h>
|
#include <asm/kvm_mmu.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <asm/cputype.h>
|
#include <asm/cputype.h>
|
||||||
|
#include <asm/debug-monitors.h>
|
||||||
#include <trace/events/kvm.h>
|
#include <trace/events/kvm.h>
|
||||||
|
|
||||||
#include "sys_regs.h"
|
#include "sys_regs.h"
|
||||||
|
@ -137,10 +138,11 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
|
||||||
if (!p->is_aarch32) {
|
if (!p->is_aarch32) {
|
||||||
vcpu_sys_reg(vcpu, r->reg) = val;
|
vcpu_sys_reg(vcpu, r->reg) = val;
|
||||||
} else {
|
} else {
|
||||||
vcpu_cp15(vcpu, r->reg) = val & 0xffffffffUL;
|
|
||||||
if (!p->is_32bit)
|
if (!p->is_32bit)
|
||||||
vcpu_cp15(vcpu, r->reg + 1) = val >> 32;
|
vcpu_cp15_64_high(vcpu, r->reg) = val >> 32;
|
||||||
|
vcpu_cp15_64_low(vcpu, r->reg) = val & 0xffffffffUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,18 +165,9 @@ static bool access_sctlr(struct kvm_vcpu *vcpu,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static bool trap_raz_wi(struct kvm_vcpu *vcpu,
|
||||||
* We could trap ID_DFR0 and tell the guest we don't support performance
|
const struct sys_reg_params *p,
|
||||||
* monitoring. Unfortunately the patch to make the kernel check ID_DFR0 was
|
const struct sys_reg_desc *r)
|
||||||
* NAKed, so it will read the PMCR anyway.
|
|
||||||
*
|
|
||||||
* Therefore we tell the guest we have 0 counters. Unfortunately, we
|
|
||||||
* must always support PMCCNTR (the cycle counter): we just RAZ/WI for
|
|
||||||
* all PM registers, which doesn't crash the guest kernel at least.
|
|
||||||
*/
|
|
||||||
static bool pm_fake(struct kvm_vcpu *vcpu,
|
|
||||||
const struct sys_reg_params *p,
|
|
||||||
const struct sys_reg_desc *r)
|
|
||||||
{
|
{
|
||||||
if (p->is_write)
|
if (p->is_write)
|
||||||
return ignore_write(vcpu, p);
|
return ignore_write(vcpu, p);
|
||||||
|
@ -182,6 +175,73 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
|
||||||
return read_zero(vcpu, p);
|
return read_zero(vcpu, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool trap_oslsr_el1(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_params *p,
|
||||||
|
const struct sys_reg_desc *r)
|
||||||
|
{
|
||||||
|
if (p->is_write) {
|
||||||
|
return ignore_write(vcpu, p);
|
||||||
|
} else {
|
||||||
|
*vcpu_reg(vcpu, p->Rt) = (1 << 3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_params *p,
|
||||||
|
const struct sys_reg_desc *r)
|
||||||
|
{
|
||||||
|
if (p->is_write) {
|
||||||
|
return ignore_write(vcpu, p);
|
||||||
|
} else {
|
||||||
|
u32 val;
|
||||||
|
asm volatile("mrs %0, dbgauthstatus_el1" : "=r" (val));
|
||||||
|
*vcpu_reg(vcpu, p->Rt) = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We want to avoid world-switching all the DBG registers all the
|
||||||
|
* time:
|
||||||
|
*
|
||||||
|
* - If we've touched any debug register, it is likely that we're
|
||||||
|
* going to touch more of them. It then makes sense to disable the
|
||||||
|
* traps and start doing the save/restore dance
|
||||||
|
* - If debug is active (DBG_MDSCR_KDE or DBG_MDSCR_MDE set), it is
|
||||||
|
* then mandatory to save/restore the registers, as the guest
|
||||||
|
* depends on them.
|
||||||
|
*
|
||||||
|
* For this, we use a DIRTY bit, indicating the guest has modified the
|
||||||
|
* debug registers, used as follow:
|
||||||
|
*
|
||||||
|
* On guest entry:
|
||||||
|
* - If the dirty bit is set (because we're coming back from trapping),
|
||||||
|
* disable the traps, save host registers, restore guest registers.
|
||||||
|
* - If debug is actively in use (DBG_MDSCR_KDE or DBG_MDSCR_MDE set),
|
||||||
|
* set the dirty bit, disable the traps, save host registers,
|
||||||
|
* restore guest registers.
|
||||||
|
* - Otherwise, enable the traps
|
||||||
|
*
|
||||||
|
* On guest exit:
|
||||||
|
* - If the dirty bit is set, save guest registers, restore host
|
||||||
|
* registers and clear the dirty bit. This ensure that the host can
|
||||||
|
* now use the debug registers.
|
||||||
|
*/
|
||||||
|
static bool trap_debug_regs(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_params *p,
|
||||||
|
const struct sys_reg_desc *r)
|
||||||
|
{
|
||||||
|
if (p->is_write) {
|
||||||
|
vcpu_sys_reg(vcpu, r->reg) = *vcpu_reg(vcpu, p->Rt);
|
||||||
|
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
|
||||||
|
} else {
|
||||||
|
*vcpu_reg(vcpu, p->Rt) = vcpu_sys_reg(vcpu, r->reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||||
{
|
{
|
||||||
u64 amair;
|
u64 amair;
|
||||||
|
@ -198,9 +258,39 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||||
vcpu_sys_reg(vcpu, MPIDR_EL1) = (1UL << 31) | (vcpu->vcpu_id & 0xff);
|
vcpu_sys_reg(vcpu, MPIDR_EL1) = (1UL << 31) | (vcpu->vcpu_id & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
|
||||||
|
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
|
||||||
|
/* DBGBVRn_EL1 */ \
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b100), \
|
||||||
|
trap_debug_regs, reset_val, (DBGBVR0_EL1 + (n)), 0 }, \
|
||||||
|
/* DBGBCRn_EL1 */ \
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b101), \
|
||||||
|
trap_debug_regs, reset_val, (DBGBCR0_EL1 + (n)), 0 }, \
|
||||||
|
/* DBGWVRn_EL1 */ \
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b110), \
|
||||||
|
trap_debug_regs, reset_val, (DBGWVR0_EL1 + (n)), 0 }, \
|
||||||
|
/* DBGWCRn_EL1 */ \
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0000), CRm((n)), Op2(0b111), \
|
||||||
|
trap_debug_regs, reset_val, (DBGWCR0_EL1 + (n)), 0 }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Architected system registers.
|
* Architected system registers.
|
||||||
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
|
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
|
||||||
|
*
|
||||||
|
* We could trap ID_DFR0 and tell the guest we don't support performance
|
||||||
|
* monitoring. Unfortunately the patch to make the kernel check ID_DFR0 was
|
||||||
|
* NAKed, so it will read the PMCR anyway.
|
||||||
|
*
|
||||||
|
* Therefore we tell the guest we have 0 counters. Unfortunately, we
|
||||||
|
* must always support PMCCNTR (the cycle counter): we just RAZ/WI for
|
||||||
|
* all PM registers, which doesn't crash the guest kernel at least.
|
||||||
|
*
|
||||||
|
* Debug handling: We do trap most, if not all debug related system
|
||||||
|
* registers. The implementation is good enough to ensure that a guest
|
||||||
|
* can use these with minimal performance degradation. The drawback is
|
||||||
|
* that we don't implement any of the external debug, none of the
|
||||||
|
* OSlock protocol. This should be revisited if we ever encounter a
|
||||||
|
* more demanding guest...
|
||||||
*/
|
*/
|
||||||
static const struct sys_reg_desc sys_reg_descs[] = {
|
static const struct sys_reg_desc sys_reg_descs[] = {
|
||||||
/* DC ISW */
|
/* DC ISW */
|
||||||
|
@ -213,12 +303,71 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||||
{ Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010),
|
{ Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010),
|
||||||
access_dcsw },
|
access_dcsw },
|
||||||
|
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(0),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(1),
|
||||||
|
/* MDCCINT_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b000),
|
||||||
|
trap_debug_regs, reset_val, MDCCINT_EL1, 0 },
|
||||||
|
/* MDSCR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b010),
|
||||||
|
trap_debug_regs, reset_val, MDSCR_EL1, 0 },
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(2),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(3),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(4),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(5),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(6),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(7),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(8),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(9),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(10),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(11),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(12),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(13),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(14),
|
||||||
|
DBG_BCR_BVR_WCR_WVR_EL1(15),
|
||||||
|
|
||||||
|
/* MDRAR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* OSLAR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b100),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* OSLSR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0001), Op2(0b100),
|
||||||
|
trap_oslsr_el1 },
|
||||||
|
/* OSDLR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0011), Op2(0b100),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* DBGPRCR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0001), CRm(0b0100), Op2(0b100),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* DBGCLAIMSET_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0111), CRm(0b1000), Op2(0b110),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* DBGCLAIMCLR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0111), CRm(0b1001), Op2(0b110),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* DBGAUTHSTATUS_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b110),
|
||||||
|
trap_dbgauthstatus_el1 },
|
||||||
|
|
||||||
/* TEECR32_EL1 */
|
/* TEECR32_EL1 */
|
||||||
{ Op0(0b10), Op1(0b010), CRn(0b0000), CRm(0b0000), Op2(0b000),
|
{ Op0(0b10), Op1(0b010), CRn(0b0000), CRm(0b0000), Op2(0b000),
|
||||||
NULL, reset_val, TEECR32_EL1, 0 },
|
NULL, reset_val, TEECR32_EL1, 0 },
|
||||||
/* TEEHBR32_EL1 */
|
/* TEEHBR32_EL1 */
|
||||||
{ Op0(0b10), Op1(0b010), CRn(0b0001), CRm(0b0000), Op2(0b000),
|
{ Op0(0b10), Op1(0b010), CRn(0b0001), CRm(0b0000), Op2(0b000),
|
||||||
NULL, reset_val, TEEHBR32_EL1, 0 },
|
NULL, reset_val, TEEHBR32_EL1, 0 },
|
||||||
|
|
||||||
|
/* MDCCSR_EL1 */
|
||||||
|
{ Op0(0b10), Op1(0b011), CRn(0b0000), CRm(0b0001), Op2(0b000),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* DBGDTR_EL0 */
|
||||||
|
{ Op0(0b10), Op1(0b011), CRn(0b0000), CRm(0b0100), Op2(0b000),
|
||||||
|
trap_raz_wi },
|
||||||
|
/* DBGDTR[TR]X_EL0 */
|
||||||
|
{ Op0(0b10), Op1(0b011), CRn(0b0000), CRm(0b0101), Op2(0b000),
|
||||||
|
trap_raz_wi },
|
||||||
|
|
||||||
/* DBGVCR32_EL2 */
|
/* DBGVCR32_EL2 */
|
||||||
{ Op0(0b10), Op1(0b100), CRn(0b0000), CRm(0b0111), Op2(0b000),
|
{ Op0(0b10), Op1(0b100), CRn(0b0000), CRm(0b0111), Op2(0b000),
|
||||||
NULL, reset_val, DBGVCR32_EL2, 0 },
|
NULL, reset_val, DBGVCR32_EL2, 0 },
|
||||||
|
@ -260,10 +409,10 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||||
|
|
||||||
/* PMINTENSET_EL1 */
|
/* PMINTENSET_EL1 */
|
||||||
{ Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b001),
|
{ Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b001),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMINTENCLR_EL1 */
|
/* PMINTENCLR_EL1 */
|
||||||
{ Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b010),
|
{ Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b010),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
|
|
||||||
/* MAIR_EL1 */
|
/* MAIR_EL1 */
|
||||||
{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000),
|
{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000),
|
||||||
|
@ -292,43 +441,43 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||||
|
|
||||||
/* PMCR_EL0 */
|
/* PMCR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b000),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b000),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMCNTENSET_EL0 */
|
/* PMCNTENSET_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b001),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b001),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMCNTENCLR_EL0 */
|
/* PMCNTENCLR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b010),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b010),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMOVSCLR_EL0 */
|
/* PMOVSCLR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b011),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b011),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMSWINC_EL0 */
|
/* PMSWINC_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b100),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b100),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMSELR_EL0 */
|
/* PMSELR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b101),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b101),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMCEID0_EL0 */
|
/* PMCEID0_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b110),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b110),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMCEID1_EL0 */
|
/* PMCEID1_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b111),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b111),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMCCNTR_EL0 */
|
/* PMCCNTR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b000),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b000),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMXEVTYPER_EL0 */
|
/* PMXEVTYPER_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b001),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b001),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMXEVCNTR_EL0 */
|
/* PMXEVCNTR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b010),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b010),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMUSERENR_EL0 */
|
/* PMUSERENR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b000),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b000),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
/* PMOVSSET_EL0 */
|
/* PMOVSSET_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b011),
|
{ Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b011),
|
||||||
pm_fake },
|
trap_raz_wi },
|
||||||
|
|
||||||
/* TPIDR_EL0 */
|
/* TPIDR_EL0 */
|
||||||
{ Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b010),
|
{ Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b010),
|
||||||
|
@ -348,13 +497,161 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||||
NULL, reset_val, FPEXC32_EL2, 0x70 },
|
NULL, reset_val, FPEXC32_EL2, 0x70 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool trap_dbgidr(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_params *p,
|
||||||
|
const struct sys_reg_desc *r)
|
||||||
|
{
|
||||||
|
if (p->is_write) {
|
||||||
|
return ignore_write(vcpu, p);
|
||||||
|
} else {
|
||||||
|
u64 dfr = read_cpuid(ID_AA64DFR0_EL1);
|
||||||
|
u64 pfr = read_cpuid(ID_AA64PFR0_EL1);
|
||||||
|
u32 el3 = !!((pfr >> 12) & 0xf);
|
||||||
|
|
||||||
|
*vcpu_reg(vcpu, p->Rt) = ((((dfr >> 20) & 0xf) << 28) |
|
||||||
|
(((dfr >> 12) & 0xf) << 24) |
|
||||||
|
(((dfr >> 28) & 0xf) << 20) |
|
||||||
|
(6 << 16) | (el3 << 14) | (el3 << 12));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool trap_debug32(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_params *p,
|
||||||
|
const struct sys_reg_desc *r)
|
||||||
|
{
|
||||||
|
if (p->is_write) {
|
||||||
|
vcpu_cp14(vcpu, r->reg) = *vcpu_reg(vcpu, p->Rt);
|
||||||
|
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
|
||||||
|
} else {
|
||||||
|
*vcpu_reg(vcpu, p->Rt) = vcpu_cp14(vcpu, r->reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DBG_BCR_BVR_WCR_WVR(n) \
|
||||||
|
/* DBGBVRn */ \
|
||||||
|
{ Op1( 0), CRn( 0), CRm((n)), Op2( 4), trap_debug32, \
|
||||||
|
NULL, (cp14_DBGBVR0 + (n) * 2) }, \
|
||||||
|
/* DBGBCRn */ \
|
||||||
|
{ Op1( 0), CRn( 0), CRm((n)), Op2( 5), trap_debug32, \
|
||||||
|
NULL, (cp14_DBGBCR0 + (n) * 2) }, \
|
||||||
|
/* DBGWVRn */ \
|
||||||
|
{ Op1( 0), CRn( 0), CRm((n)), Op2( 6), trap_debug32, \
|
||||||
|
NULL, (cp14_DBGWVR0 + (n) * 2) }, \
|
||||||
|
/* DBGWCRn */ \
|
||||||
|
{ Op1( 0), CRn( 0), CRm((n)), Op2( 7), trap_debug32, \
|
||||||
|
NULL, (cp14_DBGWCR0 + (n) * 2) }
|
||||||
|
|
||||||
|
#define DBGBXVR(n) \
|
||||||
|
{ Op1( 0), CRn( 1), CRm((n)), Op2( 1), trap_debug32, \
|
||||||
|
NULL, cp14_DBGBXVR0 + n * 2 }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trapped cp14 registers. We generally ignore most of the external
|
||||||
|
* debug, on the principle that they don't really make sense to a
|
||||||
|
* guest. Revisit this one day, whould this principle change.
|
||||||
|
*/
|
||||||
|
static const struct sys_reg_desc cp14_regs[] = {
|
||||||
|
/* DBGIDR */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 0), Op2( 0), trap_dbgidr },
|
||||||
|
/* DBGDTRRXext */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 0), Op2( 2), trap_raz_wi },
|
||||||
|
|
||||||
|
DBG_BCR_BVR_WCR_WVR(0),
|
||||||
|
/* DBGDSCRint */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 1), Op2( 0), trap_raz_wi },
|
||||||
|
DBG_BCR_BVR_WCR_WVR(1),
|
||||||
|
/* DBGDCCINT */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 2), Op2( 0), trap_debug32 },
|
||||||
|
/* DBGDSCRext */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 2), Op2( 2), trap_debug32 },
|
||||||
|
DBG_BCR_BVR_WCR_WVR(2),
|
||||||
|
/* DBGDTR[RT]Xint */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 3), Op2( 0), trap_raz_wi },
|
||||||
|
/* DBGDTR[RT]Xext */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 3), Op2( 2), trap_raz_wi },
|
||||||
|
DBG_BCR_BVR_WCR_WVR(3),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(4),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(5),
|
||||||
|
/* DBGWFAR */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 6), Op2( 0), trap_raz_wi },
|
||||||
|
/* DBGOSECCR */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 6), Op2( 2), trap_raz_wi },
|
||||||
|
DBG_BCR_BVR_WCR_WVR(6),
|
||||||
|
/* DBGVCR */
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 7), Op2( 0), trap_debug32 },
|
||||||
|
DBG_BCR_BVR_WCR_WVR(7),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(8),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(9),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(10),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(11),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(12),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(13),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(14),
|
||||||
|
DBG_BCR_BVR_WCR_WVR(15),
|
||||||
|
|
||||||
|
/* DBGDRAR (32bit) */
|
||||||
|
{ Op1( 0), CRn( 1), CRm( 0), Op2( 0), trap_raz_wi },
|
||||||
|
|
||||||
|
DBGBXVR(0),
|
||||||
|
/* DBGOSLAR */
|
||||||
|
{ Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_raz_wi },
|
||||||
|
DBGBXVR(1),
|
||||||
|
/* DBGOSLSR */
|
||||||
|
{ Op1( 0), CRn( 1), CRm( 1), Op2( 4), trap_oslsr_el1 },
|
||||||
|
DBGBXVR(2),
|
||||||
|
DBGBXVR(3),
|
||||||
|
/* DBGOSDLR */
|
||||||
|
{ Op1( 0), CRn( 1), CRm( 3), Op2( 4), trap_raz_wi },
|
||||||
|
DBGBXVR(4),
|
||||||
|
/* DBGPRCR */
|
||||||
|
{ Op1( 0), CRn( 1), CRm( 4), Op2( 4), trap_raz_wi },
|
||||||
|
DBGBXVR(5),
|
||||||
|
DBGBXVR(6),
|
||||||
|
DBGBXVR(7),
|
||||||
|
DBGBXVR(8),
|
||||||
|
DBGBXVR(9),
|
||||||
|
DBGBXVR(10),
|
||||||
|
DBGBXVR(11),
|
||||||
|
DBGBXVR(12),
|
||||||
|
DBGBXVR(13),
|
||||||
|
DBGBXVR(14),
|
||||||
|
DBGBXVR(15),
|
||||||
|
|
||||||
|
/* DBGDSAR (32bit) */
|
||||||
|
{ Op1( 0), CRn( 2), CRm( 0), Op2( 0), trap_raz_wi },
|
||||||
|
|
||||||
|
/* DBGDEVID2 */
|
||||||
|
{ Op1( 0), CRn( 7), CRm( 0), Op2( 7), trap_raz_wi },
|
||||||
|
/* DBGDEVID1 */
|
||||||
|
{ Op1( 0), CRn( 7), CRm( 1), Op2( 7), trap_raz_wi },
|
||||||
|
/* DBGDEVID */
|
||||||
|
{ Op1( 0), CRn( 7), CRm( 2), Op2( 7), trap_raz_wi },
|
||||||
|
/* DBGCLAIMSET */
|
||||||
|
{ Op1( 0), CRn( 7), CRm( 8), Op2( 6), trap_raz_wi },
|
||||||
|
/* DBGCLAIMCLR */
|
||||||
|
{ Op1( 0), CRn( 7), CRm( 9), Op2( 6), trap_raz_wi },
|
||||||
|
/* DBGAUTHSTATUS */
|
||||||
|
{ Op1( 0), CRn( 7), CRm(14), Op2( 6), trap_dbgauthstatus_el1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Trapped cp14 64bit registers */
|
||||||
|
static const struct sys_reg_desc cp14_64_regs[] = {
|
||||||
|
/* DBGDRAR (64bit) */
|
||||||
|
{ Op1( 0), CRm( 1), .access = trap_raz_wi },
|
||||||
|
|
||||||
|
/* DBGDSAR (64bit) */
|
||||||
|
{ Op1( 0), CRm( 2), .access = trap_raz_wi },
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Trapped cp15 registers. TTBR0/TTBR1 get a double encoding,
|
* Trapped cp15 registers. TTBR0/TTBR1 get a double encoding,
|
||||||
* depending on the way they are accessed (as a 32bit or a 64bit
|
* depending on the way they are accessed (as a 32bit or a 64bit
|
||||||
* register).
|
* register).
|
||||||
*/
|
*/
|
||||||
static const struct sys_reg_desc cp15_regs[] = {
|
static const struct sys_reg_desc cp15_regs[] = {
|
||||||
{ Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
|
|
||||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
|
{ Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
|
||||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
|
{ Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
|
||||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
|
{ Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
|
||||||
|
@ -374,26 +671,30 @@ static const struct sys_reg_desc cp15_regs[] = {
|
||||||
{ Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
|
{ Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
|
||||||
{ Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
|
{ Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
|
||||||
|
|
||||||
{ Op1( 0), CRn( 9), CRm(12), Op2( 0), pm_fake },
|
/* PMU */
|
||||||
{ Op1( 0), CRn( 9), CRm(12), Op2( 1), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(12), Op2( 0), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(12), Op2( 2), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(12), Op2( 1), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(12), Op2( 3), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(12), Op2( 2), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(12), Op2( 5), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(12), Op2( 3), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(12), Op2( 6), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(12), Op2( 5), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(12), Op2( 7), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(12), Op2( 6), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(13), Op2( 0), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(12), Op2( 7), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(13), Op2( 1), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(13), Op2( 0), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(13), Op2( 2), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(13), Op2( 1), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(14), Op2( 0), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(13), Op2( 2), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(14), Op2( 1), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(14), Op2( 0), trap_raz_wi },
|
||||||
{ Op1( 0), CRn( 9), CRm(14), Op2( 2), pm_fake },
|
{ Op1( 0), CRn( 9), CRm(14), Op2( 1), trap_raz_wi },
|
||||||
|
{ Op1( 0), CRn( 9), CRm(14), Op2( 2), trap_raz_wi },
|
||||||
|
|
||||||
{ Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, c10_PRRR },
|
{ Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, c10_PRRR },
|
||||||
{ Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
|
{ Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
|
||||||
{ Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
|
{ Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
|
||||||
{ Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
|
{ Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
|
||||||
{ Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
|
{ Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sys_reg_desc cp15_64_regs[] = {
|
||||||
|
{ Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
|
||||||
{ Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
|
{ Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -454,26 +755,29 @@ int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
/*
|
||||||
|
* emulate_cp -- tries to match a sys_reg access in a handling table, and
|
||||||
|
* call the corresponding trap handler.
|
||||||
|
*
|
||||||
|
* @params: pointer to the descriptor of the access
|
||||||
|
* @table: array of trap descriptors
|
||||||
|
* @num: size of the trap descriptor array
|
||||||
|
*
|
||||||
|
* Return 0 if the access has been handled, and -1 if not.
|
||||||
|
*/
|
||||||
|
static int emulate_cp(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_params *params,
|
||||||
|
const struct sys_reg_desc *table,
|
||||||
|
size_t num)
|
||||||
{
|
{
|
||||||
kvm_inject_undefined(vcpu);
|
const struct sys_reg_desc *r;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void emulate_cp15(struct kvm_vcpu *vcpu,
|
if (!table)
|
||||||
const struct sys_reg_params *params)
|
return -1; /* Not handled */
|
||||||
{
|
|
||||||
size_t num;
|
|
||||||
const struct sys_reg_desc *table, *r;
|
|
||||||
|
|
||||||
table = get_target_table(vcpu->arch.target, false, &num);
|
|
||||||
|
|
||||||
/* Search target-specific then generic table. */
|
|
||||||
r = find_reg(params, table, num);
|
r = find_reg(params, table, num);
|
||||||
if (!r)
|
|
||||||
r = find_reg(params, cp15_regs, ARRAY_SIZE(cp15_regs));
|
|
||||||
|
|
||||||
if (likely(r)) {
|
if (r) {
|
||||||
/*
|
/*
|
||||||
* Not having an accessor means that we have
|
* Not having an accessor means that we have
|
||||||
* configured a trap that we don't know how to
|
* configured a trap that we don't know how to
|
||||||
|
@ -485,22 +789,51 @@ static void emulate_cp15(struct kvm_vcpu *vcpu,
|
||||||
if (likely(r->access(vcpu, params, r))) {
|
if (likely(r->access(vcpu, params, r))) {
|
||||||
/* Skip instruction, since it was emulated */
|
/* Skip instruction, since it was emulated */
|
||||||
kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
|
kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
/* If access function fails, it should complain. */
|
|
||||||
|
/* Handled */
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_err("Unsupported guest CP15 access at: %08lx\n", *vcpu_pc(vcpu));
|
/* Not handled */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unhandled_cp_access(struct kvm_vcpu *vcpu,
|
||||||
|
struct sys_reg_params *params)
|
||||||
|
{
|
||||||
|
u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
|
||||||
|
int cp;
|
||||||
|
|
||||||
|
switch(hsr_ec) {
|
||||||
|
case ESR_EL2_EC_CP15_32:
|
||||||
|
case ESR_EL2_EC_CP15_64:
|
||||||
|
cp = 15;
|
||||||
|
break;
|
||||||
|
case ESR_EL2_EC_CP14_MR:
|
||||||
|
case ESR_EL2_EC_CP14_64:
|
||||||
|
cp = 14;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WARN_ON((cp = -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_err("Unsupported guest CP%d access at: %08lx\n",
|
||||||
|
cp, *vcpu_pc(vcpu));
|
||||||
print_sys_reg_instr(params);
|
print_sys_reg_instr(params);
|
||||||
kvm_inject_undefined(vcpu);
|
kvm_inject_undefined(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvm_handle_cp15_64 -- handles a mrrc/mcrr trap on a guest CP15 access
|
* kvm_handle_cp_64 -- handles a mrrc/mcrr trap on a guest CP15 access
|
||||||
* @vcpu: The VCPU pointer
|
* @vcpu: The VCPU pointer
|
||||||
* @run: The kvm_run struct
|
* @run: The kvm_run struct
|
||||||
*/
|
*/
|
||||||
int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_desc *global,
|
||||||
|
size_t nr_global,
|
||||||
|
const struct sys_reg_desc *target_specific,
|
||||||
|
size_t nr_specific)
|
||||||
{
|
{
|
||||||
struct sys_reg_params params;
|
struct sys_reg_params params;
|
||||||
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
||||||
|
@ -529,8 +862,14 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
*vcpu_reg(vcpu, params.Rt) = val;
|
*vcpu_reg(vcpu, params.Rt) = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
emulate_cp15(vcpu, ¶ms);
|
if (!emulate_cp(vcpu, ¶ms, target_specific, nr_specific))
|
||||||
|
goto out;
|
||||||
|
if (!emulate_cp(vcpu, ¶ms, global, nr_global))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
unhandled_cp_access(vcpu, ¶ms);
|
||||||
|
|
||||||
|
out:
|
||||||
/* Do the opposite hack for the read side */
|
/* Do the opposite hack for the read side */
|
||||||
if (!params.is_write) {
|
if (!params.is_write) {
|
||||||
u64 val = *vcpu_reg(vcpu, params.Rt);
|
u64 val = *vcpu_reg(vcpu, params.Rt);
|
||||||
|
@ -546,7 +885,11 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
* @vcpu: The VCPU pointer
|
* @vcpu: The VCPU pointer
|
||||||
* @run: The kvm_run struct
|
* @run: The kvm_run struct
|
||||||
*/
|
*/
|
||||||
int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
static int kvm_handle_cp_32(struct kvm_vcpu *vcpu,
|
||||||
|
const struct sys_reg_desc *global,
|
||||||
|
size_t nr_global,
|
||||||
|
const struct sys_reg_desc *target_specific,
|
||||||
|
size_t nr_specific)
|
||||||
{
|
{
|
||||||
struct sys_reg_params params;
|
struct sys_reg_params params;
|
||||||
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
u32 hsr = kvm_vcpu_get_hsr(vcpu);
|
||||||
|
@ -561,10 +904,51 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
params.Op1 = (hsr >> 14) & 0x7;
|
params.Op1 = (hsr >> 14) & 0x7;
|
||||||
params.Op2 = (hsr >> 17) & 0x7;
|
params.Op2 = (hsr >> 17) & 0x7;
|
||||||
|
|
||||||
emulate_cp15(vcpu, ¶ms);
|
if (!emulate_cp(vcpu, ¶ms, target_specific, nr_specific))
|
||||||
|
return 1;
|
||||||
|
if (!emulate_cp(vcpu, ¶ms, global, nr_global))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
unhandled_cp_access(vcpu, ¶ms);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
const struct sys_reg_desc *target_specific;
|
||||||
|
size_t num;
|
||||||
|
|
||||||
|
target_specific = get_target_table(vcpu->arch.target, false, &num);
|
||||||
|
return kvm_handle_cp_64(vcpu,
|
||||||
|
cp15_64_regs, ARRAY_SIZE(cp15_64_regs),
|
||||||
|
target_specific, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
const struct sys_reg_desc *target_specific;
|
||||||
|
size_t num;
|
||||||
|
|
||||||
|
target_specific = get_target_table(vcpu->arch.target, false, &num);
|
||||||
|
return kvm_handle_cp_32(vcpu,
|
||||||
|
cp15_regs, ARRAY_SIZE(cp15_regs),
|
||||||
|
target_specific, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_handle_cp14_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
return kvm_handle_cp_64(vcpu,
|
||||||
|
cp14_64_regs, ARRAY_SIZE(cp14_64_regs),
|
||||||
|
NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
return kvm_handle_cp_32(vcpu,
|
||||||
|
cp14_regs, ARRAY_SIZE(cp14_regs),
|
||||||
|
NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static int emulate_sys_reg(struct kvm_vcpu *vcpu,
|
static int emulate_sys_reg(struct kvm_vcpu *vcpu,
|
||||||
const struct sys_reg_params *params)
|
const struct sys_reg_params *params)
|
||||||
{
|
{
|
||||||
|
@ -776,17 +1160,15 @@ static struct sys_reg_desc invariant_sys_regs[] = {
|
||||||
NULL, get_ctr_el0 },
|
NULL, get_ctr_el0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int reg_from_user(void *val, const void __user *uaddr, u64 id)
|
static int reg_from_user(u64 *val, const void __user *uaddr, u64 id)
|
||||||
{
|
{
|
||||||
/* This Just Works because we are little endian. */
|
|
||||||
if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0)
|
if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reg_to_user(void __user *uaddr, const void *val, u64 id)
|
static int reg_to_user(void __user *uaddr, const u64 *val, u64 id)
|
||||||
{
|
{
|
||||||
/* This Just Works because we are little endian. */
|
|
||||||
if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0)
|
if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -962,7 +1344,7 @@ static unsigned int num_demux_regs(void)
|
||||||
|
|
||||||
static int write_demux_regids(u64 __user *uindices)
|
static int write_demux_regids(u64 __user *uindices)
|
||||||
{
|
{
|
||||||
u64 val = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
|
u64 val = KVM_REG_ARM64 | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
val |= KVM_REG_ARM_DEMUX_ID_CCSIDR;
|
val |= KVM_REG_ARM_DEMUX_ID_CCSIDR;
|
||||||
|
@ -1069,14 +1451,32 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
|
||||||
return write_demux_regids(uindices);
|
return write_demux_regids(uindices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_sysreg_table(const struct sys_reg_desc *table, unsigned int n)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 1; i < n; i++) {
|
||||||
|
if (cmp_sys_reg(&table[i-1], &table[i]) >= 0) {
|
||||||
|
kvm_err("sys_reg table %p out of order (%d)\n", table, i - 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void kvm_sys_reg_table_init(void)
|
void kvm_sys_reg_table_init(void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
struct sys_reg_desc clidr;
|
struct sys_reg_desc clidr;
|
||||||
|
|
||||||
/* Make sure tables are unique and in order. */
|
/* Make sure tables are unique and in order. */
|
||||||
for (i = 1; i < ARRAY_SIZE(sys_reg_descs); i++)
|
BUG_ON(check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs)));
|
||||||
BUG_ON(cmp_sys_reg(&sys_reg_descs[i-1], &sys_reg_descs[i]) >= 0);
|
BUG_ON(check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs)));
|
||||||
|
BUG_ON(check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs)));
|
||||||
|
BUG_ON(check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs)));
|
||||||
|
BUG_ON(check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs)));
|
||||||
|
BUG_ON(check_sysreg_table(invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs)));
|
||||||
|
|
||||||
/* We abuse the reset function to overwrite the table itself. */
|
/* We abuse the reset function to overwrite the table itself. */
|
||||||
for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
|
for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012,2013 - ARM Ltd
|
||||||
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <linux/irqchip/arm-gic.h>
|
||||||
|
|
||||||
|
#include <asm/assembler.h>
|
||||||
|
#include <asm/memory.h>
|
||||||
|
#include <asm/asm-offsets.h>
|
||||||
|
#include <asm/kvm.h>
|
||||||
|
#include <asm/kvm_asm.h>
|
||||||
|
#include <asm/kvm_arm.h>
|
||||||
|
#include <asm/kvm_mmu.h>
|
||||||
|
|
||||||
|
.text
|
||||||
|
.pushsection .hyp.text, "ax"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the VGIC CPU state into memory
|
||||||
|
* x0: Register pointing to VCPU struct
|
||||||
|
* Do not corrupt x1!!!
|
||||||
|
*/
|
||||||
|
ENTRY(__save_vgic_v2_state)
|
||||||
|
__save_vgic_v2_state:
|
||||||
|
/* Get VGIC VCTRL base into x2 */
|
||||||
|
ldr x2, [x0, #VCPU_KVM]
|
||||||
|
kern_hyp_va x2
|
||||||
|
ldr x2, [x2, #KVM_VGIC_VCTRL]
|
||||||
|
kern_hyp_va x2
|
||||||
|
cbz x2, 2f // disabled
|
||||||
|
|
||||||
|
/* Compute the address of struct vgic_cpu */
|
||||||
|
add x3, x0, #VCPU_VGIC_CPU
|
||||||
|
|
||||||
|
/* Save all interesting registers */
|
||||||
|
ldr w4, [x2, #GICH_HCR]
|
||||||
|
ldr w5, [x2, #GICH_VMCR]
|
||||||
|
ldr w6, [x2, #GICH_MISR]
|
||||||
|
ldr w7, [x2, #GICH_EISR0]
|
||||||
|
ldr w8, [x2, #GICH_EISR1]
|
||||||
|
ldr w9, [x2, #GICH_ELRSR0]
|
||||||
|
ldr w10, [x2, #GICH_ELRSR1]
|
||||||
|
ldr w11, [x2, #GICH_APR]
|
||||||
|
CPU_BE( rev w4, w4 )
|
||||||
|
CPU_BE( rev w5, w5 )
|
||||||
|
CPU_BE( rev w6, w6 )
|
||||||
|
CPU_BE( rev w7, w7 )
|
||||||
|
CPU_BE( rev w8, w8 )
|
||||||
|
CPU_BE( rev w9, w9 )
|
||||||
|
CPU_BE( rev w10, w10 )
|
||||||
|
CPU_BE( rev w11, w11 )
|
||||||
|
|
||||||
|
str w4, [x3, #VGIC_V2_CPU_HCR]
|
||||||
|
str w5, [x3, #VGIC_V2_CPU_VMCR]
|
||||||
|
str w6, [x3, #VGIC_V2_CPU_MISR]
|
||||||
|
str w7, [x3, #VGIC_V2_CPU_EISR]
|
||||||
|
str w8, [x3, #(VGIC_V2_CPU_EISR + 4)]
|
||||||
|
str w9, [x3, #VGIC_V2_CPU_ELRSR]
|
||||||
|
str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)]
|
||||||
|
str w11, [x3, #VGIC_V2_CPU_APR]
|
||||||
|
|
||||||
|
/* Clear GICH_HCR */
|
||||||
|
str wzr, [x2, #GICH_HCR]
|
||||||
|
|
||||||
|
/* Save list registers */
|
||||||
|
add x2, x2, #GICH_LR0
|
||||||
|
ldr w4, [x3, #VGIC_CPU_NR_LR]
|
||||||
|
add x3, x3, #VGIC_V2_CPU_LR
|
||||||
|
1: ldr w5, [x2], #4
|
||||||
|
CPU_BE( rev w5, w5 )
|
||||||
|
str w5, [x3], #4
|
||||||
|
sub w4, w4, #1
|
||||||
|
cbnz w4, 1b
|
||||||
|
2:
|
||||||
|
ret
|
||||||
|
ENDPROC(__save_vgic_v2_state)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the VGIC CPU state from memory
|
||||||
|
* x0: Register pointing to VCPU struct
|
||||||
|
*/
|
||||||
|
ENTRY(__restore_vgic_v2_state)
|
||||||
|
__restore_vgic_v2_state:
|
||||||
|
/* Get VGIC VCTRL base into x2 */
|
||||||
|
ldr x2, [x0, #VCPU_KVM]
|
||||||
|
kern_hyp_va x2
|
||||||
|
ldr x2, [x2, #KVM_VGIC_VCTRL]
|
||||||
|
kern_hyp_va x2
|
||||||
|
cbz x2, 2f // disabled
|
||||||
|
|
||||||
|
/* Compute the address of struct vgic_cpu */
|
||||||
|
add x3, x0, #VCPU_VGIC_CPU
|
||||||
|
|
||||||
|
/* We only restore a minimal set of registers */
|
||||||
|
ldr w4, [x3, #VGIC_V2_CPU_HCR]
|
||||||
|
ldr w5, [x3, #VGIC_V2_CPU_VMCR]
|
||||||
|
ldr w6, [x3, #VGIC_V2_CPU_APR]
|
||||||
|
CPU_BE( rev w4, w4 )
|
||||||
|
CPU_BE( rev w5, w5 )
|
||||||
|
CPU_BE( rev w6, w6 )
|
||||||
|
|
||||||
|
str w4, [x2, #GICH_HCR]
|
||||||
|
str w5, [x2, #GICH_VMCR]
|
||||||
|
str w6, [x2, #GICH_APR]
|
||||||
|
|
||||||
|
/* Restore list registers */
|
||||||
|
add x2, x2, #GICH_LR0
|
||||||
|
ldr w4, [x3, #VGIC_CPU_NR_LR]
|
||||||
|
add x3, x3, #VGIC_V2_CPU_LR
|
||||||
|
1: ldr w5, [x3], #4
|
||||||
|
CPU_BE( rev w5, w5 )
|
||||||
|
str w5, [x2], #4
|
||||||
|
sub w4, w4, #1
|
||||||
|
cbnz w4, 1b
|
||||||
|
2:
|
||||||
|
ret
|
||||||
|
ENDPROC(__restore_vgic_v2_state)
|
||||||
|
|
||||||
|
.popsection
|
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012,2013 - ARM Ltd
|
||||||
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <linux/irqchip/arm-gic-v3.h>
|
||||||
|
|
||||||
|
#include <asm/assembler.h>
|
||||||
|
#include <asm/memory.h>
|
||||||
|
#include <asm/asm-offsets.h>
|
||||||
|
#include <asm/kvm.h>
|
||||||
|
#include <asm/kvm_asm.h>
|
||||||
|
#include <asm/kvm_arm.h>
|
||||||
|
|
||||||
|
.text
|
||||||
|
.pushsection .hyp.text, "ax"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We store LRs in reverse order to let the CPU deal with streaming
|
||||||
|
* access. Use this macro to make it look saner...
|
||||||
|
*/
|
||||||
|
#define LR_OFFSET(n) (VGIC_V3_CPU_LR + (15 - n) * 8)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the VGIC CPU state into memory
|
||||||
|
* x0: Register pointing to VCPU struct
|
||||||
|
* Do not corrupt x1!!!
|
||||||
|
*/
|
||||||
|
.macro save_vgic_v3_state
|
||||||
|
// Compute the address of struct vgic_cpu
|
||||||
|
add x3, x0, #VCPU_VGIC_CPU
|
||||||
|
|
||||||
|
// Make sure stores to the GIC via the memory mapped interface
|
||||||
|
// are now visible to the system register interface
|
||||||
|
dsb st
|
||||||
|
|
||||||
|
// Save all interesting registers
|
||||||
|
mrs_s x4, ICH_HCR_EL2
|
||||||
|
mrs_s x5, ICH_VMCR_EL2
|
||||||
|
mrs_s x6, ICH_MISR_EL2
|
||||||
|
mrs_s x7, ICH_EISR_EL2
|
||||||
|
mrs_s x8, ICH_ELSR_EL2
|
||||||
|
|
||||||
|
str w4, [x3, #VGIC_V3_CPU_HCR]
|
||||||
|
str w5, [x3, #VGIC_V3_CPU_VMCR]
|
||||||
|
str w6, [x3, #VGIC_V3_CPU_MISR]
|
||||||
|
str w7, [x3, #VGIC_V3_CPU_EISR]
|
||||||
|
str w8, [x3, #VGIC_V3_CPU_ELRSR]
|
||||||
|
|
||||||
|
msr_s ICH_HCR_EL2, xzr
|
||||||
|
|
||||||
|
mrs_s x21, ICH_VTR_EL2
|
||||||
|
mvn w22, w21
|
||||||
|
ubfiz w23, w22, 2, 4 // w23 = (15 - ListRegs) * 4
|
||||||
|
|
||||||
|
adr x24, 1f
|
||||||
|
add x24, x24, x23
|
||||||
|
br x24
|
||||||
|
|
||||||
|
1:
|
||||||
|
mrs_s x20, ICH_LR15_EL2
|
||||||
|
mrs_s x19, ICH_LR14_EL2
|
||||||
|
mrs_s x18, ICH_LR13_EL2
|
||||||
|
mrs_s x17, ICH_LR12_EL2
|
||||||
|
mrs_s x16, ICH_LR11_EL2
|
||||||
|
mrs_s x15, ICH_LR10_EL2
|
||||||
|
mrs_s x14, ICH_LR9_EL2
|
||||||
|
mrs_s x13, ICH_LR8_EL2
|
||||||
|
mrs_s x12, ICH_LR7_EL2
|
||||||
|
mrs_s x11, ICH_LR6_EL2
|
||||||
|
mrs_s x10, ICH_LR5_EL2
|
||||||
|
mrs_s x9, ICH_LR4_EL2
|
||||||
|
mrs_s x8, ICH_LR3_EL2
|
||||||
|
mrs_s x7, ICH_LR2_EL2
|
||||||
|
mrs_s x6, ICH_LR1_EL2
|
||||||
|
mrs_s x5, ICH_LR0_EL2
|
||||||
|
|
||||||
|
adr x24, 1f
|
||||||
|
add x24, x24, x23
|
||||||
|
br x24
|
||||||
|
|
||||||
|
1:
|
||||||
|
str x20, [x3, #LR_OFFSET(15)]
|
||||||
|
str x19, [x3, #LR_OFFSET(14)]
|
||||||
|
str x18, [x3, #LR_OFFSET(13)]
|
||||||
|
str x17, [x3, #LR_OFFSET(12)]
|
||||||
|
str x16, [x3, #LR_OFFSET(11)]
|
||||||
|
str x15, [x3, #LR_OFFSET(10)]
|
||||||
|
str x14, [x3, #LR_OFFSET(9)]
|
||||||
|
str x13, [x3, #LR_OFFSET(8)]
|
||||||
|
str x12, [x3, #LR_OFFSET(7)]
|
||||||
|
str x11, [x3, #LR_OFFSET(6)]
|
||||||
|
str x10, [x3, #LR_OFFSET(5)]
|
||||||
|
str x9, [x3, #LR_OFFSET(4)]
|
||||||
|
str x8, [x3, #LR_OFFSET(3)]
|
||||||
|
str x7, [x3, #LR_OFFSET(2)]
|
||||||
|
str x6, [x3, #LR_OFFSET(1)]
|
||||||
|
str x5, [x3, #LR_OFFSET(0)]
|
||||||
|
|
||||||
|
tbnz w21, #29, 6f // 6 bits
|
||||||
|
tbz w21, #30, 5f // 5 bits
|
||||||
|
// 7 bits
|
||||||
|
mrs_s x20, ICH_AP0R3_EL2
|
||||||
|
str w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
|
||||||
|
mrs_s x19, ICH_AP0R2_EL2
|
||||||
|
str w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
|
||||||
|
6: mrs_s x18, ICH_AP0R1_EL2
|
||||||
|
str w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
|
||||||
|
5: mrs_s x17, ICH_AP0R0_EL2
|
||||||
|
str w17, [x3, #VGIC_V3_CPU_AP0R]
|
||||||
|
|
||||||
|
tbnz w21, #29, 6f // 6 bits
|
||||||
|
tbz w21, #30, 5f // 5 bits
|
||||||
|
// 7 bits
|
||||||
|
mrs_s x20, ICH_AP1R3_EL2
|
||||||
|
str w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
|
||||||
|
mrs_s x19, ICH_AP1R2_EL2
|
||||||
|
str w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
|
||||||
|
6: mrs_s x18, ICH_AP1R1_EL2
|
||||||
|
str w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
|
||||||
|
5: mrs_s x17, ICH_AP1R0_EL2
|
||||||
|
str w17, [x3, #VGIC_V3_CPU_AP1R]
|
||||||
|
|
||||||
|
// Restore SRE_EL1 access and re-enable SRE at EL1.
|
||||||
|
mrs_s x5, ICC_SRE_EL2
|
||||||
|
orr x5, x5, #ICC_SRE_EL2_ENABLE
|
||||||
|
msr_s ICC_SRE_EL2, x5
|
||||||
|
isb
|
||||||
|
mov x5, #1
|
||||||
|
msr_s ICC_SRE_EL1, x5
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the VGIC CPU state from memory
|
||||||
|
* x0: Register pointing to VCPU struct
|
||||||
|
*/
|
||||||
|
.macro restore_vgic_v3_state
|
||||||
|
// Disable SRE_EL1 access. Necessary, otherwise
|
||||||
|
// ICH_VMCR_EL2.VFIQEn becomes one, and FIQ happens...
|
||||||
|
msr_s ICC_SRE_EL1, xzr
|
||||||
|
isb
|
||||||
|
|
||||||
|
// Compute the address of struct vgic_cpu
|
||||||
|
add x3, x0, #VCPU_VGIC_CPU
|
||||||
|
|
||||||
|
// Restore all interesting registers
|
||||||
|
ldr w4, [x3, #VGIC_V3_CPU_HCR]
|
||||||
|
ldr w5, [x3, #VGIC_V3_CPU_VMCR]
|
||||||
|
|
||||||
|
msr_s ICH_HCR_EL2, x4
|
||||||
|
msr_s ICH_VMCR_EL2, x5
|
||||||
|
|
||||||
|
mrs_s x21, ICH_VTR_EL2
|
||||||
|
|
||||||
|
tbnz w21, #29, 6f // 6 bits
|
||||||
|
tbz w21, #30, 5f // 5 bits
|
||||||
|
// 7 bits
|
||||||
|
ldr w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
|
||||||
|
msr_s ICH_AP1R3_EL2, x20
|
||||||
|
ldr w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
|
||||||
|
msr_s ICH_AP1R2_EL2, x19
|
||||||
|
6: ldr w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
|
||||||
|
msr_s ICH_AP1R1_EL2, x18
|
||||||
|
5: ldr w17, [x3, #VGIC_V3_CPU_AP1R]
|
||||||
|
msr_s ICH_AP1R0_EL2, x17
|
||||||
|
|
||||||
|
tbnz w21, #29, 6f // 6 bits
|
||||||
|
tbz w21, #30, 5f // 5 bits
|
||||||
|
// 7 bits
|
||||||
|
ldr w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
|
||||||
|
msr_s ICH_AP0R3_EL2, x20
|
||||||
|
ldr w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
|
||||||
|
msr_s ICH_AP0R2_EL2, x19
|
||||||
|
6: ldr w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
|
||||||
|
msr_s ICH_AP0R1_EL2, x18
|
||||||
|
5: ldr w17, [x3, #VGIC_V3_CPU_AP0R]
|
||||||
|
msr_s ICH_AP0R0_EL2, x17
|
||||||
|
|
||||||
|
and w22, w21, #0xf
|
||||||
|
mvn w22, w21
|
||||||
|
ubfiz w23, w22, 2, 4 // w23 = (15 - ListRegs) * 4
|
||||||
|
|
||||||
|
adr x24, 1f
|
||||||
|
add x24, x24, x23
|
||||||
|
br x24
|
||||||
|
|
||||||
|
1:
|
||||||
|
ldr x20, [x3, #LR_OFFSET(15)]
|
||||||
|
ldr x19, [x3, #LR_OFFSET(14)]
|
||||||
|
ldr x18, [x3, #LR_OFFSET(13)]
|
||||||
|
ldr x17, [x3, #LR_OFFSET(12)]
|
||||||
|
ldr x16, [x3, #LR_OFFSET(11)]
|
||||||
|
ldr x15, [x3, #LR_OFFSET(10)]
|
||||||
|
ldr x14, [x3, #LR_OFFSET(9)]
|
||||||
|
ldr x13, [x3, #LR_OFFSET(8)]
|
||||||
|
ldr x12, [x3, #LR_OFFSET(7)]
|
||||||
|
ldr x11, [x3, #LR_OFFSET(6)]
|
||||||
|
ldr x10, [x3, #LR_OFFSET(5)]
|
||||||
|
ldr x9, [x3, #LR_OFFSET(4)]
|
||||||
|
ldr x8, [x3, #LR_OFFSET(3)]
|
||||||
|
ldr x7, [x3, #LR_OFFSET(2)]
|
||||||
|
ldr x6, [x3, #LR_OFFSET(1)]
|
||||||
|
ldr x5, [x3, #LR_OFFSET(0)]
|
||||||
|
|
||||||
|
adr x24, 1f
|
||||||
|
add x24, x24, x23
|
||||||
|
br x24
|
||||||
|
|
||||||
|
1:
|
||||||
|
msr_s ICH_LR15_EL2, x20
|
||||||
|
msr_s ICH_LR14_EL2, x19
|
||||||
|
msr_s ICH_LR13_EL2, x18
|
||||||
|
msr_s ICH_LR12_EL2, x17
|
||||||
|
msr_s ICH_LR11_EL2, x16
|
||||||
|
msr_s ICH_LR10_EL2, x15
|
||||||
|
msr_s ICH_LR9_EL2, x14
|
||||||
|
msr_s ICH_LR8_EL2, x13
|
||||||
|
msr_s ICH_LR7_EL2, x12
|
||||||
|
msr_s ICH_LR6_EL2, x11
|
||||||
|
msr_s ICH_LR5_EL2, x10
|
||||||
|
msr_s ICH_LR4_EL2, x9
|
||||||
|
msr_s ICH_LR3_EL2, x8
|
||||||
|
msr_s ICH_LR2_EL2, x7
|
||||||
|
msr_s ICH_LR1_EL2, x6
|
||||||
|
msr_s ICH_LR0_EL2, x5
|
||||||
|
|
||||||
|
// Ensure that the above will have reached the
|
||||||
|
// (re)distributors. This ensure the guest will read
|
||||||
|
// the correct values from the memory-mapped interface.
|
||||||
|
isb
|
||||||
|
dsb sy
|
||||||
|
|
||||||
|
// Prevent the guest from touching the GIC system registers
|
||||||
|
mrs_s x5, ICC_SRE_EL2
|
||||||
|
and x5, x5, #~ICC_SRE_EL2_ENABLE
|
||||||
|
msr_s ICC_SRE_EL2, x5
|
||||||
|
.endm
|
||||||
|
|
||||||
|
ENTRY(__save_vgic_v3_state)
|
||||||
|
save_vgic_v3_state
|
||||||
|
ret
|
||||||
|
ENDPROC(__save_vgic_v3_state)
|
||||||
|
|
||||||
|
ENTRY(__restore_vgic_v3_state)
|
||||||
|
restore_vgic_v3_state
|
||||||
|
ret
|
||||||
|
ENDPROC(__restore_vgic_v3_state)
|
||||||
|
|
||||||
|
ENTRY(__vgic_v3_get_ich_vtr_el2)
|
||||||
|
mrs_s x0, ICH_VTR_EL2
|
||||||
|
ret
|
||||||
|
ENDPROC(__vgic_v3_get_ich_vtr_el2)
|
||||||
|
|
||||||
|
.popsection
|
|
@ -67,6 +67,10 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
|
||||||
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
|
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
|
||||||
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
|
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
|
||||||
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
|
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
|
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);
|
||||||
|
int kvm_arm_timer_set_reg(struct kvm_vcpu *, u64 regid, u64 value);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static inline int kvm_timer_hyp_init(void)
|
static inline int kvm_timer_hyp_init(void)
|
||||||
{
|
{
|
||||||
|
@ -84,6 +88,16 @@ static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {}
|
||||||
static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {}
|
||||||
static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {}
|
||||||
static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {}
|
static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {}
|
||||||
|
|
||||||
|
static inline int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include <linux/irqreturn.h>
|
#include <linux/irqreturn.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/irqchip/arm-gic.h>
|
|
||||||
|
|
||||||
#define VGIC_NR_IRQS 256
|
#define VGIC_NR_IRQS 256
|
||||||
#define VGIC_NR_SGIS 16
|
#define VGIC_NR_SGIS 16
|
||||||
|
@ -32,7 +31,9 @@
|
||||||
#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS)
|
#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS)
|
||||||
#define VGIC_NR_SHARED_IRQS (VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
|
#define VGIC_NR_SHARED_IRQS (VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
|
||||||
#define VGIC_MAX_CPUS KVM_MAX_VCPUS
|
#define VGIC_MAX_CPUS KVM_MAX_VCPUS
|
||||||
#define VGIC_MAX_LRS (1 << 6)
|
|
||||||
|
#define VGIC_V2_MAX_LRS (1 << 6)
|
||||||
|
#define VGIC_V3_MAX_LRS 16
|
||||||
|
|
||||||
/* Sanity checks... */
|
/* Sanity checks... */
|
||||||
#if (VGIC_MAX_CPUS > 8)
|
#if (VGIC_MAX_CPUS > 8)
|
||||||
|
@ -68,9 +69,62 @@ struct vgic_bytemap {
|
||||||
u32 shared[VGIC_NR_SHARED_IRQS / 4];
|
u32 shared[VGIC_NR_SHARED_IRQS / 4];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct kvm_vcpu;
|
||||||
|
|
||||||
|
enum vgic_type {
|
||||||
|
VGIC_V2, /* Good ol' GICv2 */
|
||||||
|
VGIC_V3, /* New fancy GICv3 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LR_STATE_PENDING (1 << 0)
|
||||||
|
#define LR_STATE_ACTIVE (1 << 1)
|
||||||
|
#define LR_STATE_MASK (3 << 0)
|
||||||
|
#define LR_EOI_INT (1 << 2)
|
||||||
|
|
||||||
|
struct vgic_lr {
|
||||||
|
u16 irq;
|
||||||
|
u8 source;
|
||||||
|
u8 state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vgic_vmcr {
|
||||||
|
u32 ctlr;
|
||||||
|
u32 abpr;
|
||||||
|
u32 bpr;
|
||||||
|
u32 pmr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vgic_ops {
|
||||||
|
struct vgic_lr (*get_lr)(const struct kvm_vcpu *, int);
|
||||||
|
void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr);
|
||||||
|
void (*sync_lr_elrsr)(struct kvm_vcpu *, int, struct vgic_lr);
|
||||||
|
u64 (*get_elrsr)(const struct kvm_vcpu *vcpu);
|
||||||
|
u64 (*get_eisr)(const struct kvm_vcpu *vcpu);
|
||||||
|
u32 (*get_interrupt_status)(const struct kvm_vcpu *vcpu);
|
||||||
|
void (*enable_underflow)(struct kvm_vcpu *vcpu);
|
||||||
|
void (*disable_underflow)(struct kvm_vcpu *vcpu);
|
||||||
|
void (*get_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||||
|
void (*set_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||||
|
void (*enable)(struct kvm_vcpu *vcpu);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vgic_params {
|
||||||
|
/* vgic type */
|
||||||
|
enum vgic_type type;
|
||||||
|
/* Physical address of vgic virtual cpu interface */
|
||||||
|
phys_addr_t vcpu_base;
|
||||||
|
/* Number of list registers */
|
||||||
|
u32 nr_lr;
|
||||||
|
/* Interrupt number */
|
||||||
|
unsigned int maint_irq;
|
||||||
|
/* Virtual control interface base address */
|
||||||
|
void __iomem *vctrl_base;
|
||||||
|
};
|
||||||
|
|
||||||
struct vgic_dist {
|
struct vgic_dist {
|
||||||
#ifdef CONFIG_KVM_ARM_VGIC
|
#ifdef CONFIG_KVM_ARM_VGIC
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
bool in_kernel;
|
||||||
bool ready;
|
bool ready;
|
||||||
|
|
||||||
/* Virtual control interface mapping */
|
/* Virtual control interface mapping */
|
||||||
|
@ -110,6 +164,29 @@ struct vgic_dist {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct vgic_v2_cpu_if {
|
||||||
|
u32 vgic_hcr;
|
||||||
|
u32 vgic_vmcr;
|
||||||
|
u32 vgic_misr; /* Saved only */
|
||||||
|
u32 vgic_eisr[2]; /* Saved only */
|
||||||
|
u32 vgic_elrsr[2]; /* Saved only */
|
||||||
|
u32 vgic_apr;
|
||||||
|
u32 vgic_lr[VGIC_V2_MAX_LRS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vgic_v3_cpu_if {
|
||||||
|
#ifdef CONFIG_ARM_GIC_V3
|
||||||
|
u32 vgic_hcr;
|
||||||
|
u32 vgic_vmcr;
|
||||||
|
u32 vgic_misr; /* Saved only */
|
||||||
|
u32 vgic_eisr; /* Saved only */
|
||||||
|
u32 vgic_elrsr; /* Saved only */
|
||||||
|
u32 vgic_ap0r[4];
|
||||||
|
u32 vgic_ap1r[4];
|
||||||
|
u64 vgic_lr[VGIC_V3_MAX_LRS];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
struct vgic_cpu {
|
struct vgic_cpu {
|
||||||
#ifdef CONFIG_KVM_ARM_VGIC
|
#ifdef CONFIG_KVM_ARM_VGIC
|
||||||
/* per IRQ to LR mapping */
|
/* per IRQ to LR mapping */
|
||||||
|
@ -120,24 +197,24 @@ struct vgic_cpu {
|
||||||
DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS);
|
DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS);
|
||||||
|
|
||||||
/* Bitmap of used/free list registers */
|
/* Bitmap of used/free list registers */
|
||||||
DECLARE_BITMAP( lr_used, VGIC_MAX_LRS);
|
DECLARE_BITMAP( lr_used, VGIC_V2_MAX_LRS);
|
||||||
|
|
||||||
/* Number of list registers on this CPU */
|
/* Number of list registers on this CPU */
|
||||||
int nr_lr;
|
int nr_lr;
|
||||||
|
|
||||||
/* CPU vif control registers for world switch */
|
/* CPU vif control registers for world switch */
|
||||||
u32 vgic_hcr;
|
union {
|
||||||
u32 vgic_vmcr;
|
struct vgic_v2_cpu_if vgic_v2;
|
||||||
u32 vgic_misr; /* Saved only */
|
struct vgic_v3_cpu_if vgic_v3;
|
||||||
u32 vgic_eisr[2]; /* Saved only */
|
};
|
||||||
u32 vgic_elrsr[2]; /* Saved only */
|
|
||||||
u32 vgic_apr;
|
|
||||||
u32 vgic_lr[VGIC_MAX_LRS];
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LR_EMPTY 0xff
|
#define LR_EMPTY 0xff
|
||||||
|
|
||||||
|
#define INT_STATUS_EOI (1 << 0)
|
||||||
|
#define INT_STATUS_UNDERFLOW (1 << 1)
|
||||||
|
|
||||||
struct kvm;
|
struct kvm;
|
||||||
struct kvm_vcpu;
|
struct kvm_vcpu;
|
||||||
struct kvm_run;
|
struct kvm_run;
|
||||||
|
@ -157,9 +234,25 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
|
||||||
bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
||||||
struct kvm_exit_mmio *mmio);
|
struct kvm_exit_mmio *mmio);
|
||||||
|
|
||||||
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base))
|
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
|
||||||
#define vgic_initialized(k) ((k)->arch.vgic.ready)
|
#define vgic_initialized(k) ((k)->arch.vgic.ready)
|
||||||
|
|
||||||
|
int vgic_v2_probe(struct device_node *vgic_node,
|
||||||
|
const struct vgic_ops **ops,
|
||||||
|
const struct vgic_params **params);
|
||||||
|
#ifdef CONFIG_ARM_GIC_V3
|
||||||
|
int vgic_v3_probe(struct device_node *vgic_node,
|
||||||
|
const struct vgic_ops **ops,
|
||||||
|
const struct vgic_params **params);
|
||||||
|
#else
|
||||||
|
static inline int vgic_v3_probe(struct device_node *vgic_node,
|
||||||
|
const struct vgic_ops **ops,
|
||||||
|
const struct vgic_params **params)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static inline int kvm_vgic_hyp_init(void)
|
static inline int kvm_vgic_hyp_init(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012,2013 ARM Limited, All Rights Reserved.
|
||||||
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/cpu.h>
|
||||||
|
#include <linux/kvm.h>
|
||||||
|
#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>
|
||||||
|
|
||||||
|
#include <asm/kvm_emulate.h>
|
||||||
|
#include <asm/kvm_arm.h>
|
||||||
|
#include <asm/kvm_mmu.h>
|
||||||
|
|
||||||
|
static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr)
|
||||||
|
{
|
||||||
|
struct vgic_lr lr_desc;
|
||||||
|
u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr];
|
||||||
|
|
||||||
|
lr_desc.irq = val & GICH_LR_VIRTUALID;
|
||||||
|
if (lr_desc.irq <= 15)
|
||||||
|
lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
|
||||||
|
else
|
||||||
|
lr_desc.source = 0;
|
||||||
|
lr_desc.state = 0;
|
||||||
|
|
||||||
|
if (val & GICH_LR_PENDING_BIT)
|
||||||
|
lr_desc.state |= LR_STATE_PENDING;
|
||||||
|
if (val & GICH_LR_ACTIVE_BIT)
|
||||||
|
lr_desc.state |= LR_STATE_ACTIVE;
|
||||||
|
if (val & GICH_LR_EOI)
|
||||||
|
lr_desc.state |= LR_EOI_INT;
|
||||||
|
|
||||||
|
return lr_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr,
|
||||||
|
struct vgic_lr lr_desc)
|
||||||
|
{
|
||||||
|
u32 lr_val = (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | lr_desc.irq;
|
||||||
|
|
||||||
|
if (lr_desc.state & LR_STATE_PENDING)
|
||||||
|
lr_val |= GICH_LR_PENDING_BIT;
|
||||||
|
if (lr_desc.state & LR_STATE_ACTIVE)
|
||||||
|
lr_val |= GICH_LR_ACTIVE_BIT;
|
||||||
|
if (lr_desc.state & LR_EOI_INT)
|
||||||
|
lr_val |= GICH_LR_EOI;
|
||||||
|
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v2_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
|
||||||
|
struct vgic_lr lr_desc)
|
||||||
|
{
|
||||||
|
if (!(lr_desc.state & LR_STATE_MASK))
|
||||||
|
set_bit(lr, (unsigned long *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
#if BITS_PER_LONG == 64
|
||||||
|
val = vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[1];
|
||||||
|
val <<= 32;
|
||||||
|
val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[0];
|
||||||
|
#else
|
||||||
|
val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr;
|
||||||
|
#endif
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 vgic_v2_get_eisr(const struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
#if BITS_PER_LONG == 64
|
||||||
|
val = vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[1];
|
||||||
|
val <<= 32;
|
||||||
|
val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[0];
|
||||||
|
#else
|
||||||
|
val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr;
|
||||||
|
#endif
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
u32 misr = vcpu->arch.vgic_cpu.vgic_v2.vgic_misr;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
|
if (misr & GICH_MISR_EOI)
|
||||||
|
ret |= INT_STATUS_EOI;
|
||||||
|
if (misr & GICH_MISR_U)
|
||||||
|
ret |= INT_STATUS_UNDERFLOW;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v2_enable_underflow(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= GICH_HCR_UIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v2_disable_underflow(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_UIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||||
|
{
|
||||||
|
u32 vmcr = vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr;
|
||||||
|
|
||||||
|
vmcrp->ctlr = (vmcr & GICH_VMCR_CTRL_MASK) >> GICH_VMCR_CTRL_SHIFT;
|
||||||
|
vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >> GICH_VMCR_ALIAS_BINPOINT_SHIFT;
|
||||||
|
vmcrp->bpr = (vmcr & GICH_VMCR_BINPOINT_MASK) >> GICH_VMCR_BINPOINT_SHIFT;
|
||||||
|
vmcrp->pmr = (vmcr & GICH_VMCR_PRIMASK_MASK) >> GICH_VMCR_PRIMASK_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||||
|
{
|
||||||
|
u32 vmcr;
|
||||||
|
|
||||||
|
vmcr = (vmcrp->ctlr << GICH_VMCR_CTRL_SHIFT) & GICH_VMCR_CTRL_MASK;
|
||||||
|
vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) & GICH_VMCR_ALIAS_BINPOINT_MASK;
|
||||||
|
vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) & GICH_VMCR_BINPOINT_MASK;
|
||||||
|
vmcr |= (vmcrp->pmr << GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK;
|
||||||
|
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v2_enable(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* By forcing VMCR to zero, the GIC will restore the binary
|
||||||
|
* points to their reset values. Anything else resets to zero
|
||||||
|
* anyway.
|
||||||
|
*/
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0;
|
||||||
|
|
||||||
|
/* Get the show on the road... */
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct vgic_ops vgic_v2_ops = {
|
||||||
|
.get_lr = vgic_v2_get_lr,
|
||||||
|
.set_lr = vgic_v2_set_lr,
|
||||||
|
.sync_lr_elrsr = vgic_v2_sync_lr_elrsr,
|
||||||
|
.get_elrsr = vgic_v2_get_elrsr,
|
||||||
|
.get_eisr = vgic_v2_get_eisr,
|
||||||
|
.get_interrupt_status = vgic_v2_get_interrupt_status,
|
||||||
|
.enable_underflow = vgic_v2_enable_underflow,
|
||||||
|
.disable_underflow = vgic_v2_disable_underflow,
|
||||||
|
.get_vmcr = vgic_v2_get_vmcr,
|
||||||
|
.set_vmcr = vgic_v2_set_vmcr,
|
||||||
|
.enable = vgic_v2_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct vgic_params vgic_v2_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
|
||||||
|
*
|
||||||
|
* 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 ret;
|
||||||
|
struct resource vctrl_res;
|
||||||
|
struct resource vcpu_res;
|
||||||
|
struct vgic_params *vgic = &vgic_v2_params;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (!vgic->vctrl_base) {
|
||||||
|
kvm_err("Cannot ioremap GICH\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR);
|
||||||
|
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);
|
||||||
|
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)) {
|
||||||
|
kvm_err("GICV physical address 0x%llx not page aligned\n",
|
||||||
|
(unsigned long long)vcpu_res.start);
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto out_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
PAGE_SIZE);
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto out_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
vgic->vcpu_base = vcpu_res.start;
|
||||||
|
|
||||||
|
kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
|
||||||
|
vctrl_res.start, vgic->maint_irq);
|
||||||
|
|
||||||
|
vgic->type = VGIC_V2;
|
||||||
|
*ops = &vgic_v2_ops;
|
||||||
|
*params = vgic;
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
out_unmap:
|
||||||
|
iounmap(vgic->vctrl_base);
|
||||||
|
out:
|
||||||
|
of_node_put(vgic_node);
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,247 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 ARM Limited, All Rights Reserved.
|
||||||
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/cpu.h>
|
||||||
|
#include <linux/kvm.h>
|
||||||
|
#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 <asm/kvm_emulate.h>
|
||||||
|
#include <asm/kvm_arm.h>
|
||||||
|
#include <asm/kvm_mmu.h>
|
||||||
|
|
||||||
|
/* These are for GICv2 emulation only */
|
||||||
|
#define GICH_LR_VIRTUALID (0x3ffUL << 0)
|
||||||
|
#define GICH_LR_PHYSID_CPUID_SHIFT (10)
|
||||||
|
#define GICH_LR_PHYSID_CPUID (7UL << GICH_LR_PHYSID_CPUID_SHIFT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LRs are stored in reverse order in memory. make sure we index them
|
||||||
|
* correctly.
|
||||||
|
*/
|
||||||
|
#define LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
|
||||||
|
|
||||||
|
static u32 ich_vtr_el2;
|
||||||
|
|
||||||
|
static struct vgic_lr vgic_v3_get_lr(const struct kvm_vcpu *vcpu, int lr)
|
||||||
|
{
|
||||||
|
struct vgic_lr lr_desc;
|
||||||
|
u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)];
|
||||||
|
|
||||||
|
lr_desc.irq = val & GICH_LR_VIRTUALID;
|
||||||
|
if (lr_desc.irq <= 15)
|
||||||
|
lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
|
||||||
|
else
|
||||||
|
lr_desc.source = 0;
|
||||||
|
lr_desc.state = 0;
|
||||||
|
|
||||||
|
if (val & ICH_LR_PENDING_BIT)
|
||||||
|
lr_desc.state |= LR_STATE_PENDING;
|
||||||
|
if (val & ICH_LR_ACTIVE_BIT)
|
||||||
|
lr_desc.state |= LR_STATE_ACTIVE;
|
||||||
|
if (val & ICH_LR_EOI)
|
||||||
|
lr_desc.state |= LR_EOI_INT;
|
||||||
|
|
||||||
|
return lr_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr,
|
||||||
|
struct vgic_lr lr_desc)
|
||||||
|
{
|
||||||
|
u64 lr_val = (((u32)lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) |
|
||||||
|
lr_desc.irq);
|
||||||
|
|
||||||
|
if (lr_desc.state & LR_STATE_PENDING)
|
||||||
|
lr_val |= ICH_LR_PENDING_BIT;
|
||||||
|
if (lr_desc.state & LR_STATE_ACTIVE)
|
||||||
|
lr_val |= ICH_LR_ACTIVE_BIT;
|
||||||
|
if (lr_desc.state & LR_EOI_INT)
|
||||||
|
lr_val |= ICH_LR_EOI;
|
||||||
|
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)] = lr_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v3_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
|
||||||
|
struct vgic_lr lr_desc)
|
||||||
|
{
|
||||||
|
if (!(lr_desc.state & LR_STATE_MASK))
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 vgic_v3_get_elrsr(const struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 vgic_v3_get_eisr(const struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return vcpu->arch.vgic_cpu.vgic_v3.vgic_eisr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 vgic_v3_get_interrupt_status(const struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
u32 misr = vcpu->arch.vgic_cpu.vgic_v3.vgic_misr;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
|
if (misr & ICH_MISR_EOI)
|
||||||
|
ret |= INT_STATUS_EOI;
|
||||||
|
if (misr & ICH_MISR_U)
|
||||||
|
ret |= INT_STATUS_UNDERFLOW;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||||
|
{
|
||||||
|
u32 vmcr = vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr;
|
||||||
|
|
||||||
|
vmcrp->ctlr = (vmcr & ICH_VMCR_CTLR_MASK) >> ICH_VMCR_CTLR_SHIFT;
|
||||||
|
vmcrp->abpr = (vmcr & ICH_VMCR_BPR1_MASK) >> ICH_VMCR_BPR1_SHIFT;
|
||||||
|
vmcrp->bpr = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT;
|
||||||
|
vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v3_enable_underflow(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr |= ICH_HCR_UIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v3_disable_underflow(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr &= ~ICH_HCR_UIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||||
|
{
|
||||||
|
u32 vmcr;
|
||||||
|
|
||||||
|
vmcr = (vmcrp->ctlr << ICH_VMCR_CTLR_SHIFT) & ICH_VMCR_CTLR_MASK;
|
||||||
|
vmcr |= (vmcrp->abpr << ICH_VMCR_BPR1_SHIFT) & ICH_VMCR_BPR1_MASK;
|
||||||
|
vmcr |= (vmcrp->bpr << ICH_VMCR_BPR0_SHIFT) & ICH_VMCR_BPR0_MASK;
|
||||||
|
vmcr |= (vmcrp->pmr << ICH_VMCR_PMR_SHIFT) & ICH_VMCR_PMR_MASK;
|
||||||
|
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = vmcr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* By forcing VMCR to zero, the GIC will restore the binary
|
||||||
|
* points to their reset values. Anything else resets to zero
|
||||||
|
* anyway.
|
||||||
|
*/
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = 0;
|
||||||
|
|
||||||
|
/* Get the show on the road... */
|
||||||
|
vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr = ICH_HCR_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct vgic_ops vgic_v3_ops = {
|
||||||
|
.get_lr = vgic_v3_get_lr,
|
||||||
|
.set_lr = vgic_v3_set_lr,
|
||||||
|
.sync_lr_elrsr = vgic_v3_sync_lr_elrsr,
|
||||||
|
.get_elrsr = vgic_v3_get_elrsr,
|
||||||
|
.get_eisr = vgic_v3_get_eisr,
|
||||||
|
.get_interrupt_status = vgic_v3_get_interrupt_status,
|
||||||
|
.enable_underflow = vgic_v3_enable_underflow,
|
||||||
|
.disable_underflow = vgic_v3_disable_underflow,
|
||||||
|
.get_vmcr = vgic_v3_get_vmcr,
|
||||||
|
.set_vmcr = vgic_v3_set_vmcr,
|
||||||
|
.enable = vgic_v3_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct vgic_params vgic_v3_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
|
||||||
|
*
|
||||||
|
* 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,
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The ListRegs field is 5 bits, but there is a architectural
|
||||||
|
* maximum of 16 list registers. Just ignore bit 4...
|
||||||
|
*/
|
||||||
|
vgic->nr_lr = (ich_vtr_el2 & 0xf) + 1;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
kvm_err("Cannot obtain GICV region\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PAGE_ALIGNED(vcpu_res.start)) {
|
||||||
|
kvm_err("GICV physical address 0x%llx not page aligned\n",
|
||||||
|
(unsigned long long)vcpu_res.start);
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
PAGE_SIZE);
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
vgic->vcpu_base = vcpu_res.start;
|
||||||
|
vgic->vctrl_base = NULL;
|
||||||
|
vgic->type = VGIC_V3;
|
||||||
|
|
||||||
|
kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
|
||||||
|
vcpu_res.start, vgic->maint_irq);
|
||||||
|
|
||||||
|
*ops = &vgic_v3_ops;
|
||||||
|
*params = vgic;
|
||||||
|
|
||||||
|
out:
|
||||||
|
of_node_put(vgic_node);
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -76,14 +76,6 @@
|
||||||
#define IMPLEMENTER_ARM 0x43b
|
#define IMPLEMENTER_ARM 0x43b
|
||||||
#define GICC_ARCH_VERSION_V2 0x2
|
#define GICC_ARCH_VERSION_V2 0x2
|
||||||
|
|
||||||
/* Physical address of vgic virtual cpu interface */
|
|
||||||
static phys_addr_t vgic_vcpu_base;
|
|
||||||
|
|
||||||
/* Virtual control interface base address */
|
|
||||||
static void __iomem *vgic_vctrl_base;
|
|
||||||
|
|
||||||
static struct device_node *vgic_node;
|
|
||||||
|
|
||||||
#define ACCESS_READ_VALUE (1 << 0)
|
#define ACCESS_READ_VALUE (1 << 0)
|
||||||
#define ACCESS_READ_RAZ (0 << 0)
|
#define ACCESS_READ_RAZ (0 << 0)
|
||||||
#define ACCESS_READ_MASK(x) ((x) & (1 << 0))
|
#define ACCESS_READ_MASK(x) ((x) & (1 << 0))
|
||||||
|
@ -94,21 +86,46 @@ static struct device_node *vgic_node;
|
||||||
#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
|
#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
|
||||||
|
|
||||||
static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
|
static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
|
||||||
|
static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
|
||||||
static void vgic_update_state(struct kvm *kvm);
|
static void vgic_update_state(struct kvm *kvm);
|
||||||
static void vgic_kick_vcpus(struct kvm *kvm);
|
static void vgic_kick_vcpus(struct kvm *kvm);
|
||||||
static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
|
static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
|
||||||
static u32 vgic_nr_lr;
|
static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
|
||||||
|
static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
|
||||||
|
static void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||||
|
static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
|
||||||
|
|
||||||
static unsigned int vgic_maint_irq;
|
static const struct vgic_ops *vgic_ops;
|
||||||
|
static const struct vgic_params *vgic;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct vgic_bitmap contains unions that provide two views of
|
||||||
|
* the same data. In one case it is an array of registers of
|
||||||
|
* u32's, and in the other case it is a bitmap of unsigned
|
||||||
|
* longs.
|
||||||
|
*
|
||||||
|
* This does not work on 64-bit BE systems, because the bitmap access
|
||||||
|
* will store two consecutive 32-bit words with the higher-addressed
|
||||||
|
* register's bits at the lower index and the lower-addressed register's
|
||||||
|
* bits at the higher index.
|
||||||
|
*
|
||||||
|
* Therefore, swizzle the register index when accessing the 32-bit word
|
||||||
|
* registers to access the right register's value.
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_CPU_BIG_ENDIAN) && BITS_PER_LONG == 64
|
||||||
|
#define REG_OFFSET_SWIZZLE 1
|
||||||
|
#else
|
||||||
|
#define REG_OFFSET_SWIZZLE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
|
static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
|
||||||
int cpuid, u32 offset)
|
int cpuid, u32 offset)
|
||||||
{
|
{
|
||||||
offset >>= 2;
|
offset >>= 2;
|
||||||
if (!offset)
|
if (!offset)
|
||||||
return x->percpu[cpuid].reg;
|
return x->percpu[cpuid].reg + (offset ^ REG_OFFSET_SWIZZLE);
|
||||||
else
|
else
|
||||||
return x->shared.reg + offset - 1;
|
return x->shared.reg + ((offset - 1) ^ REG_OFFSET_SWIZZLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
|
static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
|
||||||
|
@ -241,12 +258,12 @@ static void vgic_cpu_irq_clear(struct kvm_vcpu *vcpu, int irq)
|
||||||
|
|
||||||
static u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask)
|
static u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask)
|
||||||
{
|
{
|
||||||
return *((u32 *)mmio->data) & mask;
|
return le32_to_cpu(*((u32 *)mmio->data)) & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value)
|
static void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value)
|
||||||
{
|
{
|
||||||
*((u32 *)mmio->data) = value & mask;
|
*((u32 *)mmio->data) = cpu_to_le32(value) & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -593,18 +610,6 @@ static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LR_CPUID(lr) \
|
|
||||||
(((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT)
|
|
||||||
#define LR_IRQID(lr) \
|
|
||||||
((lr) & GICH_LR_VIRTUALID)
|
|
||||||
|
|
||||||
static void vgic_retire_lr(int lr_nr, int irq, struct vgic_cpu *vgic_cpu)
|
|
||||||
{
|
|
||||||
clear_bit(lr_nr, vgic_cpu->lr_used);
|
|
||||||
vgic_cpu->vgic_lr[lr_nr] &= ~GICH_LR_STATE;
|
|
||||||
vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vgic_unqueue_irqs - move pending IRQs from LRs to the distributor
|
* vgic_unqueue_irqs - move pending IRQs from LRs to the distributor
|
||||||
* @vgic_cpu: Pointer to the vgic_cpu struct holding the LRs
|
* @vgic_cpu: Pointer to the vgic_cpu struct holding the LRs
|
||||||
|
@ -622,13 +627,10 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
||||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||||
int vcpu_id = vcpu->vcpu_id;
|
int vcpu_id = vcpu->vcpu_id;
|
||||||
int i, irq, source_cpu;
|
int i;
|
||||||
u32 *lr;
|
|
||||||
|
|
||||||
for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
|
for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
|
||||||
lr = &vgic_cpu->vgic_lr[i];
|
struct vgic_lr lr = vgic_get_lr(vcpu, i);
|
||||||
irq = LR_IRQID(*lr);
|
|
||||||
source_cpu = LR_CPUID(*lr);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are three options for the state bits:
|
* There are three options for the state bits:
|
||||||
|
@ -640,7 +642,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
||||||
* If the LR holds only an active interrupt (not pending) then
|
* If the LR holds only an active interrupt (not pending) then
|
||||||
* just leave it alone.
|
* just leave it alone.
|
||||||
*/
|
*/
|
||||||
if ((*lr & GICH_LR_STATE) == GICH_LR_ACTIVE_BIT)
|
if ((lr.state & LR_STATE_MASK) == LR_STATE_ACTIVE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -649,18 +651,19 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
||||||
* is fine, then we are only setting a few bits that were
|
* is fine, then we are only setting a few bits that were
|
||||||
* already set.
|
* already set.
|
||||||
*/
|
*/
|
||||||
vgic_dist_irq_set(vcpu, irq);
|
vgic_dist_irq_set(vcpu, lr.irq);
|
||||||
if (irq < VGIC_NR_SGIS)
|
if (lr.irq < VGIC_NR_SGIS)
|
||||||
dist->irq_sgi_sources[vcpu_id][irq] |= 1 << source_cpu;
|
dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source;
|
||||||
*lr &= ~GICH_LR_PENDING_BIT;
|
lr.state &= ~LR_STATE_PENDING;
|
||||||
|
vgic_set_lr(vcpu, i, lr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there's no state left on the LR (it could still be
|
* If there's no state left on the LR (it could still be
|
||||||
* active), then the LR does not hold any useful info and can
|
* active), then the LR does not hold any useful info and can
|
||||||
* be marked as free for other use.
|
* be marked as free for other use.
|
||||||
*/
|
*/
|
||||||
if (!(*lr & GICH_LR_STATE))
|
if (!(lr.state & LR_STATE_MASK))
|
||||||
vgic_retire_lr(i, irq, vgic_cpu);
|
vgic_retire_lr(i, lr.irq, vcpu);
|
||||||
|
|
||||||
/* Finally update the VGIC state. */
|
/* Finally update the VGIC state. */
|
||||||
vgic_update_state(vcpu->kvm);
|
vgic_update_state(vcpu->kvm);
|
||||||
|
@ -989,8 +992,73 @@ static void vgic_update_state(struct kvm *kvm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MK_LR_PEND(src, irq) \
|
static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr)
|
||||||
(GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq))
|
{
|
||||||
|
return vgic_ops->get_lr(vcpu, lr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr,
|
||||||
|
struct vgic_lr vlr)
|
||||||
|
{
|
||||||
|
vgic_ops->set_lr(vcpu, lr, vlr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr,
|
||||||
|
struct vgic_lr vlr)
|
||||||
|
{
|
||||||
|
vgic_ops->sync_lr_elrsr(vcpu, lr, vlr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 vgic_get_elrsr(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return vgic_ops->get_elrsr(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 vgic_get_eisr(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return vgic_ops->get_eisr(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 vgic_get_interrupt_status(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return vgic_ops->get_interrupt_status(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vgic_enable_underflow(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vgic_ops->enable_underflow(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vgic_disable_underflow(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vgic_ops->disable_underflow(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
|
||||||
|
{
|
||||||
|
vgic_ops->get_vmcr(vcpu, vmcr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr)
|
||||||
|
{
|
||||||
|
vgic_ops->set_vmcr(vcpu, vmcr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void vgic_enable(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
vgic_ops->enable(vcpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||||
|
struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr);
|
||||||
|
|
||||||
|
vlr.state = 0;
|
||||||
|
vgic_set_lr(vcpu, lr_nr, vlr);
|
||||||
|
clear_bit(lr_nr, vgic_cpu->lr_used);
|
||||||
|
vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An interrupt may have been disabled after being made pending on the
|
* An interrupt may have been disabled after being made pending on the
|
||||||
|
@ -1006,13 +1074,13 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
|
||||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||||
int lr;
|
int lr;
|
||||||
|
|
||||||
for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
|
for_each_set_bit(lr, vgic_cpu->lr_used, vgic->nr_lr) {
|
||||||
int irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
|
struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
|
||||||
|
|
||||||
if (!vgic_irq_is_enabled(vcpu, irq)) {
|
if (!vgic_irq_is_enabled(vcpu, vlr.irq)) {
|
||||||
vgic_retire_lr(lr, irq, vgic_cpu);
|
vgic_retire_lr(lr, vlr.irq, vcpu);
|
||||||
if (vgic_irq_is_active(vcpu, irq))
|
if (vgic_irq_is_active(vcpu, vlr.irq))
|
||||||
vgic_irq_clear_active(vcpu, irq);
|
vgic_irq_clear_active(vcpu, vlr.irq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1024,6 +1092,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
|
||||||
static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
||||||
{
|
{
|
||||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||||
|
struct vgic_lr vlr;
|
||||||
int lr;
|
int lr;
|
||||||
|
|
||||||
/* Sanitize the input... */
|
/* Sanitize the input... */
|
||||||
|
@ -1036,28 +1105,34 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
||||||
lr = vgic_cpu->vgic_irq_lr_map[irq];
|
lr = vgic_cpu->vgic_irq_lr_map[irq];
|
||||||
|
|
||||||
/* Do we have an active interrupt for the same CPUID? */
|
/* Do we have an active interrupt for the same CPUID? */
|
||||||
if (lr != LR_EMPTY &&
|
if (lr != LR_EMPTY) {
|
||||||
(LR_CPUID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) {
|
vlr = vgic_get_lr(vcpu, lr);
|
||||||
kvm_debug("LR%d piggyback for IRQ%d %x\n",
|
if (vlr.source == sgi_source_id) {
|
||||||
lr, irq, vgic_cpu->vgic_lr[lr]);
|
kvm_debug("LR%d piggyback for IRQ%d\n", lr, vlr.irq);
|
||||||
BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
|
BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
|
||||||
vgic_cpu->vgic_lr[lr] |= GICH_LR_PENDING_BIT;
|
vlr.state |= LR_STATE_PENDING;
|
||||||
return true;
|
vgic_set_lr(vcpu, lr, vlr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to use another LR for this interrupt */
|
/* Try to use another LR for this interrupt */
|
||||||
lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used,
|
lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used,
|
||||||
vgic_cpu->nr_lr);
|
vgic->nr_lr);
|
||||||
if (lr >= vgic_cpu->nr_lr)
|
if (lr >= vgic->nr_lr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
|
kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
|
||||||
vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
|
|
||||||
vgic_cpu->vgic_irq_lr_map[irq] = lr;
|
vgic_cpu->vgic_irq_lr_map[irq] = lr;
|
||||||
set_bit(lr, vgic_cpu->lr_used);
|
set_bit(lr, vgic_cpu->lr_used);
|
||||||
|
|
||||||
|
vlr.irq = irq;
|
||||||
|
vlr.source = sgi_source_id;
|
||||||
|
vlr.state = LR_STATE_PENDING;
|
||||||
if (!vgic_irq_is_edge(vcpu, irq))
|
if (!vgic_irq_is_edge(vcpu, irq))
|
||||||
vgic_cpu->vgic_lr[lr] |= GICH_LR_EOI;
|
vlr.state |= LR_EOI_INT;
|
||||||
|
|
||||||
|
vgic_set_lr(vcpu, lr, vlr);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1155,9 +1230,9 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
epilog:
|
epilog:
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
vgic_cpu->vgic_hcr |= GICH_HCR_UIE;
|
vgic_enable_underflow(vcpu);
|
||||||
} else {
|
} else {
|
||||||
vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE;
|
vgic_disable_underflow(vcpu);
|
||||||
/*
|
/*
|
||||||
* We're about to run this VCPU, and we've consumed
|
* We're about to run this VCPU, and we've consumed
|
||||||
* everything the distributor had in store for
|
* everything the distributor had in store for
|
||||||
|
@ -1170,44 +1245,46 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
u32 status = vgic_get_interrupt_status(vcpu);
|
||||||
bool level_pending = false;
|
bool level_pending = false;
|
||||||
|
|
||||||
kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr);
|
kvm_debug("STATUS = %08x\n", status);
|
||||||
|
|
||||||
if (vgic_cpu->vgic_misr & GICH_MISR_EOI) {
|
if (status & INT_STATUS_EOI) {
|
||||||
/*
|
/*
|
||||||
* Some level interrupts have been EOIed. Clear their
|
* Some level interrupts have been EOIed. Clear their
|
||||||
* active bit.
|
* active bit.
|
||||||
*/
|
*/
|
||||||
int lr, irq;
|
u64 eisr = vgic_get_eisr(vcpu);
|
||||||
|
unsigned long *eisr_ptr = (unsigned long *)&eisr;
|
||||||
|
int lr;
|
||||||
|
|
||||||
for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr,
|
for_each_set_bit(lr, eisr_ptr, vgic->nr_lr) {
|
||||||
vgic_cpu->nr_lr) {
|
struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
|
||||||
irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
|
|
||||||
|
|
||||||
vgic_irq_clear_active(vcpu, irq);
|
vgic_irq_clear_active(vcpu, vlr.irq);
|
||||||
vgic_cpu->vgic_lr[lr] &= ~GICH_LR_EOI;
|
WARN_ON(vlr.state & LR_STATE_MASK);
|
||||||
|
vlr.state = 0;
|
||||||
|
vgic_set_lr(vcpu, lr, vlr);
|
||||||
|
|
||||||
/* Any additional pending interrupt? */
|
/* Any additional pending interrupt? */
|
||||||
if (vgic_dist_irq_is_pending(vcpu, irq)) {
|
if (vgic_dist_irq_is_pending(vcpu, vlr.irq)) {
|
||||||
vgic_cpu_irq_set(vcpu, irq);
|
vgic_cpu_irq_set(vcpu, vlr.irq);
|
||||||
level_pending = true;
|
level_pending = true;
|
||||||
} else {
|
} else {
|
||||||
vgic_cpu_irq_clear(vcpu, irq);
|
vgic_cpu_irq_clear(vcpu, vlr.irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Despite being EOIed, the LR may not have
|
* Despite being EOIed, the LR may not have
|
||||||
* been marked as empty.
|
* been marked as empty.
|
||||||
*/
|
*/
|
||||||
set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
|
vgic_sync_lr_elrsr(vcpu, lr, vlr);
|
||||||
vgic_cpu->vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vgic_cpu->vgic_misr & GICH_MISR_U)
|
if (status & INT_STATUS_UNDERFLOW)
|
||||||
vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE;
|
vgic_disable_underflow(vcpu);
|
||||||
|
|
||||||
return level_pending;
|
return level_pending;
|
||||||
}
|
}
|
||||||
|
@ -1220,29 +1297,31 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||||
|
u64 elrsr;
|
||||||
|
unsigned long *elrsr_ptr;
|
||||||
int lr, pending;
|
int lr, pending;
|
||||||
bool level_pending;
|
bool level_pending;
|
||||||
|
|
||||||
level_pending = vgic_process_maintenance(vcpu);
|
level_pending = vgic_process_maintenance(vcpu);
|
||||||
|
elrsr = vgic_get_elrsr(vcpu);
|
||||||
|
elrsr_ptr = (unsigned long *)&elrsr;
|
||||||
|
|
||||||
/* Clear mappings for empty LRs */
|
/* Clear mappings for empty LRs */
|
||||||
for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr,
|
for_each_set_bit(lr, elrsr_ptr, vgic->nr_lr) {
|
||||||
vgic_cpu->nr_lr) {
|
struct vgic_lr vlr;
|
||||||
int irq;
|
|
||||||
|
|
||||||
if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
|
if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
|
vlr = vgic_get_lr(vcpu, lr);
|
||||||
|
|
||||||
BUG_ON(irq >= VGIC_NR_IRQS);
|
BUG_ON(vlr.irq >= VGIC_NR_IRQS);
|
||||||
vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
|
vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we still have something up our sleeve... */
|
/* Check if we still have something up our sleeve... */
|
||||||
pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr,
|
pending = find_first_zero_bit(elrsr_ptr, vgic->nr_lr);
|
||||||
vgic_cpu->nr_lr);
|
if (level_pending || pending < vgic->nr_lr)
|
||||||
if (level_pending || pending < vgic_cpu->nr_lr)
|
|
||||||
set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
|
set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1432,21 +1511,20 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* By forcing VMCR to zero, the GIC will restore the binary
|
* Store the number of LRs per vcpu, so we don't have to go
|
||||||
* points to their reset values. Anything else resets to zero
|
* all the way to the distributor structure to find out. Only
|
||||||
* anyway.
|
* assembly code should use this one.
|
||||||
*/
|
*/
|
||||||
vgic_cpu->vgic_vmcr = 0;
|
vgic_cpu->nr_lr = vgic->nr_lr;
|
||||||
|
|
||||||
vgic_cpu->nr_lr = vgic_nr_lr;
|
vgic_enable(vcpu);
|
||||||
vgic_cpu->vgic_hcr = GICH_HCR_EN; /* Get the show on the road... */
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vgic_init_maintenance_interrupt(void *info)
|
static void vgic_init_maintenance_interrupt(void *info)
|
||||||
{
|
{
|
||||||
enable_percpu_irq(vgic_maint_irq, 0);
|
enable_percpu_irq(vgic->maint_irq, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vgic_cpu_notify(struct notifier_block *self,
|
static int vgic_cpu_notify(struct notifier_block *self,
|
||||||
|
@ -1459,7 +1537,7 @@ static int vgic_cpu_notify(struct notifier_block *self,
|
||||||
break;
|
break;
|
||||||
case CPU_DYING:
|
case CPU_DYING:
|
||||||
case CPU_DYING_FROZEN:
|
case CPU_DYING_FROZEN:
|
||||||
disable_percpu_irq(vgic_maint_irq);
|
disable_percpu_irq(vgic->maint_irq);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1470,30 +1548,37 @@ static struct notifier_block vgic_cpu_nb = {
|
||||||
.notifier_call = vgic_cpu_notify,
|
.notifier_call = vgic_cpu_notify,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id vgic_ids[] = {
|
||||||
|
{ .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, },
|
||||||
|
{ .compatible = "arm,gic-v3", .data = vgic_v3_probe, },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
int kvm_vgic_hyp_init(void)
|
int kvm_vgic_hyp_init(void)
|
||||||
{
|
{
|
||||||
|
const struct of_device_id *matched_id;
|
||||||
|
int (*vgic_probe)(struct device_node *,const struct vgic_ops **,
|
||||||
|
const struct vgic_params **);
|
||||||
|
struct device_node *vgic_node;
|
||||||
int ret;
|
int ret;
|
||||||
struct resource vctrl_res;
|
|
||||||
struct resource vcpu_res;
|
|
||||||
|
|
||||||
vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic");
|
vgic_node = of_find_matching_node_and_match(NULL,
|
||||||
|
vgic_ids, &matched_id);
|
||||||
if (!vgic_node) {
|
if (!vgic_node) {
|
||||||
kvm_err("error: no compatible vgic node in DT\n");
|
kvm_err("error: no compatible GIC node found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
vgic_maint_irq = irq_of_parse_and_map(vgic_node, 0);
|
vgic_probe = matched_id->data;
|
||||||
if (!vgic_maint_irq) {
|
ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
|
||||||
kvm_err("error getting vgic maintenance irq from DT\n");
|
if (ret)
|
||||||
ret = -ENXIO;
|
return ret;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = request_percpu_irq(vgic_maint_irq, vgic_maintenance_handler,
|
ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler,
|
||||||
"vgic", kvm_get_running_vcpus());
|
"vgic", kvm_get_running_vcpus());
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kvm_err("Cannot register interrupt %d\n", vgic_maint_irq);
|
kvm_err("Cannot register interrupt %d\n", vgic->maint_irq);
|
||||||
goto out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = __register_cpu_notifier(&vgic_cpu_nb);
|
ret = __register_cpu_notifier(&vgic_cpu_nb);
|
||||||
|
@ -1502,65 +1587,15 @@ int kvm_vgic_hyp_init(void)
|
||||||
goto out_free_irq;
|
goto out_free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_address_to_resource(vgic_node, 2, &vctrl_res);
|
/* Callback into for arch code for setup */
|
||||||
if (ret) {
|
vgic_arch_setup(vgic);
|
||||||
kvm_err("Cannot obtain VCTRL resource\n");
|
|
||||||
goto out_free_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
vgic_vctrl_base = of_iomap(vgic_node, 2);
|
|
||||||
if (!vgic_vctrl_base) {
|
|
||||||
kvm_err("Cannot ioremap VCTRL\n");
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out_free_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
vgic_nr_lr = readl_relaxed(vgic_vctrl_base + GICH_VTR);
|
|
||||||
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);
|
|
||||||
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 VCPU resource\n");
|
|
||||||
ret = -ENXIO;
|
|
||||||
goto out_unmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PAGE_ALIGNED(vcpu_res.start)) {
|
|
||||||
kvm_err("GICV physical address 0x%llx not page aligned\n",
|
|
||||||
(unsigned long long)vcpu_res.start);
|
|
||||||
ret = -ENXIO;
|
|
||||||
goto out_unmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
PAGE_SIZE);
|
|
||||||
ret = -ENXIO;
|
|
||||||
goto out_unmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
vgic_vcpu_base = vcpu_res.start;
|
|
||||||
|
|
||||||
kvm_info("%s@%llx IRQ%d\n", vgic_node->name,
|
|
||||||
vctrl_res.start, vgic_maint_irq);
|
|
||||||
on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1);
|
on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1);
|
||||||
|
|
||||||
goto out;
|
return 0;
|
||||||
|
|
||||||
out_unmap:
|
|
||||||
iounmap(vgic_vctrl_base);
|
|
||||||
out_free_irq:
|
out_free_irq:
|
||||||
free_percpu_irq(vgic_maint_irq, kvm_get_running_vcpus());
|
free_percpu_irq(vgic->maint_irq, kvm_get_running_vcpus());
|
||||||
out:
|
|
||||||
of_node_put(vgic_node);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1593,7 +1628,7 @@ int kvm_vgic_init(struct kvm *kvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base,
|
ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base,
|
||||||
vgic_vcpu_base, KVM_VGIC_V2_CPU_SIZE);
|
vgic->vcpu_base, KVM_VGIC_V2_CPU_SIZE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kvm_err("Unable to remap VGIC CPU to VCPU\n");
|
kvm_err("Unable to remap VGIC CPU to VCPU\n");
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1639,7 +1674,8 @@ int kvm_vgic_create(struct kvm *kvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_init(&kvm->arch.vgic.lock);
|
spin_lock_init(&kvm->arch.vgic.lock);
|
||||||
kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
|
kvm->arch.vgic.in_kernel = true;
|
||||||
|
kvm->arch.vgic.vctrl_base = vgic->vctrl_base;
|
||||||
kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
|
kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
|
||||||
kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
|
kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
|
||||||
|
|
||||||
|
@ -1738,39 +1774,40 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
|
||||||
static bool handle_cpu_mmio_misc(struct kvm_vcpu *vcpu,
|
static bool handle_cpu_mmio_misc(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_exit_mmio *mmio, phys_addr_t offset)
|
struct kvm_exit_mmio *mmio, phys_addr_t offset)
|
||||||
{
|
{
|
||||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
||||||
u32 reg, mask = 0, shift = 0;
|
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
|
struct vgic_vmcr vmcr;
|
||||||
|
u32 *vmcr_field;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
vgic_get_vmcr(vcpu, &vmcr);
|
||||||
|
|
||||||
switch (offset & ~0x3) {
|
switch (offset & ~0x3) {
|
||||||
case GIC_CPU_CTRL:
|
case GIC_CPU_CTRL:
|
||||||
mask = GICH_VMCR_CTRL_MASK;
|
vmcr_field = &vmcr.ctlr;
|
||||||
shift = GICH_VMCR_CTRL_SHIFT;
|
|
||||||
break;
|
break;
|
||||||
case GIC_CPU_PRIMASK:
|
case GIC_CPU_PRIMASK:
|
||||||
mask = GICH_VMCR_PRIMASK_MASK;
|
vmcr_field = &vmcr.pmr;
|
||||||
shift = GICH_VMCR_PRIMASK_SHIFT;
|
|
||||||
break;
|
break;
|
||||||
case GIC_CPU_BINPOINT:
|
case GIC_CPU_BINPOINT:
|
||||||
mask = GICH_VMCR_BINPOINT_MASK;
|
vmcr_field = &vmcr.bpr;
|
||||||
shift = GICH_VMCR_BINPOINT_SHIFT;
|
|
||||||
break;
|
break;
|
||||||
case GIC_CPU_ALIAS_BINPOINT:
|
case GIC_CPU_ALIAS_BINPOINT:
|
||||||
mask = GICH_VMCR_ALIAS_BINPOINT_MASK;
|
vmcr_field = &vmcr.abpr;
|
||||||
shift = GICH_VMCR_ALIAS_BINPOINT_SHIFT;
|
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mmio->is_write) {
|
if (!mmio->is_write) {
|
||||||
reg = (vgic_cpu->vgic_vmcr & mask) >> shift;
|
reg = *vmcr_field;
|
||||||
mmio_data_write(mmio, ~0, reg);
|
mmio_data_write(mmio, ~0, reg);
|
||||||
} else {
|
} else {
|
||||||
reg = mmio_data_read(mmio, ~0);
|
reg = mmio_data_read(mmio, ~0);
|
||||||
reg = (reg << shift) & mask;
|
if (reg != *vmcr_field) {
|
||||||
if (reg != (vgic_cpu->vgic_vmcr & mask))
|
*vmcr_field = reg;
|
||||||
|
vgic_set_vmcr(vcpu, &vmcr);
|
||||||
updated = true;
|
updated = true;
|
||||||
vgic_cpu->vgic_vmcr &= ~mask;
|
}
|
||||||
vgic_cpu->vgic_vmcr |= reg;
|
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue