Add support running nested Microsoft Hypervisor
Subsystems (5)
cover Add support running nested Microsoft Hypervisor
From: Jinank Jain <[email protected]>
This patch series plans to add support for running nested Microsoft Hypervisor. In case of nested Microsoft Hypervisor there are few privileged hypercalls which need to go L0 Hypervisor instead of L1 Hypervisor. This patches series basically identifies such hypercalls and replace them with nested hypercalls.
Jinank Jain (6):
mshv: Add support for detecting nested hypervisor
hv: Setup synic registers in case of nested root partition
hv: Set the correct EOM register in case of nested hypervisor
hv: Add an interface to do nested hypercalls
hv: Enable vmbus driver for nested root partition hv, mshv : Change interrupt vector for nested root partition
arch/x86/include/asm/hyperv-tlfs.h | 17 ++++++++- arch/x86/include/asm/idtentry.h | 2 ++ arch/x86/include/asm/irq_vectors.h | 6 ++++ arch/x86/include/asm/mshyperv.h | 42 ++++++++++++++++++++--- arch/x86/kernel/cpu/mshyperv.c | 22 ++++++++++++ arch/x86/kernel/idt.c | 9 +++++ drivers/hv/hv.c | 55 ++++++++++++++++++------------ drivers/hv/vmbus_drv.c | 5 +-- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 7 +++- 10 files changed, 137 insertions(+), 29 deletions(-)
Signature · 1 line
2.25.1
PATCH 1/6 mshv: Add support for detecting nested hypervisor
When Linux runs as a root partition for Microsoft Hypervisor. It is possible to detect if it is running as nested hypervisor using hints exposed by mshv. While at it expose a new variable called hv_nested which can be used later for making decisions specific to nested use case.
--- arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ include/asm-generic/mshyperv.h | 2 ++ 3 files changed, 12 insertions(+)
arch/x86/include/asm/hyperv-tlfs.h +3 -0
@@ -114,6 +114,9 @@/* Recommend using the newer ExProcessorMasks interface */ #define HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED BIT(11) +/* Indicates that the hypervisor is nested within a Hyper-V partition. */ +#define HV_X64_HYPERV_NESTED BIT(12) +/* Recommend using enlightened VMCS */ #define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED BIT(14)
arch/x86/kernel/cpu/mshyperv.c +7 -0
@@ -37,6 +37,8 @@/* Is Linux running as the root partition? */ bool hv_root_partition; +/* Is Linux running on nested Microsoft Hypervisor */ +bool hv_nested;struct ms_hyperv_info ms_hyperv;#if IS_ENABLED (CONFIG_HYPERV )
@@ -301,6 +303,11 @@ static void __init ms_hyperv_init_platform(void)pr_info ("Hyper-V: running as root partition\n" ); } +if (ms_hyperv.hints &HV_X64_HYPERV_NESTED ) { + hv_nested = true; +pr_info ("Hyper-V: Linux running on a nested hypervisor\n" ); + } +/* * Extract host information. */
include/asm-generic/mshyperv.h +2 -0
@@ -115,6 +115,8 @@ static inline u64 hv_generate_guest_id(u64 kernel_version)return guest_id; } +extern bool hv_nested; +/* Free the message slot and signal end-of-message if required */ static inline void vmbus_signal_eom (struct hv_message *msg,u32 old_msg_type) {
Signature · 1 line
2.25.1
PATCH 2/6 hv: Setup synic registers in case of nested root partition
In reply to Jinank JainChild partitions are free to allocate SynIC message and event page but in case of root partition it must use the pages allocated by Microsoft Hypervisor (MSHV). Base address for these pages can be found using synthetic MSRs exposed by MSHV. There is a slight difference in those MSRs for nested vs non-nested root partition.
--- arch/x86/include/asm/hyperv-tlfs.h | 11 ++++++ drivers/hv/hv.c | 55 ++++++++++++++++++------------ 2 files changed, 45 insertions(+), 21 deletions(-)
arch/x86/include/asm/hyperv-tlfs.h +11 -0
@@ -225,6 +225,17 @@ enum hv_isolation_type {#define HV_REGISTER_SINT14 0x4000009E#define HV_REGISTER_SINT15 0x4000009F +/* +* Define synthetic interrupt controller model specific registers for +* nested hypervisor. +*/ +#define HV_REGISTER_NESTED_SCONTROL 0x40001080 +#define HV_REGISTER_NESTED_SVERSION 0x40001081 +#define HV_REGISTER_NESTED_SIEFP 0x40001082 +#define HV_REGISTER_NESTED_SIMP 0x40001083 +#define HV_REGISTER_NESTED_EOM 0x40001084 +#define HV_REGISTER_NESTED_SINT0 0x40001090 +/* * Synthetic Timer MSRs. Four timers per vcpu. */
drivers/hv/hv.c +34 -21
142 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH 2/6] hv: Setup synic registers in case of nested root partition
Quoted 79 lines · Jinank Jain
Child partitions are free to allocate SynIC message and event page but in case of root partition it must use the pages allocated by Microsoft Hypervisor (MSHV). Base address for these pages can be found using synthetic MSRs exposed by MSHV. There is a slight difference in those MSRs for nested vs non-nested root partition.
Signed-off-by Jinank Jain <[email protected]>--- arch/x86/include/asm/hyperv-tlfs.h | 11 ++++++ drivers/hv/hv.c | 55 ++++++++++++++++++------------ 2 files changed, 45 insertions(+), 21 deletions(-)arch/x86/include/asm/hyperv-tlfs.h +11
-0@@ -225,6 +225,17 @@ enum hv_isolation_type {#define HV_REGISTER_SINT14 0x4000009E#define HV_REGISTER_SINT15 0x4000009F +/* +* Define synthetic interrupt controller model specific registers for +* nested hypervisor. +*/ +#define HV_REGISTER_NESTED_SCONTROL 0x40001080 +#define HV_REGISTER_NESTED_SVERSION 0x40001081 +#define HV_REGISTER_NESTED_SIEFP 0x40001082 +#define HV_REGISTER_NESTED_SIMP 0x40001083 +#define HV_REGISTER_NESTED_EOM 0x40001084 +#define HV_REGISTER_NESTED_SINT0 0x40001090 +/* * Synthetic Timer MSRs. Four timers per vcpu. */ drivers/hv/hv.c +16
-3@@ -25,6 +25,11 @@/* The one and only */ struct hv_context hv_context; +#define REG_SIMP (hv_nested ? HV_REGISTER_NESTED_SIMP : HV_REGISTER_SIMP) +#define REG_SIEFP (hv_nested ? HV_REGISTER_NESTED_SIEFP : HV_REGISTER_SIEFP) +#define REG_SCTRL (hv_nested ? HV_REGISTER_NESTED_SCONTROL : HV_REGISTER_SCONTROL) +#define REG_SINT0 (hv_nested ? HV_REGISTER_NESTED_SINT0 : HV_REGISTER_SINT0) +/* * hv_init - Main initialization routine. * @@ -147,7 +152,7 @@ int hv_synic_alloc(void)* Synic message and event pages are allocated by paravisor. * Skip these pages allocation here. */ -if (!hv_isolation_type_snp ()) { +if (!hv_isolation_type_snp () && !hv_root_partition) { hv_cpu->synic_message_page = (void *)get_zeroed_page (GFP_ATOMIC );if (hv_cpu->synic_message_page ==NULL ) {@@ -188,8 +193,16 @@ void hv_synic_free(void)struct hv_per_cpu_context *hv_cpu =per_cpu_ptr (hv_context.cpu_context, cpu); -free_page ((unsigned long )hv_cpu->synic_event_page); -free_page ((unsigned long )hv_cpu->synic_message_page); +if (hv_root_partition) { +if (hv_cpu->synic_event_page !=NULL ) +memunmap (hv_cpu->synic_event_page); + +if (hv_cpu->synic_message_page !=NULL ) +memunmap (hv_cpu->synic_message_page); + }else { +free_page ((unsigned long )hv_cpu->synic_event_page); +free_page ((unsigned long )hv_cpu->synic_message_page); + }free_page ((unsigned long )hv_cpu->post_msg_page); }
drivers/hv/hv.c void hv_synic_enable_regs(unsigned int cpu) · line 229
Quoted 5 lines
drivers/hv/hv.c +1
-1@@ -213,10 +226,10 @@ void hv_synic_enable_regs(unsigned int cpu)union hv_synic_scontrol sctrl;/* Setup the Synic's message page */ - simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP ); + simp.as_uint64 =hv_get_register (REG_SIMP );
To avoid all this code churn (here and in the next patch dealing with EOM), would it make sense to move the logic picking nested/non-nested register into hv_{get,set}_register() instead?
E.g. something like (untested, incomplete):
static inline u32 hv_get_nested_reg(u32 reg) {
switch (reg) {
HV_REGISTER_SIMP: return HV_REGISTER_NESTED_SIMP;
HV_REGISTER_NESTED_SVERSION: return HV_REGISTER_NESTED_SVERSION;
...
default: return reg;
}}
static inline u64 hv_get_register(unsigned int reg)
{
u64 value;
if (hv_nested)
reg = hv_get_nested_reg(reg);
if (hv_is_synic_reg(reg) && hv_isolation_type_snp())
hv_ghcb_msr_read(reg, &value);
else
rdmsrl(reg, value);
return value;
}static inline void hv_set_register(unsigned int reg, u64 value)
{
if (hv_nested)
reg = hv_get_nested_reg(reg);
if (hv_is_synic_reg(reg) && hv_isolation_type_snp()) {
hv_ghcb_msr_write(reg, value);
/* Write proxy bit via wrmsl instruction */
if (reg >= HV_REGISTER_SINT0 &&
reg <= HV_REGISTER_SINT15)
wrmsrl(reg, value | 1 << 20);
} else {
wrmsrl(reg, value);
}
}Quoted 106 lines
drivers/hv/hv.c +17
-17simp.simp_enabled =1 ; -if (hv_isolation_type_snp ()) { +if (hv_isolation_type_snp () || hv_root_partition) { hv_cpu->synic_message_page =memremap (simp.base_simp_gpa <<HV_HYP_PAGE_SHIFT ,HV_HYP_PAGE_SIZE ,MEMREMAP_WB );@@ -227,13 +240,13 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } -hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); +hv_set_register (REG_SIMP , simp.as_uint64);/* Setup the Synic's event page */ - siefp.as_uint64 =hv_get_register (HV_REGISTER_SIEFP ); + siefp.as_uint64 =hv_get_register (REG_SIEFP ); siefp.siefp_enabled =1 ; -if (hv_isolation_type_snp ()) { +if (hv_isolation_type_snp () || hv_root_partition) { hv_cpu->synic_event_page =memremap (siefp.base_siefp_gpa <<HV_HYP_PAGE_SHIFT ,HV_HYP_PAGE_SIZE ,MEMREMAP_WB );@@ -245,12 +258,12 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } -hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64); +hv_set_register (REG_SIEFP , siefp.as_uint64);/* Setup the shared SINT. */ if (vmbus_irq !=-1 )enable_percpu_irq (vmbus_irq,0 ); - shared_sint.as_uint64 =hv_get_register (HV_REGISTER_SINT0 + + shared_sint.as_uint64 =hv_get_register (REG_SINT0 +VMBUS_MESSAGE_SINT ); shared_sint.vector = vmbus_interrupt;@@ -266,14 +279,14 @@ void hv_synic_enable_regs(unsigned int cpu)#else shared_sint.auto_eoi =0 ;#endif -hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , +hv_set_register (REG_SINT0 +VMBUS_MESSAGE_SINT , shared_sint.as_uint64);/* Enable the global synic bit */ - sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL ); + sctrl.as_uint64 =hv_get_register (REG_SCTRL ); sctrl.enable =1 ; -hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +hv_set_register (REG_SCTRL , sctrl.as_uint64); }int hv_synic_init (unsigned int cpu)@@ -297,17 +310,17 @@ void hv_synic_disable_regs(unsigned int cpu)union hv_synic_siefp siefp;union hv_synic_scontrol sctrl; - shared_sint.as_uint64 =hv_get_register (HV_REGISTER_SINT0 + + shared_sint.as_uint64 =hv_get_register (REG_SINT0 +VMBUS_MESSAGE_SINT ); shared_sint.masked =1 ;/* Need to correctly cleanup in the case of SMP!!! */ /* Disable the interrupt */ -hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , +hv_set_register (REG_SINT0 +VMBUS_MESSAGE_SINT , shared_sint.as_uint64); - simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP ); + simp.as_uint64 =hv_get_register (REG_SIMP );/* * In Isolation VM, sim and sief pages are allocated by * paravisor. These pages also will be used by kdump @@ -320,9 +333,9 @@ void hv_synic_disable_regs(unsigned int cpu)else simp.base_simp_gpa =0 ; -hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); +hv_set_register (REG_SIMP , simp.as_uint64); - siefp.as_uint64 =hv_get_register (HV_REGISTER_SIEFP ); + siefp.as_uint64 =hv_get_register (REG_SIEFP ); siefp.siefp_enabled =0 ;if (hv_isolation_type_snp ())@@ -330,12 +343,12 @@ void hv_synic_disable_regs(unsigned int cpu)else siefp.base_siefp_gpa =0 ; -hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64); +hv_set_register (REG_SIEFP , siefp.as_uint64);/* Disable the global synic bit */ - sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL ); + sctrl.as_uint64 =hv_get_register (REG_SCTRL ); sctrl.enable =0 ; -hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +hv_set_register (REG_SCTRL , sctrl.as_uint64);if (vmbus_irq !=-1 )disable_percpu_irq (vmbus_irq);
Signature · 1 line
Vitaly
PATCH 3/6 hv: Set the correct EOM register in case of nested hypervisor
In reply to Jinank JainCurrently we are using the default EOM register value. But this needs to changes when running under nested MSHV setup.
--- include/asm-generic/mshyperv.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
include/asm-generic/mshyperv.h +3 -1
@@ -117,6 +117,8 @@ static inline u64 hv_generate_guest_id(u64 kernel_version)extern bool hv_nested; +#define REG_EOM (hv_nested ? HV_REGISTER_NESTED_EOM : HV_REGISTER_EOM) +/* Free the message slot and signal end-of-message if required */ static inline void vmbus_signal_eom (struct hv_message *msg,u32 old_msg_type) {
@@ -148,7 +150,7 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)* possibly deliver another msg from the * hypervisor */ -hv_set_register (HV_REGISTER_EOM ,0 ); +hv_set_register (REG_EOM ,0 ); } }
Signature · 1 line
2.25.1
PATCH 4/6 hv: Add an interface to do nested hypercalls
In reply to Jinank JainAccording to TLFS, in order to communicate to L0 hypervisor there needs to be an additional bit set in the control register. This communication is required to perform priviledged instructions which can only be performed by L0 hypervisor. An example of that could be setting up the VMBus infrastructure.
--- arch/x86/include/asm/hyperv-tlfs.h | 3 ++- arch/x86/include/asm/mshyperv.h | 42 +++++++++++++++++++++++++++--- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 1 + 4 files changed, 42 insertions(+), 5 deletions(-)
arch/x86/include/asm/hyperv-tlfs.h +2 -1
@@ -380,7 +380,8 @@ struct hv_nested_enlightenments_control { __u32 reserved:31; } features;struct { -__u32 reserved; +__u32 inter_partition_comm:1 ; +__u32 reserved:31 ; } hypercallControls; } __packed;
arch/x86/include/asm/mshyperv.h +38 -4
64 lines not on this page · raw message
include/asm-generic/hyperv-tlfs.h +1 -0
@@ -185,6 +185,7 @@ enum HV_GENERIC_SET_FORMAT {#define HV_HYPERCALL_VARHEAD_OFFSET 17#define HV_HYPERCALL_VARHEAD_MASK GENMASK_ULL(26, 17)#define HV_HYPERCALL_RSVD0_MASK GENMASK_ULL(31, 27) +#define HV_HYPERCALL_NESTED BIT(31)#define HV_HYPERCALL_REP_COMP_OFFSET 32#define HV_HYPERCALL_REP_COMP_1 BIT_ULL(32)#define HV_HYPERCALL_REP_COMP_MASK GENMASK_ULL(43, 32)
include/asm-generic/mshyperv.h +1 -0
@@ -53,6 +53,7 @@ extern void * __percpu *hyperv_pcpu_input_arg;extern void *__percpu *hyperv_pcpu_output_arg;extern u64 hv_do_hypercall (u64 control,void *inputaddr,void *outputaddr); +extern u64 hv_do_nested_hypercall (u64 control,void *inputaddr,void *outputaddr);extern u64 hv_do_fast_hypercall8 (u16 control,u64 input8);extern bool hv_isolation_type_snp (void );
Signature · 1 line
2.25.1
PATCH 5/6 hv: Enable vmbus driver for nested root partition
In reply to Jinank JainCurrently VMBus driver is not initialized for root partition but we need to enable the VMBus driver for nested root partition. This is required to expose VMBus devices to the L2 guest in the nested setup.
--- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hv/vmbus_drv.c +1 -1
@@ -2723,7 +2723,7 @@ static int __init hv_acpi_init(void)if (!hv_is_hyperv_initialized ())return -ENODEV ; -if (hv_root_partition) +if (hv_root_partition && !hv_nested)return 0 ;/*
Signature · 1 line
2.25.1
PATCH 6/6 hv, mshv : Change interrupt vector for nested root partition
In reply to Jinank JainTraditionally we have been using the HYPERVISOR_CALLBACK_VECTOR to relay the VMBus interrupt. But this does not work in case of nested hypervisor. Microsoft Hypervisor reserves 0x31 to 0x34 as the interrupt vector range for VMBus and thus we have to use one of the vectors from that range and setup the IDT accordingly.
--- arch/x86/include/asm/idtentry.h | 2 ++ arch/x86/include/asm/irq_vectors.h | 6 ++++++ arch/x86/kernel/cpu/mshyperv.c | 15 +++++++++++++++ arch/x86/kernel/idt.c | 9 +++++++++ drivers/hv/vmbus_drv.c | 3 ++- 5 files changed, 34 insertions(+), 1 deletion(-)
arch/x86/include/asm/idtentry.h +2 -0
@@ -686,6 +686,8 @@ DECLARE_IDTENTRY_SYSVEC(POSTED_INTR_NESTED_VECTOR, sysvec_kvm_posted_intr_nestedDECLARE_IDTENTRY_SYSVEC (HYPERVISOR_CALLBACK_VECTOR , sysvec_hyperv_callback);DECLARE_IDTENTRY_SYSVEC (HYPERV_REENLIGHTENMENT_VECTOR , sysvec_hyperv_reenlightenment);DECLARE_IDTENTRY_SYSVEC (HYPERV_STIMER0_VECTOR , sysvec_hyperv_stimer0); +DECLARE_IDTENTRY_SYSVEC (HYPERV_INTR_NESTED_VMBUS_VECTOR , + sysvec_hyperv_nested_vmbus_intr);#endif #if IS_ENABLED (CONFIG_ACRN_GUEST )
arch/x86/include/asm/irq_vectors.h +6 -0
@@ -102,6 +102,12 @@#if IS_ENABLED (CONFIG_HYPERV )#define HYPERV_REENLIGHTENMENT_VECTOR 0xee#define HYPERV_STIMER0_VECTOR 0xed +/* +* FIXME: Change this, once Microsoft Hypervisor changes its assumption +* around VMBus interrupt vector allocation for nested root partition. +* Or provides a better interface to detect this instead of hardcoding. +*/ +#define HYPERV_INTR_NESTED_VMBUS_VECTOR 0x31#endif #define LOCAL_TIMER_VECTOR 0xec
arch/x86/kernel/cpu/mshyperv.c +15 -0
@@ -61,6 +61,21 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)set_irq_regs (old_regs); } +DEFINE_IDTENTRY_SYSVEC (sysvec_hyperv_nested_vmbus_intr) +{ +struct pt_regs *old_regs =set_irq_regs (regs); + +inc_irq_stat (irq_hv_callback_count); + +if (vmbus_handler) +vmbus_handler (); + +if (ms_hyperv.hints &HV_DEPRECATING_AEOI_RECOMMENDED ) +ack_APIC_irq (); + +set_irq_regs (old_regs); +} +void hv_setup_vmbus_handler (void (*handler)(void )) { vmbus_handler = handler;
arch/x86/kernel/idt.c +9 -0
@@ -160,6 +160,15 @@ static const __initconst struct idt_data apic_idts[] = {# endif INTG (SPURIOUS_APIC_VECTOR , asm_sysvec_spurious_apic_interrupt),INTG (ERROR_APIC_VECTOR , asm_sysvec_error_interrupt), +#ifdef CONFIG_HYPERV +/* +* This is a hack because we cannot install this interrupt handler via alloc_intr_gate +* as it does not allow interrupt vector less than FIRST_SYSTEM_VECTORS. And hyperv +* does not want anything other than 0x31-0x34 as the interrupt vector for vmbus +* interrupt in case of nested setup. + */ + INTG(HYPERV_INTR_NESTED_VMBUS_VECTOR, asm_sysvec_hyperv_nested_vmbus_intr), +#endif #endif };
drivers/hv/vmbus_drv.c +2 -1
@@ -2746,7 +2746,8 @@ static int __init hv_acpi_init(void)* normal Linux IRQ mechanism is not used in this case. */ #ifdef HYPERVISOR_CALLBACK_VECTOR - vmbus_interrupt =HYPERVISOR_CALLBACK_VECTOR ; + vmbus_interrupt = hv_nested ?HYPERV_INTR_NESTED_VMBUS_VECTOR : +HYPERVISOR_CALLBACK_VECTOR ; vmbus_irq =-1 ;#endif
Signature · 1 line
2.25.1
Wei Liu
maintainerRe: [PATCH 6/6] hv, mshv : Change interrupt vector for nested root partition
Quoted 10 lines · Jinank Jain
Traditionally we have been using the
HYPERVISOR_CALLBACK_VECTORto relay the VMBus interrupt. But this does not work in case of nested hypervisor. Microsoft Hypervisor reserves0x31to0x34as the interrupt vector range for VMBus and thus we have to use one of the vectors from that range and setup the IDT accordingly.Signed-off-by Jinank Jain <[email protected]>--- arch/x86/include/asm/idtentry.h | 2 ++ arch/x86/include/asm/irq_vectors.h | 6 ++++++
[...]
Quoted 6 lines
#if
IS_ENABLED(CONFIG_ACRN_GUEST)arch/x86/include/asm/irq_vectors.h +0
-0
arch/x86/include/asm/irq_vectors.h line 110
Quoted 9 lines
arch/x86/include/asm/irq_vectors.h +6
-0@@ -102,6 +102,12 @@#if IS_ENABLED (CONFIG_HYPERV )#define HYPERV_REENLIGHTENMENT_VECTOR 0xee#define HYPERV_STIMER0_VECTOR 0xed +/* +* FIXME: Change this, once Microsoft Hypervisor changes its assumption +* around VMBus interrupt vector allocation for nested root partition. +* Or provides a better interface to detect this instead of hardcoding. +*/ +#define HYPERV_INTR_NESTED_VMBUS_VECTOR 0x31
I would like to hear x86 maintainers opinion on this.
Thanks,
Wei.
v5 cover Add support running nested Microsoft Hypervisor
In reply to Jinank JainThis patch series plans to add support for running nested Microsoft Hypervisor. In case of nested Microsoft Hypervisor there are few privileged hypercalls which need to go L0 Hypervisor instead of L1 Hypervisor. This patches series basically identifies such hypercalls and replace them with nested hypercalls.
Jinank Jain (5):
x86/hyperv: Add support for detecting nested hypervisor
Drivers: hv: Setup synic registers in case of nested root partition x86/hyperv: Add an interface to do nested hypercalls
Drivers: hv: Enable vmbus driver for nested root partition x86/hyperv: Change interrupt vector for nested root partition
[v4]
- Fix ARM64 compilation
[v5]
- Fix comments from Michael Kelly
arch/arm64/hyperv/mshyperv.c | 6 +++ arch/x86/include/asm/hyperv-tlfs.h | 17 ++++++- arch/x86/include/asm/idtentry.h | 2 + arch/x86/include/asm/irq_vectors.h | 6 +++ arch/x86/include/asm/mshyperv.h | 68 ++++++++++++++++------------ arch/x86/kernel/cpu/mshyperv.c | 71 ++++++++++++++++++++++++++++++ arch/x86/kernel/idt.c | 9 ++++ drivers/hv/hv.c | 18 +++++--- drivers/hv/hv_common.c | 7 ++- drivers/hv/vmbus_drv.c | 5 ++- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 1 + 12 files changed, 173 insertions(+), 38 deletions(-)
Signature · 1 line
2.25.1
PATCH v5 1/5 x86/hyperv: Add support for detecting nested hypervisor
When Linux runs as a root partition for Microsoft Hypervisor. It is possible to detect if it is running as nested hypervisor using hints exposed by mshv. While at it expose a new variable called hv_nested which can be used later for making decisions specific to nested use case.
--- arch/arm64/hyperv/mshyperv.c | 6 ++++++ arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ drivers/hv/hv_common.c | 7 +++++-- include/asm-generic/mshyperv.h | 1 + 5 files changed, 22 insertions(+), 2 deletions(-)
arch/arm64/hyperv/mshyperv.c +6 -0
@@ -19,6 +19,9 @@static bool hyperv_initialized; +/* Is Linux running on nested Microsoft Hypervisor */ +bool hv_nested; +static int __init hyperv_init (void ) {struct hv_get_vp_registers_output result;
@@ -63,6 +66,9 @@ static int __init hyperv_init(void)pr_info ("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n" , b >>16 , b &0xFFFF , a, d &0xFFFFFF , c, d >>24 ); +/* ARM64 does not support nested virtualization */ + hv_nested = false; + ret =hv_common_init ();if (ret)return ret;
arch/x86/include/asm/hyperv-tlfs.h +3 -0
@@ -114,6 +114,9 @@/* Recommend using the newer ExProcessorMasks interface */ #define HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED BIT(11) +/* Indicates that the hypervisor is nested within a Hyper-V partition. */ +#define HV_X64_HYPERV_NESTED BIT(12) +/* Recommend using enlightened VMCS */ #define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED BIT(14)
arch/x86/kernel/cpu/mshyperv.c +7 -0
@@ -37,6 +37,8 @@/* Is Linux running as the root partition? */ bool hv_root_partition; +/* Is Linux running on nested Microsoft Hypervisor */ +bool hv_nested;struct ms_hyperv_info ms_hyperv;#if IS_ENABLED (CONFIG_HYPERV )
@@ -301,6 +303,11 @@ static void __init ms_hyperv_init_platform(void)pr_info ("Hyper-V: running as root partition\n" ); } +if (ms_hyperv.hints &HV_X64_HYPERV_NESTED ) { + hv_nested = true; +pr_info ("Hyper-V: running on a nested hypervisor\n" ); + } +/* * Extract host information. */
drivers/hv/hv_common.c +5 -2
@@ -25,8 +25,8 @@#include <asm/mshyperv.h> /* -* hv_root_partition and ms_hyperv are defined here with other Hyper-V -* specific globals so they are shared across all architectures and are +* hv_root_partition, ms_hyperv and hv_nested are defined here with other +* Hyper-V specific globals so they are shared across all architectures and are * built only when CONFIG_HYPERV is defined. But on x86, * ms_hyperv_init_platform() is built even when CONFIG_HYPERV is not * defined, and it uses these two variables. So mark them as __weak
@@ -36,6 +36,9 @@bool __weak hv_root_partition;EXPORT_SYMBOL_GPL (hv_root_partition); +bool __weak hv_nested; +EXPORT_SYMBOL_GPL (hv_nested); +struct ms_hyperv_info __weak ms_hyperv;EXPORT_SYMBOL_GPL (ms_hyperv);
include/asm-generic/mshyperv.h +1 -0
@@ -164,6 +164,7 @@ extern int vmbus_interrupt;extern int vmbus_irq;extern bool hv_root_partition; +extern bool hv_nested;#if IS_ENABLED (CONFIG_HYPERV )/*
Signature · 1 line
2.25.1
PATCH v5 2/5 Drivers: hv: Setup synic registers in case of nested root partition
In reply to Jinank JainChild partitions are free to allocate SynIC message and event page but in case of root partition it must use the pages allocated by Microsoft Hypervisor (MSHV). Base address for these pages can be found using synthetic MSRs exposed by MSHV. There is a slight difference in those MSRs for nested vs non-nested root partition.
--- arch/x86/include/asm/hyperv-tlfs.h | 11 +++++++ arch/x86/include/asm/mshyperv.h | 26 ++-------------- arch/x86/kernel/cpu/mshyperv.c | 49 ++++++++++++++++++++++++++++++ drivers/hv/hv.c | 18 ++++++++--- 4 files changed, 75 insertions(+), 29 deletions(-)
arch/x86/include/asm/hyperv-tlfs.h +11 -0
@@ -225,6 +225,17 @@ enum hv_isolation_type {#define HV_REGISTER_SINT14 0x4000009E#define HV_REGISTER_SINT15 0x4000009F +/* +* Define synthetic interrupt controller model specific registers for +* nested hypervisor. +*/ +#define HV_REGISTER_NESTED_SCONTROL 0x40001080 +#define HV_REGISTER_NESTED_SVERSION 0x40001081 +#define HV_REGISTER_NESTED_SIEFP 0x40001082 +#define HV_REGISTER_NESTED_SIMP 0x40001083 +#define HV_REGISTER_NESTED_EOM 0x40001084 +#define HV_REGISTER_NESTED_SINT0 0x40001090 +/* * Synthetic Timer MSRs. Four timers per vcpu. */
arch/x86/include/asm/mshyperv.h +2 -24
@@ -198,30 +198,8 @@ static inline bool hv_is_synic_reg(unsigned int reg) return false; } -static inline u64 hv_get_register (unsigned int reg) -{ -u64 value; - -if (hv_is_synic_reg (reg) &&hv_isolation_type_snp ()) -hv_ghcb_msr_read (reg, &value); -else -rdmsrl (reg, value); -return value; -} - -static inline void hv_set_register (unsigned int reg,u64 value) -{ -if (hv_is_synic_reg (reg) &&hv_isolation_type_snp ()) { -hv_ghcb_msr_write (reg, value); - -/* Write proxy bit via wrmsl instruction */ -if (reg >=HV_REGISTER_SINT0 && - reg <=HV_REGISTER_SINT15 ) -wrmsrl (reg, value |1 <<20 ); - }else { -wrmsrl (reg, value); - } -} +u64 hv_get_register (unsigned int reg); +void hv_set_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}
arch/x86/kernel/cpu/mshyperv.c +49 -0
55 lines not on this page · raw message
drivers/hv/hv.c +13 -5
@@ -147,7 +147,7 @@ int hv_synic_alloc(void)* Synic message and event pages are allocated by paravisor. * Skip these pages allocation here. */ -if (!hv_isolation_type_snp ()) { +if (!hv_isolation_type_snp () && !hv_root_partition) { hv_cpu->synic_message_page = (void *)get_zeroed_page (GFP_ATOMIC );if (hv_cpu->synic_message_page ==NULL ) {
@@ -188,8 +188,16 @@ void hv_synic_free(void)struct hv_per_cpu_context *hv_cpu =per_cpu_ptr (hv_context.cpu_context, cpu); -free_page ((unsigned long )hv_cpu->synic_event_page); -free_page ((unsigned long )hv_cpu->synic_message_page); +if (hv_root_partition) { +if (hv_cpu->synic_event_page !=NULL ) +memunmap (hv_cpu->synic_event_page); + +if (hv_cpu->synic_message_page !=NULL ) +memunmap (hv_cpu->synic_message_page); + }else { +free_page ((unsigned long )hv_cpu->synic_event_page); +free_page ((unsigned long )hv_cpu->synic_message_page); + }free_page ((unsigned long )hv_cpu->post_msg_page); }
@@ -216,7 +224,7 @@ void hv_synic_enable_regs(unsigned int cpu) simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP ); simp.simp_enabled =1 ; -if (hv_isolation_type_snp ()) { +if (hv_isolation_type_snp () || hv_root_partition) { hv_cpu->synic_message_page =memremap (simp.base_simp_gpa <<HV_HYP_PAGE_SHIFT ,HV_HYP_PAGE_SIZE ,MEMREMAP_WB );
@@ -233,7 +241,7 @@ void hv_synic_enable_regs(unsigned int cpu) siefp.as_uint64 =hv_get_register (HV_REGISTER_SIEFP ); siefp.siefp_enabled =1 ; -if (hv_isolation_type_snp ()) { +if (hv_isolation_type_snp () || hv_root_partition) { hv_cpu->synic_event_page =memremap (siefp.base_siefp_gpa <<HV_HYP_PAGE_SHIFT ,HV_HYP_PAGE_SIZE ,MEMREMAP_WB );
Signature · 1 line
2.25.1
PATCH v5 3/5 x86/hyperv: Add an interface to do nested hypercalls
In reply to Jinank JainAccording to TLFS, in order to communicate to L0 hypervisor there needs to be an additional bit set in the control register. This communication is required to perform priviledged instructions which can only be performed by L0 hypervisor. An example of that could be setting up the VMBus infrastructure.
--- arch/x86/include/asm/hyperv-tlfs.h | 3 ++- arch/x86/include/asm/mshyperv.h | 42 +++++++++++++++++++++++++++--- include/asm-generic/hyperv-tlfs.h | 1 + 3 files changed, 41 insertions(+), 5 deletions(-)
arch/x86/include/asm/hyperv-tlfs.h +2 -1
@@ -380,7 +380,8 @@ struct hv_nested_enlightenments_control { __u32 reserved:31; } features;struct { -__u32 reserved; +__u32 inter_partition_comm:1 ; +__u32 reserved:31 ; } hypercallControls; } __packed;
arch/x86/include/asm/mshyperv.h +38 -4
64 lines not on this page · raw message
include/asm-generic/hyperv-tlfs.h +1 -0
@@ -194,6 +194,7 @@ enum HV_GENERIC_SET_FORMAT {#define HV_HYPERCALL_VARHEAD_OFFSET 17#define HV_HYPERCALL_VARHEAD_MASK GENMASK_ULL(26, 17)#define HV_HYPERCALL_RSVD0_MASK GENMASK_ULL(31, 27) +#define HV_HYPERCALL_NESTED BIT_ULL(31)#define HV_HYPERCALL_REP_COMP_OFFSET 32#define HV_HYPERCALL_REP_COMP_1 BIT_ULL(32)#define HV_HYPERCALL_REP_COMP_MASK GENMASK_ULL(43, 32)
Signature · 1 line
2.25.1
PATCH v5 4/5 Drivers: hv: Enable vmbus driver for nested root partition
In reply to Jinank JainCurrently VMBus driver is not initialized for root partition but we need to enable the VMBus driver for nested root partition. This is required, so that L2 root can use the VMBus devices.
--- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hv/vmbus_drv.c +1 -1
@@ -2744,7 +2744,7 @@ static int __init hv_acpi_init(void)if (!hv_is_hyperv_initialized ())return -ENODEV ; -if (hv_root_partition) +if (hv_root_partition && !hv_nested)return 0 ;/*
Signature · 1 line
2.25.1
PATCH v5 5/5 x86/hyperv: Change interrupt vector for nested root partition
In reply to Jinank JainTraditionally we have been using the HYPERVISOR_CALLBACK_VECTOR to relay the VMBus interrupt. But this does not work in case of nested hypervisor. Microsoft Hypervisor reserves 0x31 to 0x34 as the interrupt vector range for VMBus and thus we have to use one of the vectors from that range and setup the IDT accordingly.
--- arch/x86/include/asm/idtentry.h | 2 ++ arch/x86/include/asm/irq_vectors.h | 6 ++++++ arch/x86/kernel/cpu/mshyperv.c | 15 +++++++++++++++ arch/x86/kernel/idt.c | 9 +++++++++ drivers/hv/vmbus_drv.c | 3 ++- 5 files changed, 34 insertions(+), 1 deletion(-)
arch/x86/include/asm/idtentry.h +2 -0
@@ -686,6 +686,8 @@ DECLARE_IDTENTRY_SYSVEC(POSTED_INTR_NESTED_VECTOR, sysvec_kvm_posted_intr_nestedDECLARE_IDTENTRY_SYSVEC (HYPERVISOR_CALLBACK_VECTOR , sysvec_hyperv_callback);DECLARE_IDTENTRY_SYSVEC (HYPERV_REENLIGHTENMENT_VECTOR , sysvec_hyperv_reenlightenment);DECLARE_IDTENTRY_SYSVEC (HYPERV_STIMER0_VECTOR , sysvec_hyperv_stimer0); +DECLARE_IDTENTRY_SYSVEC (HYPERV_INTR_NESTED_VMBUS_VECTOR , + sysvec_hyperv_nested_vmbus_intr);#endif #if IS_ENABLED (CONFIG_ACRN_GUEST )
arch/x86/include/asm/irq_vectors.h +6 -0
@@ -102,6 +102,12 @@#if IS_ENABLED (CONFIG_HYPERV )#define HYPERV_REENLIGHTENMENT_VECTOR 0xee#define HYPERV_STIMER0_VECTOR 0xed +/* +* FIXME: Change this, once Microsoft Hypervisor changes its assumption +* around VMBus interrupt vector allocation for nested root partition. +* Or provides a better interface to detect this instead of hardcoding. +*/ +#define HYPERV_INTR_NESTED_VMBUS_VECTOR 0x31#endif #define LOCAL_TIMER_VECTOR 0xec
arch/x86/kernel/cpu/mshyperv.c +15 -0
@@ -110,6 +110,21 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)set_irq_regs (old_regs); } +DEFINE_IDTENTRY_SYSVEC (sysvec_hyperv_nested_vmbus_intr) +{ +struct pt_regs *old_regs =set_irq_regs (regs); + +inc_irq_stat (irq_hv_callback_count); + +if (vmbus_handler) +vmbus_handler (); + +if (ms_hyperv.hints &HV_DEPRECATING_AEOI_RECOMMENDED ) +ack_APIC_irq (); + +set_irq_regs (old_regs); +} +void hv_setup_vmbus_handler (void (*handler)(void )) { vmbus_handler = handler;
arch/x86/kernel/idt.c +9 -0
@@ -160,6 +160,15 @@ static const __initconst struct idt_data apic_idts[] = {# endif INTG (SPURIOUS_APIC_VECTOR , asm_sysvec_spurious_apic_interrupt),INTG (ERROR_APIC_VECTOR , asm_sysvec_error_interrupt), +#ifdef CONFIG_HYPERV +/* +* This is a hack because we cannot install this interrupt handler via alloc_intr_gate +* as it does not allow interrupt vector less than FIRST_SYSTEM_VECTORS. And hyperv +* does not want anything other than 0x31-0x34 as the interrupt vector for vmbus +* interrupt in case of nested setup. + */ + INTG(HYPERV_INTR_NESTED_VMBUS_VECTOR, asm_sysvec_hyperv_nested_vmbus_intr), +#endif #endif };
drivers/hv/vmbus_drv.c +2 -1
@@ -2767,7 +2767,8 @@ static int __init hv_acpi_init(void)* normal Linux IRQ mechanism is not used in this case. */ #ifdef HYPERVISOR_CALLBACK_VECTOR - vmbus_interrupt =HYPERVISOR_CALLBACK_VECTOR ; + vmbus_interrupt = hv_nested ?HYPERV_INTR_NESTED_VMBUS_VECTOR : +HYPERVISOR_CALLBACK_VECTOR ; vmbus_irq =-1 ;#endif
Signature · 1 line
2.25.1
Please ignore the v5 patch series I posted the wrong set of patches.
Regards,
Jinank
Quoted 33 lines · Jinank Jain
This patch series plans to add support for running nested Microsoft Hypervisor. In case of nested Microsoft Hypervisor there are few privileged hypercalls which need to go L0 Hypervisor instead of L1 Hypervisor. This patches series basically identifies such hypercalls and replace them with nested hypercalls.
Jinank Jain (5):
x86/hyperv: Add support for detecting nested hypervisor
Drivers: hv: Setup synic registers in case of nested root partition x86/hyperv: Add an interface to do nested hypercalls
Drivers: hv: Enable vmbus driver for nested root partition x86/hyperv: Change interrupt vector for nested root partition[v4]
- Fix ARM64 compilation[v5]
- Fix comments from Michael Kellyarch/arm64/hyperv/mshyperv.c | 6 +++ arch/x86/include/asm/hyperv-tlfs.h | 17 ++++++- arch/x86/include/asm/idtentry.h | 2 + arch/x86/include/asm/irq_vectors.h | 6 +++ arch/x86/include/asm/mshyperv.h | 68 ++++++++++++++++------------ arch/x86/kernel/cpu/mshyperv.c | 71 ++++++++++++++++++++++++++++++ arch/x86/kernel/idt.c | 9 ++++ drivers/hv/hv.c | 18 +++++--- drivers/hv/hv_common.c | 7 ++- drivers/hv/vmbus_drv.c | 5 ++- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 1 + 12 files changed, 173 insertions(+), 38 deletions(-)