Commit | Line | Data |
---|---|---|
901d209a JG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
418492ba | 3 | #include <linux/acpi.h> |
98f2a47a VK |
4 | #include <linux/cpu.h> |
5 | #include <linux/kexec.h> | |
a5d5f328 | 6 | #include <linux/memblock.h> |
251e90e7 | 7 | #include <linux/virtio_anchor.h> |
98f2a47a VK |
8 | |
9 | #include <xen/features.h> | |
10 | #include <xen/events.h> | |
b1c3497e JM |
11 | #include <xen/hvm.h> |
12 | #include <xen/interface/hvm/hvm_op.h> | |
98f2a47a VK |
13 | #include <xen/interface/memory.h> |
14 | ||
c8980fcb | 15 | #include <asm/apic.h> |
98f2a47a VK |
16 | #include <asm/cpu.h> |
17 | #include <asm/smp.h> | |
13c01139 | 18 | #include <asm/io_apic.h> |
98f2a47a VK |
19 | #include <asm/reboot.h> |
20 | #include <asm/setup.h> | |
cb09ea29 | 21 | #include <asm/idtentry.h> |
98f2a47a | 22 | #include <asm/hypervisor.h> |
a5d5f328 | 23 | #include <asm/e820/api.h> |
4ca83dcf | 24 | #include <asm/early_ioremap.h> |
98f2a47a VK |
25 | |
26 | #include <asm/xen/cpuid.h> | |
27 | #include <asm/xen/hypervisor.h> | |
a5d5f328 | 28 | #include <asm/xen/page.h> |
98f2a47a VK |
29 | |
30 | #include "xen-ops.h" | |
31 | #include "mmu.h" | |
32 | #include "smp.h" | |
33 | ||
4ca83dcf JG |
34 | static unsigned long shared_info_pfn; |
35 | ||
b1c3497e JM |
36 | __ro_after_init bool xen_percpu_upcall; |
37 | EXPORT_SYMBOL_GPL(xen_percpu_upcall); | |
38 | ||
10231f69 | 39 | void xen_hvm_init_shared_info(void) |
98f2a47a | 40 | { |
98f2a47a | 41 | struct xen_add_to_physmap xatp; |
98f2a47a | 42 | |
98f2a47a VK |
43 | xatp.domid = DOMID_SELF; |
44 | xatp.idx = 0; | |
45 | xatp.space = XENMAPSPACE_shared_info; | |
4ca83dcf | 46 | xatp.gpfn = shared_info_pfn; |
98f2a47a VK |
47 | if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) |
48 | BUG(); | |
98f2a47a VK |
49 | } |
50 | ||
10231f69 JG |
51 | static void __init reserve_shared_info(void) |
52 | { | |
53 | u64 pa; | |
54 | ||
55 | /* | |
56 | * Search for a free page starting at 4kB physical address. | |
57 | * Low memory is preferred to avoid an EPT large page split up | |
58 | * by the mapping. | |
59 | * Starting below X86_RESERVE_LOW (usually 64kB) is fine as | |
60 | * the BIOS used for HVM guests is well behaved and won't | |
61 | * clobber memory other than the first 4kB. | |
62 | */ | |
63 | for (pa = PAGE_SIZE; | |
64 | !e820__mapped_all(pa, pa + PAGE_SIZE, E820_TYPE_RAM) || | |
65 | memblock_is_reserved(pa); | |
66 | pa += PAGE_SIZE) | |
67 | ; | |
68 | ||
4ca83dcf JG |
69 | shared_info_pfn = PHYS_PFN(pa); |
70 | ||
10231f69 | 71 | memblock_reserve(pa, PAGE_SIZE); |
4ca83dcf JG |
72 | HYPERVISOR_shared_info = early_memremap(pa, PAGE_SIZE); |
73 | } | |
74 | ||
75 | static void __init xen_hvm_init_mem_mapping(void) | |
76 | { | |
77 | early_memunmap(HYPERVISOR_shared_info, PAGE_SIZE); | |
78 | HYPERVISOR_shared_info = __va(PFN_PHYS(shared_info_pfn)); | |
d1ecfa9d LF |
79 | |
80 | /* | |
81 | * The virtual address of the shared_info page has changed, so | |
82 | * the vcpu_info pointer for VCPU 0 is now stale. | |
83 | * | |
84 | * The prepare_boot_cpu callback will re-initialize it via | |
85 | * xen_vcpu_setup, but we can't rely on that to be called for | |
86 | * old Xen versions (xen_have_vector_callback == 0). | |
87 | * | |
88 | * It is, in any case, bad to have a stale vcpu_info pointer | |
89 | * so reset it now. | |
90 | */ | |
91 | xen_vcpu_info_reset(0); | |
10231f69 JG |
92 | } |
93 | ||
98f2a47a VK |
94 | static void __init init_hvm_pv_info(void) |
95 | { | |
96 | int major, minor; | |
97 | uint32_t eax, ebx, ecx, edx, base; | |
98 | ||
99 | base = xen_cpuid_base(); | |
100 | eax = cpuid_eax(base + 1); | |
101 | ||
102 | major = eax >> 16; | |
103 | minor = eax & 0xffff; | |
104 | printk(KERN_INFO "Xen version %d.%d.\n", major, minor); | |
105 | ||
106 | xen_domain_type = XEN_HVM_DOMAIN; | |
107 | ||
108 | /* PVH set up hypercall page in xen_prepare_pvh(). */ | |
109 | if (xen_pvh_domain()) | |
110 | pv_info.name = "Xen PVH"; | |
111 | else { | |
112 | u64 pfn; | |
113 | uint32_t msr; | |
114 | ||
115 | pv_info.name = "Xen HVM"; | |
116 | msr = cpuid_ebx(base + 2); | |
117 | pfn = __pa(hypercall_page); | |
118 | wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32)); | |
119 | } | |
120 | ||
121 | xen_setup_features(); | |
122 | ||
123 | cpuid(base + 4, &eax, &ebx, &ecx, &edx); | |
124 | if (eax & XEN_HVM_CPUID_VCPU_ID_PRESENT) | |
125 | this_cpu_write(xen_vcpu_id, ebx); | |
126 | else | |
127 | this_cpu_write(xen_vcpu_id, smp_processor_id()); | |
128 | } | |
129 | ||
cb09ea29 TG |
130 | DEFINE_IDTENTRY_SYSVEC(sysvec_xen_hvm_callback) |
131 | { | |
132 | struct pt_regs *old_regs = set_irq_regs(regs); | |
133 | ||
b1c3497e | 134 | if (xen_percpu_upcall) |
670c04ad | 135 | apic_eoi(); |
b1c3497e | 136 | |
cb09ea29 TG |
137 | inc_irq_stat(irq_hv_callback_count); |
138 | ||
37510dd5 | 139 | xen_evtchn_do_upcall(); |
cb09ea29 TG |
140 | |
141 | set_irq_regs(old_regs); | |
142 | } | |
143 | ||
98f2a47a VK |
144 | #ifdef CONFIG_KEXEC_CORE |
145 | static void xen_hvm_shutdown(void) | |
146 | { | |
147 | native_machine_shutdown(); | |
148 | if (kexec_in_progress) | |
149 | xen_reboot(SHUTDOWN_soft_reset); | |
150 | } | |
151 | ||
152 | static void xen_hvm_crash_shutdown(struct pt_regs *regs) | |
153 | { | |
154 | native_machine_crash_shutdown(regs); | |
155 | xen_reboot(SHUTDOWN_soft_reset); | |
156 | } | |
157 | #endif | |
158 | ||
159 | static int xen_cpu_up_prepare_hvm(unsigned int cpu) | |
160 | { | |
c9b5d98b | 161 | int rc = 0; |
98f2a47a VK |
162 | |
163 | /* | |
ab24eb9a TG |
164 | * If a CPU was offlined earlier and offlining timed out then the |
165 | * lock mechanism is still initialized. Uninit it unconditionally | |
166 | * as it's safe to call even if already uninited. Interrupts and | |
167 | * timer have already been handled in xen_cpu_dead_hvm(). | |
98f2a47a | 168 | */ |
ab24eb9a | 169 | xen_uninit_lock_cpu(cpu); |
98f2a47a | 170 | |
1a5d0f62 | 171 | if (cpu_acpi_id(cpu) != CPU_ACPIID_INVALID) |
98f2a47a VK |
172 | per_cpu(xen_vcpu_id, cpu) = cpu_acpi_id(cpu); |
173 | else | |
174 | per_cpu(xen_vcpu_id, cpu) = cpu; | |
12ad6cfc JG |
175 | xen_vcpu_setup(cpu); |
176 | if (!xen_have_vector_callback) | |
177 | return 0; | |
98f2a47a | 178 | |
b1c3497e JM |
179 | if (xen_percpu_upcall) { |
180 | rc = xen_set_upcall_vector(cpu); | |
181 | if (rc) { | |
182 | WARN(1, "HVMOP_set_evtchn_upcall_vector" | |
183 | " for CPU %d failed: %d\n", cpu, rc); | |
184 | return rc; | |
185 | } | |
186 | } | |
187 | ||
4621dc6a | 188 | if (xen_feature(XENFEAT_hvm_safe_pvclock)) |
98f2a47a VK |
189 | xen_setup_timer(cpu); |
190 | ||
191 | rc = xen_smp_intr_init(cpu); | |
192 | if (rc) { | |
193 | WARN(1, "xen_smp_intr_init() for CPU %d failed: %d\n", | |
194 | cpu, rc); | |
98f2a47a | 195 | } |
c9b5d98b | 196 | return rc; |
98f2a47a VK |
197 | } |
198 | ||
199 | static int xen_cpu_dead_hvm(unsigned int cpu) | |
200 | { | |
201 | xen_smp_intr_free(cpu); | |
202 | ||
84d582d2 | 203 | if (xen_have_vector_callback && xen_feature(XENFEAT_hvm_safe_pvclock)) |
98f2a47a | 204 | xen_teardown_timer(cpu); |
afea27dc | 205 | return 0; |
98f2a47a VK |
206 | } |
207 | ||
208 | static void __init xen_hvm_guest_init(void) | |
209 | { | |
210 | if (xen_pv_domain()) | |
211 | return; | |
212 | ||
251e90e7 | 213 | if (IS_ENABLED(CONFIG_XEN_VIRTIO_FORCE_GRANT)) |
61367688 | 214 | virtio_set_mem_acc_cb(xen_virtio_restricted_mem_acc); |
fa1f5742 | 215 | |
98f2a47a VK |
216 | init_hvm_pv_info(); |
217 | ||
10231f69 | 218 | reserve_shared_info(); |
98f2a47a VK |
219 | xen_hvm_init_shared_info(); |
220 | ||
0b64ffb8 AA |
221 | /* |
222 | * xen_vcpu is a pointer to the vcpu_info struct in the shared_info | |
223 | * page, we use it in the event channel upcall and in some pvclock | |
224 | * related functions. | |
225 | */ | |
226 | xen_vcpu_info_reset(0); | |
227 | ||
98f2a47a VK |
228 | xen_panic_handler_init(); |
229 | ||
98f2a47a VK |
230 | xen_hvm_smp_init(); |
231 | WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_hvm, xen_cpu_dead_hvm)); | |
232 | xen_unplug_emulated_devices(); | |
233 | x86_init.irqs.intr_init = xen_init_IRQ; | |
234 | xen_hvm_init_time_ops(); | |
235 | xen_hvm_init_mmu_ops(); | |
236 | ||
98f2a47a VK |
237 | #ifdef CONFIG_KEXEC_CORE |
238 | machine_ops.shutdown = xen_hvm_shutdown; | |
239 | machine_ops.crash_shutdown = xen_hvm_crash_shutdown; | |
240 | #endif | |
241 | } | |
242 | ||
98f2a47a VK |
243 | static __init int xen_parse_nopv(char *arg) |
244 | { | |
b39b0497 ZD |
245 | pr_notice("\"xen_nopv\" is deprecated, please use \"nopv\" instead\n"); |
246 | ||
247 | if (xen_cpuid_base()) | |
248 | nopv = true; | |
249 | return 0; | |
98f2a47a VK |
250 | } |
251 | early_param("xen_nopv", xen_parse_nopv); | |
252 | ||
b36b0fe9 DW |
253 | static __init int xen_parse_no_vector_callback(char *arg) |
254 | { | |
b1c3497e | 255 | xen_have_vector_callback = false; |
b36b0fe9 DW |
256 | return 0; |
257 | } | |
258 | early_param("xen_no_vector_callback", xen_parse_no_vector_callback); | |
259 | ||
c8980fcb | 260 | static __init bool xen_x2apic_available(void) |
98f2a47a | 261 | { |
c8980fcb | 262 | return x2apic_supported(); |
98f2a47a | 263 | } |
98f2a47a | 264 | |
e07e98da RPM |
265 | static bool __init msi_ext_dest_id(void) |
266 | { | |
267 | return cpuid_eax(xen_cpuid_base() + 4) & XEN_HVM_CPUID_EXT_DEST_ID; | |
268 | } | |
269 | ||
418492ba JG |
270 | static __init void xen_hvm_guest_late_init(void) |
271 | { | |
272 | #ifdef CONFIG_XEN_PVH | |
273 | /* Test for PVH domain (PVH boot path taken overrides ACPI flags). */ | |
274 | if (!xen_pvh && | |
275 | (x86_platform.legacy.rtc || !x86_platform.legacy.no_vga)) | |
276 | return; | |
277 | ||
278 | /* PVH detected. */ | |
279 | xen_pvh = true; | |
280 | ||
bef6e0ae ZD |
281 | if (nopv) |
282 | panic("\"nopv\" and \"xen_nopv\" parameters are unsupported in PVH guest."); | |
283 | ||
418492ba JG |
284 | /* Make sure we don't fall back to (default) ACPI_IRQ_MODEL_PIC. */ |
285 | if (!nr_ioapics && acpi_irq_model == ACPI_IRQ_MODEL_PIC) | |
286 | acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM; | |
287 | ||
288 | machine_ops.emergency_restart = xen_emergency_restart; | |
289 | pv_info.name = "Xen PVH"; | |
290 | #endif | |
291 | } | |
292 | ||
bef6e0ae ZD |
293 | static uint32_t __init xen_platform_hvm(void) |
294 | { | |
295 | uint32_t xen_domain = xen_cpuid_base(); | |
296 | struct x86_hyper_init *h = &x86_hyper_xen_hvm.init; | |
297 | ||
298 | if (xen_pv_domain()) | |
299 | return 0; | |
300 | ||
301 | if (xen_pvh_domain() && nopv) { | |
302 | /* Guest booting via the Xen-PVH boot entry goes here */ | |
303 | pr_info("\"nopv\" parameter is ignored in PVH guest\n"); | |
304 | nopv = false; | |
305 | } else if (nopv && xen_domain) { | |
306 | /* | |
307 | * Guest booting via normal boot entry (like via grub2) goes | |
308 | * here. | |
309 | * | |
310 | * Use interface functions for bare hardware if nopv, | |
311 | * xen_hvm_guest_late_init is an exception as we need to | |
312 | * detect PVH and panic there. | |
313 | */ | |
314 | h->init_platform = x86_init_noop; | |
315 | h->x2apic_available = bool_x86_init_noop; | |
316 | h->init_mem_mapping = x86_init_noop; | |
317 | h->init_after_bootmem = x86_init_noop; | |
318 | h->guest_late_init = xen_hvm_guest_late_init; | |
319 | x86_hyper_xen_hvm.runtime.pin_vcpu = x86_op_int_noop; | |
320 | } | |
321 | return xen_domain; | |
322 | } | |
323 | ||
cc8f3b4d | 324 | struct hypervisor_x86 x86_hyper_xen_hvm __initdata = { |
98f2a47a VK |
325 | .name = "Xen HVM", |
326 | .detect = xen_platform_hvm, | |
03b2a320 | 327 | .type = X86_HYPER_XEN_HVM, |
f72e38e8 | 328 | .init.init_platform = xen_hvm_guest_init, |
c8980fcb | 329 | .init.x2apic_available = xen_x2apic_available, |
f72e38e8 | 330 | .init.init_mem_mapping = xen_hvm_init_mem_mapping, |
418492ba | 331 | .init.guest_late_init = xen_hvm_guest_late_init, |
e07e98da | 332 | .init.msi_ext_dest_id = msi_ext_dest_id, |
f72e38e8 | 333 | .runtime.pin_vcpu = xen_pin_vcpu, |
bef6e0ae | 334 | .ignore_nopv = true, |
98f2a47a | 335 | }; |