Skip to content

Commit 810a521

Browse files
Tianyu Lanliuw
authored andcommitted
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 <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Signed-off-by: Tianyu Lan <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ wei: fix conflicts with tip ] Signed-off-by: Wei Liu <[email protected]>
1 parent af788f3 commit 810a521

File tree

7 files changed

+154
-7
lines changed

7 files changed

+154
-7
lines changed

arch/x86/hyperv/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# 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
33
obj-$(CONFIG_X86_64) += hv_apic.o hv_proc.o
44

55
ifdef CONFIG_X86_64

arch/x86/hyperv/hv_init.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,12 @@ EXPORT_SYMBOL_GPL(hv_get_isolation_type);
603603

604604
bool hv_is_isolation_supported(void)
605605
{
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+
606612
return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
607613
}
608614

arch/x86/hyperv/ivm.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Hyper-V Isolation VM interface with paravisor and hypervisor
4+
*
5+
* Author:
6+
* Tianyu Lan <[email protected]>
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+
}

arch/x86/include/asm/hyperv-tlfs.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,23 @@ enum hv_isolation_type {
276276
#define HV_X64_MSR_TIME_REF_COUNT HV_REGISTER_TIME_REF_COUNT
277277
#define HV_X64_MSR_REFERENCE_TSC HV_REGISTER_REFERENCE_TSC
278278

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+
279296
/*
280297
* Declare the MSR used to setup pages used to communicate with the hypervisor.
281298
*/

arch/x86/include/asm/mshyperv.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ struct irq_domain *hv_create_pci_msi_domain(void);
192192
int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector,
193193
struct hv_interrupt_entry *entry);
194194
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);
196196
#else /* CONFIG_HYPERV */
197197
static inline void hyperv_init(void) {}
198198
static inline void hyperv_setup_mmu_ops(void) {}
@@ -209,6 +209,11 @@ static inline int hyperv_flush_guest_mapping_range(u64 as,
209209
{
210210
return -1;
211211
}
212+
static inline int hv_set_mem_host_visibility(unsigned long addr, int numpages,
213+
bool visible)
214+
{
215+
return -1;
216+
}
212217
#endif /* CONFIG_HYPERV */
213218

214219

arch/x86/mm/pat/set_memory.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <asm/proto.h>
3131
#include <asm/memtype.h>
3232
#include <asm/set_memory.h>
33+
#include <asm/hyperv-tlfs.h>
34+
#include <asm/mshyperv.h>
3335

3436
#include "../mm_internal.h"
3537

@@ -1981,15 +1983,15 @@ int set_memory_global(unsigned long addr, int numpages)
19811983
__pgprot(_PAGE_GLOBAL), 0);
19821984
}
19831985

1984-
static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
1986+
/*
1987+
* __set_memory_enc_pgtable() is used for the hypervisors that get
1988+
* informed about "encryption" status via page tables.
1989+
*/
1990+
static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc)
19851991
{
19861992
struct cpa_data cpa;
19871993
int ret;
19881994

1989-
/* Nothing to do if memory encryption is not active */
1990-
if (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))
1991-
return 0;
1992-
19931995
/* Should not be working on unaligned addresses */
19941996
if (WARN_ONCE(addr & ~PAGE_MASK, "misaligned address: %#lx\n", addr))
19951997
addr &= PAGE_MASK;
@@ -2024,6 +2026,17 @@ static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
20242026
return ret;
20252027
}
20262028

2029+
static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
2030+
{
2031+
if (hv_is_isolation_supported())
2032+
return hv_set_mem_host_visibility(addr, numpages, !enc);
2033+
2034+
if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
2035+
return __set_memory_enc_pgtable(addr, numpages, enc);
2036+
2037+
return 0;
2038+
}
2039+
20272040
int set_memory_encrypted(unsigned long addr, int numpages)
20282041
{
20292042
return __set_memory_enc_dec(addr, numpages, true);

include/asm-generic/hyperv-tlfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ struct ms_hyperv_tsc_page {
158158
#define HVCALL_RETARGET_INTERRUPT 0x007e
159159
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
160160
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
161+
#define HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY 0x00db
161162

162163
/* Extended hypercalls */
163164
#define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001

0 commit comments

Comments
 (0)