mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'kvmarm-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into HEAD
KVM/arm64 updates for 7.2
* New features:
- None. Zilch. Nada. Que dalle.
* Fixes and other improvements:
- Significant cleanup of the vgic-v5 PPI support which was merged in
7.1. This makes the code more maintainable, and squashes a couple
of bugs in the meantime.
- Set of fixes for the handling of the MMU in an NV context,
particularly VNCR-triggered faults. S1POE support is fixed
as well.
- Large set of pKVM fixes, mostly addressing recurring issues
around hypervisor tracking of donated pages in obscure cases
where the donation could fail and leave things in a bizarre
state.
- Fixes for the so-called "lazy vgic init", which resulted in
sleeping operations in non-preemptible sections. This turned
out to be far more invasive than initially expected...
- Reduce the overhead of L1/L2 context switch by not touching
the FP registers.
- Fix the way non-implemented page sizes are dealt with when
a guest insist on using them for S2 translation.
- The usual set of low-impact fixes and cleanups all over the map.
This commit is contained in:
@@ -12,8 +12,8 @@ Only one VGIC instance may be instantiated through this API. The created VGIC
|
||||
will act as the VM interrupt controller, requiring emulated user-space devices
|
||||
to inject interrupts to the VGIC instead of directly to CPUs.
|
||||
|
||||
Creating a guest GICv5 device requires a host GICv5 host. The current VGICv5
|
||||
device only supports PPI interrupts. These can either be injected from emulated
|
||||
Creating a guest GICv5 device requires a GICv5 host. The current VGICv5 device
|
||||
only supports PPI interrupts. These can either be injected from emulated
|
||||
in-kernel devices (such as the Arch Timer, or PMU), or via the KVM_IRQ_LINE
|
||||
ioctl.
|
||||
|
||||
@@ -25,7 +25,7 @@ Groups:
|
||||
request the initialization of the VGIC, no additional parameter in
|
||||
kvm_device_attr.addr. Must be called after all VCPUs have been created.
|
||||
|
||||
KVM_DEV_ARM_VGIC_USERPSPACE_PPIs
|
||||
KVM_DEV_ARM_VGIC_USERSPACE_PPIS
|
||||
request the mask of userspace-drivable PPIs. Only a subset of the PPIs can
|
||||
be directly driven from userspace with GICv5, and the returned mask
|
||||
informs userspace of which it is allowed to drive via KVM_IRQ_LINE.
|
||||
|
||||
@@ -37,8 +37,11 @@ Returns:
|
||||
A value describing the PMUv3 (Performance Monitor Unit v3) overflow interrupt
|
||||
number for this vcpu. This interrupt could be a PPI or SPI, but the interrupt
|
||||
type must be same for each vcpu. As a PPI, the interrupt number is the same for
|
||||
all vcpus, while as an SPI it must be a separate number per vcpu. For
|
||||
GICv5-based guests, the architected PPI (23) must be used.
|
||||
all vcpus, while as an SPI it must be a separate number per vcpu.
|
||||
|
||||
For GICv5-based guests, the architected PPI (23) must be used, and must be
|
||||
communicated as the full GICv5-style Interrupt ID, i.e., 0x20000017. This ioctl
|
||||
can be omitted altogether for a GICv5-based guest.
|
||||
|
||||
1.2 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_INIT
|
||||
---------------------------------------
|
||||
|
||||
@@ -1112,7 +1112,8 @@ struct kvm_vcpu_arch {
|
||||
#define IN_NESTED_ERET __vcpu_single_flag(sflags, BIT(7))
|
||||
/* SError pending for nested guest */
|
||||
#define NESTED_SERROR_PENDING __vcpu_single_flag(sflags, BIT(8))
|
||||
|
||||
/* KVM is currently emulating an L2 to L1 exception */
|
||||
#define IN_NESTED_EXCEPTION __vcpu_single_flag(sflags, BIT(9))
|
||||
|
||||
/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
|
||||
#define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) + \
|
||||
@@ -1273,13 +1274,14 @@ void kvm_arm_resume_guest(struct kvm *kvm);
|
||||
#define vcpu_has_run_once(vcpu) (!!READ_ONCE((vcpu)->pid))
|
||||
|
||||
#ifndef __KVM_NVHE_HYPERVISOR__
|
||||
#define kvm_call_hyp_nvhe(f, ...) \
|
||||
#define kvm_call_hyp_nvhe(f, ...) \
|
||||
({ \
|
||||
struct arm_smccc_res res; \
|
||||
\
|
||||
arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f), \
|
||||
##__VA_ARGS__, &res); \
|
||||
WARN_ON(res.a0 != SMCCC_RET_SUCCESS); \
|
||||
if (WARN_ON(res.a0 != SMCCC_RET_SUCCESS)) \
|
||||
res.a1 = -EOPNOTSUPP; \
|
||||
\
|
||||
res.a1; \
|
||||
})
|
||||
|
||||
@@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icache_flags);
|
||||
extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
|
||||
extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl);
|
||||
extern unsigned long kvm_nvhe_sym(hyp_nr_cpus);
|
||||
extern unsigned int kvm_nvhe_sym(hyp_gicv3_nr_lr);
|
||||
|
||||
#endif /* __ARM64_KVM_HYP_H__ */
|
||||
|
||||
@@ -318,8 +318,7 @@ static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu)
|
||||
* Must be called from hyp code running at EL2 with an updated VTTBR
|
||||
* and interrupts disabled.
|
||||
*/
|
||||
static __always_inline void __load_stage2(struct kvm_s2_mmu *mmu,
|
||||
struct kvm_arch *arch)
|
||||
static __always_inline void __load_stage2(struct kvm_s2_mmu *mmu)
|
||||
{
|
||||
write_sysreg(mmu->vtcr, vtcr_el2);
|
||||
write_sysreg(kvm_get_vttbr(mmu), vttbr_el2);
|
||||
|
||||
@@ -266,6 +266,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar1[] = {
|
||||
};
|
||||
|
||||
static const struct arm64_ftr_bits ftr_id_aa64isar2[] = {
|
||||
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_ATS1A_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_LUT_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_CSSC_SHIFT, 4, 0),
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_RPRFM_SHIFT, 4, 0),
|
||||
|
||||
@@ -104,11 +104,9 @@ SYM_CODE_START_LOCAL(__finalise_el2)
|
||||
mov_q x0, HCR_HOST_VHE_FLAGS
|
||||
msr_hcr_el2 x0
|
||||
|
||||
// Use the EL1 allocated stack, per-cpu offset
|
||||
// Use the EL1 allocated stack
|
||||
mrs x0, sp_el1
|
||||
mov sp, x0
|
||||
mrs x0, tpidr_el1
|
||||
msr tpidr_el2, x0
|
||||
|
||||
// FP configuration, vectors
|
||||
mrs_s x0, SYS_CPACR_EL12
|
||||
|
||||
+63
-74
@@ -39,10 +39,9 @@ static const u8 default_ppi[] = {
|
||||
[TIMER_HVTIMER] = 28,
|
||||
};
|
||||
|
||||
static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx);
|
||||
static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
|
||||
struct arch_timer_context *timer_ctx);
|
||||
static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx);
|
||||
static bool kvm_timer_pending(struct arch_timer_context *timer_ctx);
|
||||
static void kvm_arm_timer_write(struct kvm_vcpu *vcpu,
|
||||
struct arch_timer_context *timer,
|
||||
enum kvm_arch_timer_regs treg,
|
||||
@@ -52,11 +51,17 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
|
||||
enum kvm_arch_timer_regs treg);
|
||||
static bool kvm_arch_timer_get_input_level(int vintid);
|
||||
|
||||
static struct irq_ops arch_timer_irq_ops = {
|
||||
static unsigned long kvm_arch_timer_get_irq_flags(void)
|
||||
{
|
||||
return kvm_vgic_global_state.no_hw_deactivation ? VGIC_IRQ_SW_RESAMPLE : 0;
|
||||
}
|
||||
|
||||
static const struct irq_ops arch_timer_irq_ops = {
|
||||
.get_flags = kvm_arch_timer_get_irq_flags,
|
||||
.get_input_level = kvm_arch_timer_get_input_level,
|
||||
};
|
||||
|
||||
static struct irq_ops arch_timer_irq_ops_vgic_v5 = {
|
||||
static const struct irq_ops arch_timer_irq_ops_vgic_v5 = {
|
||||
.get_input_level = kvm_arch_timer_get_input_level,
|
||||
.queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock,
|
||||
.set_direct_injection = vgic_v5_set_ppi_dvi,
|
||||
@@ -224,7 +229,7 @@ static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
|
||||
else
|
||||
ctx = map.direct_ptimer;
|
||||
|
||||
if (kvm_timer_should_fire(ctx))
|
||||
if (kvm_timer_pending(ctx))
|
||||
kvm_timer_update_irq(vcpu, true, ctx);
|
||||
|
||||
if (userspace_irqchip(vcpu->kvm) &&
|
||||
@@ -257,7 +262,7 @@ static u64 kvm_timer_compute_delta(struct arch_timer_context *timer_ctx)
|
||||
return kvm_counter_compute_delta(timer_ctx, timer_get_cval(timer_ctx));
|
||||
}
|
||||
|
||||
static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx)
|
||||
static bool kvm_timer_enabled(struct arch_timer_context *timer_ctx)
|
||||
{
|
||||
WARN_ON(timer_ctx && timer_ctx->loaded);
|
||||
return timer_ctx &&
|
||||
@@ -294,7 +299,7 @@ static u64 kvm_timer_earliest_exp(struct kvm_vcpu *vcpu)
|
||||
struct arch_timer_context *ctx = &vcpu->arch.timer_cpu.timers[i];
|
||||
|
||||
WARN(ctx->loaded, "timer %d loaded\n", i);
|
||||
if (kvm_timer_irq_can_fire(ctx))
|
||||
if (kvm_timer_enabled(ctx))
|
||||
min_delta = min(min_delta, kvm_timer_compute_delta(ctx));
|
||||
}
|
||||
|
||||
@@ -358,7 +363,7 @@ static enum hrtimer_restart kvm_hrtimer_expire(struct hrtimer *hrt)
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx)
|
||||
static bool kvm_timer_pending(struct arch_timer_context *timer_ctx)
|
||||
{
|
||||
enum kvm_arch_timers index;
|
||||
u64 cval, now;
|
||||
@@ -391,7 +396,7 @@ static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx)
|
||||
!(cnt_ctl & ARCH_TIMER_CTRL_IT_MASK);
|
||||
}
|
||||
|
||||
if (!kvm_timer_irq_can_fire(timer_ctx))
|
||||
if (!kvm_timer_enabled(timer_ctx))
|
||||
return false;
|
||||
|
||||
cval = timer_get_cval(timer_ctx);
|
||||
@@ -405,22 +410,30 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||
return vcpu_has_wfit_active(vcpu) && wfit_delay_ns(vcpu) == 0;
|
||||
}
|
||||
|
||||
static u64 kvm_timer_needs_notify(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 v = vcpu->run->s.regs.device_irq_level;
|
||||
|
||||
v ^= kvm_timer_pending(vcpu_vtimer(vcpu)) ? KVM_ARM_DEV_EL1_VTIMER : 0;
|
||||
v ^= kvm_timer_pending(vcpu_ptimer(vcpu)) ? KVM_ARM_DEV_EL1_PTIMER : 0;
|
||||
|
||||
return v & (KVM_ARM_DEV_EL1_VTIMER | KVM_ARM_DEV_EL1_PTIMER);
|
||||
}
|
||||
|
||||
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !!kvm_timer_needs_notify(vcpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reflect the timer output level into the kvm_run structure
|
||||
*/
|
||||
void kvm_timer_update_run(struct kvm_vcpu *vcpu)
|
||||
bool kvm_timer_update_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
|
||||
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
|
||||
struct kvm_sync_regs *regs = &vcpu->run->s.regs;
|
||||
|
||||
/* Populate the device bitmap with the timer states */
|
||||
regs->device_irq_level &= ~(KVM_ARM_DEV_EL1_VTIMER |
|
||||
KVM_ARM_DEV_EL1_PTIMER);
|
||||
if (kvm_timer_should_fire(vtimer))
|
||||
regs->device_irq_level |= KVM_ARM_DEV_EL1_VTIMER;
|
||||
if (kvm_timer_should_fire(ptimer))
|
||||
regs->device_irq_level |= KVM_ARM_DEV_EL1_PTIMER;
|
||||
u64 mask = kvm_timer_needs_notify(vcpu);
|
||||
if (mask)
|
||||
vcpu->run->s.regs.device_irq_level ^= mask;
|
||||
return !!mask;
|
||||
}
|
||||
|
||||
static void kvm_timer_update_status(struct arch_timer_context *ctx, bool level)
|
||||
@@ -446,9 +459,8 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
|
||||
{
|
||||
kvm_timer_update_status(timer_ctx, new_level);
|
||||
|
||||
timer_ctx->irq.level = new_level;
|
||||
trace_kvm_timer_update_irq(vcpu->vcpu_id, timer_irq(timer_ctx),
|
||||
timer_ctx->irq.level);
|
||||
new_level);
|
||||
|
||||
if (userspace_irqchip(vcpu->kvm))
|
||||
return;
|
||||
@@ -466,28 +478,25 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
|
||||
|
||||
kvm_vgic_inject_irq(vcpu->kvm, vcpu,
|
||||
timer_irq(timer_ctx),
|
||||
timer_ctx->irq.level,
|
||||
new_level,
|
||||
timer_ctx);
|
||||
}
|
||||
|
||||
/* Only called for a fully emulated timer */
|
||||
static void timer_emulate(struct arch_timer_context *ctx)
|
||||
{
|
||||
bool should_fire = kvm_timer_should_fire(ctx);
|
||||
bool pending = kvm_timer_pending(ctx);
|
||||
|
||||
trace_kvm_timer_emulate(ctx, should_fire);
|
||||
trace_kvm_timer_emulate(ctx, pending);
|
||||
|
||||
if (should_fire != ctx->irq.level)
|
||||
kvm_timer_update_irq(timer_context_to_vcpu(ctx), should_fire, ctx);
|
||||
|
||||
kvm_timer_update_status(ctx, should_fire);
|
||||
kvm_timer_update_irq(timer_context_to_vcpu(ctx), pending, ctx);
|
||||
|
||||
/*
|
||||
* If the timer can fire now, we don't need to have a soft timer
|
||||
* scheduled for the future. If the timer cannot fire at all,
|
||||
* then we also don't need a soft timer.
|
||||
* If the timer is pending, we don't need to have a soft timer
|
||||
* scheduled for the future. If the timer is disabled, then
|
||||
* we don't need a soft timer either.
|
||||
*/
|
||||
if (should_fire || !kvm_timer_irq_can_fire(ctx))
|
||||
if (pending || !kvm_timer_enabled(ctx))
|
||||
return;
|
||||
|
||||
soft_timer_start(&ctx->hrtimer, kvm_timer_compute_delta(ctx));
|
||||
@@ -594,10 +603,10 @@ static void kvm_timer_blocking(struct kvm_vcpu *vcpu)
|
||||
* If no timers are capable of raising interrupts (disabled or
|
||||
* masked), then there's no more work for us to do.
|
||||
*/
|
||||
if (!kvm_timer_irq_can_fire(map.direct_vtimer) &&
|
||||
!kvm_timer_irq_can_fire(map.direct_ptimer) &&
|
||||
!kvm_timer_irq_can_fire(map.emul_vtimer) &&
|
||||
!kvm_timer_irq_can_fire(map.emul_ptimer) &&
|
||||
if (!kvm_timer_enabled(map.direct_vtimer) &&
|
||||
!kvm_timer_enabled(map.direct_ptimer) &&
|
||||
!kvm_timer_enabled(map.emul_vtimer) &&
|
||||
!kvm_timer_enabled(map.emul_ptimer) &&
|
||||
!vcpu_has_wfit_active(vcpu))
|
||||
return;
|
||||
|
||||
@@ -677,6 +686,7 @@ static inline void set_timer_irq_phys_active(struct arch_timer_context *ctx, boo
|
||||
static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = timer_context_to_vcpu(ctx);
|
||||
bool pending = kvm_timer_pending(ctx);
|
||||
bool phys_active = false;
|
||||
|
||||
/*
|
||||
@@ -685,12 +695,12 @@ static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx)
|
||||
* this point and the register restoration, we'll take the
|
||||
* interrupt anyway.
|
||||
*/
|
||||
kvm_timer_update_irq(vcpu, kvm_timer_should_fire(ctx), ctx);
|
||||
kvm_timer_update_irq(vcpu, pending, ctx);
|
||||
|
||||
if (irqchip_in_kernel(vcpu->kvm))
|
||||
phys_active = kvm_vgic_map_is_active(vcpu, timer_irq(ctx));
|
||||
|
||||
phys_active |= ctx->irq.level;
|
||||
phys_active |= pending;
|
||||
phys_active |= vgic_is_v5(vcpu->kvm);
|
||||
|
||||
set_timer_irq_phys_active(ctx, phys_active);
|
||||
@@ -699,6 +709,7 @@ static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx)
|
||||
static void kvm_timer_vcpu_load_nogic(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
|
||||
bool pending = kvm_timer_pending(vtimer);
|
||||
|
||||
/*
|
||||
* Update the timer output so that it is likely to match the
|
||||
@@ -706,7 +717,7 @@ static void kvm_timer_vcpu_load_nogic(struct kvm_vcpu *vcpu)
|
||||
* this point and the register restoration, we'll take the
|
||||
* interrupt anyway.
|
||||
*/
|
||||
kvm_timer_update_irq(vcpu, kvm_timer_should_fire(vtimer), vtimer);
|
||||
kvm_timer_update_irq(vcpu, pending, vtimer);
|
||||
|
||||
/*
|
||||
* When using a userspace irqchip with the architected timers and a
|
||||
@@ -718,7 +729,7 @@ static void kvm_timer_vcpu_load_nogic(struct kvm_vcpu *vcpu)
|
||||
* being de-asserted, we unmask the interrupt again so that we exit
|
||||
* from the guest when the timer fires.
|
||||
*/
|
||||
if (vtimer->irq.level)
|
||||
if (pending)
|
||||
disable_percpu_irq(host_vtimer_irq);
|
||||
else
|
||||
enable_percpu_irq(host_vtimer_irq, host_vtimer_irq_flags);
|
||||
@@ -904,23 +915,6 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu)
|
||||
timer_set_traps(vcpu, &map);
|
||||
}
|
||||
|
||||
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
|
||||
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
|
||||
struct kvm_sync_regs *sregs = &vcpu->run->s.regs;
|
||||
bool vlevel, plevel;
|
||||
|
||||
if (likely(irqchip_in_kernel(vcpu->kvm)))
|
||||
return false;
|
||||
|
||||
vlevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_VTIMER;
|
||||
plevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_PTIMER;
|
||||
|
||||
return kvm_timer_should_fire(vtimer) != vlevel ||
|
||||
kvm_timer_should_fire(ptimer) != plevel;
|
||||
}
|
||||
|
||||
void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = vcpu_timer(vcpu);
|
||||
@@ -1006,7 +1000,7 @@ static void unmask_vtimer_irq_user(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
|
||||
|
||||
if (!kvm_timer_should_fire(vtimer)) {
|
||||
if (!kvm_timer_pending(vtimer)) {
|
||||
kvm_timer_update_irq(vcpu, false, vtimer);
|
||||
if (static_branch_likely(&has_gic_active_state))
|
||||
set_timer_irq_phys_active(vtimer, false);
|
||||
@@ -1288,7 +1282,12 @@ static int timer_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
|
||||
static int timer_irq_set_irqchip_state(struct irq_data *d,
|
||||
enum irqchip_irq_state which, bool val)
|
||||
{
|
||||
if (which != IRQCHIP_STATE_ACTIVE || !irqd_is_forwarded_to_vcpu(d))
|
||||
bool passthrough = which != IRQCHIP_STATE_ACTIVE ||
|
||||
!irqd_is_forwarded_to_vcpu(d) ||
|
||||
(kvm_vgic_global_state.type == VGIC_V5 &&
|
||||
vgic_is_v3(kvm_get_running_vcpu()->kvm));
|
||||
|
||||
if (passthrough)
|
||||
return irq_chip_set_parent_state(d, which, val);
|
||||
|
||||
if (val)
|
||||
@@ -1301,15 +1300,7 @@ static int timer_irq_set_irqchip_state(struct irq_data *d,
|
||||
|
||||
static void timer_irq_eoi(struct irq_data *d)
|
||||
{
|
||||
/*
|
||||
* On a GICv5 host, we still need to call EOI on the parent for
|
||||
* PPIs. The host driver already handles irqs which are forwarded to
|
||||
* vcpus, and skips the GIC CDDI while still doing the GIC CDEOI. This
|
||||
* is required to emulate the EOIMode=1 on GICv5 hardware. Failure to
|
||||
* call EOI unsurprisingly results in *BAD* lock-ups.
|
||||
*/
|
||||
if (!irqd_is_forwarded_to_vcpu(d) ||
|
||||
kvm_vgic_global_state.type == VGIC_V5)
|
||||
if (!irqd_is_forwarded_to_vcpu(d))
|
||||
irq_chip_eoi_parent(d);
|
||||
}
|
||||
|
||||
@@ -1392,8 +1383,6 @@ static int kvm_irq_init(struct arch_timer_kvm_info *info)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (kvm_vgic_global_state.no_hw_deactivation)
|
||||
arch_timer_irq_ops.flags |= VGIC_IRQ_SW_RESAMPLE;
|
||||
WARN_ON(irq_domain_push_irq(domain, host_vtimer_irq,
|
||||
(void *)TIMER_VTIMER));
|
||||
}
|
||||
@@ -1579,7 +1568,7 @@ static bool kvm_arch_timer_get_input_level(int vintid)
|
||||
|
||||
ctx = vcpu_get_timer(vcpu, i);
|
||||
if (timer_irq(ctx) == vintid)
|
||||
return kvm_timer_should_fire(ctx);
|
||||
return kvm_timer_pending(ctx);
|
||||
}
|
||||
|
||||
/* A timer IRQ has fired, but no matching timer was found? */
|
||||
@@ -1591,8 +1580,8 @@ static bool kvm_arch_timer_get_input_level(int vintid)
|
||||
int kvm_timer_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct arch_timer_cpu *timer = vcpu_timer(vcpu);
|
||||
const struct irq_ops *ops;
|
||||
struct timer_map map;
|
||||
struct irq_ops *ops;
|
||||
int ret;
|
||||
|
||||
if (timer->enabled)
|
||||
|
||||
+29
-12
@@ -52,6 +52,7 @@
|
||||
|
||||
#include <linux/irqchip/arm-gic-v5.h>
|
||||
|
||||
#include "vgic/vgic.h"
|
||||
#include "sys_regs.h"
|
||||
|
||||
static enum kvm_mode kvm_mode = KVM_MODE_DEFAULT;
|
||||
@@ -1166,6 +1167,15 @@ static bool vcpu_mode_is_bad_32bit(struct kvm_vcpu *vcpu)
|
||||
return !kvm_supports_32bit_el0();
|
||||
}
|
||||
|
||||
static bool kvm_irq_update_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
bool r;
|
||||
|
||||
r = kvm_timer_update_run(vcpu);
|
||||
r |= kvm_pmu_update_run(vcpu);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vcpu_exit_request - returns true if the VCPU should *not* enter the guest
|
||||
* @vcpu: The VCPU pointer
|
||||
@@ -1187,13 +1197,11 @@ static bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu, int *ret)
|
||||
/*
|
||||
* If we're using a userspace irqchip, then check if we need
|
||||
* to tell a userspace irqchip about timer or PMU level
|
||||
* changes and if so, exit to userspace (the actual level
|
||||
* state gets updated in kvm_timer_update_run and
|
||||
* kvm_pmu_update_run below).
|
||||
* changes and if so, exit to userspace while updating the run
|
||||
* state.
|
||||
*/
|
||||
if (unlikely(!irqchip_in_kernel(vcpu->kvm))) {
|
||||
if (kvm_timer_should_notify_user(vcpu) ||
|
||||
kvm_pmu_should_notify_user(vcpu)) {
|
||||
if (unlikely(kvm_irq_update_run(vcpu))) {
|
||||
*ret = -EINTR;
|
||||
run->exit_reason = KVM_EXIT_INTR;
|
||||
return true;
|
||||
@@ -1408,11 +1416,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
||||
ret = handle_exit(vcpu, ret);
|
||||
}
|
||||
|
||||
/* Tell userspace about in-kernel device output levels */
|
||||
if (unlikely(!irqchip_in_kernel(vcpu->kvm))) {
|
||||
kvm_timer_update_run(vcpu);
|
||||
kvm_pmu_update_run(vcpu);
|
||||
}
|
||||
if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
|
||||
kvm_irq_update_run(vcpu);
|
||||
|
||||
kvm_sigset_deactivate(vcpu);
|
||||
|
||||
@@ -1496,8 +1501,13 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
|
||||
|
||||
return vcpu_interrupt_line(vcpu, irq_num, level);
|
||||
case KVM_ARM_IRQ_TYPE_PPI:
|
||||
if (!irqchip_in_kernel(kvm))
|
||||
if (irqchip_in_kernel(kvm)) {
|
||||
int ret = vgic_lazy_init(kvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
|
||||
if (!vcpu)
|
||||
@@ -1524,8 +1534,13 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
|
||||
|
||||
return kvm_vgic_inject_irq(kvm, vcpu, irq_num, level, NULL);
|
||||
case KVM_ARM_IRQ_TYPE_SPI:
|
||||
if (!irqchip_in_kernel(kvm))
|
||||
if (irqchip_in_kernel(kvm)) {
|
||||
int ret = vgic_lazy_init(kvm);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (vgic_is_v5(kvm)) {
|
||||
/* Build a GICv5-style IntID here */
|
||||
@@ -2426,6 +2441,8 @@ static int __init init_subsystems(void)
|
||||
switch (err) {
|
||||
case 0:
|
||||
vgic_present = true;
|
||||
if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
|
||||
kvm_nvhe_sym(hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr;
|
||||
break;
|
||||
case -ENODEV:
|
||||
case -ENXIO:
|
||||
|
||||
+114
-32
@@ -136,14 +136,106 @@ static void compute_s1poe(struct kvm_vcpu *vcpu, struct s1_walk_info *wi)
|
||||
wi->e0poe = (wi->regime != TR_EL2) && (val & TCR2_EL1_E0POE);
|
||||
}
|
||||
|
||||
#define _has_tgran(__r, __sz) \
|
||||
({ \
|
||||
u64 _s1, _mmfr0 = __r; \
|
||||
\
|
||||
_s1 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, \
|
||||
TGRAN##__sz, _mmfr0); \
|
||||
\
|
||||
_s1 != ID_AA64MMFR0_EL1_TGRAN##__sz##_NI; \
|
||||
})
|
||||
|
||||
static bool has_tgran(u64 mmfr0, unsigned int shift)
|
||||
{
|
||||
switch (shift) {
|
||||
case 12:
|
||||
return _has_tgran(mmfr0, 4);
|
||||
case 14:
|
||||
return _has_tgran(mmfr0, 16);
|
||||
case 16:
|
||||
return _has_tgran(mmfr0, 64);
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int tcr_to_tg0_pgshift(u64 tcr)
|
||||
{
|
||||
u64 tg0 = tcr & TCR_TG0_MASK;
|
||||
|
||||
switch (tg0) {
|
||||
case TCR_TG0_4K:
|
||||
return 12;
|
||||
case TCR_TG0_16K:
|
||||
return 14;
|
||||
case TCR_TG0_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int tcr_to_tg1_pgshift(u64 tcr)
|
||||
{
|
||||
u64 tg1 = tcr & TCR_TG1_MASK;
|
||||
|
||||
switch (tg1) {
|
||||
case TCR_TG1_4K:
|
||||
return 12;
|
||||
case TCR_TG1_16K:
|
||||
return 14;
|
||||
case TCR_TG1_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int fallback_tgran_shift(u64 mmfr0)
|
||||
{
|
||||
if (has_tgran(mmfr0, PAGE_SHIFT))
|
||||
return PAGE_SHIFT;
|
||||
else if (has_tgran(mmfr0, 12))
|
||||
return 12;
|
||||
else if (has_tgran(mmfr0, 14))
|
||||
return 14;
|
||||
else if (has_tgran(mmfr0, 16))
|
||||
return 16;
|
||||
else /* Should be unreacheable */
|
||||
return PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static unsigned int tcr_tg_pgshift(struct kvm *kvm, u64 tcr, bool upper_range)
|
||||
{
|
||||
u64 mmfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64MMFR0_EL1);
|
||||
unsigned int shift;
|
||||
|
||||
/* Someone was silly enough to encode TG0/TG1 differently */
|
||||
if (upper_range)
|
||||
shift = tcr_to_tg1_pgshift(tcr);
|
||||
else
|
||||
shift = tcr_to_tg0_pgshift(tcr);
|
||||
|
||||
/*
|
||||
* If TGx is programmed to an unimplemented value (not advertised in
|
||||
* ID_AA64MMFR0_EL1), we should treat it as if an implemented value is
|
||||
* written, as per the architecture. Choose an available one while
|
||||
* prioritizing PAGE_SIZE.
|
||||
*/
|
||||
if (!has_tgran(mmfr0, shift))
|
||||
return fallback_tgran_shift(mmfr0);
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
||||
struct s1_walk_result *wr, u64 va)
|
||||
{
|
||||
u64 hcr, sctlr, tcr, tg, ps, ia_bits, ttbr;
|
||||
u64 hcr, sctlr, tcr, ps, ia_bits, ttbr;
|
||||
unsigned int stride, x;
|
||||
bool va55, tbi, lva;
|
||||
bool va55, tbi, lva, upper_range;
|
||||
|
||||
va55 = va & BIT(55);
|
||||
upper_range = va55 && wi->regime != TR_EL2;
|
||||
|
||||
if (vcpu_has_nv(vcpu)) {
|
||||
hcr = __vcpu_sys_reg(vcpu, HCR_EL2);
|
||||
@@ -174,35 +266,12 @@ static int setup_s1_walk(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Someone was silly enough to encode TG0/TG1 differently */
|
||||
if (va55 && wi->regime != TR_EL2) {
|
||||
if (upper_range)
|
||||
wi->txsz = FIELD_GET(TCR_T1SZ_MASK, tcr);
|
||||
tg = FIELD_GET(TCR_TG1_MASK, tcr);
|
||||
|
||||
switch (tg << TCR_TG1_SHIFT) {
|
||||
case TCR_TG1_4K:
|
||||
wi->pgshift = 12; break;
|
||||
case TCR_TG1_16K:
|
||||
wi->pgshift = 14; break;
|
||||
case TCR_TG1_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
wi->pgshift = 16; break;
|
||||
}
|
||||
} else {
|
||||
else
|
||||
wi->txsz = FIELD_GET(TCR_T0SZ_MASK, tcr);
|
||||
tg = FIELD_GET(TCR_TG0_MASK, tcr);
|
||||
|
||||
switch (tg << TCR_TG0_SHIFT) {
|
||||
case TCR_TG0_4K:
|
||||
wi->pgshift = 12; break;
|
||||
case TCR_TG0_16K:
|
||||
wi->pgshift = 14; break;
|
||||
case TCR_TG0_64K:
|
||||
default: /* IMPDEF: treat any other value as 64k */
|
||||
wi->pgshift = 16; break;
|
||||
}
|
||||
}
|
||||
|
||||
wi->pgshift = tcr_tg_pgshift(vcpu->kvm, tcr, upper_range);
|
||||
wi->pa52bit = has_52bit_pa(vcpu, wi, tcr);
|
||||
|
||||
ia_bits = get_ia_size(wi);
|
||||
@@ -423,6 +492,9 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
||||
|
||||
if (wi->s2) {
|
||||
ret = kvm_walk_nested_s2(vcpu, ipa, &s2_trans);
|
||||
if (ret == -EAGAIN)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
fail_s1_walk(wr,
|
||||
(s2_trans.esr & ~ESR_ELx_FSC_LEVEL) | level,
|
||||
@@ -492,15 +564,18 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
||||
/* Block mapping, check the validity of the level */
|
||||
if (!(desc & BIT(1))) {
|
||||
bool valid_block = false;
|
||||
bool lpa = kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, PARANGE, 52);
|
||||
|
||||
switch (BIT(wi->pgshift)) {
|
||||
case SZ_4K:
|
||||
valid_block = level == 1 || level == 2 || (wi->pa52bit && level == 0);
|
||||
break;
|
||||
case SZ_16K:
|
||||
case SZ_64K:
|
||||
valid_block = level == 2 || (wi->pa52bit && level == 1);
|
||||
break;
|
||||
case SZ_64K:
|
||||
valid_block = level == 2 || (lpa && level == 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid_block)
|
||||
@@ -521,8 +596,12 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
|
||||
}
|
||||
|
||||
ret = kvm_swap_s1_desc(vcpu, ipa, desc, new_desc, wi);
|
||||
if (ret)
|
||||
if (ret == -EAGAIN)
|
||||
return ret;
|
||||
if (ret) {
|
||||
fail_s1_walk(wr, ESR_ELx_FSC_SEA_TTW(level), false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
desc = new_desc;
|
||||
}
|
||||
@@ -1380,7 +1459,7 @@ static u64 __kvm_at_s1e01_fast(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
|
||||
}
|
||||
}
|
||||
write_sysreg_el1(vcpu_read_sys_reg(vcpu, SCTLR_EL1), SYS_SCTLR);
|
||||
__load_stage2(mmu, mmu->arch);
|
||||
__load_stage2(mmu);
|
||||
|
||||
skip_mmu_switch:
|
||||
/* Temporarily switch back to guest context */
|
||||
@@ -1553,7 +1632,10 @@ int __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
__kvm_at_s1e01(vcpu, op, vaddr);
|
||||
ret = __kvm_at_s1e01(vcpu, op, vaddr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
par = vcpu_read_sys_reg(vcpu, PAR_EL1);
|
||||
if (par & SYS_PAR_EL1_F)
|
||||
return 0;
|
||||
|
||||
@@ -2631,6 +2631,14 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index)
|
||||
fgtreg = HFGITR2_EL2;
|
||||
break;
|
||||
|
||||
case ICH_HFGRTR_GROUP:
|
||||
fgtreg = is_read ? ICH_HFGRTR_EL2 : ICH_HFGWTR_EL2;
|
||||
break;
|
||||
|
||||
case ICH_HFGITR_GROUP:
|
||||
fgtreg = ICH_HFGITR_EL2;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Something is really wrong, bail out */
|
||||
WARN_ONCE(1, "Bad FGT group (encoding %08x, config %016llx)\n",
|
||||
@@ -2862,6 +2870,8 @@ static int kvm_inject_nested(struct kvm_vcpu *vcpu, u64 esr_el2,
|
||||
|
||||
preempt_disable();
|
||||
|
||||
vcpu_set_flag(vcpu, IN_NESTED_EXCEPTION);
|
||||
|
||||
/*
|
||||
* We may have an exception or PC update in the EL0/EL1 context.
|
||||
* Commit it before entering EL2.
|
||||
@@ -2884,6 +2894,8 @@ static int kvm_inject_nested(struct kvm_vcpu *vcpu, u64 esr_el2,
|
||||
__kvm_adjust_pc(vcpu);
|
||||
|
||||
kvm_arch_vcpu_load(vcpu, smp_processor_id());
|
||||
vcpu_clear_flag(vcpu, IN_NESTED_EXCEPTION);
|
||||
|
||||
preempt_enable();
|
||||
|
||||
if (kvm_vcpu_has_pmu(vcpu))
|
||||
|
||||
@@ -28,6 +28,20 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu)
|
||||
if (!system_supports_fpsimd())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Avoid needless save/restore of the guest's common
|
||||
* FPSIMD/SVE/SME regs during transitions between L1/L2.
|
||||
*
|
||||
* These transitions only happens in a non-preemptible context
|
||||
* where the host regs have already been saved and unbound. The
|
||||
* live registers are either free or owned by the guest.
|
||||
*/
|
||||
if (vcpu_get_flag(vcpu, IN_NESTED_ERET) ||
|
||||
vcpu_get_flag(vcpu, IN_NESTED_EXCEPTION)) {
|
||||
WARN_ON_ONCE(host_owns_fp_regs());
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that any host FPSIMD/SVE/SME state is saved and unbound such
|
||||
* that the host kernel is responsible for restoring this state upon
|
||||
@@ -102,6 +116,18 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* See comment in kvm_arch_vcpu_load_fp(). Note that we also rely on
|
||||
* the guest's max VL to have been set by fpsimd_lazy_switch_to_host()
|
||||
* so that any intervening kernel-mode SIMD (NEON or otherwise)
|
||||
* operation sees the full guest state that needs saving.
|
||||
*/
|
||||
if (vcpu_get_flag(vcpu, IN_NESTED_ERET) ||
|
||||
vcpu_get_flag(vcpu, IN_NESTED_EXCEPTION)) {
|
||||
WARN_ON_ONCE(host_owns_fp_regs());
|
||||
return;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (guest_owns_fp_regs()) {
|
||||
|
||||
@@ -141,7 +141,7 @@ static inline void __activate_cptr_traps_vhe(struct kvm_vcpu *vcpu)
|
||||
if (!(SYS_FIELD_GET(CPACR_EL1, ZEN, cptr) & BIT(0)))
|
||||
val &= ~CPACR_EL1_ZEN;
|
||||
|
||||
if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S2POE, IMP))
|
||||
if (kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S1POE, IMP))
|
||||
val |= cptr & CPACR_EL1_E0POE;
|
||||
|
||||
val |= cptr & CPTR_EL2_TCPAC;
|
||||
|
||||
@@ -56,6 +56,7 @@ int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot p
|
||||
int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id);
|
||||
int kvm_host_prepare_stage2(void *pgt_pool_base);
|
||||
int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd);
|
||||
void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm);
|
||||
void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt);
|
||||
|
||||
int hyp_pin_shared_mem(void *from, void *to);
|
||||
@@ -67,7 +68,7 @@ int refill_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages,
|
||||
static __always_inline void __load_host_stage2(void)
|
||||
{
|
||||
if (static_branch_likely(&kvm_protected_mode_initialized))
|
||||
__load_stage2(&host_mmu.arch.mmu, &host_mmu.arch);
|
||||
__load_stage2(&host_mmu.arch.mmu);
|
||||
else
|
||||
write_sysreg(0, vttbr_el2);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
|
||||
DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
|
||||
|
||||
/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */
|
||||
unsigned int hyp_gicv3_nr_lr;
|
||||
|
||||
void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);
|
||||
|
||||
static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu)
|
||||
@@ -128,10 +131,18 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
|
||||
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
|
||||
|
||||
/* __hyp_running_vcpu must be NULL in a guest context. */
|
||||
hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL;
|
||||
|
||||
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
|
||||
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
|
||||
/*
|
||||
* HCR_EL2.VSE is host-owned (a pending virtual SError to inject), not a
|
||||
* trap-control bit, so it must flow to the hyp vCPU alongside TWI/TWE
|
||||
* for the vSError to be delivered. sync_hyp_vcpu() reflects it back.
|
||||
*/
|
||||
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE | HCR_VSE);
|
||||
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
|
||||
(HCR_TWI | HCR_TWE);
|
||||
(HCR_TWI | HCR_TWE | HCR_VSE);
|
||||
|
||||
hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags;
|
||||
|
||||
@@ -139,6 +150,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
|
||||
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
/* Bound used_lrs by the number of implemented list registers. */
|
||||
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
|
||||
min_t(unsigned int,
|
||||
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
|
||||
hyp_gicv3_nr_lr);
|
||||
|
||||
hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,6 @@ static void *guest_s2_zalloc_page(void *mc)
|
||||
memset(addr, 0, PAGE_SIZE);
|
||||
p = hyp_virt_to_page(addr);
|
||||
p->refcount = 1;
|
||||
p->order = 0;
|
||||
|
||||
return addr;
|
||||
}
|
||||
@@ -306,23 +305,27 @@ int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_guest_destroy_stage2(struct pkvm_hyp_vm *vm)
|
||||
{
|
||||
guest_lock_component(vm);
|
||||
kvm_pgtable_stage2_destroy(&vm->pgt);
|
||||
vm->kvm.arch.mmu.pgd_phys = 0ULL;
|
||||
guest_unlock_component(vm);
|
||||
}
|
||||
|
||||
void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
|
||||
{
|
||||
struct hyp_page *page;
|
||||
void *addr;
|
||||
|
||||
/* Dump all pgtable pages in the hyp_pool */
|
||||
guest_lock_component(vm);
|
||||
kvm_pgtable_stage2_destroy(&vm->pgt);
|
||||
vm->kvm.arch.mmu.pgd_phys = 0ULL;
|
||||
guest_unlock_component(vm);
|
||||
kvm_guest_destroy_stage2(vm);
|
||||
|
||||
/* Drain the hyp_pool into the memcache */
|
||||
addr = hyp_alloc_pages(&vm->pool, 0);
|
||||
while (addr) {
|
||||
page = hyp_virt_to_page(addr);
|
||||
page->refcount = 0;
|
||||
page->order = 0;
|
||||
push_hyp_memcache(mc, addr, hyp_virt_to_phys);
|
||||
WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(addr), 1));
|
||||
addr = hyp_alloc_pages(&vm->pool, 0);
|
||||
@@ -352,7 +355,7 @@ int __pkvm_prot_finalize(void)
|
||||
kvm_flush_dcache_to_poc(params, sizeof(*params));
|
||||
|
||||
write_sysreg_hcr(params->hcr_el2);
|
||||
__load_stage2(&host_mmu.arch.mmu, &host_mmu.arch);
|
||||
__load_stage2(&host_mmu.arch.mmu);
|
||||
|
||||
/*
|
||||
* Make sure to have an ISB before the TLB maintenance below but only
|
||||
@@ -851,6 +854,16 @@ static int __hyp_check_page_state_range(phys_addr_t phys, u64 size, enum pkvm_pa
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hyp_check_page_count_range(phys_addr_t phys, u64 size)
|
||||
{
|
||||
for_each_hyp_page(page, phys, size) {
|
||||
if (page->refcount)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool guest_pte_is_poisoned(kvm_pte_t pte)
|
||||
{
|
||||
if (kvm_pte_valid(pte))
|
||||
@@ -1049,7 +1062,6 @@ unlock:
|
||||
int __pkvm_host_unshare_hyp(u64 pfn)
|
||||
{
|
||||
u64 phys = hyp_pfn_to_phys(pfn);
|
||||
u64 virt = (u64)__hyp_va(phys);
|
||||
u64 size = PAGE_SIZE;
|
||||
int ret;
|
||||
|
||||
@@ -1062,10 +1074,9 @@ int __pkvm_host_unshare_hyp(u64 pfn)
|
||||
ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
if (hyp_page_count((void *)virt)) {
|
||||
ret = -EBUSY;
|
||||
ret = __hyp_check_page_count_range(phys, size);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
__hyp_set_page_state_range(phys, size, PKVM_NOPAGE);
|
||||
WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_OWNED));
|
||||
@@ -1128,6 +1139,10 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages)
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = __hyp_check_page_count_range(phys, size);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
__hyp_set_page_state_range(phys, size, PKVM_NOPAGE);
|
||||
WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, size) != size);
|
||||
WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_HOST));
|
||||
|
||||
@@ -94,13 +94,22 @@ static void __hyp_attach_page(struct hyp_pool *pool,
|
||||
struct hyp_page *p)
|
||||
{
|
||||
phys_addr_t phys = hyp_page_to_phys(p);
|
||||
u8 order = p->order;
|
||||
struct hyp_page *buddy;
|
||||
bool coalesce = true;
|
||||
u8 order = p->order;
|
||||
|
||||
memset(hyp_page_to_virt(p), 0, PAGE_SIZE << p->order);
|
||||
/*
|
||||
* 'external' pages are never coalesced and their ->order field
|
||||
* untrusted as they bypass hyp_pool_init(). Enforce order-0.
|
||||
*/
|
||||
if (phys < pool->range_start || phys >= pool->range_end) {
|
||||
order = 0;
|
||||
coalesce = false;
|
||||
}
|
||||
|
||||
/* Skip coalescing for 'external' pages being freed into the pool. */
|
||||
if (phys < pool->range_start || phys >= pool->range_end)
|
||||
memset(hyp_page_to_virt(p), 0, PAGE_SIZE << order);
|
||||
|
||||
if (!coalesce)
|
||||
goto insert;
|
||||
|
||||
/*
|
||||
@@ -237,8 +246,10 @@ int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages,
|
||||
|
||||
/* Init the vmemmap portion */
|
||||
p = hyp_phys_to_page(phys);
|
||||
for (i = 0; i < nr_pages; i++)
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
hyp_set_page_refcounted(&p[i]);
|
||||
p[i].order = 0;
|
||||
}
|
||||
|
||||
/* Attach the unused pages to the buddy tree */
|
||||
for (i = reserved_pages; i < nr_pages; i++)
|
||||
|
||||
@@ -853,10 +853,12 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
|
||||
/* Must be called last since this publishes the VM. */
|
||||
ret = insert_vm_table_entry(handle, hyp_vm);
|
||||
if (ret)
|
||||
goto err_remove_mappings;
|
||||
goto err_destroy_stage2;
|
||||
|
||||
return 0;
|
||||
|
||||
err_destroy_stage2:
|
||||
kvm_guest_destroy_stage2(hyp_vm);
|
||||
err_remove_mappings:
|
||||
unmap_donated_memory(hyp_vm, vm_size);
|
||||
unmap_donated_memory(pgd, pgd_size);
|
||||
|
||||
@@ -315,7 +315,7 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
__sysreg_restore_state_nvhe(guest_ctxt);
|
||||
|
||||
mmu = kern_hyp_va(vcpu->arch.hw_mmu);
|
||||
__load_stage2(mmu, kern_hyp_va(mmu->arch));
|
||||
__load_stage2(mmu);
|
||||
__activate_traps(vcpu);
|
||||
|
||||
__hyp_vgic_restore_state(vcpu);
|
||||
|
||||
@@ -110,7 +110,7 @@ static void enter_vmid_context(struct kvm_s2_mmu *mmu,
|
||||
if (vcpu)
|
||||
__load_host_stage2();
|
||||
else
|
||||
__load_stage2(mmu, kern_hyp_va(mmu->arch));
|
||||
__load_stage2(mmu);
|
||||
|
||||
asm(ALTERNATIVE("isb", "nop", ARM64_WORKAROUND_SPECULATIVE_AT));
|
||||
}
|
||||
@@ -128,7 +128,7 @@ static void exit_vmid_context(struct tlb_inv_context *cxt)
|
||||
return;
|
||||
|
||||
if (vcpu)
|
||||
__load_stage2(mmu, kern_hyp_va(mmu->arch));
|
||||
__load_stage2(mmu);
|
||||
else
|
||||
__load_host_stage2();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user