Merge branch kvm-arm64/pkvm-state-7.4 into kvmarm-master/next

* kvm-arm64/pkvm-state-7.4:
  : \
  : State protection for protected pKVM guests, finally starting to deliver
  : on its promises... Patches courtesy of Fuad Tabba.
  :
  : "Following the vCPU state-sync series, this series completes the
  : job for protected VMs: a protected guest's register state stays at
  : EL2, and the host sees only what handling each exit requires."
  : /
  KVM: arm64: Only emulate an SError's entry for vCPUs with NV
  KVM: arm64: Document the protected VM userspace API
  KVM: arm64: Advertise the capabilities that protected VMs support
  KVM: arm64: Reject host power-on of a vCPU that EL2 holds powered off
  KVM: arm64: Reject host access to protected VM private state
  KVM: arm64: Add per-EC entry/exit state marshalling for protected guests
  KVM: arm64: Inject an UNDEF at EL2 for unhandled protected guest exits
  KVM: arm64: Prevent host PC adjustments for protected vCPUs
  KVM: arm64: Restrict KVM_ARM_VCPU_INIT and PSCI version for protected VMs
  KVM: arm64: Handle PSCI calls for protected VMs at EL2
  KVM: arm64: Implement HVC handling for protected guests at EL2
  KVM: arm64: Add system register reset framework for protected VMs
  KVM: arm64: Add {flush,sync}_hyp_timer_state() primitives
  KVM: arm64: Skip fixed-feature state flush for protected vCPUs
  KVM: arm64: Introduce per-EC entry handlers for pKVM
  KVM: arm64: Disable steal time for protected VMs
  KVM: arm64: Pin the host vCPU before adjusting its PC under pKVM
  KVM: arm64: Validate the host vCPU's VM before reading it under pKVM
  KVM: arm64: Sync HCR_EL2.VSE back to the host vCPU under pKVM

Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
Marc Zyngier 2026-10-11 10:54:06 +01:00
commit 0ff05136bb
21 changed files with 1506 additions and 91 deletions

View File

@ -1323,6 +1323,9 @@ guests, across different userspace implementations. Nevertheless, userspace
can still emulate all Arm exceptions by manipulating individual registers
using the KVM_SET_ONE_REG API.
For a protected VM, setting ext_dabt_pending returns -EPERM; see
Documentation/virt/kvm/arm/pkvm.rst. Injecting an SError is unaffected.
See KVM_GET_VCPU_EVENTS for the data structure.
Calling this ioctl on a vCPU that hasn't been initialized will return
@ -1546,7 +1549,9 @@ Possible values are:
KVM_MP_STATE_RUNNABLE the vcpu is currently running
[x86,arm64,riscv,loongarch]
KVM_MP_STATE_UNINITIALIZED the vcpu is an application processor (AP)
which has not yet received an INIT signal [x86]
which has not yet received an INIT signal
[x86], or a protected VM's vcpu that the
guest holds powered off [arm64]
KVM_MP_STATE_INIT_RECEIVED the vcpu has received an INIT signal, and is
now ready for a SIPI [x86]
KVM_MP_STATE_HALTED the vcpu has executed a HLT instruction and
@ -1625,6 +1630,12 @@ For arm64/riscv:
The only states that are valid are KVM_MP_STATE_STOPPED and
KVM_MP_STATE_RUNNABLE which reflect if the vcpu should be paused or not.
On arm64, once a protected VM's vcpu has run, it reads as
KVM_MP_STATE_UNINITIALIZED while the guest holds it powered off, with
CPU_OFF or by not yet bringing it online with CPU_ON, and
KVM_MP_STATE_RUNNABLE and KVM_MP_STATE_SUSPENDED return -EPERM on it: only
the guest can power it on. See Documentation/virt/kvm/arm/pkvm.rst.
On LoongArch, only the KVM_MP_STATE_RUNNABLE state is used to reflect
whether the vcpu is runnable.
@ -2325,7 +2336,8 @@ Errors:
ENOENT no such register
EINVAL invalid register ID, or no such register or used with VMs in
protected virtualization mode on s390
EPERM (arm64) register access not allowed before vcpu finalization
EPERM (arm64) register access not allowed before vcpu
finalization, or after a protected VM's vcpu has run
EBUSY (riscv) changing register value not allowed after the vcpu
has run at least once
====== ============================================================
@ -2950,7 +2962,8 @@ Errors include:
ENOENT no such register
EINVAL invalid register ID, or no such register or used with VMs in
protected virtualization mode on s390
EPERM (arm64) register access not allowed before vcpu finalization
EPERM (arm64) register access not allowed before vcpu
finalization, or after a protected VM's vcpu has run
======== ============================================================
(These error codes are indicative only: do not rely on a specific error
@ -3485,6 +3498,7 @@ Errors:
====== =================================================================
EINVAL the target is unknown, or the combination of features is invalid.
ENOENT a features bit specified is unknown.
EPERM the vcpu belongs to a protected VM and has already run.
====== =================================================================
This tells KVM what type of CPU to present to the guest, and what
@ -3513,6 +3527,10 @@ after the vcpu has been run. This will reset the vcpu to its initial
state. All calls to this function after the initial call must use the same
target and same set of feature flags, otherwise EINVAL will be returned.
For a protected VM this ioctl returns EPERM once the vcpu has run, and the
features it accepts are restricted: KVM_ARM_VCPU_PSCI_0_2 is required. See
Documentation/virt/kvm/arm/pkvm.rst.
Possible features:
- KVM_ARM_VCPU_POWER_OFF: Starts the CPU in a power-off state.
@ -3785,6 +3803,9 @@ can be determined by querying the KVM_CAP_GUEST_DEBUG_HW_BPS and
KVM_CAP_GUEST_DEBUG_HW_WPS capabilities which return a positive number
indicating the number of supported registers.
On arm64, this ioctl returns -EPERM for a protected VM: debugging a
protected guest is not supported. See Documentation/virt/kvm/arm/pkvm.rst.
For ppc, the KVM_CAP_PPC_GUEST_DEBUG_SSTEP capability indicates whether
the single-step debug event (KVM_GUESTDBG_SINGLESTEP) is supported.

View File

@ -33,6 +33,8 @@ The following registers are defined:
highest PSCI version implemented by KVM and compatible with v0.2)
- Allows any PSCI version implemented by KVM and compatible with
v0.2 to be set with SET_ONE_REG
- Returns -EINVAL for a version below v1.1 on a protected VM (see
Documentation/virt/kvm/arm/pkvm.rst)
- Affects the whole VM (even if the register view is per-vcpu)
* KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:

View File

