Skip to content

Commit 812b059

Browse files
kelleymhbp3tk0v
authored andcommitted
x86/hyperv: Change vTOM handling to use standard coco mechanisms
Hyper-V guests on AMD SEV-SNP hardware have the option of using the "virtual Top Of Memory" (vTOM) feature specified by the SEV-SNP architecture. With vTOM, shared vs. private memory accesses are controlled by splitting the guest physical address space into two halves. vTOM is the dividing line where the uppermost bit of the physical address space is set; e.g., with 47 bits of guest physical address space, vTOM is 0x400000000000 (bit 46 is set). Guest physical memory is accessible at two parallel physical addresses -- one below vTOM and one above vTOM. Accesses below vTOM are private (encrypted) while accesses above vTOM are shared (decrypted). In this sense, vTOM is like the GPA.SHARED bit in Intel TDX. Support for Hyper-V guests using vTOM was added to the Linux kernel in two patch sets[1][2]. This support treats the vTOM bit as part of the physical address. For accessing shared (decrypted) memory, these patch sets create a second kernel virtual mapping that maps to physical addresses above vTOM. A better approach is to treat the vTOM bit as a protection flag, not as part of the physical address. This new approach is like the approach for the GPA.SHARED bit in Intel TDX. Rather than creating a second kernel virtual mapping, the existing mapping is updated using recently added coco mechanisms. When memory is changed between private and shared using set_memory_decrypted() and set_memory_encrypted(), the PTEs for the existing kernel mapping are changed to add or remove the vTOM bit in the guest physical address, just as with TDX. The hypercalls to change the memory status on the host side are made using the existing callback mechanism. Everything just works, with a minor tweak to map the IO-APIC to use private accesses. To accomplish the switch in approach, the following must be done: * Update Hyper-V initialization to set the cc_mask based on vTOM and do other coco initialization. * Update physical_mask so the vTOM bit is no longer treated as part of the physical address * Remove CC_VENDOR_HYPERV and merge the associated vTOM functionality under CC_VENDOR_AMD. Update cc_mkenc() and cc_mkdec() to set/clear the vTOM bit as a protection flag. * Code already exists to make hypercalls to inform Hyper-V about pages changing between shared and private. Update this code to run as a callback from __set_memory_enc_pgtable(). * Remove the Hyper-V special case from __set_memory_enc_dec() * Remove the Hyper-V specific call to swiotlb_update_mem_attributes() since mem_encrypt_init() will now do it. * Add a Hyper-V specific implementation of the is_private_mmio() callback that returns true for the IO-APIC and vTPM MMIO addresses [1] https://lore.kernel.org/all/[email protected]/ [2] https://lore.kernel.org/all/[email protected]/ [ bp: Touchups. ] Signed-off-by: Michael Kelley <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent e45e761 commit 812b059

File tree

10 files changed

+111
-51
lines changed

10 files changed

+111
-51
lines changed

arch/x86/coco/core.c

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ static bool intel_cc_platform_has(enum cc_attr attr)
2929
}
3030
}
3131

