[PATCH RFC] KVM: SVM: reduce guest MAXPHYADDR by one in case C-bit is a physical bit

From: Vitaly Kuznetsov
Date: Fri Oct 15 2021 - 11:05:37 EST


Several selftests (memslot_modification_stress_test, kvm_page_table_test,
dirty_log_perf_test,.. ) which rely on vm_get_max_gfn() started to fail
since commit ef4c9f4f65462 ("KVM: selftests: Fix 32-bit truncation of
vm_get_max_gfn()") on AMD EPYC 7401P:

./tools/testing/selftests/kvm/demand_paging_test
Testing guest mode: PA-bits:ANY, VA-bits:48, 4K pages
guest physical test memory offset: 0xffffbffff000
Finished creating vCPUs and starting uffd threads
Started all vCPUs
==== Test Assertion Failure ====
demand_paging_test.c:63: false
pid=47131 tid=47134 errno=0 - Success
1 0x000000000040281b: vcpu_worker at demand_paging_test.c:63
2 0x00007fb36716e431: ?? ??:0
3 0x00007fb36709c912: ?? ??:0
Invalid guest sync status: exit_reason=SHUTDOWN

The commit, however, seems to be correct, it just revealed an already
present issue. AMD CPUs which support SEV may have a reduced physical
address space, e.g. on AMD EPYC 7401P I see:

Address sizes: 43 bits physical, 48 bits virtual

The guest physical address space, however, is not reduced as stated in
commit e39f00f60ebd ("KVM: x86: Use kernel's x86_phys_bits to handle
reduced MAXPHYADDR"). This seems to be almost correct, however, APM has one
more clause (15.34.6):

Note that because guest physical addresses are always translated through
the nested page tables, the size of the guest physical address space is
not impacted by any physical address space reduction indicated in CPUID
8000_001F[EBX]. If the C-bit is a physical address bit however, the guest
physical address space is effectively reduced by 1 bit.

Implement the reduction.

Fixes: e39f00f60ebd (KVM: x86: Use kernel's x86_phys_bits to handle reduced MAXPHYADDR)
Signed-off-by: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx>
---
- RFC: I may have misdiagnosed the problem as I didn't dig to where exactly
the guest crashes.
---
arch/x86/kvm/cpuid.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 751aa85a3001..04ae280a0b66 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -923,13 +923,20 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
*
* If TDP is enabled but an explicit guest MAXPHYADDR is not
* provided, use the raw bare metal MAXPHYADDR as reductions to
- * the HPAs do not affect GPAs.
+ * the HPAs do not affect GPAs. The value, however, has to be
+ * reduced by 1 in case C-bit is a physical bit (APM section
+ * 15.34.6).
*/
- if (!tdp_enabled)
+ if (!tdp_enabled) {
g_phys_as = boot_cpu_data.x86_phys_bits;
- else if (!g_phys_as)
+ } else if (!g_phys_as) {
g_phys_as = phys_as;

+ if (kvm_cpu_cap_has(X86_FEATURE_SEV) &&
+ (cpuid_ebx(0x8000001f) & 0x3f) < g_phys_as)
+ g_phys_as -= 1;
+ }
+
entry->eax = g_phys_as | (virt_as << 8);
entry->edx = 0;
cpuid_entry_override(entry, CPUID_8000_0008_EBX);
--
2.31.1