/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _ASM_X86_MSHYPER_H #define _ASM_X86_MSHYPER_H #include #include #include #include #include #include #include typedef int (*hyperv_fill_flush_list_func)( struct hv_guest_mapping_flush_list *flush, void *data); static inline void hv_set_register(unsigned int reg, u64 value) { wrmsrl(reg, value); } static inline u64 hv_get_register(unsigned int reg) { u64 value; rdmsrl(reg, value); return value; } #define hv_recommend_using_aeoi() \ (!(ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED)) #define hv_set_clocksource_vdso(val) \ ((val).vdso_clock_mode = VDSO_CLOCKMODE_HVCLOCK) #define hv_enable_vdso_clocksource() \ vclocks_set_used(VDSO_CLOCKMODE_HVCLOCK); #define hv_get_raw_timer() rdtsc_ordered() #define hv_get_vector() HYPERVISOR_CALLBACK_VECTOR /* * Reference to pv_ops must be inline so objtool * detection of noinstr violations can work correctly. */ static __always_inline void hv_setup_sched_clock(void *sched_clock) { #ifdef CONFIG_PARAVIRT pv_ops.time.sched_clock = sched_clock; #endif } void hyperv_vector_handler(struct pt_regs *regs); static inline void hv_enable_stimer0_percpu_irq(int irq) {} static inline void hv_disable_stimer0_percpu_irq(int irq) {} #if IS_ENABLED(CONFIG_HYPERV) extern int hyperv_init_cpuhp; extern void *hv_hypercall_pg; extern void __percpu **hyperv_pcpu_input_arg; extern void __percpu **hyperv_pcpu_output_arg; extern u64 hv_current_partition_id; int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages); int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id); int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); static inline u64 hv_do_hypercall(u64 control, void *input, void *output) { u64 input_address = input ? virt_to_phys(input) : 0; u64 output_address = output ? virt_to_phys(output) : 0; u64 hv_status; #ifdef CONFIG_X86_64 if (!hv_hypercall_pg) return U64_MAX; __asm__ __volatile__("mov %4, %%r8\n" CALL_NOSPEC : "=a" (hv_status), ASM_CALL_CONSTRAINT, "+c" (control), "+d" (input_address) : "r" (output_address), THUNK_TARGET(hv_hypercall_pg) : "cc", "memory", "r8", "r9", "r10", "r11"); #else u32 input_address_hi = upper_32_bits(input_address); u32 input_address_lo = lower_32_bits(input_address); u32 output_address_hi = upper_32_bits(output_address); u32 output_address_lo = lower_32_bits(output_address); if (!hv_hypercall_pg) return U64_MAX; __asm__ __volatile__(CALL_NOSPEC : "=A" (hv_status), "+c" (input_address_lo), ASM_CALL_CONSTRAINT : "A" (control), "b" (input_address_hi), "D"(output_address_hi), "S"(output_address_lo), THUNK_TARGET(hv_hypercall_pg) : "cc", "memory"); #endif /* !x86_64 */ return hv_status; } /* Fast hypercall with 8 bytes of input and no output */ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1) { u64 hv_status, control = (u64)code | HV_HYPERCALL_FAST_BIT; #ifdef CONFIG_X86_64 { __asm__ __volatile__(CALL_NOSPEC : "=a" (hv_status), ASM_CALL_CONSTRAINT, "+c" (control), "+d" (input1) : THUNK_TARGET(hv_hypercall_pg) : "cc", "r8", "r9", "r10", "r11"); } #else { u32 input1_hi = upper_32_bits(input1); u32 input1_lo = lower_32_bits(input1); __asm__ __volatile__ (CALL_NOSPEC : "=A"(hv_status), "+c"(input1_lo), ASM_CALL_CONSTRAINT : "A" (control), "b" (input1_hi), THUNK_TARGET(hv_hypercall_pg) : "cc", "edi", "esi"); } #endif return hv_status; } /* Fast hypercall with 16 bytes of input */ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2) { u64 hv_status, control = (u64)code | HV_HYPERCALL_FAST_BIT; #ifdef CONFIG_X86_64 { __asm__ __volatile__("mov %4, %%r8\n" CALL_NOSPEC : "=a" (hv_status), ASM_CALL_CONSTRAINT, "+c" (control), "+d" (input1) : "r" (input2), THUNK_TARGET(hv_hypercall_pg) : "cc", "r8", "r9", "r10", "r11"); } #else { u32 input1_hi = upper_32_bits(input1); u32 input1_lo = lower_32_bits(input1); u32 input2_hi = upper_32_bits(input2); u32 input2_lo = lower_32_bits(input2); __asm__ __volatile__ (CALL_NOSPEC : "=A"(hv_status), "+c"(input1_lo), ASM_CALL_CONSTRAINT : "A" (control), "b" (input1_hi), "D"(input2_hi), "S"(input2_lo), THUNK_TARGET(hv_hypercall_pg) : "cc"); } #endif return hv_status; } /* * Rep hypercalls. Callers of this functions are supposed to ensure that * rep_count and varhead_size comply with Hyper-V hypercall definition. */ static inline u64 hv_do_rep_hypercall(u16 code, u16 rep_count, u16 varhead_size, void *input, void *output) { u64 control = code; u64 status; u16 rep_comp; control |= (u64)varhead_size << HV_HYPERCALL_VARHEAD_OFFSET; control |= (u64)rep_count << HV_HYPERCALL_REP_COMP_OFFSET; do { status = hv_do_hypercall(control, input, output); if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) return status; /* Bits 32-43 of status have 'Reps completed' data. */ rep_comp = (status & HV_HYPERCALL_REP_COMP_MASK) >> HV_HYPERCALL_REP_COMP_OFFSET; control &= ~HV_HYPERCALL_REP_START_MASK; control |= (u64)rep_comp << HV_HYPERCALL_REP_START_OFFSET; touch_nmi_watchdog(); } while (rep_comp < rep_count); return status; } 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) { if (!hv_vp_assist_page) return NULL; return hv_vp_assist_page[cpu]; } void __init hyperv_init(void); void hyperv_setup_mmu_ops(void); void set_hv_tscchange_cb(void (*cb)(void)); void clear_hv_tscchange_cb(void); void hyperv_stop_tsc_emulation(void); int hyperv_flush_guest_mapping(u64 as); int hyperv_flush_guest_mapping_range(u64 as, hyperv_fill_flush_list_func fill_func, void *data); int hyperv_fill_flush_guest_mapping_list( struct hv_guest_mapping_flush_list *flush, u64 start_gfn, u64 end_gfn); extern bool hv_root_partition; #ifdef CONFIG_X86_64 void hv_apic_init(void); void __init hv_init_spinlocks(void); bool hv_vcpu_is_preempted(int vcpu); #else static inline void hv_apic_init(void) {} #endif static inline void hv_set_msi_entry_from_desc(union hv_msi_entry *msi_entry, struct msi_desc *msi_desc) { msi_entry->address.as_uint32 = msi_desc->msg.address_lo; msi_entry->data.as_uint32 = msi_desc->msg.data; } struct irq_domain *hv_create_pci_msi_domain(void); int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector, struct hv_interrupt_entry *entry); int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); #else /* CONFIG_HYPERV */ static inline void hyperv_init(void) {} static inline void hyperv_setup_mmu_ops(void) {} static inline void set_hv_tscchange_cb(void (*cb)(void)) {} static inline void clear_hv_tscchange_cb(void) {} static inline void hyperv_stop_tsc_emulation(void) {}; static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) { return NULL; } static inline int hyperv_flush_guest_mapping(u64 as) { return -1; } static inline int hyperv_flush_guest_mapping_range(u64 as, hyperv_fill_flush_list_func fill_func, void *data) { return -1; } #endif /* CONFIG_HYPERV */ #include #endif