Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/hyperv: Add new hvcall guest address host visibility support

Add new hvcall guest address host visibility support to mark
memory visible to host. Call it inside set_memory_decrypted
/encrypted(). Add HYPERVISOR feature check in the
hv_is_isolation_supported() to optimize in non-virtualization
environment.

Acked-by: Dave Hansen <dave.hansen@intel.com>
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
Link: https://lore.kernel.org/r/20211025122116.264793-4-ltykernel@gmail.com
[ wei: fix conflicts with tip ]
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Tianyu Lan and committed by
Wei Liu
810a5212 af788f35

+154 -7
+1 -1
arch/x86/hyperv/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 - obj-y := hv_init.o mmu.o nested.o irqdomain.o 2 + obj-y := hv_init.o mmu.o nested.o irqdomain.o ivm.o 3 3 obj-$(CONFIG_X86_64) += hv_apic.o hv_proc.o 4 4 5 5 ifdef CONFIG_X86_64
+6
arch/x86/hyperv/hv_init.c
··· 603 603 604 604 bool hv_is_isolation_supported(void) 605 605 { 606 + if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) 607 + return false; 608 + 609 + if (!hypervisor_is_type(X86_HYPER_MS_HYPERV)) 610 + return false; 611 + 606 612 return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE; 607 613 } 608 614
+105
arch/x86/hyperv/ivm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Hyper-V Isolation VM interface with paravisor and hypervisor 4 + * 5 + * Author: 6 + * Tianyu Lan <Tianyu.Lan@microsoft.com> 7 + */ 8 + 9 + #include <linux/hyperv.h> 10 + #include <linux/types.h> 11 + #include <linux/bitfield.h> 12 + #include <linux/slab.h> 13 + #include <asm/io.h> 14 + #include <asm/mshyperv.h> 15 + 16 + /* 17 + * hv_mark_gpa_visibility - Set pages visible to host via hvcall. 18 + * 19 + * In Isolation VM, all guest memory is encrypted from host and guest 20 + * needs to set memory visible to host via hvcall before sharing memory 21 + * with host. 22 + */ 23 + static int hv_mark_gpa_visibility(u16 count, const u64 pfn[], 24 + enum hv_mem_host_visibility visibility) 25 + { 26 + struct hv_gpa_range_for_visibility **input_pcpu, *input; 27 + u16 pages_processed; 28 + u64 hv_status; 29 + unsigned long flags; 30 + 31 + /* no-op if partition isolation is not enabled */ 32 + if (!hv_is_isolation_supported()) 33 + return 0; 34 + 35 + if (count > HV_MAX_MODIFY_GPA_REP_COUNT) { 36 + pr_err("Hyper-V: GPA count:%d exceeds supported:%lu\n", count, 37 + HV_MAX_MODIFY_GPA_REP_COUNT); 38 + return -EINVAL; 39 + } 40 + 41 + local_irq_save(flags); 42 + input_pcpu = (struct hv_gpa_range_for_visibility **) 43 + this_cpu_ptr(hyperv_pcpu_input_arg); 44 + input = *input_pcpu; 45 + if (unlikely(!input)) { 46 + local_irq_restore(flags); 47 + return -EINVAL; 48 + } 49 + 50 + input->partition_id = HV_PARTITION_ID_SELF; 51 + input->host_visibility = visibility; 52 + input->reserved0 = 0; 53 + input->reserved1 = 0; 54 + memcpy((void *)input->gpa_page_list, pfn, count * sizeof(*pfn)); 55 + hv_status = hv_do_rep_hypercall( 56 + HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY, count, 57 + 0, input, &pages_processed); 58 + local_irq_restore(flags); 59 + 60 + if (hv_result_success(hv_status)) 61 + return 0; 62 + else 63 + return -EFAULT; 64 + } 65 + 66 + /* 67 + * hv_set_mem_host_visibility - Set specified memory visible to host. 68 + * 69 + * In Isolation VM, all guest memory is encrypted from host and guest 70 + * needs to set memory visible to host via hvcall before sharing memory 71 + * with host. This function works as wrap of hv_mark_gpa_visibility() 72 + * with memory base and size. 73 + */ 74 + int hv_set_mem_host_visibility(unsigned long kbuffer, int pagecount, bool visible) 75 + { 76 + enum hv_mem_host_visibility visibility = visible ? 77 + VMBUS_PAGE_VISIBLE_READ_WRITE : VMBUS_PAGE_NOT_VISIBLE; 78 + u64 *pfn_array; 79 + int ret = 0; 80 + int i, pfn; 81 + 82 + if (!hv_is_isolation_supported() || !hv_hypercall_pg) 83 + return 0; 84 + 85 + pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL); 86 + if (!pfn_array) 87 + return -ENOMEM; 88 + 89 + for (i = 0, pfn = 0; i < pagecount; i++) { 90 + pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE); 91 + pfn++; 92 + 93 + if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) { 94 + ret = hv_mark_gpa_visibility(pfn, pfn_array, 95 + visibility); 96 + if (ret) 97 + goto err_free_pfn_array; 98 + pfn = 0; 99 + } 100 + } 101 + 102 + err_free_pfn_array: 103 + kfree(pfn_array); 104 + return ret; 105 + }
+17
arch/x86/include/asm/hyperv-tlfs.h
··· 276 276 #define HV_X64_MSR_TIME_REF_COUNT HV_REGISTER_TIME_REF_COUNT 277 277 #define HV_X64_MSR_REFERENCE_TSC HV_REGISTER_REFERENCE_TSC 278 278 279 + /* Hyper-V memory host visibility */ 280 + enum hv_mem_host_visibility { 281 + VMBUS_PAGE_NOT_VISIBLE = 0, 282 + VMBUS_PAGE_VISIBLE_READ_ONLY = 1, 283 + VMBUS_PAGE_VISIBLE_READ_WRITE = 3 284 + }; 285 + 286 + /* HvCallModifySparseGpaPageHostVisibility hypercall */ 287 + #define HV_MAX_MODIFY_GPA_REP_COUNT ((PAGE_SIZE / sizeof(u64)) - 2) 288 + struct hv_gpa_range_for_visibility { 289 + u64 partition_id; 290 + u32 host_visibility:2; 291 + u32 reserved0:30; 292 + u32 reserved1; 293 + u64 gpa_page_list[HV_MAX_MODIFY_GPA_REP_COUNT]; 294 + } __packed; 295 + 279 296 /* 280 297 * Declare the MSR used to setup pages used to communicate with the hypervisor. 281 298 */
+6 -1
arch/x86/include/asm/mshyperv.h
··· 192 192 int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector, 193 193 struct hv_interrupt_entry *entry); 194 194 int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); 195 - 195 + int hv_set_mem_host_visibility(unsigned long addr, int numpages, bool visible); 196 196 #else /* CONFIG_HYPERV */ 197 197 static inline void hyperv_init(void) {} 198 198 static inline void hyperv_setup_mmu_ops(void) {} ··· 206 206 static inline int hyperv_flush_guest_mapping(u64 as) { return -1; } 207 207 static inline int hyperv_flush_guest_mapping_range(u64 as, 208 208 hyperv_fill_flush_list_func fill_func, void *data) 209 + { 210 + return -1; 211 + } 212 + static inline int hv_set_mem_host_visibility(unsigned long addr, int numpages, 213 + bool visible) 209 214 { 210 215 return -1; 211 216 }
+18 -5
arch/x86/mm/pat/set_memory.c
··· 30 30 #include <asm/proto.h> 31 31 #include <asm/memtype.h> 32 32 #include <asm/set_memory.h> 33 + #include <asm/hyperv-tlfs.h> 34 + #include <asm/mshyperv.h> 33 35 34 36 #include "../mm_internal.h" 35 37 ··· 1983 1981 __pgprot(_PAGE_GLOBAL), 0); 1984 1982 } 1985 1983 1986 - static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc) 1984 + /* 1985 + * __set_memory_enc_pgtable() is used for the hypervisors that get 1986 + * informed about "encryption" status via page tables. 1987 + */ 1988 + static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc) 1987 1989 { 1988 1990 struct cpa_data cpa; 1989 1991 int ret; 1990 - 1991 - /* Nothing to do if memory encryption is not active */ 1992 - if (!cc_platform_has(CC_ATTR_MEM_ENCRYPT)) 1993 - return 0; 1994 1992 1995 1993 /* Should not be working on unaligned addresses */ 1996 1994 if (WARN_ONCE(addr & ~PAGE_MASK, "misaligned address: %#lx\n", addr)) ··· 2024 2022 cpa_flush(&cpa, 0); 2025 2023 2026 2024 return ret; 2025 + } 2026 + 2027 + static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc) 2028 + { 2029 + if (hv_is_isolation_supported()) 2030 + return hv_set_mem_host_visibility(addr, numpages, !enc); 2031 + 2032 + if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) 2033 + return __set_memory_enc_pgtable(addr, numpages, enc); 2034 + 2035 + return 0; 2027 2036 } 2028 2037 2029 2038 int set_memory_encrypted(unsigned long addr, int numpages)
+1
include/asm-generic/hyperv-tlfs.h
··· 158 158 #define HVCALL_RETARGET_INTERRUPT 0x007e 159 159 #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af 160 160 #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0 161 + #define HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY 0x00db 161 162 162 163 /* Extended hypercalls */ 163 164 #define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001