1/*
2 * HyperV  Detection code.
3 *
4 * Copyright (C) 2010, Novell, Inc.
5 * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 */
12
13#include <linux/types.h>
14#include <linux/time.h>
15#include <linux/clocksource.h>
16#include <linux/module.h>
17#include <linux/hardirq.h>
18#include <linux/efi.h>
19#include <linux/interrupt.h>
20#include <linux/irq.h>
21#include <asm/processor.h>
22#include <asm/hypervisor.h>
23#include <asm/hyperv.h>
24#include <asm/mshyperv.h>
25#include <asm/desc.h>
26#include <asm/idle.h>
27#include <asm/irq_regs.h>
28#include <asm/i8259.h>
29#include <asm/apic.h>
30#include <asm/timer.h>
31
32struct ms_hyperv_info ms_hyperv;
33EXPORT_SYMBOL_GPL(ms_hyperv);
34
35#if IS_ENABLED(CONFIG_HYPERV)
36static void (*vmbus_handler)(void);
37
38void hyperv_vector_handler(struct pt_regs *regs)
39{
40	struct pt_regs *old_regs = set_irq_regs(regs);
41
42	irq_enter();
43	exit_idle();
44
45	inc_irq_stat(irq_hv_callback_count);
46	if (vmbus_handler)
47		vmbus_handler();
48
49	irq_exit();
50	set_irq_regs(old_regs);
51}
52
53void hv_setup_vmbus_irq(void (*handler)(void))
54{
55	vmbus_handler = handler;
56	/*
57	 * Setup the IDT for hypervisor callback. Prevent reallocation
58	 * at module reload.
59	 */
60	if (!test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors))
61		alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR,
62				hyperv_callback_vector);
63}
64
65void hv_remove_vmbus_irq(void)
66{
67	/* We have no way to deallocate the interrupt gate */
68	vmbus_handler = NULL;
69}
70EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
71EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
72#endif
73
74static uint32_t  __init ms_hyperv_platform(void)
75{
76	u32 eax;
77	u32 hyp_signature[3];
78
79	if (!boot_cpu_has(X86_FEATURE_HYPERVISOR))
80		return 0;
81
82	cpuid(HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS,
83	      &eax, &hyp_signature[0], &hyp_signature[1], &hyp_signature[2]);
84
85	if (eax >= HYPERV_CPUID_MIN &&
86	    eax <= HYPERV_CPUID_MAX &&
87	    !memcmp("Microsoft Hv", hyp_signature, 12))
88		return HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS;
89
90	return 0;
91}
92
93static cycle_t read_hv_clock(struct clocksource *arg)
94{
95	cycle_t current_tick;
96	/*
97	 * Read the partition counter to get the current tick count. This count
98	 * is set to 0 when the partition is created and is incremented in
99	 * 100 nanosecond units.
100	 */
101	rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
102	return current_tick;
103}
104
105static struct clocksource hyperv_cs = {
106	.name		= "hyperv_clocksource",
107	.rating		= 400, /* use this when running on Hyperv*/
108	.read		= read_hv_clock,
109	.mask		= CLOCKSOURCE_MASK(64),
110	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
111};
112
113static void __init ms_hyperv_init_platform(void)
114{
115	/*
116	 * Extract the features and hints
117	 */
118	ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES);
119	ms_hyperv.hints    = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
120
121	printk(KERN_INFO "HyperV: features 0x%x, hints 0x%x\n",
122	       ms_hyperv.features, ms_hyperv.hints);
123
124#ifdef CONFIG_X86_LOCAL_APIC
125	if (ms_hyperv.features & HV_X64_MSR_APIC_FREQUENCY_AVAILABLE) {
126		/*
127		 * Get the APIC frequency.
128		 */
129		u64	hv_lapic_frequency;
130
131		rdmsrl(HV_X64_MSR_APIC_FREQUENCY, hv_lapic_frequency);
132		hv_lapic_frequency = div_u64(hv_lapic_frequency, HZ);
133		lapic_timer_frequency = hv_lapic_frequency;
134		printk(KERN_INFO "HyperV: LAPIC Timer Frequency: %#x\n",
135				lapic_timer_frequency);
136	}
137#endif
138
139	if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)
140		clocksource_register_hz(&hyperv_cs, NSEC_PER_SEC/100);
141
142#ifdef CONFIG_X86_IO_APIC
143	no_timer_check = 1;
144#endif
145
146}
147
148const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = {
149	.name			= "Microsoft HyperV",
150	.detect			= ms_hyperv_platform,
151	.init_platform		= ms_hyperv_init_platform,
152};
153EXPORT_SYMBOL(x86_hyper_ms_hyperv);
154