@ -15,7 +15,9 @@ Overview
Booting a host kernel with '``kvm-arm.mode=protected``' enables
"Protected KVM" (pKVM). During boot, pKVM installs a stage-2 identity
map page-table for the host and uses it to isolate the hypervisor
running at EL2 from the rest of the host running at EL1/0.
running at EL2 from the rest of the host running at EL1/0. pKVM supports
GICv3 only: GICv2 is not supported in protected mode, and neither are
GICv5-based guests.
pKVM permits creation of protected virtual machines (pVMs) by passing
the ``KVM_VM_TYPE_ARM_PROTECTED`` machine type identifier to the
@ -31,7 +33,7 @@ See hypercalls.rst for more details.
Isolation mechanisms
====================
pKVM relies on a number of mechanisms to isolate PVMs from the host:
pKVM relies on a number of mechanisms to isolate pVMs from the host:
CPU memory isolation
--------------------
@ -50,8 +52,9 @@ stage-2 identity map as part of the donation procedure. This gives rise
to some user-visible differences when compared to non-protected VMs,
largely due to the lack of MMU notifiers:
* Memslots cannot be moved or deleted once the pVM has started running.
* Read-only memslots and dirty logging are not supported.
* Memslots cannot be moved or deleted once the pVM has started running
(``-EPERM``).
* Read-only memslots and dirty logging are not supported (``-EPERM``).
* With the exception of swap, file-backed pages cannot be mapped into a
pVM.
* Donated pages are accounted against ``RLIMIT_MLOCK`` and so the VMM
@ -72,7 +75,14 @@ largely due to the lack of MMU notifiers:
CPU state isolation
-------------------
Status: **Unimplemented.**
Status: CPU register state of protected vCPUs is managed entirely at EL2.
pKVM performs the complete context switch for protected vCPUs within the
hypervisor. The hypervisor sets a protected vCPU's initial state, not the
host, and only what each exit needs is synchronised back.
The user-visible consequences are described under `API behaviour for
protected VMs`_.
DMA isolation using an IOMMU
----------------------------
@ -89,13 +99,153 @@ The FF-A proxy ensures that the host cannot share pVM or hypervisor
memory with Trustzone as part of a "confused deputy" attack.
The PSCI proxy ensures that CPUs always have the stage-2 identity map
installed when they are executing in the host.
installed when they are executing in the host. This proxy is distinct
from the PSCI handling provided to protected guests, which is described
under `API behaviour for protected VMs`_.
Protected VM firmware (pvmfw)
-----------------------------
Status: **Unimplemented.**
API behaviour for protected VMs
===============================
Protected vCPU state is owned by EL2 (see `CPU state isolation`_). The VMM
configures a vCPU before its first ``KVM_RUN``; afterwards the state is
private to the guest and the ioctls that access it return ``-EPERM``. The
vCPU ioctls' errors follow one rule: ``-EPERM`` means the host asked for
state that the guest owns, ``-EINVAL`` means the request is not valid for a
protected VM, and a feature pKVM doesn't support for protected VMs is
rejected the way KVM rejects one that isn't implemented (``-ENXIO`` from
``KVM_ARM_VCPU_PVTIME_CTRL``, for example). The ioctls themselves are
described in Documentation/virt/kvm/api.rst.
Boot
----
A protected VM boots from a single primary vCPU. Before the first
``KVM_RUN``, the VMM prepares the boot state:
* Set ``KVM_MP_STATE_RUNNABLE`` on the primary vCPU and
``KVM_MP_STATE_STOPPED`` on every other vCPU. EL2 allows only one
RUNNABLE primary per protected VM. A second RUNNABLE vCPU fails at its
first ``KVM_RUN``.
* Set the primary vCPU's boot state with ``KVM_SET_ONE_REG``: the kernel
entry address in ``PC`` and the DTB pointer in ``x0``.
``PC`` and ``x0`` are the only registers EL2 takes from the host. Other
pre-run writes are accepted, but the guest starts from the state the
hypervisor resets it to.
Secondary vCPUs are started by the guest itself through PSCI ``CPU_ON``
(see `Power state`_), which supplies their entry point and context ID.
The VMM cannot choose where they boot.
vCPU state
----------
* ``KVM_GET_ONE_REG`` and ``KVM_SET_ONE_REG`` return ``-EPERM`` once the
vCPU has run. Before that, they access the host-side copy from which
EL2 builds the guest's boot state (see `Boot`_).
* ``KVM_ARM_VCPU_INIT`` accepts only the vCPU features that a protected
guest supports and returns ``-EINVAL`` otherwise.
``KVM_ARM_VCPU_PSCI_0_2`` is required, as EL2 implements PSCI 1.1 for
the guest (see `Power state`_). ``KVM_ARM_VCPU_EL1_32BIT`` is not
supported: protected guests run in AArch64 only and see no AArch32
support in ``ID_AA64PFR0_EL1``. Once the vCPU has run,
``KVM_ARM_VCPU_INIT`` returns ``-EPERM``, as re-initialising it would
reset the host-side copy alone.
* ``KVM_SET_VCPU_EVENTS`` returns ``-EPERM`` for external-abort injection
(``ext_dabt_pending``), and ``KVM_CAP_ARM_INJECT_EXT_DABT`` is not
advertised to a protected VM. SError injection is unaffected.
* ``KVM_SET_GUEST_DEBUG`` returns ``-EPERM``, and ``KVM_CAP_SET_GUEST_DEBUG``
is not advertised to a protected VM (see `Debug`_).
Power state
-----------
EL2 implements PSCI 1.1 for a protected guest. The calls that move a
vCPU's power state, ``CPU_ON`` and ``CPU_OFF``, are handled at EL2, and
the host cannot change the outcome: for ``CPU_ON`` the host only
schedules the target, which EL2 has already reset to the entry point
the guest chose, and for ``CPU_OFF`` it only stops scheduling it.
``AFFINITY_INFO`` is handled by the host, which returns ``OFF`` once
it has stopped scheduling every vCPU the query covers, as for a
non-protected VM. A vCPU becomes a valid ``CPU_ON`` target at its first
``KVM_RUN``, and before that EL2 returns ``INVALID_PARAMETERS``. The
platform calls, ``CPU_SUSPEND``, ``SYSTEM_OFF``, ``SYSTEM_RESET`` and
``SYSTEM_RESET2``, are forwarded to the host and behave as for a
non-protected VM, with the ``SYSTEM_*`` calls exiting to the VMM as
``KVM_EXIT_SYSTEM_EVENT``; the ``SYSTEM_RESET2`` reset type and cookie
are in the guest's registers, which ``KVM_GET_ONE_REG`` rejects once
the vCPU has run. Any other function returns ``NOT_SUPPORTED``, and
``PSCI_FEATURES`` reports the same set. Because the host handles those
forwarded calls, ``KVM_SET_ONE_REG`` on ``KVM_REG_ARM_PSCI_VERSION``
returns ``-EINVAL`` for a version below 1.1.
Once a vCPU has run, its power state follows the guest's PSCI calls, not
the VMM's. A vCPU that the guest has powered off with ``CPU_OFF``, or has
not yet brought online with ``CPU_ON``, reads as
``KVM_MP_STATE_UNINITIALIZED``, and only an in-guest ``CPU_ON`` can power
it on: ``KVM_SET_MP_STATE`` returns ``-EPERM`` for ``KVM_MP_STATE_RUNNABLE``
and ``KVM_MP_STATE_SUSPENDED`` on it, accepts ``KVM_MP_STATE_STOPPED`` and
leaves it as it is, and never takes ``KVM_MP_STATE_UNINITIALIZED`` from
the VMM. ``KVM_MP_STATE_STOPPED`` still stops a vCPU EL2 has powered on,
and ``KVM_MP_STATE_RUNNABLE`` resumes it, so the VMM can pause it.
Other interface differences
---------------------------
* ``KVM_CHECK_EXTENSION`` reports only the arm64 capabilities that pKVM
supports for protected guests, and ``KVM_ENABLE_CAP`` accepts only
those. Query them on the VM file descriptor: the system file descriptor
has no VM to filter against. The capabilities the generic KVM code
answers itself (``KVM_CAP_SYNC_MMU`` or ``KVM_CAP_DEVICE_CTRL``, for
example) are reported as for any VM, and don't mean the feature works
for a protected one. The filter does not cover every interface either:
device-fd configuration (for example the VGIC after
``KVM_CREATE_DEVICE``) and vCPU attributes are unfiltered, so a feature
the filter excludes is rejected by the attribute itself.
``KVM_ARM_VCPU_PVTIME_CTRL`` returns ``-ENXIO``, for example, as it does
where steal time isn't implemented.
* The vGIC of a protected VM remains host-managed: device creation,
configuration and interrupt injection all work as they do for a
non-protected VM.
* ``KVM_ARM_SET_COUNTER_OFFSET`` and ``KVM_ARM_GET_REG_WRITABLE_MASKS``
return ``-EINVAL``: their capabilities are not advertised to a protected
VM, whose counter offset and ID registers are set by EL2. A protected
guest sees the physical timebase.
* A protected guest's first access to each page of memory exits to the
host, since the hypervisor cannot tell memory from a device before the
page is mapped. For a store, the host sees the value of the register
the syndrome names, clamped to the access width, and nothing else from
the register file. For a load, the register the syndrome names takes
the value the host returns, as for any emulated access.
* A protected guest that uses a feature it was not given, or executes an
``SMC``, takes an undefined instruction exception from the hypervisor;
the host is not involved.
* Apart from PSCI, the hypervisor handles a protected guest's SMCCC
calls itself and does not involve the host. A function it does not
implement returns ``NOT_SUPPORTED``, and the host's SMCCC filter does
not apply: the VM device attribute ioctls (``KVM_ARM_VM_SMCCC_CTRL``)
return ``-EINVAL`` for a protected VM.
* The hypervisor can decode a trapped guest access only from the CPU's
instruction syndrome, which is provided only for a load or store of a
single general-purpose register. An access without one (for example a
load/store pair or a SIMD/FP access) cannot be decoded. For a
non-protected VM it can exit to the VMM as ``KVM_EXIT_ARM_NISV``. For a
protected VM it cannot be emulated by the VMM, so the guest takes a
synchronous external abort instead.
Debug
-----
Hardware-assisted debugging is not available to protected guests.
``MDSCR_EL1``, ``OSLAR_EL1``, ``OSLSR_EL1``, ``OSDLR_EL1``, and breakpoint
and watchpoint 0 are RAZ/WI; any other debug register access takes an
undefined instruction exception.
Resources
=========

View File

@ -231,7 +231,8 @@ Returns:
Specifies the base address of the stolen time structure for this VCPU. The
base address must be 64 byte aligned and exist within a valid guest memory
region. See Documentation/virt/kvm/arm/pvtime.rst for more information
including the layout of the stolen time structure.
including the layout of the stolen time structure. Stolen time is not
supported for protected VMs (see Documentation/virt/kvm/arm/pkvm.rst).
4. GROUP: KVM_VCPU_TSC_CTRL
===========================

View File

@ -1062,11 +1062,28 @@ struct kvm_vcpu_arch {
set; \
})
#define __vcpu_copy_flag(vt, vs, flagset, f, m) \
do { \
typeof(vs->arch.flagset) tmp, val; \
\
__build_check_flag(vs, flagset, f, m); \
\
val = READ_ONCE(vs->arch.flagset); \
val &= (m); \
__vcpu_flags_preempt_disable(); \
tmp = READ_ONCE(vt->arch.flagset); \
tmp &= ~(m); \
tmp |= val; \
WRITE_ONCE(vt->arch.flagset, tmp); \
__vcpu_flags_preempt_enable(); \
} while (0)
#define vcpu_get_flag(v, ...) __vcpu_get_flag((v), __VA_ARGS__)
#define vcpu_set_flag(v, ...) __vcpu_set_flag((v), __VA_ARGS__)
#define vcpu_clear_flag(v, ...) __vcpu_clear_flag((v), __VA_ARGS__)
#define vcpu_test_and_clear_flag(v, ...) \
__vcpu_test_and_clear_flag((v), __VA_ARGS__)
#define vcpu_copy_flag(vt, vs, ...) __vcpu_copy_flag((vt), (vs), __VA_ARGS__)
/* KVM_ARM_VCPU_INIT completed */
#define VCPU_INITIALIZED __vcpu_single_flag(cflags, BIT(0))
@ -1089,6 +1106,8 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
#define INCREMENT_PC __vcpu_single_flag(iflags, BIT(1))
/* Target EL/MODE (not a single flag, but let's abuse the macro) */
#define EXCEPT_MASK __vcpu_single_flag(iflags, GENMASK(3, 1))
/* Cover both PENDING_EXCEPTION and EXCEPT_MASK for global operations */
#define PC_UPDATE_REQ __vcpu_single_flag(iflags, GENMASK(3, 0))
/* Host-set: the hyp flushes the non-protected vCPU state in on entry */
#define PKVM_HOST_STATE_DIRTY __vcpu_single_flag(iflags, BIT(4))
@ -1343,7 +1362,7 @@ long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu);
void kvm_update_stolen_time(struct kvm_vcpu *vcpu);
bool kvm_arm_pvtime_supported(void);
bool kvm_arm_pvtime_supported(struct kvm *kvm);
int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
struct kvm_device_attr *attr);
int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
@ -1541,6 +1560,7 @@ static inline void kvm_hyp_reserve(void) { }
void kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu);
bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu);
bool kvm_pkvm_vcpu_is_powered_off(struct kvm_vcpu *vcpu);
static inline u64 *__vm_id_reg(struct kvm_arch *ka, u32 reg)
{

View File

@ -10,6 +10,7 @@
#include <linux/compiler.h>
#include <linux/kvm_host.h>
#include <asm/alternative.h>
#include <asm/spectre.h>
#include <asm/sysreg.h>
DECLARE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt);
@ -153,6 +154,9 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val);
extern unsigned long kvm_nvhe_sym(__icache_flags);
extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
extern enum mitigation_state kvm_nvhe_sym(spectre_v2_state);
extern enum mitigation_state kvm_nvhe_sym(spectre_v4_state);
extern enum mitigation_state kvm_nvhe_sym(spectre_bhb_state);
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);

View File

