diff options
| -rw-r--r-- | arch/x86/hyperv/Makefile | 2 | ||||
| -rw-r--r-- | arch/x86/hyperv/hv_apic.c | 104 | ||||
| -rw-r--r-- | arch/x86/hyperv/hv_init.c | 5 | ||||
| -rw-r--r-- | arch/x86/include/asm/mshyperv.h | 4 |
4 files changed, 112 insertions, 3 deletions
diff --git a/arch/x86/hyperv/Makefile b/arch/x86/hyperv/Makefile index 367a8203cfcf..00ce4df01a09 100644 --- a/arch/x86/hyperv/Makefile +++ b/arch/x86/hyperv/Makefile | |||
| @@ -1 +1 @@ | |||
| obj-y := hv_init.o mmu.o | obj-y := hv_init.o mmu.o hv_apic.o | ||
diff --git a/arch/x86/hyperv/hv_apic.c b/arch/x86/hyperv/hv_apic.c new file mode 100644 index 000000000000..ca20e31d311c --- /dev/null +++ b/arch/x86/hyperv/hv_apic.c | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | /* | ||
| 4 | * Hyper-V specific APIC code. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2018, Microsoft, Inc. | ||
| 7 | * | ||
| 8 | * Author : K. Y. Srinivasan <kys@microsoft.com> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify it | ||
| 11 | * under the terms of the GNU General Public License version 2 as published | ||
| 12 | * by the Free Software Foundation. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, but | ||
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
| 17 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
| 18 | * details. | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/version.h> | ||
| 24 | #include <linux/vmalloc.h> | ||
| 25 | #include <linux/mm.h> | ||
| 26 | #include <linux/clockchips.h> | ||
| 27 | #include <linux/hyperv.h> | ||
| 28 | #include <linux/slab.h> | ||
| 29 | #include <linux/cpuhotplug.h> | ||
| 30 | #include <asm/hypervisor.h> | ||
| 31 | #include <asm/mshyperv.h> | ||
| 32 | |||
| 33 | #ifdef CONFIG_X86_64 | ||
| 34 | #if IS_ENABLED(CONFIG_HYPERV) | ||
| 35 | |||
| 36 | static u64 hv_apic_icr_read(void) | ||
| 37 | { | ||
| 38 | u64 reg_val; | ||
| 39 | |||
| 40 | rdmsrl(HV_X64_MSR_ICR, reg_val); | ||
| 41 | return reg_val; | ||
| 42 | } | ||
| 43 | |||
| 44 | static void hv_apic_icr_write(u32 low, u32 id) | ||
| 45 | { | ||
| 46 | u64 reg_val; | ||
| 47 | |||
| 48 | reg_val = SET_APIC_DEST_FIELD(id); | ||
| 49 | reg_val = reg_val << 32; | ||
| 50 | reg_val |= low; | ||
| 51 | |||
| 52 | wrmsrl(HV_X64_MSR_ICR, reg_val); | ||
| 53 | } | ||
| 54 | |||
| 55 | static u32 hv_apic_read(u32 reg) | ||
| 56 | { | ||
| 57 | u32 reg_val, hi; | ||
| 58 | |||
| 59 | switch (reg) { | ||
| 60 | case APIC_EOI: | ||
| 61 | rdmsr(HV_X64_MSR_EOI, reg_val, hi); | ||
| 62 | return reg_val; | ||
| 63 | case APIC_TASKPRI: | ||
| 64 | rdmsr(HV_X64_MSR_TPR, reg_val, hi); | ||
| 65 | return reg_val; | ||
| 66 | |||
| 67 | default: | ||
| 68 | return native_apic_mem_read(reg); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | static void hv_apic_write(u32 reg, u32 val) | ||
| 73 | { | ||
| 74 | switch (reg) { | ||
| 75 | case APIC_EOI: | ||
| 76 | wrmsr(HV_X64_MSR_EOI, val, 0); | ||
| 77 | break; | ||
| 78 | case APIC_TASKPRI: | ||
| 79 | wrmsr(HV_X64_MSR_TPR, val, 0); | ||
| 80 | break; | ||
| 81 | default: | ||
| 82 | native_apic_mem_write(reg, val); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | static void hv_apic_eoi_write(u32 reg, u32 val) | ||
| 87 | { | ||
| 88 | wrmsr(HV_X64_MSR_EOI, val, 0); | ||
| 89 | } | ||
| 90 | |||
| 91 | void __init hv_apic_init(void) | ||
| 92 | { | ||
| 93 | if (ms_hyperv.hints & HV_X64_APIC_ACCESS_RECOMMENDED) { | ||
| 94 | pr_info("Hyper-V: Using MSR based APIC access\n"); | ||
| 95 | apic_set_eoi_write(hv_apic_eoi_write); | ||
| 96 | apic->read = hv_apic_read; | ||
| 97 | apic->write = hv_apic_write; | ||
| 98 | apic->icr_write = hv_apic_icr_write; | ||
| 99 | apic->icr_read = hv_apic_icr_read; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | #endif /* CONFIG_HYPERV */ | ||
| 104 | #endif /* CONFIG_X86_64 */ | ||
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index cfecc2272f2d..71e50fc2b7ef 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c | |||
| @@ -242,8 +242,9 @@ static int hv_cpu_die(unsigned int cpu) | |||
| 242 | * | 242 | * |
| 243 | * 1. Setup the hypercall page. | 243 | * 1. Setup the hypercall page. |
| 244 | * 2. Register Hyper-V specific clocksource. | 244 | * 2. Register Hyper-V specific clocksource. |
| 245 | * 3. Setup Hyper-V specific APIC entry points. | ||
| 245 | */ | 246 | */ |
| 246 | void hyperv_init(void) | 247 | void __init hyperv_init(void) |
| 247 | { | 248 | { |
| 248 | u64 guest_id, required_msrs; | 249 | u64 guest_id, required_msrs; |
| 249 | union hv_x64_msr_hypercall_contents hypercall_msr; | 250 | union hv_x64_msr_hypercall_contents hypercall_msr; |
| @@ -298,6 +299,8 @@ void hyperv_init(void) | |||
| 298 | 299 | ||
| 299 | hyper_alloc_mmu(); | 300 | hyper_alloc_mmu(); |
| 300 | 301 | ||
| 302 | hv_apic_init(); | ||
| 303 | |||
| 301 | /* | 304 | /* |
| 302 | * Register Hyper-V specific clocksource. | 305 | * Register Hyper-V specific clocksource. |
| 303 | */ | 306 | */ |
diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index b90e79610cf7..162977b82e2e 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h | |||
| @@ -258,7 +258,7 @@ static inline int hv_cpu_number_to_vp_number(int cpu_number) | |||
| 258 | return hv_vp_index[cpu_number]; | 258 | return hv_vp_index[cpu_number]; |
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | void hyperv_init(void); | 261 | void __init hyperv_init(void); |
| 262 | void hyperv_setup_mmu_ops(void); | 262 | void hyperv_setup_mmu_ops(void); |
| 263 | void hyper_alloc_mmu(void); | 263 | void hyper_alloc_mmu(void); |
| 264 | void hyperv_report_panic(struct pt_regs *regs, long err); | 264 | void hyperv_report_panic(struct pt_regs *regs, long err); |
| @@ -269,6 +269,7 @@ void hyperv_reenlightenment_intr(struct pt_regs *regs); | |||
| 269 | void set_hv_tscchange_cb(void (*cb)(void)); | 269 | void set_hv_tscchange_cb(void (*cb)(void)); |
| 270 | void clear_hv_tscchange_cb(void); | 270 | void clear_hv_tscchange_cb(void); |
| 271 | void hyperv_stop_tsc_emulation(void); | 271 | void hyperv_stop_tsc_emulation(void); |
| 272 | void hv_apic_init(void); | ||
| 272 | #else /* CONFIG_HYPERV */ | 273 | #else /* CONFIG_HYPERV */ |
| 273 | static inline void hyperv_init(void) {} | 274 | static inline void hyperv_init(void) {} |
| 274 | static inline bool hv_is_hyperv_initialized(void) { return false; } | 275 | static inline bool hv_is_hyperv_initialized(void) { return false; } |
| @@ -277,6 +278,7 @@ static inline void hyperv_setup_mmu_ops(void) {} | |||
| 277 | static inline void set_hv_tscchange_cb(void (*cb)(void)) {} | 278 | static inline void set_hv_tscchange_cb(void (*cb)(void)) {} |
| 278 | static inline void clear_hv_tscchange_cb(void) {} | 279 | static inline void clear_hv_tscchange_cb(void) {} |
| 279 | static inline void hyperv_stop_tsc_emulation(void) {}; | 280 | static inline void hyperv_stop_tsc_emulation(void) {}; |
| 281 | static inline void hv_apic_init(void) {} | ||
| 280 | static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) | 282 | static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu) |
| 281 | { | 283 | { |
| 282 | return NULL; | 284 | return NULL; |