32+
/*
33+
* Handle the SEV-SNP vTOM case where sme_me_mask is zero, and
34+
* the other levels of SME/SEV functionality, including C-bit
35+
* based SEV-SNP, are not enabled.
36+
*/
37+
static __maybe_unused bool amd_cc_platform_vtom(enum cc_attr attr)
38+
{
39+
switch (attr) {
40+
case CC_ATTR_GUEST_MEM_ENCRYPT:
41+
case CC_ATTR_MEM_ENCRYPT:
42+
return true;
43+
default:
44+
return false;
45+
}
46+
}
47+
3248
/*
3349
* SME and SEV are very similar but they are not the same, so there are
3450
* times that the kernel will need to distinguish between SME and SEV. The
@@ -41,9 +57,14 @@ static bool intel_cc_platform_has(enum cc_attr attr)
4157
* up under SME the trampoline area cannot be encrypted, whereas under SEV
4258
* the trampoline area must be encrypted.
4359
*/
60+
4461
static bool amd_cc_platform_has(enum cc_attr attr)
4562
{
4663
#ifdef CONFIG_AMD_MEM_ENCRYPT
64+
65+
if (sev_status & MSR_AMD64_SNP_VTOM)
66+
return amd_cc_platform_vtom(attr);
67+
4768
switch (attr) {
4869
case CC_ATTR_MEM_ENCRYPT:
4970
return sme_me_mask;
@@ -76,20 +97,13 @@ static bool amd_cc_platform_has(enum cc_attr attr)
7697
#endif
7798
}
7899

79-
static bool hyperv_cc_platform_has(enum cc_attr attr)
80-
{
81-
return attr == CC_ATTR_GUEST_MEM_ENCRYPT;
82-
}
83-
84100
bool cc_platform_has(enum cc_attr attr)
85101
{
86102
switch (vendor) {
87103
case CC_VENDOR_AMD:
88104
return amd_cc_platform_has(attr);
89105
case CC_VENDOR_INTEL:
90106
return intel_cc_platform_has(attr);
91-
case CC_VENDOR_HYPERV:
92-
return hyperv_cc_platform_has(attr);
93107
default:
94108
return false;
95109
}
@@ -103,11 +117,14 @@ u64 cc_mkenc(u64 val)
103117
* encryption status of the page.
104118
*
105119
* - for AMD, bit *set* means the page is encrypted
106-
* - for Intel *clear* means encrypted.
120+
* - for AMD with vTOM and for Intel, *clear* means encrypted
107121
*/
108122
switch (vendor) {
109123
case CC_VENDOR_AMD:
110-
return val | cc_mask;
124+
if (sev_status & MSR_AMD64_SNP_VTOM)
125+
return val & ~cc_mask;
126+
else
127+
return val | cc_mask;
111128
case CC_VENDOR_INTEL:
112129
return val & ~cc_mask;
113130
default:
@@ -120,7 +137,10 @@ u64 cc_mkdec(u64 val)
120137
/* See comment in cc_mkenc() */
121138
switch (vendor) {
122139
case CC_VENDOR_AMD:
123-
return val & ~cc_mask;
140+
if (sev_status & MSR_AMD64_SNP_VTOM)
141+
return val | cc_mask;
142+
else
143+
return val & ~cc_mask;
124144
case CC_VENDOR_INTEL:
125145
return val | cc_mask;
126146
default:

arch/x86/hyperv/hv_init.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include <linux/syscore_ops.h>
3030
#include <clocksource/hyperv_timer.h>
3131
#include <linux/highmem.h>
32-
#include <linux/swiotlb.h>
3332

3433
int hyperv_init_cpuhp;
3534
u64 hv_current_partition_id = ~0ull;
@@ -504,16 +503,6 @@ void __init hyperv_init(void)
504503
/* Query the VMs extended capability once, so that it can be cached. */
505504
hv_query_ext_cap(0);
506505

507-
#ifdef CONFIG_SWIOTLB
508-
/*
509-
* Swiotlb bounce buffer needs to be mapped in extra address
510-
* space. Map function doesn't work in the early place and so
511-
* call swiotlb_update_mem_attributes() here.
512-
*/
513-
if (hv_is_isolation_supported())
514-
swiotlb_update_mem_attributes();
515-
#endif
516-
517506
return;
518507

519508
clean_guest_os_id:

arch/x86/hyperv/ivm.c

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <asm/svm.h>
1414
#include <asm/sev.h>
1515
#include <asm/io.h>
16+
#include <asm/coco.h>
17+
#include <asm/mem_encrypt.h>
1618
#include <asm/mshyperv.h>
1719
#include <asm/hypervisor.h>
1820

@@ -233,7 +235,6 @@ void hv_ghcb_msr_read(u64 msr, u64 *value)
233235
local_irq_restore(flags);
234236
}
235237
EXPORT_SYMBOL_GPL(hv_ghcb_msr_read);
236-
#endif
237238

238239
/*
239240
* hv_mark_gpa_visibility - Set pages visible to host via hvcall.
@@ -286,27 +287,25 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
286287
}
287288

288289
/*
289-
* hv_set_mem_host_visibility - Set specified memory visible to host.
290+
* hv_vtom_set_host_visibility - Set specified memory visible to host.
290291
*
291292
* In Isolation VM, all guest memory is encrypted from host and guest
292293
* needs to set memory visible to host via hvcall before sharing memory
293294
* with host. This function works as wrap of hv_mark_gpa_visibility()
294295
* with memory base and size.
295296
*/
296-
int hv_set_mem_host_visibility(unsigned long kbuffer, int pagecount, bool visible)
297+
static bool hv_vtom_set_host_visibility(unsigned long kbuffer, int pagecount, bool enc)
297298
{
298-
enum hv_mem_host_visibility visibility = visible ?
299-
VMBUS_PAGE_VISIBLE_READ_WRITE : VMBUS_PAGE_NOT_VISIBLE;
299+
enum hv_mem_host_visibility visibility = enc ?
300+
VMBUS_PAGE_NOT_VISIBLE : VMBUS_PAGE_VISIBLE_READ_WRITE;
300301
u64 *pfn_array;
301302
int ret = 0;
303+
bool result = true;
302304
int i, pfn;
303305

304-
if (!hv_is_isolation_supported() || !hv_hypercall_pg)
305-
return 0;
306-
307306
pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
308307
if (!pfn_array)
309-
return -ENOMEM;
308+
return false;
310309

311310
for (i = 0, pfn = 0; i < pagecount; i++) {
312311
pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE);
@@ -315,17 +314,68 @@ int hv_set_mem_host_visibility(unsigned long kbuffer, int pagecount, bool visibl
315314
if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) {
316315
ret = hv_mark_gpa_visibility(pfn, pfn_array,
317316
visibility);
318-
if (ret)
317+
if (ret) {
318+
result = false;
319319
goto err_free_pfn_array;
320+
}
320321
pfn = 0;
321322
}
322323
}
323324

324325
err_free_pfn_array:
325326
kfree(pfn_array);
326-
return ret;
327+
return result;
327328
}
328329

330+
static bool hv_vtom_tlb_flush_required(bool private)
331+
{
332+
return true;
333+
}
334+
335+
static bool hv_vtom_cache_flush_required(void)
336+
{
337+
return false;
338+
}
339+
340+
static bool hv_is_private_mmio(u64 addr)
341+
{
342+
/*
343+
* Hyper-V always provides a single IO-APIC in a guest VM.
344+
* When a paravisor is used, it is emulated by the paravisor
345+
* in the guest context and must be mapped private.
346+
*/
347+
if (addr >= HV_IOAPIC_BASE_ADDRESS &&
348+
addr < (HV_IOAPIC_BASE_ADDRESS + PAGE_SIZE))
349+
return true;
350+
351+
/* Same with a vTPM */
352+
if (addr >= VTPM_BASE_ADDRESS &&
353+
addr < (VTPM_BASE_ADDRESS + PAGE_SIZE))
354+
return true;
355+
356+
return false;
357+
}
358+
359+
void __init hv_vtom_init(void)
360+
{
361+
/*
362+
* By design, a VM using vTOM doesn't see the SEV setting,
363+
* so SEV initialization is bypassed and sev_status isn't set.
364+
* Set it here to indicate a vTOM VM.
365+
*/
366+
sev_status = MSR_AMD64_SNP_VTOM;
367+
cc_set_vendor(CC_VENDOR_AMD);
368+
cc_set_mask(ms_hyperv.shared_gpa_boundary);
369+
physical_mask &= ms_hyperv.shared_gpa_boundary - 1;
370+
371+
x86_platform.hyper.is_private_mmio = hv_is_private_mmio;
372+
x86_platform.guest.enc_cache_flush_required = hv_vtom_cache_flush_required;
373+
x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
374+
x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
375+
}
376+
377+
#endif /* CONFIG_AMD_MEM_ENCRYPT */
378+
329379
/*
330380
* hv_map_memory - map memory to extra space in the AMD SEV-SNP Isolation VM.
331381
*/

arch/x86/include/asm/coco.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
enum cc_vendor {
88
CC_VENDOR_NONE,
99
CC_VENDOR_AMD,
10-
CC_VENDOR_HYPERV,
1110
CC_VENDOR_INTEL,
1211
};
1312

arch/x86/include/asm/mem_encrypt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ void __init sev_es_init_vc_handling(void);
5656
#else /* !CONFIG_AMD_MEM_ENCRYPT */
5757

5858
#define sme_me_mask 0ULL
59+
#define sev_status 0ULL
5960

6061
static inline void __init sme_early_encrypt(resource_size_t paddr,
6162
unsigned long size) { }

arch/x86/include/asm/mshyperv.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
#include <asm/paravirt.h>
1212
#include <asm/mshyperv.h>
1313

14+
/*
15+
* Hyper-V always provides a single IO-APIC at this MMIO address.
16+
* Ideally, the value should be looked up in ACPI tables, but it
17+
* is needed for mapping the IO-APIC early in boot on Confidential
18+
* VMs, before ACPI functions can be used.
19+
*/
20+
#define HV_IOAPIC_BASE_ADDRESS 0xfec00000
21+
1422
union hv_ghcb;
1523

1624
DECLARE_STATIC_KEY_FALSE(isolation_type_snp);
@@ -206,18 +214,19 @@ struct irq_domain *hv_create_pci_msi_domain(void);
206214
int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector,
207215
struct hv_interrupt_entry *entry);
208216
int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);
209-
int hv_set_mem_host_visibility(unsigned long addr, int numpages, bool visible);
210217