@ -42,6 +42,15 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
case KVM_CAP_ARM_VM_IPA_SIZE:
case KVM_CAP_ARM_PTRAUTH_ADDRESS:
case KVM_CAP_ARM_PTRAUTH_GENERIC:
case KVM_CAP_ONE_REG:
case KVM_CAP_MP_STATE:
case KVM_CAP_VCPU_EVENTS:
case KVM_CAP_VCPU_ATTRIBUTES:
case KVM_CAP_IMMEDIATE_EXIT:
case KVM_CAP_IOEVENTFD:
case KVM_CAP_IRQFD_RESAMPLE:
case KVM_CAP_ARM_IRQ_LINE_LAYOUT_2:
case KVM_CAP_ARM_INJECT_SERROR_ESR:
return true;
case KVM_CAP_ARM_MTE:
case KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE:
@ -53,6 +62,30 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext)
}
}
/*
* The vCPU features a protected VM may use: checked by the host at
* KVM_ARM_VCPU_INIT, applied by EL2 when the hyp VM is created.
*/
static inline void kvm_pkvm_vcpu_allowed_features(struct kvm *kvm,
unsigned long *allowed)
{
bitmap_zero(allowed, KVM_VCPU_MAX_FEATURES);
set_bit(KVM_ARM_VCPU_PSCI_0_2, allowed);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PMU_V3))
set_bit(KVM_ARM_VCPU_PMU_V3, allowed);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PTRAUTH_ADDRESS))
set_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, allowed);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PTRAUTH_GENERIC))
set_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, allowed);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_SVE))
set_bit(KVM_ARM_VCPU_SVE, allowed);
}
/*
* Check whether the KVM VM IOCTL is allowed in pKVM.
*

View File

@ -452,7 +452,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
r = system_supports_mte();
break;
case KVM_CAP_STEAL_TIME:
r = kvm_arm_pvtime_supported();
r = kvm_arm_pvtime_supported(kvm);
break;
case KVM_CAP_ARM_EL1_32BIT:
r = cpus_have_final_cap(ARM64_HAS_32BIT_EL1);
@ -775,23 +775,28 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
vcpu->cpu = -1;
}
static void __kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu)
static void __kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu, u32 mp_state)
{
WRITE_ONCE(vcpu->arch.mp_state.mp_state, KVM_MP_STATE_STOPPED);
WRITE_ONCE(vcpu->arch.mp_state.mp_state, mp_state);
kvm_make_request(KVM_REQ_SLEEP, vcpu);
kvm_vcpu_kick(vcpu);
}
/* The guest's own CPU_OFF: EL2 has already powered the vCPU off. */
void kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu)
{
u32 mp_state = vcpu_is_protected(vcpu) ? KVM_MP_STATE_UNINITIALIZED : KVM_MP_STATE_STOPPED;
spin_lock(&vcpu->arch.mp_state_lock);
__kvm_arm_vcpu_power_off(vcpu);
__kvm_arm_vcpu_power_off(vcpu, mp_state);
spin_unlock(&vcpu->arch.mp_state_lock);
}
bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu)
{
return READ_ONCE(vcpu->arch.mp_state.mp_state) == KVM_MP_STATE_STOPPED;
u32 mp_state = READ_ONCE(vcpu->arch.mp_state.mp_state);
return mp_state == KVM_MP_STATE_STOPPED || mp_state == KVM_MP_STATE_UNINITIALIZED;
}
static void kvm_arm_vcpu_suspend(struct kvm_vcpu *vcpu)
@ -806,6 +811,12 @@ static bool kvm_arm_vcpu_suspended(struct kvm_vcpu *vcpu)
return READ_ONCE(vcpu->arch.mp_state.mp_state) == KVM_MP_STATE_SUSPENDED;
}
/* Only the guest's CPU_ON may start a vCPU EL2 holds powered off. */
bool kvm_pkvm_vcpu_is_powered_off(struct kvm_vcpu *vcpu)
{
return READ_ONCE(vcpu->arch.mp_state.mp_state) == KVM_MP_STATE_UNINITIALIZED;
}
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
struct kvm_mp_state *mp_state)
{
@ -823,12 +834,24 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
switch (mp_state->mp_state) {
case KVM_MP_STATE_RUNNABLE:
if (kvm_pkvm_vcpu_is_powered_off(vcpu)) {
ret = -EPERM;
break;
}
WRITE_ONCE(vcpu->arch.mp_state, *mp_state);
break;
case KVM_MP_STATE_STOPPED:
__kvm_arm_vcpu_power_off(vcpu);
/* Keep EL2's off record: STOPPED over it would let RUNNABLE through. */
if (!kvm_pkvm_vcpu_is_powered_off(vcpu))
__kvm_arm_vcpu_power_off(vcpu, KVM_MP_STATE_STOPPED);
break;
case KVM_MP_STATE_SUSPENDED:
if (kvm_pkvm_vcpu_is_powered_off(vcpu)) {
ret = -EPERM;
break;
}
kvm_arm_vcpu_suspend(vcpu);
break;
default:
@ -1634,6 +1657,19 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu,
if (features & ~system_supported_vcpu_features())
return -EINVAL;
/* Reject features EL2 would drop when it creates the hyp VM. */
if (vcpu_is_protected(vcpu)) {
DECLARE_BITMAP(allowed, KVM_VCPU_MAX_FEATURES);
kvm_pkvm_vcpu_allowed_features(vcpu->kvm, allowed);
if (!bitmap_subset(&features, allowed, KVM_VCPU_MAX_FEATURES))
return -EINVAL;
/* EL2 implements PSCI 1.1; the host must not dispatch as 0.1. */
if (!test_bit(KVM_ARM_VCPU_PSCI_0_2, &features))
return -EINVAL;
}
/*
* For now make sure that both address/generic pointer authentication
* features are requested by the userspace together.
@ -1791,7 +1827,7 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
spin_lock(&vcpu->arch.mp_state_lock);
if (power_off)
__kvm_arm_vcpu_power_off(vcpu);
__kvm_arm_vcpu_power_off(vcpu, KVM_MP_STATE_STOPPED);
else
WRITE_ONCE(vcpu->arch.mp_state.mp_state, KVM_MP_STATE_RUNNABLE);
@ -1868,6 +1904,24 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
return __kvm_arm_vcpu_set_events(vcpu, events);
}
/*
* Once a protected vCPU has run, the host copy holds the boot state the VMM
* wrote plus what the exit handlers copy out, and EL2 has read mp_state,
* which it does only at hyp vCPU creation.
*/
static long pkvm_filter_vcpu_ioctl(struct kvm_vcpu *vcpu, unsigned int ioctl)
{
switch (ioctl) {
case KVM_ARM_VCPU_INIT:
case KVM_SET_ONE_REG:
case KVM_GET_ONE_REG:
if (vcpu_is_protected(vcpu) && vcpu_has_run_once(vcpu))
return -EPERM;
}
return 0;
}
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@ -1876,6 +1930,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
struct kvm_device_attr attr;
long r;
r = pkvm_filter_vcpu_ioctl(vcpu, ioctl);
if (r)
return r;
switch (ioctl) {
case KVM_ARM_VCPU_INIT: {
struct kvm_vcpu_init init;
@ -2638,6 +2696,10 @@ static void kvm_hyp_init_symbols(void)
kvm_nvhe_sym(__icache_flags) = __icache_flags;
kvm_nvhe_sym(kvm_arm_vmid_bits) = kvm_arm_vmid_bits;
kvm_nvhe_sym(spectre_v2_state) = arm64_get_spectre_v2_state();
kvm_nvhe_sym(spectre_v4_state) = arm64_get_spectre_v4_state();
kvm_nvhe_sym(spectre_bhb_state) = arm64_get_spectre_bhb_state();
/* Propagate the FGT state to the nVHE side */
kvm_nvhe_sym(hfgrtr_masks) = hfgrtr_masks;
kvm_nvhe_sym(hfgwtr_masks) = hfgwtr_masks;

View File

@ -792,6 +792,13 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
u64 esr = events->exception.serror_esr;
int ret = 0;
/*
* EL2 injects an external abort only to complete a forwarded abort.
* SError injection is forwarded.
*/
if (vcpu_is_protected(vcpu) && ext_dabt_pending)
return -EPERM;
/*
* Immediately commit the pending SEA to the vCPU's architectural
* state which is necessary since we do not return a pending SEA
@ -889,6 +896,10 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
{
trace_kvm_set_guest_debug(vcpu, dbg->control);
/* A protected guest's debug state is not exposed to the host. */
if (vcpu_is_protected(vcpu))
return -EPERM;
if (dbg->control & ~KVM_GUESTDBG_VALID_MASK)
return -EINVAL;

View File

@ -70,6 +70,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode,
enum exception_type type)
{
unsigned long sctlr, vbar, old, new, mode;
struct kvm *kvm;
u64 exc_offset;
mode = *vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT);
@ -109,8 +110,10 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode,
new |= (old & PSR_C_BIT);
new |= (old & PSR_V_BIT);
if (kvm_has_mte(kern_hyp_va(vcpu->kvm)))
kvm = vcpu_get_kvm(vcpu);
if (kvm && kvm_has_mte(kvm))
new |= PSR_TCO_BIT;
vcpu_put_kvm(vcpu, kvm);
new |= (old & PSR_DIT_BIT);
@ -350,12 +353,19 @@ static void kvm_inject_exception(struct kvm_vcpu *vcpu)
*/
void __kvm_adjust_pc(struct kvm_vcpu *vcpu)
{
if (vcpu_get_flag(vcpu, PENDING_EXCEPTION)) {
kvm_inject_exception(vcpu);
vcpu_clear_flag(vcpu, PENDING_EXCEPTION);
vcpu_clear_flag(vcpu, EXCEPT_MASK);
} else if (vcpu_get_flag(vcpu, INCREMENT_PC)) {
kvm_skip_instr(vcpu);
vcpu_clear_flag(vcpu, INCREMENT_PC);
struct kvm_vcpu *target = kvm_adjust_pc_get(vcpu);
if (!target)
return;
if (vcpu_get_flag(target, PENDING_EXCEPTION)) {
kvm_inject_exception(target);
vcpu_clear_flag(target, PENDING_EXCEPTION);
vcpu_clear_flag(target, EXCEPT_MASK);
} else if (vcpu_get_flag(target, INCREMENT_PC)) {
kvm_skip_instr(target);
vcpu_clear_flag(target, INCREMENT_PC);
}
kvm_adjust_pc_put(vcpu, target);
}

View File

@ -13,6 +13,34 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_host.h>
#ifdef __KVM_NVHE_HYPERVISOR__
#include <nvhe/pkvm.h>
/* Under pKVM a host vCPU's ->kvm is host-writable. */
static inline struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu)
{
if (is_protected_kvm_enabled())
return pkvm_vcpu_get_kvm(vcpu);
return kern_hyp_va(vcpu->kvm);
}
static inline void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm)
{
if (is_protected_kvm_enabled())
pkvm_vcpu_put_kvm(vcpu, kvm);
}
#else
static inline struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu)
{
return vcpu->kvm;
}
static inline void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm)
{
}
#endif
static inline void kvm_skip_instr(struct kvm_vcpu *vcpu)
{
if (vcpu_mode_is_32bit(vcpu)) {
@ -50,4 +78,24 @@ static inline void kvm_skip_host_instr(void)
write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR);
}
/*
* Under pKVM, the vCPU __kvm_adjust_pc() adjusts for @vcpu (NULL leaves the
* request on @vcpu for its next entry), and the copy of the consumed
* PC_UPDATE_REQ back to @vcpu.
*/
#ifdef __KVM_NVHE_HYPERVISOR__
struct kvm_vcpu *kvm_adjust_pc_get(struct kvm_vcpu *vcpu);
void kvm_adjust_pc_put(struct kvm_vcpu *vcpu, struct kvm_vcpu *target);
#else
static inline struct kvm_vcpu *kvm_adjust_pc_get(struct kvm_vcpu *vcpu)
{
return vcpu;
}
static inline void kvm_adjust_pc_put(struct kvm_vcpu *vcpu,
struct kvm_vcpu *target)
{
}
#endif
#endif

View File

