Skip to content

Commit d9c57d3

Browse files
npigginmpe
authored andcommitted
KVM: PPC: Book3S HV Nested: Sanitise H_ENTER_NESTED TM state
The H_ENTER_NESTED hypercall is handled by the L0, and it is a request by the L1 to switch the context of the vCPU over to that of its L2 guest, and return with an interrupt indication. The L1 is responsible for switching some registers to guest context, and the L0 switches others (including all the hypervisor privileged state). If the L2 MSR has TM active, then the L1 is responsible for recheckpointing the L2 TM state. Then the L1 exits to L0 via the H_ENTER_NESTED hcall, and the L0 saves the TM state as part of the exit, and then it recheckpoints the TM state as part of the nested entry and finally HRFIDs into the L2 with TM active MSR. Not efficient, but about the simplest approach for something that's horrendously complicated. Problems arise if the L1 exits to the L0 with a TM state which does not match the L2 TM state being requested. For example if the L1 is transactional but the L2 MSR is non-transactional, or vice versa. The L0's HRFID can take a TM Bad Thing interrupt and crash. Fix this by disallowing H_ENTER_NESTED in TM[T] state entirely, and then ensuring that if the L1 is suspended then the L2 must have TM active, and if the L1 is not suspended then the L2 must not have TM active. Fixes: 360cae3 ("KVM: PPC: Book3S HV: Nested guest entry via hypercall") Cc: [email protected] # v4.20+ Reported-by: Alexey Kardashevskiy <[email protected]> Acked-by: Michael Neuling <[email protected]> Signed-off-by: Nicholas Piggin <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent f62f3c2 commit d9c57d3

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

arch/powerpc/kvm/book3s_hv_nested.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
302302
if (vcpu->kvm->arch.l1_ptcr == 0)
303303
return H_NOT_AVAILABLE;
304304

305+
if (MSR_TM_TRANSACTIONAL(vcpu->arch.shregs.msr))
306+
return H_BAD_MODE;
307+
305308
/* copy parameters in */
306309
hv_ptr = kvmppc_get_gpr(vcpu, 4);
307310
regs_ptr = kvmppc_get_gpr(vcpu, 5);
@@ -322,6 +325,23 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
322325
if (l2_hv.vcpu_token >= NR_CPUS)
323326
return H_PARAMETER;
324327

328+
/*
329+
* L1 must have set up a suspended state to enter the L2 in a
330+
* transactional state, and only in that case. These have to be
331+
* filtered out here to prevent causing a TM Bad Thing in the
332+
* host HRFID. We could synthesize a TM Bad Thing back to the L1
333+
* here but there doesn't seem like much point.
334+
*/
335+
if (MSR_TM_SUSPENDED(vcpu->arch.shregs.msr)) {
336+
if (!MSR_TM_ACTIVE(l2_regs.msr))
337+
return H_BAD_MODE;
338+
} else {
339+
if (l2_regs.msr & MSR_TS_MASK)
340+
return H_BAD_MODE;
341+
if (WARN_ON_ONCE(vcpu->arch.shregs.msr & MSR_TS_MASK))
342+
return H_BAD_MODE;
343+
}
344+
325345
/* translate lpid */
326346
l2 = kvmhv_get_nested(vcpu->kvm, l2_hv.lpid, true);
327347
if (!l2)

0 commit comments

Comments
 (0)