ANDROID: KVM: arm64: Reset primary vCPU according to PVM firmware boot protocol

When a PVM firmware image is present for a protected VM, treat the first
running vCPU as the "primary" vCPU and reset its registers accordingly,
in particular by initialising its PC to enter the firmware at startup.

Signed-off-by: Will Deacon <will@kernel.org>
Bug: 209580772
Change-Id: I26676637145c7d809c5dc5ac0ad0e1fadaf275d2
Signed-off-by: Will Deacon <willdeacon@google.com>
This commit is contained in:
Will Deacon
2021-12-09 12:46:14 +00:00
committed by Will Deacon
parent adee75e0a4
commit 0ba692e7af
2 changed files with 31 additions and 2 deletions

View File

@@ -33,6 +33,9 @@ struct kvm_shadow_vm {
/* Pointers to the shadow vcpus of the shadow vm. */
struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
/* Primary vCPU pending entry to the pvmfw */
struct kvm_vcpu *pvmfw_entry_vcpu;
/* The host's kvm structure. */
struct kvm *host_kvm;

View File

@@ -391,6 +391,13 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, int nr
if (test_bit(KVM_ARM_VCPU_POWER_OFF, shadow_vcpu->arch.features)) {
shadow_vcpu->arch.pkvm.power_state = PSCI_0_2_AFFINITY_LEVEL_OFF;
} else if (pvm_has_pvmfw(vm)) {
if (vm->pvmfw_entry_vcpu)
return -EINVAL;
vm->pvmfw_entry_vcpu = shadow_vcpu;
shadow_vcpu->arch.reset_state.reset = true;
shadow_vcpu->arch.pkvm.power_state = PSCI_0_2_AFFINITY_LEVEL_ON_PENDING;
} else {
struct vcpu_reset_state *reset_state = &shadow_vcpu->arch.reset_state;
@@ -700,6 +707,7 @@ int pkvm_load_pvmfw_pages(struct kvm_shadow_vm *vm, u64 ipa, phys_addr_t phys,
void pkvm_reset_vcpu(struct kvm_vcpu *vcpu)
{
struct vcpu_reset_state *reset_state = &vcpu->arch.reset_state;
struct kvm_shadow_vm *vm = vcpu->arch.pkvm.shadow_vm;
WARN_ON(!reset_state->reset);
@@ -724,8 +732,26 @@ void pkvm_reset_vcpu(struct kvm_vcpu *vcpu)
if (reset_state->be)
kvm_vcpu_set_be(vcpu);
*vcpu_pc(vcpu) = reset_state->pc;
vcpu_set_reg(vcpu, 0, reset_state->r0);
if (vm->pvmfw_entry_vcpu == vcpu) {
struct kvm_vcpu *host_vcpu = vcpu->arch.pkvm.host_vcpu;
u64 entry = vm->arch.pkvm.pvmfw_load_addr;
int i;
/* X0 - X14 provided by the VMM (preserved) */
for (i = 0; i <= 14; ++i)
vcpu_set_reg(vcpu, i, vcpu_get_reg(host_vcpu, i));
/* X15: Boot protocol version */
vcpu_set_reg(vcpu, 15, 0);
/* PC: IPA of pvmfw base */
*vcpu_pc(vcpu) = entry;
vm->pvmfw_entry_vcpu = NULL;
} else {
*vcpu_pc(vcpu) = reset_state->pc;
vcpu_set_reg(vcpu, 0, reset_state->r0);
}
reset_state->reset = false;