@ -27,6 +27,15 @@ struct pkvm_hyp_vcpu {
* per-cpu pointer tracking us. Otherwise, NULL if not loaded.
*/
struct pkvm_hyp_vcpu **loaded_hyp_vcpu;
/* The previous exit's ARM_EXCEPTION_* code. */
u32 exit_code;
/*
* PSCI_0_2_AFFINITY_LEVEL_{OFF, ON_PENDING, ON}. A non-protected
* vCPU is always ON.
*/
int power_state;
};
/*
@ -44,6 +53,12 @@ struct pkvm_hyp_vm {
struct hyp_pool pool;
hyp_spinlock_t lock;
/*
* The vCPU initialised RUNNABLE: claimed under vm_table_lock,
* released only if its own init fails.
*/
struct pkvm_hyp_vcpu *primary_vcpu;
/* Array of the hyp vCPU structures for this VM. */
struct pkvm_hyp_vcpu *vcpus[];
};
@ -84,6 +99,8 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
unsigned int vcpu_idx);
void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu);
struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void);
struct kvm *pkvm_vcpu_get_kvm(struct kvm_vcpu *vcpu);
void pkvm_vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm);
struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle);
struct pkvm_hyp_vm *get_np_pkvm_hyp_vm(pkvm_handle_t handle);
@ -93,6 +110,9 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code);
bool kvm_handle_pvm_sysreg(struct kvm_vcpu *vcpu, u64 *exit_code);
bool kvm_handle_pvm_restricted(struct kvm_vcpu *vcpu, u64 *exit_code);
void kvm_init_pvm_id_regs(struct kvm_vcpu *vcpu);
void kvm_reset_pvm_sys_regs(struct kvm_vcpu *vcpu);
int kvm_check_pvm_sysreg_table(void);
int pkvm_reset_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu);
struct pkvm_hyp_vcpu *pkvm_mpidr_to_hyp_vcpu(struct pkvm_hyp_vm *vm, unsigned long mpidr);
#endif /* __ARM64_KVM_NVHE_PKVM_H__ */

View File