211218
#ifdef CONFIG_AMD_MEM_ENCRYPT
212219
void hv_ghcb_msr_write(u64 msr, u64 value);
213220
void hv_ghcb_msr_read(u64 msr, u64 *value);
214221
bool hv_ghcb_negotiate_protocol(void);
215222
void hv_ghcb_terminate(unsigned int set, unsigned int reason);
223+
void hv_vtom_init(void);
216224
#else
217225
static inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
218226
static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
219227
static inline bool hv_ghcb_negotiate_protocol(void) { return false; }
220228
static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}
229+
static inline void hv_vtom_init(void) {}
221230
#endif
222231

223232
extern bool hv_isolation_type_snp(void);
@@ -259,11 +268,6 @@ static inline void hv_set_register(unsigned int reg, u64 value) { }
259268
static inline u64 hv_get_register(unsigned int reg) { return 0; }
260269
static inline void hv_set_non_nested_register(unsigned int reg, u64 value) { }
261270
static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; }
262-
static inline int hv_set_mem_host_visibility(unsigned long addr, int numpages,
263-
bool visible)
264-
{
265-
return -1;
266-
}
267271
#endif /* CONFIG_HYPERV */
268272

269273

arch/x86/kernel/cpu/mshyperv.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
#include <asm/nmi.h>
3434
#include <clocksource/hyperv_timer.h>
3535
#include <asm/numa.h>
36-
#include <asm/coco.h>
3736

