Add support running nested Microsoft Hypervisor
Subsystems (5)
A message that is not in the archive (sent off-list or not yet received): https://lore.kernel.org/linux-hyperv/[email protected]/T/#t
v2 cover Add support running nested Microsoft Hypervisor
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):
mshv: Add support for detecting nested hypervisor
hv: Setup synic registers in case of nested root partition
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 | 68 ++++++++++++++++++++++++++++-- arch/x86/kernel/cpu/mshyperv.c | 22 ++++++++++ arch/x86/kernel/idt.c | 9 ++++ drivers/hv/hv.c | 18 +++++--- drivers/hv/vmbus_drv.c | 5 ++- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 1 + 10 files changed, 137 insertions(+), 12 deletions(-)
Signature · 1 line
2.25.1
PATCH v2 1/5 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/include/asm/mshyperv.h | 2 ++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ 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/include/asm/mshyperv.h +2 -0
@@ -190,6 +190,8 @@ static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}extern bool hv_isolation_type_snp (void ); +extern bool hv_nested; +static inline bool hv_is_synic_reg (unsigned int reg) { if ((reg >= HV_REGISTER_SCONTROL) &&
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. */
Signature · 1 line
2.25.1
RE: [PATCH v2 1/5] mshv: Add support for detecting nested hypervisor
From: Jinank Jain <[email protected]> Sent: Wednesday, November 2, 2022 9:36 AM
Quoted 1 line
The email subject prefix for a patch like this is usually "x86/hyperv" Check the commit log for arch/x86/kernel/cpu/mshyperv.c, and you'll see what is usually done. It's best to maintain consistency.
Same comment applies to other patches in this series: Check the commit log for the relevant files and pick the prefix that fits best.
Quoted 54 lines
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_nestedwhich can be used later for making decisions specific to nested use case.Signed-off-by Jinank Jain <[email protected]>--- arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/include/asm/mshyperv.h | 2 ++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ 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/include/asm/mshyperv.h +2
-0@@ -190,6 +190,8 @@ static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}extern bool hv_isolation_type_snp (void ); +extern bool hv_nested; +static inline bool hv_is_synic_reg (unsigned int reg) { if ((reg >= HV_REGISTER_SCONTROL) &&arch/x86/kernel/cpu/mshyperv.c +2
-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 )
arch/x86/kernel/cpu/mshyperv.c static void __init ms_hyperv_init_platform(void) · line 307
Quoted 5 lines
arch/x86/kernel/cpu/mshyperv.c +2
-0@@ -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;
This reference to hv_nested will cause problems similar to what happens
with hv_root_partition. See the comments in hv_common.c about howhv_root_partition is handled. I think you'll need to do the same thing with hv_nested.
arch/x86/kernel/cpu/mshyperv.c static void __init ms_hyperv_init_platform(void) · line 308
Quoted 1 line
arch/x86/kernel/cpu/mshyperv.c +1
-0+pr_info ("Hyper-V: Linux running on a nested hypervisor\n" );
A nit: Let's drop the word "Linux" from the above message so it is consistent with the previous message about "running as root partition".
Quoted 7 lines
arch/x86/kernel/cpu/mshyperv.c +2
-1+ } +/* * Extract host information. */ --2.25.1
PATCH v2 2/5 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 | 24 ++++++++++++++++++++++++ drivers/hv/hv.c | 18 +++++++++++++----- 3 files changed, 48 insertions(+), 5 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 +24 -0
@@ -200,10 +200,31 @@ static inline bool hv_is_synic_reg(unsigned int reg)return false; } +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP : +return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +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
@@ -213,6 +234,9 @@ static inline u64 hv_get_register(unsigned int reg)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);
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
RE: [PATCH v2 2/5] hv: Setup synic registers in case of nested root partition
From: Jinank Jain <[email protected]> Sent: Wednesday, November 2, 2022 9:36 AM
Quoted 1 line
The email subject prefix for a patch like this is usually "Drivers: hv:" Check the commit log for the drivers/hv path, and you'll see what is usually done.
Quoted 86 lines
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 +++++++++++ arch/x86/include/asm/mshyperv.h | 24 ++++++++++++++++++++++++ drivers/hv/hv.c | 18 +++++++++++++----- 3 files changed, 48 insertions(+), 5 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 +24
-0@@ -200,10 +200,31 @@ static inline bool hv_is_synic_reg(unsigned int reg)return false; } +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP : +return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +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 @@ -213,6 +234,9 @@ static inline u64 hv_get_register(unsigned int reg)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);drivers/hv/hv.c +0
-0
drivers/hv/hv.c int hv_synic_alloc(void) · line 150
Quoted 5 lines
drivers/hv/hv.c +1
-1@@ -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) {
Perhaps update the comment above to also cover the root partition case?
Quoted 42 lines
drivers/hv/hv.c +12
-5hv_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 ); --2.25.1
PATCH v2 3/5 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
RE: [PATCH v2 3/5] hv: Add an interface to do nested hypercalls
From: Jinank Jain <[email protected]> Sent: Wednesday, November 2, 2022 9:36 AM
Quoted 106 lines
According 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.
Signed-off-by Jinank Jain <[email protected]>--- 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 +8
-2@@ -74,10 +74,16 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output)return hv_status; } +/* Hypercall to the L0 hypervisor */ +static inline u64 hv_do_nested_hypercall (u64 control,void *input,void *output) +{ +return hv_do_hypercall (control |HV_HYPERCALL_NESTED , input, output); +} +/* Fast hypercall with 8 bytes of input and no output */ -static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +static inline u64 _hv_do_fast_hypercall8 (u64 control,u16 code,u64 input1) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;#ifdef CONFIG_X86_64 arch/x86/include/asm/mshyperv.h +30
-2{@@ -105,10 +111,24 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1)return hv_status; } +static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall8 (control, code, input1); +} + +static inline u64 hv_do_fast_nested_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ; + +return _hv_do_fast_hypercall8 (control, code, input1); +} +/* Fast hypercall with 16 bytes of input */ -static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +static inline u64 _hv_do_fast_hypercall16 (u64 control,u16 code,u64 input1,u64 input2) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;#ifdef CONFIG_X86_64 {@@ -139,6 +159,20 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)return hv_status; } +static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall16 (control, code, input1, input2); +} + +static inline u64 hv_do_fast_nested_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ; + +return _hv_do_fast_hypercall16 (control, code, input1, input2); +} +extern struct hv_vp_assist_page **hv_vp_assist_page;static inline struct hv_vp_assist_page *hv_get_vp_assist_page (unsigned int cpu)include/asm-generic/hyperv-tlfs.h +0
-0
include/asm-generic/hyperv-tlfs.h enum HV_GENERIC_SET_FORMAT { · line 188
Quoted 4 lines
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)
Since it is used with a bunch of other u64 values, the above should probably be BIT_ULL(31).
include/asm-generic/mshyperv.h extern void * __percpu *hyperv_pcpu_input_arg; · line 56
Quoted 12 lines
include/asm-generic/hyperv-tlfs.h +0
-0#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);
Is this needed? hv_do_nested_hypercall() is implemented as a static inline function in the x86-specific version of mshyperv.h, so this seems duplicative. But maybe I'm missing something ....
Quoted 5 lines
include/asm-generic/mshyperv.h +0
-1extern u64 hv_do_fast_hypercall8 (u16 control,u64 input8);extern bool hv_isolation_type_snp (void ); --2.25.1
PATCH v2 4/5 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
RE: [PATCH v2 4/5] hv: Enable vmbus driver for nested root partition
From: Jinank Jain <[email protected]> Sent: Wednesday, November 2, 2022 9:36 AM
Quoted 15 lines
Currently 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.
Signed-off-by Jinank Jain <[email protected]>--- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/hv/vmbus_drv.c +0
-0
drivers/hv/vmbus_drv.c static int __init hv_acpi_init(void) · line 2726
Quoted 5 lines
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)
Note that this code must compile and run when Linux is built to run as a guest on Hyper-V for ARM64. There's currently no definition for hv_nested on the ARM64 side, so the compile will fail. But per my comments in Patch 1 in this series, using the same technique as for hv_root_partition in hv_common.c should solve the ARM64 problem as well.
Quoted 5 lines
drivers/hv/vmbus_drv.c +0
-1return 0 ;/* -- 2.25.1
Wei Liu
maintainerRe: [PATCH v2 4/5] hv: Enable vmbus driver for nested root partition
Quoted 19 lines · Michael Kelley (LINUX)
From: Jinank Jain <[email protected]> Sent: Wednesday, November 2, 2022 9:36 AM
Quoted 18 lines
Currently 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.
Signed-off-by Jinank Jain <[email protected]>--- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/hv/vmbus_drv.c +0
-0@@ -2723,7 +2723,7 @@ static int __init hv_acpi_init(void)if (!hv_is_hyperv_initialized ())return -ENODEV ;
Quoted 9 lines
drivers/hv/vmbus_drv.c static int __init hv_acpi_init(void) · line 2726
Quoted 2 lines
drivers/hv/vmbus_drv.c +1
-1-if (hv_root_partition) +if (hv_root_partition && !hv_nested)Note that this code must compile and run when Linux is built to run as a guest on Hyper-V for ARM64. There's currently no definition for
hv_nestedon the ARM64 side, so the compile will fail. But per my comments in Patch 1 in this series, using the same technique as forhv_root_partitioninhv_common.cshould solve the ARM64 problem as well.
Jinank, you can use the following commands to cross-compile Linux.
make ARCH=arm64 mshv_defconfig make -j$(nproc) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image
I think there are some follow-up patches in the internal tree which fixed the arm64 build. You may be able to squash some of those patches into series.
Thanks,
Wei.
Re: [PATCH v2 4/5] hv: Enable vmbus driver for nested root partition
In reply to Jinank JainQuoted 3 lines · Jinank Jain
Currently 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.
Perhaps more importantly, this is required so that L2 _root_ can use the VMBus devices.
Quoted 21 lines
Signed-off-by Jinank Jain <[email protected]>--- 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 v2 5/5 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
v4 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
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 v4 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
RE: [PATCH v4 1/5] x86/hyperv: Add support for detecting nested hypervisor
From: Jinank Jain <[email protected]> Sent: Wednesday, November 16, 2022 7:28 PM
Quoted 6 lines
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_nestedwhich can be used later for making decisions specific to nested use case.
Make the commit statement a bit more direct, and avoid equivocating words like "possible" and "can be" when there isn't anything that is doubtful. Here is my suggestion:
Detect if Linux is running as a nested hypervisor in the root partition for Microsoft Hypervisor, using flags provided by MSHV. Expose a new variable hv_nested that is used later for decisions specific to the nested use case.
Quoted 25 lines
Signed-off-by Jinank Jain <[email protected]>--- 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 +3
-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;
arch/arm64/hyperv/mshyperv.c static int __init hyperv_init(void) · line 74
Quoted 9 lines
arch/arm64/hyperv/mshyperv.c +3
-0@@ -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;
The above ARM64 additions aren't needed. An architecture that works with the default value of "0" (i.e., "false") doesn't have to do anything
as it uses the version in hv_common.c. While explicitly coding it on
the ARM64 side doesn't break anything, one of intentions of the __weak approach is that we don't have to update the ARM64 side every time we add something that is x86 only. To avoid irrelevant clutter on the ARM64 side, the preference is to *not* add such code. Similarly, you'll notice that the ARM64 code doesn't initialize hv_root_partition.
Quoted 44 lines
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 +0
-0
drivers/hv/hv_common.c line 32
Quoted 10 lines
drivers/hv/hv_common.c +2
-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
s/two/three/ (since we now have three such variables)
Quoted 24 lines
drivers/hv/hv_common.c +3
-0@@ -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
-1@@ -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 )/* -- 2.25.1
PATCH v4 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
RE: [PATCH v4 2/5] Drivers: hv: Setup synic registers in case of nested root partition
From: Jinank Jain <[email protected]> Sent: Wednesday, November 16, 2022 7:28 PM
Quoted 91 lines
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 +++++++ 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 +9
-0@@ -41,6 +41,55 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP : +return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ;
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 62
Quoted 10 lines
arch/x86/kernel/cpu/mshyperv.c +10
-0+case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + } +} + +inline u64 hv_get_register (unsigned int reg)
I don't think "inline" here does anything. There aren't any invocations of hv_get_register() in this module. Previously, when the code was in an include file, then "inline" would do something.
But I'm not an expert in compilers/linkers, so maybe I'm wrong. :-)
Quoted 4 lines
arch/x86/kernel/cpu/mshyperv.c +4
-0+{ +u64 value; + +if (hv_nested)
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 76
Quoted 10 lines
arch/x86/kernel/cpu/mshyperv.c +10
-0+ 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; +} + +inline void hv_set_register (unsigned int reg,u64 value)
Same here.
Quoted 71 lines
arch/x86/kernel/cpu/mshyperv.c +16
-0+{ +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); + } +} +#if IS_ENABLED (CONFIG_HYPERV )static void (*vmbus_handler)(void );static void (*hv_stimer0_handler)(void );drivers/hv/hv.c +13
-6@@ -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 ); --2.25.1
Re: [PATCH v4 2/5] Drivers: hv: Setup synic registers in case of nested root partition
In reply to Jinank JainQuoted 119 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 +++++++ 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 +38
-0@@ -41,6 +41,55 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP : +return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + } +} + +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; +} + +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 ()) {
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 91
Quoted 10 lines
arch/x86/kernel/cpu/mshyperv.c +10
-0+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); + } +}
This approach has a problem, in that it removes the interface for getting and setting the non-nested SIMP etc...
We will need to use the non-nested SIMP for getting intercepts in the root partition from the L1 hypervisor.
Nuno
PATCH v4 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
RE: [PATCH v4 3/5] x86/hyperv: Add an interface to do nested hypercalls
From: Jinank Jain <[email protected]> Sent: Wednesday, November 16, 2022 7:28 PM
Quoted 4 lines
According 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
s/priviledged/privileged/
Quoted 114 lines
performed by L0 hypervisor. An example of that could be setting up the VMBus infrastructure.
Signed-off-by Jinank Jain <[email protected]>--- 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 +8
-2@@ -74,10 +74,16 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output)return hv_status; } +/* Hypercall to the L0 hypervisor */ +static inline u64 hv_do_nested_hypercall (u64 control,void *input,void *output) +{ +return hv_do_hypercall (control |HV_HYPERCALL_NESTED , input, output); +} +/* Fast hypercall with 8 bytes of input and no output */ -static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +static inline u64 _hv_do_fast_hypercall8 (u64 control,u16 code,u64 input1) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;#ifdef CONFIG_X86_64 arch/x86/include/asm/mshyperv.h +16
-2{@@ -105,10 +111,24 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1)return hv_status; } +static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall8 (control, code, input1); +} + +static inline u64 hv_do_fast_nested_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ; + +return _hv_do_fast_hypercall8 (control, code, input1); +} +/* Fast hypercall with 16 bytes of input */ -static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +static inline u64 _hv_do_fast_hypercall16 (u64 control,u16 code,u64 input1,u64 input2) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;#ifdef
CONFIG_X86_64arch/x86/include/asm/mshyperv.h +14
-0{@@ -139,6 +159,20 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)return hv_status; } +static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall16 (control, code, input1, input2); +} + +static inline u64 hv_do_fast_nested_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ; + +return _hv_do_fast_hypercall16 (control, code, input1, input2); +} +extern struct hv_vp_assist_page **hv_vp_assist_page;static inline struct
hv_vp_assist_page*hv_get_vp_assist_page(unsigned int cpu)include/asm-generic/hyperv-tlfs.h +1
-1@@ -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) --2.25.1
PATCH v4 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
RE: [PATCH v4 4/5] Drivers: hv: Enable vmbus driver for nested root partition
From: Jinank Jain <[email protected]> Sent: Wednesday, November 16, 2022 7:28 PM
Quoted 24 lines
Currently 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.
Signed-off-by Jinank Jain <[email protected]>--- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/hv/vmbus_drv.c +1
-2@@ -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 ;/* --
Quoted 1 line
2.25.1
PATCH v4 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
RE: [PATCH v4 5/5] x86/hyperv: Change interrupt vector for nested root partition
From: Jinank Jain <[email protected]> Sent: Wednesday, November 16, 2022 7:28 PM
Quoted 113 lines
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 ++++++ 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 +0
-0@@ -686,6 +686,8 @@ DECLARE_IDTENTRY_SYSVEC(POSTED_INTR_NESTED_VECTOR,sysvec_kvm_posted_intr_nested DECLARE_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);arch/x86/include/asm/idtentry.h +2
-0+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 0xecarch/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
-2@@ -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 --
Quoted 1 line
2.25.1
Given the backdrop that this is a real hack due to MSHV limitations, this looks OK to me. But someone on the x86 side will have to weigh in on whether this will break anything in x86 vector management.
A somewhat dubious,
v6 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
[v6]
- Send the correct patches from the right folder
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
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 | 9 ++-- drivers/hv/vmbus_drv.c | 5 ++- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 1 + 11 files changed, 168 insertions(+), 39 deletions(-)
Signature · 1 line
2.25.1
PATCH v6 1/5 x86/hyperv: Add support for detecting nested hypervisor
Detect if Linux is running as a nested hypervisor in the root partition for Microsoft Hypervisor, using flags provided by MSHV. Expose a new variable hv_nested that is used later for decisions specific to the nested use case.
--- arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ drivers/hv/hv_common.c | 9 ++++++--- include/asm-generic/mshyperv.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-)
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 +6 -3
@@ -25,17 +25,20 @@#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 +* defined, and it uses these three variables. So mark them as __weak * here, allowing for an overriding definition in the module containing * ms_hyperv_init_platform(). */ 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 v6 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
RE: [PATCH v6 2/5] Drivers: hv: Setup synic registers in case of nested root partition
From: Jinank Jain <[email protected]> Sent: Wednesday, November 23, 2022 10:02 PM
Quoted 121 lines
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 +++++++ 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 +39
-0@@ -41,6 +41,55 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP : +return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + } +} + +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; +} + +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);
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 92
Quoted 10 lines
arch/x86/kernel/cpu/mshyperv.c +10
-0+ +/* Write proxy bit via wrmsl instruction */ +if (reg >=HV_REGISTER_SINT0 && + reg <=HV_REGISTER_SINT15 ) +wrmsrl (reg, value |1 <<20 ); + }else { +wrmsrl (reg, value); + } +} +
With hv_get_register(), hv_set_register(), and hv_get_nested_reg() moved here, they need to be under the #if IS_ENABLED(CONFIG_HYPERV). If CONFIG_HYPERV=n, this module is still compiled, and you end up with the implementation of hv_get_register() that's here and the no-op
implementation in arch/x86/include/asm/mshyperv.h. The linker
will rightfully complain.
There's also the issue of hv_ghcb_msr_read() and hv_ghcb_msr_write(). With CONFIG_AMD_MEM_ENCRYPT=y, there needs to be a non-stub implementation. But nothing in arch/x86/hyperv is built if CONFIG_HYPERV=n, so you'll get a linker error because of missing an implementation of those functions.
Putting the code under #if IS_ENABLED(CONFIG_HYPERV) should solve both problems.
You should specifically test with CONFIG_HYPERV=n after any changes to arch/x86/kernel/cpu/mshyperv.c.
Michael
Quoted 55 lines
arch/x86/kernel/cpu/mshyperv.c +0
-0#if IS_ENABLED (CONFIG_HYPERV )static void (*vmbus_handler)(void );static void (*hv_stimer0_handler)(void );drivers/hv/hv.c +13
-6@@ -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 ); --2.25.1
PATCH v6 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 privileged 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 v6 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 v6 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
v7 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
[v6]
- Send the correct patches from the right folder
[v7]
- Fix linker issues for CONFIG_HYPERV=n pointed out by Michael
- Fix comments from Nuno: created two separate functions for fetching nested vs non-nested registers.
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 | 72 +++++++++++++--------- arch/x86/kernel/cpu/mshyperv.c | 91 +++++++++++++++++++++++++++ arch/x86/kernel/idt.c | 9 +++ drivers/hv/hv.c | 99 ++++++++++++++++++++++-------- drivers/hv/hv_common.c | 9 ++- drivers/hv/vmbus_drv.c | 5 +- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 6 +- 11 files changed, 258 insertions(+), 59 deletions(-)
Signature · 1 line
2.25.1
PATCH v7 1/5 x86/hyperv: Add support for detecting nested hypervisor
Detect if Linux is running as a nested hypervisor in the root partition for Microsoft Hypervisor, using flags provided by MSHV. Expose a new variable hv_nested that is used later for decisions specific to the nested use case.
--- arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ drivers/hv/hv_common.c | 9 ++++++--- include/asm-generic/mshyperv.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-)
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 +6 -3
@@ -25,17 +25,20 @@#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 +* defined, and it uses these three variables. So mark them as __weak * here, allowing for an overriding definition in the module containing * ms_hyperv_init_platform(). */ 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
@@ -48,6 +48,7 @@ struct ms_hyperv_info {u64 shared_gpa_boundary; };extern struct ms_hyperv_info ms_hyperv; +extern bool hv_nested;extern void *__percpu *hyperv_pcpu_input_arg;extern void *__percpu *hyperv_pcpu_output_arg;
Signature · 1 line
2.25.1
PATCH v7 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 | 30 ++------- arch/x86/kernel/cpu/mshyperv.c | 69 +++++++++++++++++++++ drivers/hv/hv.c | 99 ++++++++++++++++++++++-------- include/asm-generic/mshyperv.h | 5 +- 5 files changed, 165 insertions(+), 49 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 +6 -24
@@ -198,30 +198,10 @@ 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); +u64 hv_get_nested_register (unsigned int reg); +void hv_set_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}
@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {
arch/x86/kernel/cpu/mshyperv.c +69 -0
76 lines not on this page · raw message
drivers/hv/hv.c +75 -24
178 lines not on this page · raw message
include/asm-generic/mshyperv.h +4 -1
@@ -147,7 +147,10 @@ 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 ); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_EOM ,0 ); +else +hv_set_register (HV_REGISTER_EOM ,0 ); } }
Signature · 1 line
2.25.1
RE: [PATCH v7 2/5] Drivers: hv: Setup synic registers in case of nested root partition
From: Jinank Jain <[email protected]> Sent: Thursday, December 1, 2022 3:04 AM
Quoted 100 lines
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 ++++ arch/x86/include/asm/mshyperv.h | 30 ++------- arch/x86/kernel/cpu/mshyperv.c | 69 +++++++++++++++++++++ drivers/hv/hv.c | 99 ++++++++++++++++++++++-------- include/asm-generic/mshyperv.h | 5 +- 5 files changed, 165 insertions(+), 49 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 +6
-24@@ -198,30 +198,10 @@ 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); +u64 hv_get_nested_register (unsigned int reg); +void hv_set_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {arch/x86/kernel/cpu/mshyperv.c +6
-0@@ -41,7 +41,76 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP :
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 59
Quoted 10 lines
arch/x86/kernel/cpu/mshyperv.c +10
-0+return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + }
Just a question: You added #defines for 6 nested registers. But the switch statement above maps only 5 registers. Is it intentional that there's not a mapping for HV_REGISTER_SVERSION?
Quoted 90 lines
arch/x86/kernel/cpu/mshyperv.c +53
-0+} +#if IS_ENABLED (CONFIG_HYPERV ) +static u64 _hv_get_register (unsigned int reg,bool nested) +{ +u64 value; + +if (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 void _hv_set_register (unsigned int reg,u64 value,bool nested) +{ +if (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); + } +} + +u64 hv_get_register (unsigned int reg) +{ +return _hv_get_register (reg, false); +} + +void hv_set_register (unsigned int reg,u64 value) +{ +_hv_set_register (reg, value, false); +} + +u64 hv_get_nested_register (unsigned int reg) +{ +return _hv_get_register (reg, true); +} + +void hv_set_nested_register (unsigned int reg,u64 value) +{ +_hv_set_register (reg, value, true); +} +static void (*vmbus_handler)(void );static void (*hv_stimer0_handler)(void );static void (*hv_kexec_handler)(void );drivers/hv/hv.c +11
-3@@ -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); }
drivers/hv/hv.c void hv_synic_enable_regs(unsigned int cpu) · line 225
Quoted 6 lines
drivers/hv/hv.c +2
-1@@ -213,10 +221,12 @@ 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_nested ?hv_get_nested_register (HV_REGISTER_SIMP ) : +hv_get_register (HV_REGISTER_SIMP );
Unfortunately, this code and the similar places below will run into problems on ARM64. Drivers/hv/hv.c is common code on all architectures so it needs to compile and run on ARM64 as well as x86/x64. But there's no hv_get_nested_register() defined or implemented on the ARM64 side, so the code will fail to compile.
I think there's a better way to do this. Based on Nuno's comments, it
seems like there are two hv_get_register() functions needed:
1) Get the value of the register or its nested cousin, based on the value of hv_nested. That's what you are explicitly coding here.
2) Get the value of the register. Don't access the nested cousin, regardless of the value of hv_nested.
Based on how you coded things earlier, I'm assuming #1 is what you want to use in most cases, and specifically here in drivers/hv/hv.c. That's good, because #1 can hide the testing of hv_nested in the x86-specific implementation of hv_get_register(), while the ARM64 version of hv_get_register() continues to do whatever it does now with no changes.
I'm also assuming that #2 may be used in particular cases in the code that is specifically related to nesting. Give the #2 version a different name --- maybe hv_get_nonnested_register(), or something like that -- and use it only in code under arch/x86 that is related to nesting. That way, ARM64 won't be affected.
Of course, the same approach applies to hv_set_register().
hv_get_register() and hv_get_nonnested_register() will obviously share some code. But rather than calling a common function starting with underscore like you've done above, let me suggest that hv_get_register() test hv_nested and potentially do the translation, then call hv_get_nonnested_register(). That way you'll end up with just two functions instead of three as above with hv_get_register(), hv_get_nested_register(), and _hv_get_register().
I haven't coded up any of this, so take it as a suggestion. There could be some problem with it that I haven't seen, or my assumptions might be wrong. But give it a try and see if it works out. I'm hoping it can all be handled on the x86 side without having to add complexity on the ARM64 side.
Michael
Quoted 175 lines
drivers/hv/hv.c +19
-7+ 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 );@@ -227,13 +237,18 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } -hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIMP , simp.as_uint64); +else +hv_set_register (HV_REGISTER_SIMP , simp.as_uint64);/* Setup the Synic's event page */ - siefp.as_uint64 =hv_get_register (HV_REGISTER_SIEFP ); + siefp.as_uint64 = hv_nested ? +hv_get_nested_register (HV_REGISTER_SIEFP ) : +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 );@@ -245,13 +260,19 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } -hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIEFP , siefp.as_uint64); +else +hv_set_register (HV_REGISTER_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 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = + hv_nested ? +hv_get_nested_register (HV_REGISTER_SINT0 + +VMBUS_MESSAGE_SINT ) : +hv_get_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT ); shared_sint.vector = vmbus_interrupt;drivers/hv/hv.c +12
-4shared_sint.masked = false;@@ -266,14 +287,22 @@ void hv_synic_enable_regs(unsigned int cpu)#else shared_sint.auto_eoi =0 ;#endif -hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , + shared_sint.as_uint64); +else +hv_set_register (HV_REGISTER_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_nested ? +hv_get_nested_register (HV_REGISTER_SCONTROL ) : +hv_get_register (HV_REGISTER_SCONTROL ); sctrl.enable =1 ; -hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +else +hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); }int
hv_synic_init(unsigned int cpu)drivers/hv/hv.c +12
-4@@ -297,17 +326,25 @@ 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 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = + hv_nested ? +hv_get_nested_register (HV_REGISTER_SINT0 + +VMBUS_MESSAGE_SINT ) : +hv_get_register (HV_REGISTER_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 , +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , + shared_sint.as_uint64); +else +hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , shared_sint.as_uint64); - simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP ); + simp.as_uint64 = hv_nested ?hv_get_nested_register (HV_REGISTER_SIMP ) : +hv_get_register (HV_REGISTER_SIMP );/* drivers/hv/hv.c +19
-5* In Isolation VM, sim and sief pages are allocated by * paravisor. These pages also will be used by kdump @@ -320,9 +357,14 @@ void hv_synic_disable_regs(unsigned int cpu)else simp.base_simp_gpa =0 ; -hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIMP , simp.as_uint64); +else +hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); - siefp.as_uint64 =hv_get_register (HV_REGISTER_SIEFP ); + siefp.as_uint64 = hv_nested ? +hv_get_nested_register (HV_REGISTER_SIEFP ) : +hv_get_register (HV_REGISTER_SIEFP ); siefp.siefp_enabled =0 ;if (hv_isolation_type_snp ())@@ -330,12 +372,21 @@ void hv_synic_disable_regs(unsigned int cpu)else siefp.base_siefp_gpa =0 ; -hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIEFP , siefp.as_uint64); +else +hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64);/* Disable the global synic bit */ - sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL ); + sctrl.as_uint64 = hv_nested ? +hv_get_nested_register (HV_REGISTER_SCONTROL ) : +hv_get_register (HV_REGISTER_SCONTROL ); sctrl.enable =0 ; -hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); + +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +else +hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64);if (vmbus_irq !=-1 )disable_percpu_irq (vmbus_irq);include/asm-generic/mshyperv.h +4
-2@@ -147,7 +147,10 @@ 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 ); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_EOM ,0 ); +else +hv_set_register (HV_REGISTER_EOM ,0 ); } } --2.25.1
Re: [PATCH v7 2/5] Drivers: hv: Setup synic registers in case of nested root partition
Quoted 105 lines · Michael Kelley (LINUX)
From: Jinank Jain <[email protected]> Sent: Thursday, December 1, 2022 3:04 AM
Quoted 104 lines
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 ++++ arch/x86/include/asm/mshyperv.h | 30 ++------- arch/x86/kernel/cpu/mshyperv.c | 69 +++++++++++++++++++++ drivers/hv/hv.c | 99 ++++++++++++++++++++++-------- include/asm-generic/mshyperv.h | 5 +- 5 files changed, 165 insertions(+), 49 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 +6
-24@@ -198,30 +198,10 @@ 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); +u64 hv_get_nested_register (unsigned int reg); +void hv_set_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {arch/x86/kernel/cpu/mshyperv.c +11
-0@@ -41,7 +41,76 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP : +return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ;
Quoted 8 lines
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 59
Quoted 5 lines
arch/x86/kernel/cpu/mshyperv.c +5
-0+case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + }Just a question: You added #defines for 6 nested registers. But the switch statement above maps only 5 registers. Is it intentional that there's not a mapping for
HV_REGISTER_SVERSION?
Good catch! Will fix it in the next revision.
Quoted 125 lines
Quoted 91 lines
arch/x86/kernel/cpu/mshyperv.c +53
-0+} +#if IS_ENABLED (CONFIG_HYPERV ) +static u64 _hv_get_register (unsigned int reg,bool nested) +{ +u64 value; + +if (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 void _hv_set_register (unsigned int reg,u64 value,bool nested) +{ +if (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); + } +} + +u64 hv_get_register (unsigned int reg) +{ +return _hv_get_register (reg, false); +} + +void hv_set_register (unsigned int reg,u64 value) +{ +_hv_set_register (reg, value, false); +} + +u64 hv_get_nested_register (unsigned int reg) +{ +return _hv_get_register (reg, true); +} + +void hv_set_nested_register (unsigned int reg,u64 value) +{ +_hv_set_register (reg, value, true); +} +static void (*vmbus_handler)(void );static void (*hv_stimer0_handler)(void );static void (*hv_kexec_handler)(void );drivers/hv/hv.c +11
-3@@ -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); }drivers/hv/hv.c void hv_synic_enable_regs(unsigned int cpu) · line 225
Quoted 6 lines
drivers/hv/hv.c +2
-1@@ -213,10 +221,12 @@ 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_nested ?hv_get_nested_register (HV_REGISTER_SIMP ) : +hv_get_register (HV_REGISTER_SIMP );Unfortunately, this code and the similar places below will run into problems on ARM64.
Drivers/hv/hv.cis common code on all architectures so it needs to compile and run on ARM64 as well as x86/x64. But there's nohv_get_nested_register()defined or implemented on the ARM64 side, so the code will fail to compile.I think there's a better way to do this. Based on Nuno's comments, it
seems like there are twohv_get_register()functions needed:1) Get the value of the register or its nested cousin, based on the value of
hv_nested. That's what you are explicitly coding here.
2) Get the value of the register. Don't access the nested cousin, regardless of the value ofhv_nested.Based on how you coded things earlier, I'm assuming #1 is what you want to use in most cases, and specifically here in
drivers/hv/hv.c. That's good, because #1 can hide the testing ofhv_nestedin the x86-specific implementation ofhv_get_register(), while the ARM64 version ofhv_get_register()continues to do whatever it does now with no changes.I'm also assuming that #2 may be used in particular cases in the code that is specifically related to nesting. Give the #2 version a different name --- maybe
hv_get_nonnested_register(), or something like that -- and use it only in code underarch/x86that is related to nesting. That way, ARM64 won't be affected.Of course, the same approach applies to
hv_set_register().
Quoted 7 lines
hv_get_register()andhv_get_nonnested_register()will obviously share some code. But rather than calling a common function starting with underscore like you've done above, let me suggest thathv_get_register()testhv_nestedand potentially do the translation, then callhv_get_nonnested_register(). That way you'll end up with just two functions instead of three as above withhv_get_register(),hv_get_nested_register(), and_hv_get_register().
I tried the way you suggested and it worked for ARM64 this time. But still I would have three functions. Because the base function _hv_get_register() would still be required in order to avoid code duplication in hv_get_non_nested_register().
Quoted 182 lines
I haven't coded up any of this, so take it as a suggestion. There could be some problem with it that I haven't seen, or my assumptions might be wrong. But give it a try and see if it works out. I'm hoping it can all be handled on the x86 side without having to add complexity on the ARM64 side.
Michael
Quoted 173 lines
drivers/hv/hv.c +19
-7+ 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 );@@ -227,13 +237,18 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } -hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIMP , simp.as_uint64); +else +hv_set_register (HV_REGISTER_SIMP , simp.as_uint64);/* Setup the Synic's event page */ - siefp.as_uint64 =hv_get_register (HV_REGISTER_SIEFP ); + siefp.as_uint64 = hv_nested ? +hv_get_nested_register (HV_REGISTER_SIEFP ) : +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 );@@ -245,13 +260,19 @@ void hv_synic_enable_regs(unsigned int cpu) >> HV_HYP_PAGE_SHIFT; } -hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIEFP , siefp.as_uint64); +else +hv_set_register (HV_REGISTER_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 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = + hv_nested ? +hv_get_nested_register (HV_REGISTER_SINT0 + +VMBUS_MESSAGE_SINT ) : +hv_get_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT ); shared_sint.vector = vmbus_interrupt;shared_sint.masked = false;drivers/hv/hv.c +12
-4@@ -266,14 +287,22 @@ void hv_synic_enable_regs(unsigned int cpu)#else shared_sint.auto_eoi =0 ;#endif -hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , + shared_sint.as_uint64); +else +hv_set_register (HV_REGISTER_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_nested ? +hv_get_nested_register (HV_REGISTER_SCONTROL ) : +hv_get_register (HV_REGISTER_SCONTROL ); sctrl.enable =1 ; -hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +else +hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); }int
hv_synic_init(unsigned int cpu)drivers/hv/hv.c +12
-4@@ -297,17 +326,25 @@ 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 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = + hv_nested ? +hv_get_nested_register (HV_REGISTER_SINT0 + +VMBUS_MESSAGE_SINT ) : +hv_get_register (HV_REGISTER_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 , +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , + shared_sint.as_uint64); +else +hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , shared_sint.as_uint64); - simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP ); + simp.as_uint64 = hv_nested ?hv_get_nested_register (HV_REGISTER_SIMP ) : +hv_get_register (HV_REGISTER_SIMP );/* * In Isolation VM, sim and sief pages are allocated by
* paravisor. These pages also will be used by kdumpdrivers/hv/hv.c +19
-5@@ -320,9 +357,14 @@ void hv_synic_disable_regs(unsigned int cpu)else simp.base_simp_gpa =0 ; -hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIMP , simp.as_uint64); +else +hv_set_register (HV_REGISTER_SIMP , simp.as_uint64); - siefp.as_uint64 =hv_get_register (HV_REGISTER_SIEFP ); + siefp.as_uint64 = hv_nested ? +hv_get_nested_register (HV_REGISTER_SIEFP ) : +hv_get_register (HV_REGISTER_SIEFP ); siefp.siefp_enabled =0 ;if (hv_isolation_type_snp ())@@ -330,12 +372,21 @@ void hv_synic_disable_regs(unsigned int cpu)else siefp.base_siefp_gpa =0 ; -hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SIEFP , siefp.as_uint64); +else +hv_set_register (HV_REGISTER_SIEFP , siefp.as_uint64);/* Disable the global synic bit */ - sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL ); + sctrl.as_uint64 = hv_nested ? +hv_get_nested_register (HV_REGISTER_SCONTROL ) : +hv_get_register (HV_REGISTER_SCONTROL ); sctrl.enable =0 ; -hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); + +if (hv_nested) +hv_set_nested_register (HV_REGISTER_SCONTROL , sctrl.as_uint64); +else +hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64);if (vmbus_irq !=-1 )disable_percpu_irq (vmbus_irq);include/asm-generic/mshyperv.h +4
-1@@ -147,7 +147,10 @@ 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 ); +if (hv_nested) +hv_set_nested_register (HV_REGISTER_EOM ,0 ); +else +hv_set_register (HV_REGISTER_EOM ,0 ); } }
Quoted 2 lines
Quoted 2 lines
--
2.25.1
Regards,
Jinank
RE: [PATCH v7 2/5] Drivers: hv: Setup synic registers in case of nested root partition
From: Jinank Jain <[email protected]> Sent: Thursday, December 1, 2022 11:05 PM
Quoted 253 lines · Michael Kelley (LINUX)
Quoted 1 line
From: Jinank Jain <[email protected]> Sent: Thursday, December 1,
2022 3:04 AM
Quoted 15 lines
Quoted 15 lines
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 ++++ arch/x86/include/asm/mshyperv.h | 30 ++------- arch/x86/kernel/cpu/mshyperv.c | 69 +++++++++++++++++++++ drivers/hv/hv.c | 99 ++++++++++++++++++++++-------- include/asm-generic/mshyperv.h | 5 +- 5 files changed, 165 insertions(+), 49 deletions(-)Quoted 1 line
arch/x86/include/asm/hyperv-
Quoted 1 line
arch/x86/include/asm/hyperv- renamed from arch/x86/include/asm/hyperv-tlfs.h +0
-0
tlfs.hQuoted 88 lines
Quoted 88 lines
index
58c03d18c235..b5019becb618100644arch/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 +6
-24@@ -198,30 +198,10 @@ 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); +u64 hv_get_nested_register (unsigned int reg); +void hv_set_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {arch/x86/kernel/cpu/mshyperv.c +11
-0@@ -41,7 +41,76 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_NESTED_SIEFP : +return HV_REGISTER_SIEFP ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ;Quoted 8 lines
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 59
Quoted 5 lines
arch/x86/kernel/cpu/mshyperv.c +5
-0+case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + }Just a question: You added #defines for 6 nested registers. But the switch statement above maps only 5 registers. Is it intentional that there's not a mapping for
HV_REGISTER_SVERSION?Good catch! Will fix it in the next revision.
Quoted 125 lines
Quoted 91 lines
arch/x86/kernel/cpu/mshyperv.c +53
-0+} +#if IS_ENABLED (CONFIG_HYPERV ) +static u64 _hv_get_register (unsigned int reg,bool nested) +{ +u64 value; + +if (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 void _hv_set_register (unsigned int reg,u64 value,bool nested) +{ +if (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); + } +} + +u64 hv_get_register (unsigned int reg) +{ +return _hv_get_register (reg, false); +} + +void hv_set_register (unsigned int reg,u64 value) +{ +_hv_set_register (reg, value, false); +} + +u64 hv_get_nested_register (unsigned int reg) +{ +return _hv_get_register (reg, true); +} + +void hv_set_nested_register (unsigned int reg,u64 value) +{ +_hv_set_register (reg, value, true); +} +static void (*vmbus_handler)(void );static void (*hv_stimer0_handler)(void );static void (*hv_kexec_handler)(void );drivers/hv/hv.c +11
-3@@ -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); }drivers/hv/hv.c void hv_synic_enable_regs(unsigned int cpu) · line 225
Quoted 6 lines
drivers/hv/hv.c +2
-1@@ -213,10 +221,12 @@ 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_nested ?hv_get_nested_register (HV_REGISTER_SIMP ) : +hv_get_register (HV_REGISTER_SIMP );Unfortunately, this code and the similar places below will run into problems on ARM64.
Drivers/hv/hv.cis common code on all architectures so it needs to compile and run on ARM64 as well as x86/x64. But there's nohv_get_nested_register()defined or implemented on the ARM64 side, so the code will fail to compile.I think there's a better way to do this. Based on Nuno's comments, it
seems like there are twohv_get_register()functions needed:1) Get the value of the register or its nested cousin, based on the value of
hv_nested. That's what you are explicitly coding here.
2) Get the value of the register. Don't access the nested cousin, regardless of the value ofhv_nested.Based on how you coded things earlier, I'm assuming #1 is what you want to use in most cases, and specifically here in
drivers/hv/hv.c. That's good, because #1 can hide the testing ofhv_nestedin the x86-specific implementation ofhv_get_register(), while the ARM64 version ofhv_get_register()continues to do whatever it does now with no changes.I'm also assuming that #2 may be used in particular cases in the code that is specifically related to nesting. Give the #2 version a different name --- maybe
hv_get_nonnested_register(), or something like that -- and use it only in code underarch/x86that is related to nesting. That way, ARM64 won't be affected.Of course, the same approach applies to
hv_set_register().Quoted 7 lines
hv_get_register()andhv_get_nonnested_register()will obviously share some code. But rather than calling a common function starting with underscore like you've done above, let me suggest thathv_get_register()testhv_nestedand potentially do the translation, then callhv_get_nonnested_register(). That way you'll end up with just two functions instead of three as above withhv_get_register(),hv_get_nested_register(), and_hv_get_register().
Quoted 1 line
I tried the way you suggested and it worked for ARM64 this time.
OK, good.
Quoted 3 lines
But still I would have three functions. Because the base function
_hv_get_register()would still be required in order to avoid code duplication inhv_get_non_nested_register().
To make this is a bit more concrete, here's what I was thinking (not even compile tested):
u64 hv_get_non_nested_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;
}u64 hv_get_register(unsigned int reg)
{
if (hv_nested)
reg = hv_get_nested_reg(reg);
return hv_get_non_nested_register(reg);
}But maybe I'm missing something ....
Michael
PATCH v7 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 privileged 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 v7 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
@@ -2745,7 +2745,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 v7 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
@@ -130,6 +130,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
@@ -2768,7 +2768,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
v8 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
[v6]
- Send the correct patches from the right folder
[v7]
- Fix linker issues for CONFIG_HYPERV=n pointed out by Michael
- Fix comments from Nuno: created two separate functions for fetching nested vs non-nested registers.
[v8]
- Refactor as per the recommendation from Michael Kelly
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 | 72 ++++++++++++++++---------- arch/x86/kernel/cpu/mshyperv.c | 83 ++++++++++++++++++++++++++++++ arch/x86/kernel/idt.c | 10 ++++ drivers/hv/hv.c | 32 ++++++++---- drivers/hv/hv_common.c | 9 ++-- drivers/hv/vmbus_drv.c | 5 +- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 1 + 11 files changed, 193 insertions(+), 45 deletions(-)
Signature · 1 line
2.25.1
PATCH v8 1/5 x86/hyperv: Add support for detecting nested hypervisor
Detect if Linux is running as a nested hypervisor in the root partition for Microsoft Hypervisor, using flags provided by MSHV. Expose a new variable hv_nested that is used later for decisions specific to the nested use case.
--- arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ drivers/hv/hv_common.c | 9 ++++++--- include/asm-generic/mshyperv.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-)
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 +6 -3
@@ -25,17 +25,20 @@#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 +* defined, and it uses these three variables. So mark them as __weak * here, allowing for an overriding definition in the module containing * ms_hyperv_init_platform(). */ 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
@@ -48,6 +48,7 @@ struct ms_hyperv_info {u64 shared_gpa_boundary; };extern struct ms_hyperv_info ms_hyperv; +extern bool hv_nested;extern void *__percpu *hyperv_pcpu_input_arg;extern void *__percpu *hyperv_pcpu_output_arg;
Signature · 1 line
2.25.1
Re: [PATCH v8 1/5] x86/hyperv: Add support for detecting nested hypervisor
Quoted 86 lines · Jinank Jain
Detect if Linux is running as a nested hypervisor in the root partition for Microsoft Hypervisor, using flags provided by MSHV. Expose a new variable
hv_nestedthat is used later for decisions specific to the nested use case.Signed-off-by Jinank Jain <[email protected]>--- arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ drivers/hv/hv_common.c | 9 ++++++--- include/asm-generic/mshyperv.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-)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 +6
-3@@ -25,17 +25,20 @@#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 +* defined, and it uses these three variables. So mark them as __weak * here, allowing for an overriding definition in the module containing * ms_hyperv_init_platform(). */ 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 +0
-0
include/asm-generic/mshyperv.h struct ms_hyperv_info { · line 54
Quoted 7 lines
include/asm-generic/mshyperv.h +1
-0@@ -48,6 +48,7 @@ struct ms_hyperv_info {u64 shared_gpa_boundary; };extern struct ms_hyperv_info ms_hyperv; +extern bool hv_nested;extern void *__percpu *hyperv_pcpu_input_arg;extern void *__percpu *hyperv_pcpu_output_arg;
PATCH v8 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 | 30 +++------------ arch/x86/kernel/cpu/mshyperv.c | 61 ++++++++++++++++++++++++++++++ drivers/hv/hv.c | 32 ++++++++++------ 4 files changed, 99 insertions(+), 35 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 +6 -24
@@ -198,30 +198,10 @@ 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); +u64 hv_get_non_nested_register (unsigned int reg); +void hv_set_non_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}
@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_non_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_non_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {
arch/x86/kernel/cpu/mshyperv.c +61 -0
68 lines not on this page · raw message
drivers/hv/hv.c +21 -11
@@ -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); }
@@ -214,9 +222,10 @@ void hv_synic_enable_regs(unsigned int cpu)/* Setup the Synic's message page */ 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 +242,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 );
@@ -250,8 +259,8 @@ void hv_synic_enable_regs(unsigned int cpu)/* Setup the shared SINT. */ if (vmbus_irq !=-1 )enable_percpu_irq (vmbus_irq,0 ); - shared_sint.as_uint64 =hv_get_register (HV_REGISTER_SINT0 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = +hv_get_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT ); shared_sint.vector = vmbus_interrupt; shared_sint.masked = false;
@@ -267,7 +276,7 @@ void hv_synic_enable_regs(unsigned int cpu) shared_sint.auto_eoi =0 ;#endif hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , - shared_sint.as_uint64); + shared_sint.as_uint64);/* Enable the global synic bit */ sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL );
@@ -297,15 +306,15 @@ 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 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = +hv_get_register (HV_REGISTER_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 , - shared_sint.as_uint64); + shared_sint.as_uint64); simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP );/*
@@ -335,6 +344,7 @@ void hv_synic_disable_regs(unsigned int cpu)/* Disable the global synic bit */ sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL ); sctrl.enable =0 ; +hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64);if (vmbus_irq !=-1 )
Signature · 1 line
2.25.1
RE: [PATCH v8 2/5] Drivers: hv: Setup synic registers in case of nested root partition
From: Jinank Jain <[email protected]> Sent: Thursday, December 8, 2022 9:32 PM
Quoted 145 lines
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 ++++++ arch/x86/include/asm/mshyperv.h | 30 +++------------ arch/x86/kernel/cpu/mshyperv.c | 61 ++++++++++++++++++++++++++++++ drivers/hv/hv.c | 32 ++++++++++------ 4 files changed, 99 insertions(+), 35 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 +6
-24@@ -198,30 +198,10 @@ 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); +u64 hv_get_non_nested_register (unsigned int reg); +void hv_set_non_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_non_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_non_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {arch/x86/kernel/cpu/mshyperv.c +51
-0@@ -41,7 +41,68 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_SIEFP : +return HV_REGISTER_NESTED_SIEFP ; +case HV_REGISTER_SVERSION : +return HV_REGISTER_NESTED_SVERSION ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + } +} +#if IS_ENABLED (CONFIG_HYPERV ) +u64 hv_get_non_nested_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; +} + +void hv_set_non_nested_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) +{ +if (hv_nested) + reg =hv_get_nested_reg (reg); + +return hv_get_non_nested_register (reg);
arch/x86/kernel/cpu/mshyperv.c bool hv_root_partition; · line 105
Quoted 10 lines
arch/x86/kernel/cpu/mshyperv.c +10
-0+} + +void hv_set_register (unsigned int reg,u64 value) +{ +if (hv_nested) + reg =hv_get_nested_reg (reg); + +hv_set_non_nested_register (reg, value); +} +
This refactoring looks good. But there's still one tweak needed. These four functions must be marked as exported because they are used in code in drivers/hv that is part of the Hyper-V module. If CONFIG_HYPERV=m, you'll get a link error if these functions aren't exported. By "exported", I mean adding
EXPORT_SYMBOL_GPL(<func_name>);
after each of the above four functions. A good test is to build with CONFIG_HYPERV=m instead of CONFIG_HYPERV=y.
Quoted 36 lines
arch/x86/kernel/cpu/mshyperv.c +0
-0static void (*vmbus_handler)(void );static void (*hv_stimer0_handler)(void );static void (*hv_kexec_handler)(void );drivers/hv/hv.c +11
-3@@ -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); }
drivers/hv/hv.c void hv_synic_enable_regs(unsigned int cpu) · line 225
Quoted 4 lines
drivers/hv/hv.c +1
-0@@ -214,9 +222,10 @@ void hv_synic_enable_regs(unsigned int cpu)/* Setup the Synic's message page */ simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP ); +
This additional blank line is a spurious/gratuitous whitespace change, which should be avoided. Especially when a patch has gone through several revisions, it's likely that you'll end up with some whitespace changes like this. But it's important to go back and remove them so that the patch isn't cluttered with changes that don't add any value. Such gratuitous changes make it harder to review the patch, and are unnecessary code churn.
Quoted 17 lines
drivers/hv/hv.c +2
-2simp.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 +242,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 );
drivers/hv/hv.c void hv_synic_enable_regs(unsigned int cpu) · line 263
Quoted 7 lines
drivers/hv/hv.c +2
-2@@ -250,8 +259,8 @@ void hv_synic_enable_regs(unsigned int cpu)/* Setup the shared SINT. */ if (vmbus_irq !=-1 )enable_percpu_irq (vmbus_irq,0 ); - shared_sint.as_uint64 =hv_get_register (HV_REGISTER_SINT0 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = +hv_get_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT );
The above change, and all the changes from here down in this patch, are spurious/gratuitous whitespace changes that should be removed. Go back to the original code and formatting. Doing so will make the patch a lot shorter. :-)
Michael
Quoted 41 lines
drivers/hv/hv.c +5
-5shared_sint.vector = vmbus_interrupt; shared_sint.masked = false;@@ -267,7 +276,7 @@ void hv_synic_enable_regs(unsigned int cpu) shared_sint.auto_eoi =0 ;#endif hv_set_register (HV_REGISTER_SINT0 +VMBUS_MESSAGE_SINT , - shared_sint.as_uint64); + shared_sint.as_uint64);/* Enable the global synic bit */ sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL );@@ -297,15 +306,15 @@ 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 + -VMBUS_MESSAGE_SINT ); + shared_sint.as_uint64 = +hv_get_register (HV_REGISTER_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 , - shared_sint.as_uint64); + shared_sint.as_uint64); simp.as_uint64 =hv_get_register (HV_REGISTER_SIMP );/* @@ -335,6 +344,7 @@ void hv_synic_disable_regs(unsigned int cpu)/* Disable the global synic bit */ sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL ); sctrl.enable =0 ; +hv_set_register (HV_REGISTER_SCONTROL , sctrl.as_uint64);if (vmbus_irq !=-1 ) --2.25.1
PATCH v8 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 privileged 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
Re: [PATCH v8 3/5] x86/hyperv: Add an interface to do nested hypercalls
arch/x86/include/asm/mshyperv.h static inline u64 hv_do_hypercall(u64 control, void *input, void *output) · line 86
Quoted 6 lines · Jinank Jain
/* Fast hypercall with 8 bytes of input and no output */
arch/x86/include/asm/mshyperv.h +2
-2-static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +static inline u64 _hv_do_fast_hypercall8 (u64 control,u16 code,u64 input1) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;
The parameter 'code' seems to be unused in this function now. Can we just replace it with 'control'?
Quoted 17 lines
#ifdef
CONFIG_X86_64arch/x86/include/asm/mshyperv.h +10
-0{@@ -105,10 +111,24 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1)return hv_status; } +static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall8 (control, code, input1); +} + +static inline u64 hv_do_fast_nested_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ;
arch/x86/include/asm/mshyperv.h static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1) · line 131
Quoted 10 lines
arch/x86/include/asm/mshyperv.h +6
-2+ +return _hv_do_fast_hypercall8 (control, code, input1); +} +/* Fast hypercall with 16 bytes of input */ -static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +static inline u64 _hv_do_fast_hypercall16 (u64 control,u16 code,u64 input1,u64 input2) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;
Ditto
Quoted 20 lines
arch/x86/include/asm/mshyperv.h +13
-0#ifdef CONFIG_X86_64 {@@ -139,6 +159,20 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)return hv_status; } +static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall16 (control, code, input1, input2); +} + +static inline u64 hv_do_fast_nested_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ; + +return _hv_do_fast_hypercall16 (control, code, input1, input2); +}
PATCH v8 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
@@ -2745,7 +2745,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
Re: [PATCH v8 4/5] Drivers: hv: Enable vmbus driver for nested root partition
Quoted 15 lines · Jinank Jain
Currently 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.
Signed-off-by Jinank Jain <[email protected]>--- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/hv/vmbus_drv.c +0
-0
drivers/hv/vmbus_drv.c static int __init hv_acpi_init(void) · line 2751
Quoted 8 lines
drivers/hv/vmbus_drv.c +1
-1@@ -2745,7 +2745,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 ;/*
PATCH v8 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 | 10 ++++++++++ drivers/hv/vmbus_drv.c | 3 ++- 5 files changed, 35 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
@@ -122,6 +122,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 +10 -0
@@ -160,6 +160,16 @@ 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
@@ -2768,7 +2768,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
v9 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
[v6]
- Send the correct patches from the right folder
[v7]
- Fix linker issues for CONFIG_HYPERV=n pointed out by Michael
- Fix comments from Nuno: created two separate functions for fetching nested vs non-nested registers.
[v8]
- Refactor as per the recommendation from Michael Kelly
[v9]
- Address comments from Michael and Nuno.
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 | 72 +++++++++++++++---------- arch/x86/kernel/cpu/mshyperv.c | 87 ++++++++++++++++++++++++++++++ arch/x86/kernel/idt.c | 10 ++++ drivers/hv/hv.c | 19 +++++-- drivers/hv/hv_common.c | 9 ++-- drivers/hv/vmbus_drv.c | 5 +- include/asm-generic/hyperv-tlfs.h | 1 + include/asm-generic/mshyperv.h | 1 + 11 files changed, 190 insertions(+), 39 deletions(-)
Signature · 1 line
2.25.1
PATCH v9 1/5 x86/hyperv: Add support for detecting nested hypervisor
Detect if Linux is running as a nested hypervisor in the root partition for Microsoft Hypervisor, using flags provided by MSHV. Expose a new variable hv_nested that is used later for decisions specific to the nested use case.
--- arch/x86/include/asm/hyperv-tlfs.h | 3 +++ arch/x86/kernel/cpu/mshyperv.c | 7 +++++++ drivers/hv/hv_common.c | 9 ++++++--- include/asm-generic/mshyperv.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-)
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 +6 -3
@@ -25,17 +25,20 @@#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 +* defined, and it uses these three variables. So mark them as __weak * here, allowing for an overriding definition in the module containing * ms_hyperv_init_platform(). */ 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
@@ -48,6 +48,7 @@ struct ms_hyperv_info {u64 shared_gpa_boundary; };extern struct ms_hyperv_info ms_hyperv; +extern bool hv_nested;extern void *__percpu *hyperv_pcpu_input_arg;extern void *__percpu *hyperv_pcpu_output_arg;
Signature · 1 line
2.25.1
PATCH v9 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 | 30 +++----------- arch/x86/kernel/cpu/mshyperv.c | 65 ++++++++++++++++++++++++++++++ drivers/hv/hv.c | 19 ++++++--- 4 files changed, 96 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 +6 -24
@@ -198,30 +198,10 @@ 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); +u64 hv_get_non_nested_register (unsigned int reg); +void hv_set_non_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}
@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_non_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_non_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {
arch/x86/kernel/cpu/mshyperv.c +65 -0
72 lines not on this page · raw message
drivers/hv/hv.c +14 -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); }
@@ -214,9 +222,10 @@ void hv_synic_enable_regs(unsigned int cpu)/* Setup the Synic's message page */ 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 +242,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
RE: [PATCH v9 2/5] Drivers: hv: Setup synic registers in case of nested root partition
From: Jinank Jain <[email protected]> Sent: Tuesday, December 13, 2022 10:33 PM
Quoted 8 lines
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]>
You have addressed all my previous comments and those areas look good. But I see a new issue that I had not noticed before. See comment below.
Quoted 169 lines
--- arch/x86/include/asm/hyperv-tlfs.h | 11 +++++ arch/x86/include/asm/mshyperv.h | 30 +++----------- arch/x86/kernel/cpu/mshyperv.c | 65 ++++++++++++++++++++++++++++++ drivers/hv/hv.c | 19 ++++++--- 4 files changed, 96 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 +6
-24@@ -198,30 +198,10 @@ 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); +u64 hv_get_non_nested_register (unsigned int reg); +void hv_set_non_nested_register (unsigned int reg,u64 value);#else /* CONFIG_HYPERV */ static inline void hyperv_init (void ) {}@@ -241,6 +221,8 @@ static inline int hyperv_flush_guest_mapping_range(u64 as, }static inline void hv_set_register (unsigned int reg,u64 value) { }static inline u64 hv_get_register (unsigned int reg) {return 0 ; } +static inline void hv_set_non_nested_register (unsigned int reg,u64 value) { } +static inline u64 hv_get_non_nested_register (unsigned int reg) {return 0 ; }static inline int hv_set_mem_host_visibility (unsigned long addr,int numpages,bool visible) {arch/x86/kernel/cpu/mshyperv.c +65
-0@@ -41,7 +41,72 @@ bool hv_root_partition;bool hv_nested;struct ms_hyperv_info ms_hyperv; +static inline unsigned int hv_get_nested_reg (unsigned int reg) +{ +switch (reg) { +case HV_REGISTER_SIMP : +return HV_REGISTER_NESTED_SIMP ; +case HV_REGISTER_SIEFP : +return HV_REGISTER_NESTED_SIEFP ; +case HV_REGISTER_SVERSION : +return HV_REGISTER_NESTED_SVERSION ; +case HV_REGISTER_SCONTROL : +return HV_REGISTER_NESTED_SCONTROL ; +case HV_REGISTER_SINT0 : +return HV_REGISTER_NESTED_SINT0 ; +case HV_REGISTER_EOM : +return HV_REGISTER_NESTED_EOM ; +default : +return reg; + } +} +#if IS_ENABLED (CONFIG_HYPERV ) +u64 hv_get_non_nested_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; +} +EXPORT_SYMBOL_GPL (hv_get_non_nested_register); + +void hv_set_non_nested_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); + } +} +EXPORT_SYMBOL_GPL (hv_set_non_nested_register); + +u64 hv_get_register (unsigned int reg) +{ +if (hv_nested) + reg =hv_get_nested_reg (reg); + +return hv_get_non_nested_register (reg); +} +EXPORT_SYMBOL_GPL (hv_get_register); + +void hv_set_register (unsigned int reg,u64 value) +{ +if (hv_nested) + reg =hv_get_nested_reg (reg); + +hv_set_non_nested_register (reg, value); +} +EXPORT_SYMBOL_GPL (hv_set_register); +static void (*vmbus_handler)(void );static void (*hv_stimer0_handler)(void );static void (*hv_kexec_handler)(void );drivers/hv/hv.c +1
-1@@ -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
drivers/hv/hv.c void hv_synic_free(void) · line 196
Quoted 10 lines
drivers/hv/hv.c +6
-2= 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);
These memunmap() calls seem to be done in the wrong place. There are two pairs of functions that should be symmetrical unless there's a really good reason otherwise:
1. hv_synic_alloc() and hv_synic_free()
2. hv_synic_enable_regs() and hv_synic_disable_regs()
The functions in #1 handle the allocation and freeing of these three
pages: synic_event_page, synic_message_page, and post_msg_page. If the synic_event_page and synic_message_page don't need to be allocated because they are provided by the hypervisor or paravisor, then the allocation is skipped in hv_synic_alloc(), and the free_page operations in hv_synic_free() are no-ops if the corresponding pointer is NULL.
The functions in #2 should handle the mapping and unmapping in the
case of pages provided by the hypervisor or paravisor. It appears
that the hv_isolation_type_snp() case does this (mostly) correctly. But your code does the unmap in hv_synic_free(), which isn't symmetrical. Unless there's something unique about the situation when running in the root partition, the unmap should be done in hv_synic_disable_regs() like it is for the SNP case.
My "mostly" correctly comment above is because the current code in hv_synic_disable_regs() should be setting hv_cpu->synic_message_page and hv_cpu->synic_event_page to NULL after the unmap is done. Those pointers must be reverted to NULL so that if hv_synic_free() is then run, it won't try to free pages that were provided by the hypervisor or paravisor.
Michael
Quoted 30 lines
drivers/hv/hv.c +7
-3+ }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); }@@ -214,9 +222,10 @@ void hv_synic_enable_regs(unsigned int cpu)/* Setup the Synic's message page */ 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 +242,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 ); --2.25.1
PATCH v9 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 privileged 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
Re: [PATCH v9 3/5] x86/hyperv: Add an interface to do nested hypercalls
Quoted 103 lines · Jinank Jain
According 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 privileged instructions which can only be performed by L0 hypervisor. An example of that could be setting up the VMBus infrastructure.
Signed-off-by Jinank Jain <[email protected]>--- 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@@ -74,10 +74,16 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output)return hv_status; } +/* Hypercall to the L0 hypervisor */ +static inline u64 hv_do_nested_hypercall (u64 control,void *input,void *output) +{ +return hv_do_hypercall (control |HV_HYPERCALL_NESTED , input, output); +} +/* Fast hypercall with 8 bytes of input and no output */ -static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +static inline u64 _hv_do_fast_hypercall8 (u64 control,u64 input1) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;#ifdef CONFIG_X86_64 {@@ -105,10 +111,24 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1)return hv_status; } +static inline u64 hv_do_fast_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall8 (control, input1); +} + +static inline u64 hv_do_fast_nested_hypercall8 (u16 code,u64 input1) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ; + +return _hv_do_fast_hypercall8 (control, input1); +} +/* Fast hypercall with 16 bytes of input */ -static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +static inline u64 _hv_do_fast_hypercall16 (u64 control,u64 input1,u64 input2) { -u64 hv_status, control = (u64 )code |HV_HYPERCALL_FAST_BIT ; +u64 hv_status;#ifdef CONFIG_X86_64 {@@ -139,6 +159,20 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)return hv_status; } +static inline u64 hv_do_fast_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT ; + +return _hv_do_fast_hypercall16 (control, input1, input2); +} + +static inline u64 hv_do_fast_nested_hypercall16 (u16 code,u64 input1,u64 input2) +{ +u64 control = (u64 )code |HV_HYPERCALL_FAST_BIT |HV_HYPERCALL_NESTED ; + +return _hv_do_fast_hypercall16 (control, input1, input2); +} +extern struct hv_vp_assist_page **hv_vp_assist_page;static inline struct hv_vp_assist_page *hv_get_vp_assist_page (unsigned int cpu)include/asm-generic/hyperv-tlfs.h +0
-0
include/asm-generic/hyperv-tlfs.h enum HV_GENERIC_SET_FORMAT { · line 200
Quoted 7 lines
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)
PATCH v9 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
@@ -2745,7 +2745,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 v9 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 | 10 ++++++++++ drivers/hv/vmbus_drv.c | 3 ++- 5 files changed, 35 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
@@ -126,6 +126,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 +10 -0
@@ -160,6 +160,16 @@ 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
@@ -2768,7 +2768,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