@ -4,10 +4,13 @@
* Author: Andrew Scull <ascull@google.com>
*/
#include <kvm/arm_hypercalls.h>
#include <hyp/adjust_pc.h>
#include <hyp/switch.h>
#include <linux/irqchip/arm-gic-v3.h>
#include <uapi/linux/psci.h>
#include <asm/pgtable-types.h>
#include <asm/kvm_asm.h>
@ -73,6 +76,375 @@ unsigned int hyp_gicv3_nr_lr;
void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);
typedef void (*hyp_entry_exit_handler_fn)(struct pkvm_hyp_vcpu *);
static bool pvm_sys64_is_write(u64 esr)
{
return (esr & ESR_ELx_SYS64_ISS_DIR_MASK) == ESR_ELx_SYS64_ISS_DIR_WRITE;
}
static void handle_pvm_entry_wfx(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
/* Exceptions have priority; the host injects none on WFx. */
if (vcpu_get_flag(host_vcpu, PENDING_EXCEPTION))
return;
if (vcpu_get_flag(host_vcpu, INCREMENT_PC)) {
vcpu_clear_flag(vcpu, PC_UPDATE_REQ);
kvm_incr_pc(vcpu);
}
}
static void handle_pvm_entry_sys64(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
bool pc_update;
/* Exceptions have priority over anything else */
if (vcpu_get_flag(host_vcpu, PENDING_EXCEPTION)) {
/* A host-requested exception on SYS64 is always an UNDEF. */
u32 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL;
__vcpu_assign_sys_reg(vcpu, ESR_EL1, esr);
kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC);
return;
}
/* Handle PC increment on a host-emulated access */
pc_update = vcpu_get_flag(host_vcpu, INCREMENT_PC);
if (pc_update) {
vcpu_clear_flag(vcpu, PC_UPDATE_REQ);
kvm_incr_pc(vcpu);
}
/* If the host emulated a read access, update the register */
if (pc_update && !pvm_sys64_is_write(kvm_vcpu_get_esr(vcpu))) {
/* r0 as transfer register between the guest and the host. */
u64 rt_val = READ_ONCE(vcpu_gp_regs(host_vcpu)[0]);
int rt = kvm_vcpu_sys_get_rt(vcpu);
vcpu_set_reg(vcpu, rt, rt_val);
}
}
static void handle_pvm_entry_iabt(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
unsigned long cpsr = *vcpu_cpsr(vcpu);
u32 esr = ESR_ELx_IL;
if (!vcpu_get_flag(host_vcpu, PENDING_EXCEPTION))
return;
/* The host's only IABT injection: an external abort. */
if ((cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t)
esr |= (ESR_ELx_EC_IABT_LOW << ESR_ELx_EC_SHIFT);
else
esr |= (ESR_ELx_EC_IABT_CUR << ESR_ELx_EC_SHIFT);
esr |= ESR_ELx_FSC_EXTABT;
__vcpu_assign_sys_reg(vcpu, ESR_EL1, esr);
__vcpu_assign_sys_reg(vcpu, FAR_EL1, kvm_vcpu_get_hfar(vcpu));
/* Injected by __kvm_adjust_pc() on entry. */
kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC);
}
/*
* Clamp MMIO data to the access width, so a write does not leak the
* register's upper bits and a read takes no bits beyond the load. The
* host applies endianness.
*/
static inline u64 kvm_mmio_clamp_data(struct kvm_vcpu *vcpu, u64 val)
{
unsigned int len = kvm_vcpu_dabt_get_as(vcpu);
return val & GENMASK_U64(len * 8 - 1, 0);
}
/*
* Complete an MMIO load: sign-extend from EL2's own syndrome, as the
* architecture does.
*/
static inline u64 kvm_mmio_read_data(struct kvm_vcpu *vcpu, u64 val)
{
val = kvm_mmio_clamp_data(vcpu, val);
if (kvm_vcpu_dabt_issext(vcpu))
val = sign_extend64(val, kvm_vcpu_dabt_get_as(vcpu) * 8 - 1);
if (!kvm_vcpu_dabt_issf(vcpu))
val &= GENMASK_U64(31, 0);
return val;
}
static void handle_pvm_entry_dabt(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
bool pc_update;
/* Exceptions have priority over anything else */
if (vcpu_get_flag(host_vcpu, PENDING_EXCEPTION)) {
unsigned long cpsr = *vcpu_cpsr(vcpu);
u32 esr = ESR_ELx_IL;
if ((cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t)
esr |= (ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT);
else
esr |= (ESR_ELx_EC_DABT_CUR << ESR_ELx_EC_SHIFT);
esr |= ESR_ELx_FSC_EXTABT;
__vcpu_assign_sys_reg(vcpu, ESR_EL1, esr);
__vcpu_assign_sys_reg(vcpu, FAR_EL1, kvm_vcpu_get_hfar(vcpu));
/* Injected by __kvm_adjust_pc() on entry. */
kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC);
/* Cancel any in-flight MMIO */
vcpu->mmio_needed = false;
return;
}
/* Handle PC increment on MMIO, or on a CMO the host skipped */
pc_update = vcpu_get_flag(host_vcpu, INCREMENT_PC) &&
(vcpu->mmio_needed || esr_dabt_is_cm(kvm_vcpu_get_esr(vcpu)));
if (pc_update) {
vcpu_clear_flag(vcpu, PC_UPDATE_REQ);
kvm_incr_pc(vcpu);
}
/* If the host emulated an MMIO read, update the register */
if (pc_update && vcpu->mmio_needed && !kvm_vcpu_dabt_iswrite(vcpu)) {
/* r0 as transfer register between the guest and the host. */
u64 rd_val = READ_ONCE(vcpu_gp_regs(host_vcpu)[0]);
int rd = kvm_vcpu_dabt_get_rd(vcpu);
rd_val = kvm_mmio_read_data(vcpu, rd_val);
vcpu_set_reg(vcpu, rd, rd_val);
}
vcpu->mmio_needed = false;
}
static void handle_pvm_entry_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u64 ret = READ_ONCE(vcpu_gp_regs(host_vcpu)[0]);
u32 psci_fn = smccc_get_function(vcpu);
switch (psci_fn) {
case PSCI_0_2_FN_CPU_ON:
case PSCI_0_2_FN64_CPU_ON:
/*
* Roll back a CPU_ON the host failed, unless the target
* already reached ON: it is running, and the guest sees
* SUCCESS.
*/
if (ret != PSCI_RET_SUCCESS) {
unsigned long cpu_id = smccc_get_arg1(vcpu);
struct pkvm_hyp_vcpu *target_vcpu;
struct pkvm_hyp_vm *hyp_vm;
int prev;
hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
target_vcpu = pkvm_mpidr_to_hyp_vcpu(hyp_vm, cpu_id);
/*
* pvm_psci_vcpu_on() resolved this MPIDR and vcpus[]
* entries are never removed, so the lookup cannot miss.
* The release orders this vCPU's reset_state writes
* before OFF, for the next CPU_ON.
*/
prev = cmpxchg_release(&target_vcpu->power_state,
PSCI_0_2_AFFINITY_LEVEL_ON_PENDING,
PSCI_0_2_AFFINITY_LEVEL_OFF);
switch (prev) {
case PSCI_0_2_AFFINITY_LEVEL_ON_PENDING:
/*
* Leave reset_state.reset set: a clear races a
* fresh CPU_ON's publish. The stale pc/r0/be are
* the guest's own. ALREADY_ON is PSCI's retry
* signal for a CPU_ON that raced the CPU_OFF.
*/
if (ret != PSCI_RET_ALREADY_ON)
ret = PSCI_RET_INTERNAL_FAILURE;
break;
case PSCI_0_2_AFFINITY_LEVEL_ON:
case PSCI_0_2_AFFINITY_LEVEL_OFF:
/* Target already ran (and may have stopped). */
ret = PSCI_RET_SUCCESS;
break;
default:
ret = PSCI_RET_INTERNAL_FAILURE;
break;
}
}
break;
default:
break;
}
vcpu_set_reg(vcpu, 0, ret);
}
/* The host's view of a syndrome: the guest register index is withheld. */
static u64 pvm_host_esr(u64 esr)
{
switch (ESR_ELx_EC(esr)) {
case ESR_ELx_EC_WFx:
return esr & ~ESR_ELx_WFx_ISS_RN;
case ESR_ELx_EC_SYS64:
return esr & ~ESR_ELx_SYS64_ISS_RT_MASK;
case ESR_ELx_EC_DABT_LOW:
return esr & ~ESR_ELx_SRT_MASK;
default:
return esr;
}
}
/*
* The host's view of PSTATE: the mode, with SErrors masked so that the host
* pends an SError through HCR_EL2.VSE rather than emulating the entry.
*/
static unsigned long pvm_host_pstate(unsigned long pstate)
{
return (pstate & PSR_MODE_MASK) | PSR_A_BIT;
}
static void handle_pvm_exit_wfx(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
*vcpu_cpsr(host_vcpu) = pvm_host_pstate(*vcpu_cpsr(vcpu));
}
static void handle_pvm_exit_sys64(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u64 esr = kvm_vcpu_get_esr(vcpu);
/* The mode is required for the host to emulate some sysregs */
*vcpu_cpsr(host_vcpu) = pvm_host_pstate(*vcpu_cpsr(vcpu));
/* r0 as transfer register between the guest and the host. */
if (pvm_sys64_is_write(esr)) {
int rt = kvm_vcpu_sys_get_rt(vcpu);
u64 rt_val = vcpu_get_reg(vcpu, rt);
vcpu_set_reg(host_vcpu, 0, rt_val);
}
}
static void handle_pvm_exit_iabt(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
host_vcpu->arch.fault.hpfar_el2 = vcpu->arch.fault.hpfar_el2;
}
static void handle_pvm_exit_dabt(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u64 sctlr;
/*
* EL2 has no memslot view: a decodable data abort is prepared as MMIO
* for the host to resolve. On unbacked memory the host injects an SEA
* for one with ISV clear (LDP/STP, atomics), which EL2 does not
* decode, and skips a cache maintenance operation, as for any guest.
*/
vcpu->mmio_needed = kvm_vcpu_dabt_isvalid(vcpu);
/* r0 as transfer register between the guest and the host. */
if (vcpu->mmio_needed && kvm_vcpu_dabt_iswrite(vcpu)) {
int rt = kvm_vcpu_dabt_get_rd(vcpu);
u64 rt_val = vcpu_get_reg(vcpu, rt);
rt_val = kvm_mmio_clamp_data(vcpu, rt_val);
vcpu_set_reg(host_vcpu, 0, rt_val);
}
*vcpu_cpsr(host_vcpu) = pvm_host_pstate(*vcpu_cpsr(vcpu));
host_vcpu->arch.fault.far_el2 = vcpu->arch.fault.far_el2 & GENMASK(11, 0);
host_vcpu->arch.fault.hpfar_el2 = vcpu->arch.fault.hpfar_el2;
sctlr = __vcpu_sys_reg(vcpu, SCTLR_EL1) & (SCTLR_ELx_EE | SCTLR_EL1_E0E);
__vcpu_assign_sys_reg(host_vcpu, SCTLR_EL1, sctlr);
}
static void handle_pvm_exit_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
int n, i;
switch (smccc_get_function(vcpu)) {
/*
* CPU_ON: the host uses only the target MPIDR (x1). EL2 resets the
* target from its own copy of the entry point and context id.
*/
case PSCI_0_2_FN_CPU_ON:
case PSCI_0_2_FN64_CPU_ON:
n = 2;
break;
case PSCI_0_2_FN_CPU_OFF:
case PSCI_0_2_FN_SYSTEM_OFF:
case PSCI_0_2_FN_SYSTEM_RESET:
case PSCI_0_2_FN_CPU_SUSPEND:
case PSCI_0_2_FN64_CPU_SUSPEND:
n = 1;
break;
case PSCI_0_2_FN_AFFINITY_INFO:
case PSCI_0_2_FN64_AFFINITY_INFO:
case PSCI_1_1_FN_SYSTEM_RESET2:
case PSCI_1_1_FN64_SYSTEM_RESET2:
n = 3;
break;
/* Unreachable: kvm_handle_pvm_hvc64() forwards only the calls above. */
default:
hyp_panic();
}
/* Pass the HVC function id (r0) and its arguments. */
for (i = 0; i < n; i++)
vcpu_set_reg(host_vcpu, i, vcpu_get_reg(vcpu, i));
}
static const hyp_entry_exit_handler_fn entry_hyp_pvm_handlers[] = {
[0 ... ESR_ELx_EC_MAX] = NULL,
[ESR_ELx_EC_WFx] = handle_pvm_entry_wfx,
[ESR_ELx_EC_SYS64] = handle_pvm_entry_sys64,
[ESR_ELx_EC_IABT_LOW] = handle_pvm_entry_iabt,
[ESR_ELx_EC_DABT_LOW] = handle_pvm_entry_dabt,
[ESR_ELx_EC_HVC64] = handle_pvm_entry_hvc64,
};
static const hyp_entry_exit_handler_fn exit_hyp_pvm_handlers[] = {
[0 ... ESR_ELx_EC_MAX] = NULL,
[ESR_ELx_EC_WFx] = handle_pvm_exit_wfx,
[ESR_ELx_EC_SYS64] = handle_pvm_exit_sys64,
[ESR_ELx_EC_IABT_LOW] = handle_pvm_exit_iabt,
[ESR_ELx_EC_DABT_LOW] = handle_pvm_exit_dabt,
[ESR_ELx_EC_HVC64] = handle_pvm_exit_hvc64,
};
static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu)
{
__vcpu_assign_sys_reg(vcpu, ZCR_EL1, read_sysreg_el1(SYS_ZCR));
@ -185,6 +557,35 @@ static void sync_hyp_vgic_state(struct pkvm_hyp_vcpu *hyp_vcpu)
host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
}
static void flush_hyp_timer_state(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu))
return;
/* A hyp vcpu has no offset, and sees vtime == ptime. */
write_sysreg(0, cntvoff_el2);
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTV_CVAL_EL0), SYS_CNTV_CVAL);
isb();
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTV_CTL_EL0), SYS_CNTV_CTL);
}
static void sync_hyp_timer_state(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu))
return;
/*
* Preserve the vtimer state so that it is always correct,
* even if the host tries to make a mess.
*/
__vcpu_assign_sys_reg(vcpu, CNTV_CVAL_EL0, read_sysreg_el0(SYS_CNTV_CVAL));
__vcpu_assign_sys_reg(vcpu, CNTV_CTL_EL0, read_sysreg_el0(SYS_CNTV_CTL));
}
static void __copy_vcpu_state(const struct kvm_vcpu *from_vcpu,
struct kvm_vcpu *to_vcpu)
{
@ -231,6 +632,9 @@ static void flush_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
return;
hyp_vcpu->vcpu.arch.debug_owner = host_vcpu->arch.debug_owner;
if (kvm_guest_owns_debug_regs(&hyp_vcpu->vcpu)) {
@ -249,6 +653,9 @@ static void sync_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
return;
if (kvm_guest_owns_debug_regs(&hyp_vcpu->vcpu))
host_vcpu->arch.vcpu_debug_state = hyp_vcpu->vcpu.arch.vcpu_debug_state;
else if (kvm_host_owns_debug_regs(&hyp_vcpu->vcpu))
@ -258,6 +665,8 @@ static void sync_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
hyp_entry_exit_handler_fn ec_handler;
u8 esr_ec;
fpsimd_sve_flush();
flush_debug_state(hyp_vcpu);
@ -270,42 +679,78 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
if (vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY))
flush_hyp_vcpu_state(hyp_vcpu);
} else {
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
(HCR_TWI | HCR_TWE);
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags;
}
/* __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;
/*
* 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.
* A host-injected vSError is masked by the guest's own PSTATE.A, so it
* applies to protected guests too.
*/
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_VSE);
hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags;
hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;
hyp_vcpu->vcpu.arch.hcr_el2 &= ~HCR_VSE;
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & HCR_VSE;
hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;
flush_hyp_vgic_state(hyp_vcpu);
flush_hyp_timer_state(hyp_vcpu);
hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
switch (ARM_EXCEPTION_CODE(hyp_vcpu->exit_code)) {
case ARM_EXCEPTION_IRQ:
case ARM_EXCEPTION_EL1_SERROR:
case ARM_EXCEPTION_IL:
break;
case ARM_EXCEPTION_TRAP:
/* Nothing was marshalled for this trap, see sync_hyp_vcpu(). */
if (ARM_SERROR_PENDING(hyp_vcpu->exit_code))
break;
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
esr_ec = ESR_ELx_EC(kvm_vcpu_get_esr(&hyp_vcpu->vcpu));
ec_handler = entry_hyp_pvm_handlers[esr_ec];
if (ec_handler)
ec_handler(hyp_vcpu);
}
break;
default:
BUG();
}
hyp_vcpu->exit_code = 0;
}
static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu, u32 exit_reason)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
hyp_entry_exit_handler_fn ec_handler;
u8 esr_ec;
fpsimd_sve_sync(&hyp_vcpu->vcpu);
sync_debug_state(hyp_vcpu);
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
host_vcpu->arch.ctxt = hyp_vcpu->vcpu.arch.ctxt;
/*
* Protected: the host sees ESR_EL2 as EL2 took it, register
* index withheld; the fault addresses stay withheld unless the
* EC handler below adds them.
*/
host_vcpu->arch.fault = (struct kvm_vcpu_fault_info) {
.esr_el2 = pvm_host_esr(hyp_vcpu->vcpu.arch.fault.esr_el2),
.disr_el1 = hyp_vcpu->vcpu.arch.fault.disr_el1,
};
} else {
/* Non-protected: the host gets the full fault. */
host_vcpu->arch.fault = hyp_vcpu->vcpu.arch.fault;
host_vcpu->arch.iflags = hyp_vcpu->vcpu.arch.iflags;
/*
* PC feeds trace_kvm_exit(), PSTATE.SS the host software-step
* machine, and both run before the next on-demand ctxt sync.
@ -314,11 +759,40 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
host_vcpu->arch.ctxt.regs.pstate = hyp_vcpu->vcpu.arch.ctxt.regs.pstate;
}
host_vcpu->arch.fault = hyp_vcpu->vcpu.arch.fault;
switch (ARM_EXCEPTION_CODE(exit_reason)) {
case ARM_EXCEPTION_IRQ:
break;
case ARM_EXCEPTION_TRAP:
/* SError pending: not handled at EL2, the guest replays it. */
if (ARM_SERROR_PENDING(exit_reason))
break;
host_vcpu->arch.iflags = hyp_vcpu->vcpu.arch.iflags;
/* Per-EC marshalling is for protected guests only. */
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
esr_ec = ESR_ELx_EC(kvm_vcpu_get_esr(&hyp_vcpu->vcpu));
ec_handler = exit_hyp_pvm_handlers[esr_ec];
if (ec_handler)
ec_handler(hyp_vcpu);
}
break;
case ARM_EXCEPTION_EL1_SERROR:
case ARM_EXCEPTION_IL:
break;
default:
BUG();
}
/* Cleared by hardware once the guest takes the vSError. */
host_vcpu->arch.hcr_el2 &= ~HCR_VSE;
host_vcpu->arch.hcr_el2 |= hyp_vcpu->vcpu.arch.hcr_el2 & HCR_VSE;
sync_hyp_vgic_state(hyp_vcpu);
sync_hyp_timer_state(hyp_vcpu);
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
vcpu_clear_flag(host_vcpu, PC_UPDATE_REQ);
hyp_vcpu->exit_code = exit_reason;
}
DEFINE_KVM_HOST_HCALL(void, __pkvm_vcpu_load,
@ -331,9 +805,17 @@ DEFINE_KVM_HOST_HCALL(void, __pkvm_vcpu_load,
return;
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
u64 dfr0 = read_sysreg(id_aa64dfr0_el1);
/* Propagate WFx trapping flags */
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWE | HCR_TWI);
hyp_vcpu->vcpu.arch.hcr_el2 |= hcr_el2 & (HCR_TWE | HCR_TWI);
/* HPMN == 0 is reserved without FEAT_HPMN0. */
if (pmuv3_implemented(SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, dfr0)))
u64p_replace_bits(&hyp_vcpu->vcpu.arch.mdcr_el2,
FIELD_GET(ARMV8_PMU_PMCR_N, read_sysreg(pmcr_el0)),
MDCR_EL2_HPMN);
} else {
memcpy(&hyp_vcpu->vcpu.arch.fgt, hyp_vcpu->host_vcpu->arch.fgt,
sizeof(hyp_vcpu->vcpu.arch.fgt));
@ -396,16 +878,15 @@ __get_host_hyp_vcpus_from_vgic_v3_cpu_if(struct vgic_v3_cpu_if __kern *cpu_if,
return __get_host_hyp_vcpus(host_vcpu, hyp_vcpup);
}
DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run,
struct kvm_vcpu __kern *, vcpu)
DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run, struct kvm_vcpu __kern *, vcpu)
{
struct pkvm_hyp_vcpu *hyp_vcpu;
struct kvm_vcpu *host_vcpu;
int ret;
int ret = ARM_EXCEPTION_IL;
host_vcpu = __get_host_hyp_vcpus(kern_hyp_va_host(vcpu), &hyp_vcpu);
if (!host_vcpu)
return -EINVAL;
return ret;
if (unlikely(hyp_vcpu)) {
/*
@ -415,13 +896,29 @@ DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run,
* is misbehaving.
*/
if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR)))
return -EINVAL;
return ret;
/*
* ON has a single writer, pkvm_reset_vcpu() on this CPU, so
* READ_ONCE suffices. ON_PENDING takes the reset; -ECANCELED
* is a rollback that raced it.
*/
switch (READ_ONCE(hyp_vcpu->power_state)) {
case PSCI_0_2_AFFINITY_LEVEL_ON:
break;
case PSCI_0_2_AFFINITY_LEVEL_ON_PENDING:
if (pkvm_reset_vcpu(hyp_vcpu))
return ret;
break;
default:
return ret;
}
flush_hyp_vcpu(hyp_vcpu);
ret = __kvm_vcpu_run(&hyp_vcpu->vcpu);
sync_hyp_vcpu(hyp_vcpu);
sync_hyp_vcpu(hyp_vcpu, ret);
} else {
/* The host is fully trusted, run its vCPU directly. */
fpsimd_lazy_switch_to_guest(host_vcpu);
@ -547,10 +1044,62 @@ DEFINE_KVM_HOST_HCALL(int, __pkvm_host_mkyoung_guest,
return __pkvm_host_mkyoung_guest(gfn, hyp_vcpu);
}
/*
* PKVM_HOST_STATE_DIRTY names the authoritative copy, the host's when set.
* A loaded protected vCPU takes the request at its next entry instead.
*/
struct kvm_vcpu *kvm_adjust_pc_get(struct kvm_vcpu *vcpu)
{
struct pkvm_hyp_vcpu *hyp_vcpu;
if (!is_protected_kvm_enabled())
return vcpu;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || vcpu == &hyp_vcpu->vcpu)
return vcpu;
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
return NULL;
if (vcpu_get_flag(vcpu, PKVM_HOST_STATE_DIRTY))
return vcpu;
vcpu_copy_flag(&hyp_vcpu->vcpu, vcpu, PC_UPDATE_REQ);
return &hyp_vcpu->vcpu;
}
/* Reflect the consumed request back, otherwise it stays pending. */
void kvm_adjust_pc_put(struct kvm_vcpu *vcpu, struct kvm_vcpu *target)
{
if (target != vcpu)
vcpu_copy_flag(vcpu, target, PC_UPDATE_REQ);
}
DEFINE_KVM_HOST_HCALL(void, __kvm_adjust_pc,
struct kvm_vcpu __kern *, vcpu)
{
__kvm_adjust_pc(kern_hyp_va_host(vcpu));
struct pkvm_hyp_vcpu *hyp_vcpu;
struct kvm_vcpu *host_vcpu;
host_vcpu = __get_host_hyp_vcpus(kern_hyp_va_host(vcpu), &hyp_vcpu);
if (host_vcpu) {
__kvm_adjust_pc(host_vcpu);
return;
}
/*
* With no hyp vCPU loaded for it, the host vCPU may be unpinned,
* and so unmapped at EL2: its first run pins it. A pin fails only
* for memory the host isn't sharing, a bad pointer, so the request
* is dropped.
*/
host_vcpu = kern_hyp_va_host(vcpu);
if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1))
return;
__kvm_adjust_pc(host_vcpu);
hyp_unpin_shared_mem(host_vcpu, host_vcpu + 1);
}
DEFINE_KVM_HOST_HCALL0(void, __kvm_flush_vm_context)