3837
/* Is Linux running as the root partition? */
3938
bool hv_root_partition;
@@ -397,8 +396,10 @@ static void __init ms_hyperv_init_platform(void)
397396
if (ms_hyperv.priv_high & HV_ISOLATION) {
398397
ms_hyperv.isolation_config_a = cpuid_eax(HYPERV_CPUID_ISOLATION_CONFIG);
399398
ms_hyperv.isolation_config_b = cpuid_ebx(HYPERV_CPUID_ISOLATION_CONFIG);
400-
ms_hyperv.shared_gpa_boundary =
401-
BIT_ULL(ms_hyperv.shared_gpa_boundary_bits);
399+
400+
if (ms_hyperv.shared_gpa_boundary_active)
401+
ms_hyperv.shared_gpa_boundary =
402+
BIT_ULL(ms_hyperv.shared_gpa_boundary_bits);
402403

403404
pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n",
404405
ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
@@ -409,11 +410,6 @@ static void __init ms_hyperv_init_platform(void)
409410
swiotlb_unencrypted_base = ms_hyperv.shared_gpa_boundary;
410411
#endif
411412
}
412-
/* Isolation VMs are unenlightened SEV-based VMs, thus this check: */
413-
if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT)) {
414-
if (hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE)
415-
cc_set_vendor(CC_VENDOR_HYPERV);
416-
}
417413
}
418414

419415
if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) {
@@ -482,6 +478,9 @@ static void __init ms_hyperv_init_platform(void)
482478
i8253_clear_counter_on_shutdown = false;
483479

484480
#if IS_ENABLED(CONFIG_HYPERV)
481+
if ((hv_get_isolation_type() == HV_ISOLATION_TYPE_VBS) ||
482+
(hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP))
483+
hv_vtom_init();
485484
/*
486485
* Setup the hook to get control post apic initialization.
487486
*/

arch/x86/mm/pat/set_memory.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,9 +2175,6 @@ static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc)
21752175

21762176
static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
21772177
{
2178-
if (hv_is_isolation_supported())
2179-
return hv_set_mem_host_visibility(addr, numpages, !enc);
2180-
21812178
if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
21822179
return __set_memory_enc_pgtable(addr, numpages, enc);
21832180

drivers/hv/vmbus_drv.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2156,7 +2156,6 @@ void vmbus_device_unregister(struct hv_device *device_obj)
21562156
* VMBUS is an acpi enumerated device. Get the information we
21572157
* need from DSDT.
21582158
*/
2159-
#define VTPM_BASE_ADDRESS 0xfed40000
21602159
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
21612160
{
21622161
resource_size_t start = 0;

include/asm-generic/mshyperv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include <asm/ptrace.h>
2727
#include <asm/hyperv-tlfs.h>
2828

29+
#define VTPM_BASE_ADDRESS 0xfed40000
30+
2931
struct ms_hyperv_info {
3032
u32 features;
3133
u32 priv_high;

0 commit comments

Comments
 (0)