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