View File

@ -5,17 +5,24 @@
*/
#include <kvm/arm_hypercalls.h>
#include <kvm/arm_psci.h>
#include <linux/kvm_host.h>
#include <linux/mm.h>
#include <asm/kvm_emulate.h>
#include <asm/spectre.h>
#include <nvhe/mem_protect.h>
#include <nvhe/memory.h>
#include <nvhe/pkvm.h>
#include <nvhe/trap_handler.h>
/* The host's Spectre mitigation state, for SMCCC_ARCH_FEATURES. */
enum mitigation_state spectre_v2_state;
enum mitigation_state spectre_v4_state;
enum mitigation_state spectre_bhb_state;
/* Used by icache_is_aliasing(). */
unsigned long __icache_flags;
@ -102,7 +109,7 @@ static void pvm_init_traps_mdcr(struct kvm_vcpu *vcpu)
if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, PMUVer, IMP)) {
val |= MDCR_EL2_TPM | MDCR_EL2_TPMCR;
val &= ~(MDCR_EL2_HPME | MDCR_EL2_MTPME | MDCR_EL2_HPMN_MASK);
val &= ~(MDCR_EL2_HPME | MDCR_EL2_MTPME);
}
if (!kvm_has_feat(kvm, ID_AA64DFR0_EL1, DebugVer, IMP))
@ -304,6 +311,43 @@ struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void)
}
static struct pkvm_hyp_vm *pkvm_get_loaded_hyp_vm(struct kvm_vcpu *vcpu)
{
struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (hyp_vcpu &&
(vcpu == &hyp_vcpu->vcpu || vcpu == hyp_vcpu->host_vcpu))
return pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
return NULL;
}
/*
* A loaded vCPU's VM is the hyp VM. An unloaded host vCPU's is mapped at
* EL2 only while pinned, so it's read once and pinned; the pin fails for
* memory the host isn't sharing.
*/
struct kvm *pkvm_vcpu_get_kvm(struct kvm_vcpu *vcpu)
{
struct pkvm_hyp_vm *hyp_vm = pkvm_get_loaded_hyp_vm(vcpu);
struct kvm *kvm;
if (hyp_vm)
return &hyp_vm->kvm;
kvm = kern_hyp_va(READ_ONCE(vcpu->kvm));
if (hyp_pin_shared_mem(kvm, kvm + 1))
return NULL;
return kvm;
}
void pkvm_vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm)
{
if (kvm && !pkvm_get_loaded_hyp_vm(vcpu))
hyp_unpin_shared_mem(kvm, kvm + 1);
}
struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle)
{
struct pkvm_hyp_vm *hyp_vm;
@ -366,21 +410,7 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_MTE))
kvm->arch.flags |= host_arch_flags & BIT(KVM_ARCH_FLAG_MTE_ENABLED);
bitmap_zero(allowed_features, KVM_VCPU_MAX_FEATURES);
set_bit(KVM_ARM_VCPU_PSCI_0_2, allowed_features);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PMU_V3))
set_bit(KVM_ARM_VCPU_PMU_V3, allowed_features);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PTRAUTH_ADDRESS))
set_bit(KVM_ARM_VCPU_PTRAUTH_ADDRESS, allowed_features);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_PTRAUTH_GENERIC))
set_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, allowed_features);
if (kvm_pkvm_ext_allowed(kvm, KVM_CAP_ARM_SVE))
set_bit(KVM_ARM_VCPU_SVE, allowed_features);
kvm_pkvm_vcpu_allowed_features(kvm, allowed_features);
bitmap_and(kvm->arch.vcpu_features, host_kvm->arch.vcpu_features,
allowed_features, KVM_VCPU_MAX_FEATURES);
@ -389,6 +419,40 @@ static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struc
kvm_vcpu_has_feature(kvm, KVM_ARM_VCPU_SVE));
}
static int pkvm_vcpu_init_psci(struct pkvm_hyp_vcpu *hyp_vcpu, u32 mp_state)
{
struct vcpu_reset_state *reset_state = &hyp_vcpu->vcpu.arch.reset_state;
struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
struct kvm_vcpu *host_vcpu;
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
/* The host manages a non-protected vCPU: always ON at EL2. */
hyp_vcpu->power_state = PSCI_0_2_AFFINITY_LEVEL_ON;
return 0;
}
if (mp_state != KVM_MP_STATE_RUNNABLE && mp_state != KVM_MP_STATE_STOPPED)
return -EINVAL;
if (mp_state == KVM_MP_STATE_STOPPED) {
reset_state->reset = false;
hyp_vcpu->power_state = PSCI_0_2_AFFINITY_LEVEL_OFF;
return 0;
}
hyp_assert_lock_held(&vm_table_lock);
if (hyp_vm->primary_vcpu)
return -EINVAL;
hyp_vm->primary_vcpu = hyp_vcpu;
host_vcpu = hyp_vcpu->host_vcpu;
reset_state->pc = READ_ONCE(*vcpu_pc(host_vcpu));
reset_state->r0 = READ_ONCE(vcpu_gp_regs(host_vcpu)[0]);
reset_state->reset = true;
hyp_vcpu->power_state = PSCI_0_2_AFFINITY_LEVEL_ON_PENDING;
return 0;
}
static void unpin_host_vcpu(struct kvm_vcpu *host_vcpu)
{
if (host_vcpu)
@ -511,10 +575,12 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
struct kvm_vcpu *host_vcpu)
{
int ret = 0;
u32 mp_state;
if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1))
return -EBUSY;
mp_state = READ_ONCE(host_vcpu->arch.mp_state.mp_state);
hyp_vcpu->host_vcpu = host_vcpu;
hyp_vcpu->vcpu.kvm = &hyp_vm->kvm;
@ -523,7 +589,6 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
hyp_vcpu->vcpu.arch.hw_mmu = &hyp_vm->kvm.arch.mmu;
hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags);
hyp_vcpu->vcpu.arch.mp_state.mp_state = KVM_MP_STATE_STOPPED;
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
/*
@ -548,9 +613,17 @@ static int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
goto done;
ret = pkvm_vcpu_init_sve(hyp_vcpu, host_vcpu);
done:
if (ret)
goto done;
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu))
kvm_reset_pvm_sys_regs(&hyp_vcpu->vcpu);
ret = pkvm_vcpu_init_psci(hyp_vcpu, mp_state);
done:
if (ret) {
unpin_host_vcpu(host_vcpu);
unpin_host_sve_state(hyp_vcpu);
}
return ret;
}
@ -927,13 +1000,22 @@ int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
ret = init_pkvm_hyp_vcpu(hyp_vcpu, hyp_vm, host_vcpu);
if (ret)
goto unlock;
goto unclaim;
ret = register_hyp_vcpu(hyp_vm, hyp_vcpu);
if (ret) {
unpin_host_vcpu(host_vcpu);
unpin_host_sve_state(hyp_vcpu);
goto unclaim;
}
goto unlock;
unclaim:
/*
* Under vm_table_lock, so no other claim can have landed: undo
* this one.
*/
if (hyp_vm->primary_vcpu == hyp_vcpu)
hyp_vm->primary_vcpu = NULL;
unlock:
hyp_spin_unlock(&vm_table_lock);
@ -1125,6 +1207,238 @@ static void pkvm_memunshare_call(u64 *ret, struct kvm_vcpu *vcpu)
ret[0] = SMCCC_RET_SUCCESS;
}
/*
* Reset the vCPU to its power-on state and commit ON_PENDING -> ON, on the
* target's own CPU. Returns -ECANCELED, with no side effects, if a rollback
* raced the reset.
*
* The requestor's PSCI CPU_ON, in pvm_psci_vcpu_on():
*
* cmpxchg_relaxed(power_state): OFF -> ON_PENDING
* <ctrl>
* write_reset_state();
* smp_store_release(reset_state->reset, true);
*
* The target coming online here, running, then its PSCI CPU_OFF in
* pvm_psci_vcpu_off():
*
* smp_load_acquire(reset_state->reset) == true;
* cmpxchg_relaxed(power_state): ON_PENDING -> ON;
* read_reset_state();
*
* <run guest>
*
* WRITE_ONCE(reset_state->reset, false);
* smp_store_release(power_state, OFF);
*/
int pkvm_reset_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct vcpu_reset_state *reset_state = &hyp_vcpu->vcpu.arch.reset_state;
/*
* Pairs with smp_store_release(&reset_state->reset, true) in
* pvm_psci_vcpu_on(). The acquire must precede the cmpxchg: reversed, a
* winning cmpxchg with a false acquire would leave power_state == ON
* with the reset skipped.
*/
if (!smp_load_acquire(&reset_state->reset))
return -ECANCELED;
if (cmpxchg_relaxed(&hyp_vcpu->power_state, PSCI_0_2_AFFINITY_LEVEL_ON_PENDING,
PSCI_0_2_AFFINITY_LEVEL_ON) != PSCI_0_2_AFFINITY_LEVEL_ON_PENDING)
return -ECANCELED;
kvm_reset_vcpu_core(&hyp_vcpu->vcpu);
kvm_reset_pvm_sys_regs(&hyp_vcpu->vcpu);
/* Must be done after resetting sys registers. */
kvm_reset_vcpu_psci(&hyp_vcpu->vcpu, reset_state);
hyp_vcpu->exit_code = 0;
return 0;
}
struct pkvm_hyp_vcpu *pkvm_mpidr_to_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm, unsigned long mpidr)
{
struct pkvm_hyp_vcpu *hyp_vcpu;
int i;
mpidr &= MPIDR_HWID_BITMASK;
for (i = 0; i < hyp_vm->kvm.created_vcpus; i++) {
/* Pairs with smp_store_release() in register_hyp_vcpu(). */
hyp_vcpu = smp_load_acquire(&hyp_vm->vcpus[i]);
if (!hyp_vcpu)
continue;
if (mpidr == kvm_vcpu_get_mpidr_aff(&hyp_vcpu->vcpu))
return hyp_vcpu;
}
return NULL;
}
/*
* Returns true when handled at EL2, false when the host must wake the target
* vCPU.
*/
static bool pvm_psci_vcpu_on(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
struct vcpu_reset_state *reset_state;
struct pkvm_hyp_vcpu *target;
unsigned long cpu_id, ret;
int power_state;
cpu_id = smccc_get_arg1(&hyp_vcpu->vcpu);
if (!kvm_psci_valid_affinity(&hyp_vcpu->vcpu, cpu_id)) {
ret = PSCI_RET_INVALID_PARAMS;
goto error;
}
target = pkvm_mpidr_to_hyp_vcpu(hyp_vm, cpu_id);
if (!target) {
ret = PSCI_RET_INVALID_PARAMS;
goto error;
}
/*
* vCPUs race to power on the same target. The writes below depend on
* the cmpxchg reading OFF, and that control dependency with the release
* of OFF in pvm_psci_vcpu_off() orders them after the target's accesses
* to reset_state, so relaxed suffices.
*/
power_state = cmpxchg_relaxed(&target->power_state,
PSCI_0_2_AFFINITY_LEVEL_OFF,
PSCI_0_2_AFFINITY_LEVEL_ON_PENDING);
switch (power_state) {
case PSCI_0_2_AFFINITY_LEVEL_ON_PENDING:
ret = PSCI_RET_ON_PENDING;
goto error;
case PSCI_0_2_AFFINITY_LEVEL_ON:
ret = PSCI_RET_ALREADY_ON;
goto error;
case PSCI_0_2_AFFINITY_LEVEL_OFF:
break;
default:
ret = PSCI_RET_INTERNAL_FAILURE;
goto error;
}
reset_state = &target->vcpu.arch.reset_state;
WRITE_ONCE(reset_state->pc, smccc_get_arg2(&hyp_vcpu->vcpu));
WRITE_ONCE(reset_state->r0, smccc_get_arg3(&hyp_vcpu->vcpu));
WRITE_ONCE(reset_state->be, kvm_vcpu_is_be(&hyp_vcpu->vcpu));
/*
* Publish reset_state.{pc, r0, be} to the target vCPU. Pairs with
* smp_load_acquire(&reset_state->reset) in pkvm_reset_vcpu().
*/
smp_store_release(&reset_state->reset, true);
/* The host requests KVM_REQ_VCPU_RESET and wakes the target. */
return false;
error:
smccc_set_retval(&hyp_vcpu->vcpu, ret, 0, 0, 0);
return true;
}
/*
* Returns true when handled at EL2, false when the host must stop scheduling
* the vCPU.
*/
static bool pvm_psci_vcpu_off(struct pkvm_hyp_vcpu *hyp_vcpu)
{
/* No other writer runs while this vCPU is ON and executing. */
WARN_ON(READ_ONCE(hyp_vcpu->power_state) != PSCI_0_2_AFFINITY_LEVEL_ON);
WRITE_ONCE(hyp_vcpu->vcpu.arch.reset_state.reset, false);
/*
* Orders pkvm_reset_vcpu()'s accesses to reset_state, and the clear
* above, before OFF. Pairs with the control dependency in
* pvm_psci_vcpu_on().
*/
smp_store_release(&hyp_vcpu->power_state, PSCI_0_2_AFFINITY_LEVEL_OFF);
/* Return to the host so that it can finish powering off the vcpu. */
return false;
}
static bool pvm_psci_version(struct pkvm_hyp_vcpu *hyp_vcpu)
{
/* Nothing to be handled by the host. Go back to the guest. */
smccc_set_retval(&hyp_vcpu->vcpu, KVM_ARM_PSCI_1_1, 0, 0, 0);
return true;
}
static bool pvm_psci_features(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u32 feature = smccc_get_arg1(vcpu);
unsigned long val;
switch (feature) {
case PSCI_0_2_FN_PSCI_VERSION:
case PSCI_0_2_FN_CPU_SUSPEND:
case PSCI_0_2_FN64_CPU_SUSPEND:
case PSCI_0_2_FN_CPU_OFF:
case PSCI_0_2_FN_CPU_ON:
case PSCI_0_2_FN64_CPU_ON:
case PSCI_0_2_FN_AFFINITY_INFO:
case PSCI_0_2_FN64_AFFINITY_INFO:
case PSCI_0_2_FN_SYSTEM_OFF:
case PSCI_0_2_FN_SYSTEM_RESET:
case PSCI_1_0_FN_PSCI_FEATURES:
case PSCI_1_1_FN_SYSTEM_RESET2:
case PSCI_1_1_FN64_SYSTEM_RESET2:
case ARM_SMCCC_VERSION_FUNC_ID:
val = PSCI_RET_SUCCESS;
break;
default:
val = PSCI_RET_NOT_SUPPORTED;
break;
}
/* Nothing to be handled by the host. Go back to the guest. */
smccc_set_retval(vcpu, val, 0, 0, 0);
return true;
}
static bool pkvm_handle_psci(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu;
u32 psci_fn = smccc_get_function(vcpu);
switch (psci_fn) {
case PSCI_0_2_FN_CPU_ON:
kvm_psci_narrow_to_32bit(vcpu);
fallthrough;
case PSCI_0_2_FN64_CPU_ON:
return pvm_psci_vcpu_on(hyp_vcpu);
case PSCI_0_2_FN_CPU_OFF:
return pvm_psci_vcpu_off(hyp_vcpu);
case PSCI_0_2_FN_PSCI_VERSION:
return pvm_psci_version(hyp_vcpu);
case PSCI_1_0_FN_PSCI_FEATURES:
return pvm_psci_features(hyp_vcpu);
case PSCI_0_2_FN_AFFINITY_INFO:
case PSCI_0_2_FN64_AFFINITY_INFO:
case PSCI_0_2_FN_SYSTEM_RESET:
case PSCI_0_2_FN_CPU_SUSPEND:
case PSCI_0_2_FN64_CPU_SUSPEND:
case PSCI_0_2_FN_SYSTEM_OFF:
case PSCI_1_1_FN_SYSTEM_RESET2:
case PSCI_1_1_FN64_SYSTEM_RESET2:
return false; /* Handled by the host. */
default:
/* Unknown PSCI calls are handled here, not forwarded. */
smccc_set_retval(vcpu, PSCI_RET_NOT_SUPPORTED, 0, 0, 0);
return true;
}
}
/*
* Handler for protected VM HVC calls.
*
@ -1134,10 +1448,73 @@ static void pkvm_memunshare_call(u64 *ret, struct kvm_vcpu *vcpu)
*/
bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
{
struct pkvm_hyp_vcpu *hyp_vcpu = container_of(vcpu, struct pkvm_hyp_vcpu, vcpu);
u64 val[4] = { SMCCC_RET_INVALID_PARAMETER };
bool handled = true;
u32 feature;
uuid_t uuid;
switch (smccc_get_function(vcpu)) {
case ARM_SMCCC_VERSION_FUNC_ID:
/* Nothing to be handled by the host. Go back to the guest. */
val[0] = ARM_SMCCC_VERSION_1_1;
break;
case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
/* The workaround calls themselves are handled on hyp entry. */
val[0] = SMCCC_RET_NOT_SUPPORTED;
feature = smccc_get_arg1(vcpu);
switch (feature) {
case ARM_SMCCC_VERSION_FUNC_ID:
case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
val[0] = SMCCC_RET_SUCCESS;
break;
case ARM_SMCCC_ARCH_WORKAROUND_1:
switch (spectre_v2_state) {
case SPECTRE_VULNERABLE:
break;
case SPECTRE_MITIGATED:
val[0] = SMCCC_RET_SUCCESS;
break;
case SPECTRE_UNAFFECTED:
val[0] = SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED;
break;
}
break;
case ARM_SMCCC_ARCH_WORKAROUND_2:
switch (spectre_v4_state) {
case SPECTRE_VULNERABLE:
break;
case SPECTRE_MITIGATED:
/* With SSBS advertised the guest's default is safe. */
if (kvm_has_feat(vcpu->kvm, ID_AA64PFR1_EL1, SSBS, IMP))
break;
fallthrough;
case SPECTRE_UNAFFECTED:
val[0] = SMCCC_RET_NOT_REQUIRED;
break;
}
break;
case ARM_SMCCC_ARCH_WORKAROUND_3:
switch (spectre_bhb_state) {
case SPECTRE_VULNERABLE:
break;
case SPECTRE_MITIGATED:
val[0] = SMCCC_RET_SUCCESS;
break;
case SPECTRE_UNAFFECTED:
val[0] = SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED;
break;
}
break;
}
break;
case ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID:
uuid = ARM_SMCCC_VENDOR_HYP_UID_KVM;
val[0] = smccc_uuid_to_reg(&uuid, 0);
val[1] = smccc_uuid_to_reg(&uuid, 1);
val[2] = smccc_uuid_to_reg(&uuid, 2);
val[3] = smccc_uuid_to_reg(&uuid, 3);
break;
case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO);
@ -1170,8 +1547,7 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
pkvm_memunshare_call(val, vcpu);
break;
default:
/* Punt everything else back to the host, for now. */
handled = false;
return pkvm_handle_psci(hyp_vcpu);
}
if (handled)

View File

@ -203,15 +203,36 @@ static const exit_handler_fn hyp_exit_handlers[] = {
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
};
/* WFI/WFE exit to the host, which emulates them. WFxT is not advertised. */
static bool kvm_handle_pvm_wfx(struct kvm_vcpu *vcpu, u64 *exit_code)
{
if (kvm_vcpu_get_esr(vcpu) & ESR_ELx_WFx_ISS_WFxT)
return kvm_handle_pvm_restricted(vcpu, exit_code);
return false;
}
/* Lazy FP/SIMD switch, or the UNDEF that would otherwise be left to the host. */
static bool kvm_handle_pvm_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code)
{
if (kvm_hyp_handle_fpsimd(vcpu, exit_code))
return true;
return kvm_handle_pvm_restricted(vcpu, exit_code);
}
/*
* A class not listed takes an UNDEF at EL2: the host has no way to
* inject one into a protected vCPU.
*/
static const exit_handler_fn pvm_exit_handlers[] = {
[0 ... ESR_ELx_EC_MAX] = NULL,
[0 ... ESR_ELx_EC_MAX] = kvm_handle_pvm_restricted,
[ESR_ELx_EC_WFx] = kvm_handle_pvm_wfx,
[ESR_ELx_EC_HVC64] = kvm_handle_pvm_hvc64,
[ESR_ELx_EC_SYS64] = kvm_handle_pvm_sys64,
[ESR_ELx_EC_SVE] = kvm_handle_pvm_restricted,
[ESR_ELx_EC_FP_ASIMD] = kvm_hyp_handle_fpsimd,
[ESR_ELx_EC_FP_ASIMD] = kvm_handle_pvm_fpsimd,
[ESR_ELx_EC_IABT_LOW] = kvm_hyp_handle_iabt_low,
[ESR_ELx_EC_DABT_LOW] = kvm_hyp_handle_dabt_low,
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
};

View File

@ -528,6 +528,66 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = {
/* Performance Monitoring Registers are restricted. */
};
struct sys_reg_desc_reset {
int reg;
void (*reset)(struct kvm_vcpu *vcpu, const struct sys_reg_desc_reset *rd);
u64 value;
};
static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc_reset *r)
{
__vcpu_assign_sys_reg(vcpu, r->reg, kvm_calculate_mpidr(vcpu));
}
static void reset_value(struct kvm_vcpu *vcpu, const struct sys_reg_desc_reset *r)
{
__vcpu_assign_sys_reg(vcpu, r->reg, r->value);
}
#define RESET_VAL(REG, VAL) { REG, reset_value, VAL }
#define RESET_ZERO(REG) RESET_VAL(REG, 0)
#define RESET_UNKNOWN(REG) RESET_VAL(REG, 0x1de7ec7edbadc0deULL)
#define RESET_FUNC(REG, FN) { REG, FN, 0 }
static const struct sys_reg_desc_reset pvm_sys_reg_reset_vals[] = {
RESET_FUNC(MPIDR_EL1, reset_mpidr),
RESET_UNKNOWN(TPIDR_EL0),
RESET_UNKNOWN(TPIDRRO_EL0),
RESET_UNKNOWN(TPIDR_EL1),
RESET_ZERO(CNTKCTL_EL1),
RESET_UNKNOWN(PAR_EL1),
RESET_ZERO(DISR_EL1),
RESET_ZERO(CPACR_EL1),
RESET_VAL(CONTEXTIDR_EL1, 0x00000000dbadc0deULL),
RESET_VAL(SCTLR_EL1, 0x00C50078ULL),
RESET_ZERO(TCR_EL1),
RESET_UNKNOWN(AFSR0_EL1),
RESET_UNKNOWN(AFSR1_EL1),
RESET_UNKNOWN(ESR_EL1),
RESET_UNKNOWN(MAIR_EL1),
RESET_ZERO(AMAIR_EL1),
RESET_ZERO(MDSCR_EL1),
RESET_ZERO(CNTV_CTL_EL0),
RESET_UNKNOWN(TTBR0_EL1),
RESET_UNKNOWN(TTBR1_EL1),
RESET_UNKNOWN(FAR_EL1),
RESET_VAL(VBAR_EL1, 0x1de7ec7edbadc000ULL),
};
void kvm_reset_pvm_sys_regs(struct kvm_vcpu *vcpu)
{
unsigned long i;
for (i = 0; i < ARRAY_SIZE(pvm_sys_reg_reset_vals); i++) {
const struct sys_reg_desc_reset *r = &pvm_sys_reg_reset_vals[i];
r->reset(vcpu, r);
}
}
/*
* Initializes feature registers for protected vms.
*/

View File

@ -585,6 +585,13 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
wants_02 = vcpu_has_feature(vcpu, KVM_ARM_VCPU_PSCI_0_2);
/*
* EL2 advertises PSCI 1.1; the host handles the forwarded calls
* by this version.
*/
if (vcpu_is_protected(vcpu) && val < KVM_ARM_PSCI_1_1)
return -EINVAL;
switch (val) {
case KVM_ARM_PSCI_0_1:
if (wants_02)

View File

@ -371,15 +371,15 @@ int kvm_inject_serror_esr(struct kvm_vcpu *vcpu, u64 esr)
}
/*
* Emulate the exception entry if SErrors are unmasked. This is useful if
* the vCPU is in a nested context w/ vSErrors enabled then we've already
* delegated he hardware vSError context (i.e. HCR_EL2.VSE, VSESR_EL2,
* VDISR_EL2) to the guest hypervisor.
* With NV, emulate the exception entry if SErrors are unmasked: in a
* nested context with vSErrors enabled, the hardware vSError context
* (i.e. HCR_EL2.VSE, VSESR_EL2, VDISR_EL2) is delegated to the guest
* hypervisor. Otherwise, HCR_EL2.VSE delivers it once it is unmasked.
*
* As we're emulating the SError injection we need to explicitly populate
* ESR_ELx.EC because hardware will not do it on our behalf.
*/
if (!serror_is_masked(vcpu)) {
if (vcpu_has_nv(vcpu) && !serror_is_masked(vcpu)) {
pend_serror_exception(vcpu);
esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SERROR) | ESR_ELx_IL;
vcpu_write_sys_reg(vcpu, esr, exception_esr_elx(vcpu));

View File

@ -118,12 +118,25 @@ static int __pkvm_create_hyp_vcpu(struct kvm_vcpu *vcpu)
return -ENOMEM;
ret = kvm_call_hyp_nvhe(__pkvm_init_vcpu, handle, vcpu, hyp_vcpu);
if (!ret)
vcpu_set_flag(vcpu, VCPU_PKVM_FINALIZED);
else
if (ret) {
free_pages_exact(hyp_vcpu, hyp_vcpu_sz);
return ret;
}
return ret;
/*
* Mirror EL2's seeding of power_state from mp_state. The hyp vCPU is
* published, so take mp_state_lock against kvm_psci_vcpu_on().
*/
if (vcpu_is_protected(vcpu)) {
spin_lock(&vcpu->arch.mp_state_lock);
if (kvm_arm_vcpu_stopped(vcpu))
WRITE_ONCE(vcpu->arch.mp_state.mp_state, KVM_MP_STATE_UNINITIALIZED);
spin_unlock(&vcpu->arch.mp_state_lock);
}
vcpu_set_flag(vcpu, VCPU_PKVM_FINALIZED);
return 0;
}
/*

View File

@ -63,7 +63,12 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
return PSCI_RET_INVALID_PARAMS;
spin_lock(&vcpu->arch.mp_state_lock);
if (!kvm_arm_vcpu_stopped(vcpu)) {
/*
* A protected vCPU is off once the host has acted on EL2's CPU_OFF.
* STOPPED before that is the VMM's pause.
*/
if (!kvm_arm_vcpu_stopped(vcpu) ||
(vcpu_is_protected(vcpu) && !kvm_pkvm_vcpu_is_powered_off(vcpu))) {
if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1)
ret = PSCI_RET_ALREADY_ON;
else
@ -161,7 +166,9 @@ static void kvm_prepare_system_event(struct kvm_vcpu *vcpu, u32 type, u64 flags)
*/
kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
spin_lock(&tmp->arch.mp_state_lock);
WRITE_ONCE(tmp->arch.mp_state.mp_state, KVM_MP_STATE_STOPPED);
/* Keep EL2's off record for the guest's next CPU_ON. */
if (!kvm_pkvm_vcpu_is_powered_off(tmp))
WRITE_ONCE(tmp->arch.mp_state.mp_state, KVM_MP_STATE_STOPPED);
spin_unlock(&tmp->arch.mp_state_lock);
}
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);

View File

@ -67,9 +67,9 @@ gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu)
return base;
}
bool kvm_arm_pvtime_supported(void)
bool kvm_arm_pvtime_supported(struct kvm *kvm)
{
return !!sched_info_on();
return !!sched_info_on() && (!kvm || !kvm_vm_is_protected(kvm));
}
int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
@ -81,7 +81,7 @@ int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
int ret = 0;
int idx;
if (!kvm_arm_pvtime_supported() ||
if (!kvm_arm_pvtime_supported(kvm) ||
attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
return -ENXIO;
@ -110,7 +110,7 @@ int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
u64 __user *user = (u64 __user *)attr->addr;
u64 ipa;
if (!kvm_arm_pvtime_supported() ||
if (!kvm_arm_pvtime_supported(vcpu->kvm) ||
attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
return -ENXIO;
@ -126,7 +126,7 @@ int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,
{
switch (attr->attr) {
case KVM_ARM_VCPU_PVTIME_IPA:
if (kvm_arm_pvtime_supported())
if (kvm_arm_pvtime_supported(vcpu->kvm))
return 0;
}
return -ENXIO;