1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include <xen/xen.h> |
3 | #include <xen/events.h> |
4 | #include <xen/grant_table.h> |
5 | #include <xen/hvm.h> |
6 | #include <xen/interface/vcpu.h> |
7 | #include <xen/interface/xen.h> |
8 | #include <xen/interface/memory.h> |
9 | #include <xen/interface/hvm/params.h> |
10 | #include <xen/features.h> |
11 | #include <xen/platform_pci.h> |
12 | #include <xen/xenbus.h> |
13 | #include <xen/page.h> |
14 | #include <xen/interface/sched.h> |
15 | #include <xen/xen-ops.h> |
16 | #include <asm/xen/hypervisor.h> |
17 | #include <asm/xen/hypercall.h> |
18 | #include <asm/system_misc.h> |
19 | #include <asm/efi.h> |
20 | #include <linux/interrupt.h> |
21 | #include <linux/irqreturn.h> |
22 | #include <linux/module.h> |
23 | #include <linux/of.h> |
24 | #include <linux/of_fdt.h> |
25 | #include <linux/of_irq.h> |
26 | #include <linux/of_address.h> |
27 | #include <linux/cpuidle.h> |
28 | #include <linux/cpufreq.h> |
29 | #include <linux/cpu.h> |
30 | #include <linux/console.h> |
31 | #include <linux/pvclock_gtod.h> |
32 | #include <linux/reboot.h> |
33 | #include <linux/time64.h> |
34 | #include <linux/timekeeping.h> |
35 | #include <linux/timekeeper_internal.h> |
36 | #include <linux/acpi.h> |
37 | #include <linux/virtio_anchor.h> |
38 | |
39 | #include <linux/mm.h> |
40 | |
41 | static struct start_info _xen_start_info; |
42 | struct start_info *xen_start_info = &_xen_start_info; |
43 | EXPORT_SYMBOL(xen_start_info); |
44 | |
45 | enum xen_domain_type xen_domain_type = XEN_NATIVE; |
46 | EXPORT_SYMBOL(xen_domain_type); |
47 | |
48 | struct shared_info xen_dummy_shared_info; |
49 | struct shared_info *HYPERVISOR_shared_info = (void *)&xen_dummy_shared_info; |
50 | |
51 | DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu); |
52 | static struct vcpu_info __percpu *xen_vcpu_info; |
53 | |
54 | /* Linux <-> Xen vCPU id mapping */ |
55 | DEFINE_PER_CPU(uint32_t, xen_vcpu_id); |
56 | EXPORT_PER_CPU_SYMBOL(xen_vcpu_id); |
57 | |
58 | /* These are unused until we support booting "pre-ballooned" */ |
59 | unsigned long xen_released_pages; |
60 | struct xen_memory_region [XEN_EXTRA_MEM_MAX_REGIONS] __initdata; |
61 | |
62 | static __read_mostly unsigned int xen_events_irq; |
63 | static __read_mostly phys_addr_t xen_grant_frames; |
64 | |
65 | #define GRANT_TABLE_INDEX 0 |
66 | #define EXT_REGION_INDEX 1 |
67 | |
68 | uint32_t xen_start_flags; |
69 | EXPORT_SYMBOL(xen_start_flags); |
70 | |
71 | int xen_unmap_domain_gfn_range(struct vm_area_struct *vma, |
72 | int nr, struct page **pages) |
73 | { |
74 | return xen_xlate_unmap_gfn_range(vma, nr, pages); |
75 | } |
76 | EXPORT_SYMBOL_GPL(xen_unmap_domain_gfn_range); |
77 | |
78 | static void xen_read_wallclock(struct timespec64 *ts) |
79 | { |
80 | u32 version; |
81 | struct timespec64 now, ts_monotonic; |
82 | struct shared_info *s = HYPERVISOR_shared_info; |
83 | struct pvclock_wall_clock *wall_clock = &(s->wc); |
84 | |
85 | /* get wallclock at system boot */ |
86 | do { |
87 | version = wall_clock->version; |
88 | rmb(); /* fetch version before time */ |
89 | now.tv_sec = ((uint64_t)wall_clock->sec_hi << 32) | wall_clock->sec; |
90 | now.tv_nsec = wall_clock->nsec; |
91 | rmb(); /* fetch time before checking version */ |
92 | } while ((wall_clock->version & 1) || (version != wall_clock->version)); |
93 | |
94 | /* time since system boot */ |
95 | ktime_get_ts64(ts: &ts_monotonic); |
96 | *ts = timespec64_add(lhs: now, rhs: ts_monotonic); |
97 | } |
98 | |
99 | static int xen_pvclock_gtod_notify(struct notifier_block *nb, |
100 | unsigned long was_set, void *priv) |
101 | { |
102 | /* Protected by the calling core code serialization */ |
103 | static struct timespec64 next_sync; |
104 | |
105 | struct xen_platform_op op; |
106 | struct timespec64 now, system_time; |
107 | struct timekeeper *tk = priv; |
108 | |
109 | now.tv_sec = tk->xtime_sec; |
110 | now.tv_nsec = (long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift); |
111 | system_time = timespec64_add(lhs: now, rhs: tk->wall_to_monotonic); |
112 | |
113 | /* |
114 | * We only take the expensive HV call when the clock was set |
115 | * or when the 11 minutes RTC synchronization time elapsed. |
116 | */ |
117 | if (!was_set && timespec64_compare(lhs: &now, rhs: &next_sync) < 0) |
118 | return NOTIFY_OK; |
119 | |
120 | op.cmd = XENPF_settime64; |
121 | op.u.settime64.mbz = 0; |
122 | op.u.settime64.secs = now.tv_sec; |
123 | op.u.settime64.nsecs = now.tv_nsec; |
124 | op.u.settime64.system_time = timespec64_to_ns(ts: &system_time); |
125 | (void)HYPERVISOR_platform_op(op: &op); |
126 | |
127 | /* |
128 | * Move the next drift compensation time 11 minutes |
129 | * ahead. That's emulating the sync_cmos_clock() update for |
130 | * the hardware RTC. |
131 | */ |
132 | next_sync = now; |
133 | next_sync.tv_sec += 11 * 60; |
134 | |
135 | return NOTIFY_OK; |
136 | } |
137 | |
138 | static struct notifier_block xen_pvclock_gtod_notifier = { |
139 | .notifier_call = xen_pvclock_gtod_notify, |
140 | }; |
141 | |
142 | static int xen_starting_cpu(unsigned int cpu) |
143 | { |
144 | struct vcpu_register_vcpu_info info; |
145 | struct vcpu_info *vcpup; |
146 | int err; |
147 | |
148 | /* |
149 | * VCPUOP_register_vcpu_info cannot be called twice for the same |
150 | * vcpu, so if vcpu_info is already registered, just get out. This |
151 | * can happen with cpu-hotplug. |
152 | */ |
153 | if (per_cpu(xen_vcpu, cpu) != NULL) |
154 | goto after_register_vcpu_info; |
155 | |
156 | pr_info("Xen: initializing cpu%d\n" , cpu); |
157 | vcpup = per_cpu_ptr(xen_vcpu_info, cpu); |
158 | |
159 | info.mfn = percpu_to_gfn(vcpup); |
160 | info.offset = xen_offset_in_page(vcpup); |
161 | |
162 | err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, vcpuid: xen_vcpu_nr(cpu), |
163 | extra_args: &info); |
164 | BUG_ON(err); |
165 | per_cpu(xen_vcpu, cpu) = vcpup; |
166 | |
167 | after_register_vcpu_info: |
168 | enable_percpu_irq(irq: xen_events_irq, type: 0); |
169 | return 0; |
170 | } |
171 | |
172 | static int xen_dying_cpu(unsigned int cpu) |
173 | { |
174 | disable_percpu_irq(irq: xen_events_irq); |
175 | return 0; |
176 | } |
177 | |
178 | void xen_reboot(int reason) |
179 | { |
180 | struct sched_shutdown r = { .reason = reason }; |
181 | int rc; |
182 | |
183 | rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, arg: &r); |
184 | BUG_ON(rc); |
185 | } |
186 | |
187 | static int xen_restart(struct notifier_block *nb, unsigned long action, |
188 | void *data) |
189 | { |
190 | xen_reboot(SHUTDOWN_reboot); |
191 | |
192 | return NOTIFY_DONE; |
193 | } |
194 | |
195 | static struct notifier_block xen_restart_nb = { |
196 | .notifier_call = xen_restart, |
197 | .priority = 192, |
198 | }; |
199 | |
200 | static void xen_power_off(void) |
201 | { |
202 | xen_reboot(SHUTDOWN_poweroff); |
203 | } |
204 | |
205 | static irqreturn_t xen_arm_callback(int irq, void *arg) |
206 | { |
207 | xen_evtchn_do_upcall(); |
208 | return IRQ_HANDLED; |
209 | } |
210 | |
211 | static __initdata struct { |
212 | const char *compat; |
213 | const char *prefix; |
214 | const char *version; |
215 | bool found; |
216 | } hyper_node = {"xen,xen" , "xen,xen-" , NULL, false}; |
217 | |
218 | static int __init fdt_find_hyper_node(unsigned long node, const char *uname, |
219 | int depth, void *data) |
220 | { |
221 | const void *s = NULL; |
222 | int len; |
223 | |
224 | if (depth != 1 || strcmp(uname, "hypervisor" ) != 0) |
225 | return 0; |
226 | |
227 | if (of_flat_dt_is_compatible(node, name: hyper_node.compat)) |
228 | hyper_node.found = true; |
229 | |
230 | s = of_get_flat_dt_prop(node, name: "compatible" , size: &len); |
231 | if (strlen(hyper_node.prefix) + 3 < len && |
232 | !strncmp(hyper_node.prefix, s, strlen(hyper_node.prefix))) |
233 | hyper_node.version = s + strlen(hyper_node.prefix); |
234 | |
235 | /* |
236 | * Check if Xen supports EFI by checking whether there is the |
237 | * "/hypervisor/uefi" node in DT. If so, runtime services are available |
238 | * through proxy functions (e.g. in case of Xen dom0 EFI implementation |
239 | * they call special hypercall which executes relevant EFI functions) |
240 | * and that is why they are always enabled. |
241 | */ |
242 | if (IS_ENABLED(CONFIG_XEN_EFI)) { |
243 | if ((of_get_flat_dt_subnode_by_name(node, uname: "uefi" ) > 0) && |
244 | !efi_runtime_disabled()) |
245 | set_bit(EFI_RUNTIME_SERVICES, addr: &efi.flags); |
246 | } |
247 | |
248 | return 0; |
249 | } |
250 | |
251 | /* |
252 | * see Documentation/devicetree/bindings/arm/xen.txt for the |
253 | * documentation of the Xen Device Tree format. |
254 | */ |
255 | void __init xen_early_init(void) |
256 | { |
257 | of_scan_flat_dt(it: fdt_find_hyper_node, NULL); |
258 | if (!hyper_node.found) { |
259 | pr_debug("No Xen support\n" ); |
260 | return; |
261 | } |
262 | |
263 | if (hyper_node.version == NULL) { |
264 | pr_debug("Xen version not found\n" ); |
265 | return; |
266 | } |
267 | |
268 | pr_info("Xen %s support found\n" , hyper_node.version); |
269 | |
270 | xen_domain_type = XEN_HVM_DOMAIN; |
271 | |
272 | xen_setup_features(); |
273 | |
274 | if (xen_feature(XENFEAT_dom0)) |
275 | xen_start_flags |= SIF_INITDOMAIN|SIF_PRIVILEGED; |
276 | |
277 | if (!console_set_on_cmdline && !xen_initial_domain()) |
278 | add_preferred_console(name: "hvc" , idx: 0, NULL); |
279 | } |
280 | |
281 | static void __init xen_acpi_guest_init(void) |
282 | { |
283 | #ifdef CONFIG_ACPI |
284 | struct xen_hvm_param a; |
285 | int interrupt, trigger, polarity; |
286 | |
287 | a.domid = DOMID_SELF; |
288 | a.index = HVM_PARAM_CALLBACK_IRQ; |
289 | |
290 | if (HYPERVISOR_hvm_op(HVMOP_get_param, arg: &a) |
291 | || (a.value >> 56) != HVM_PARAM_CALLBACK_TYPE_PPI) { |
292 | xen_events_irq = 0; |
293 | return; |
294 | } |
295 | |
296 | interrupt = a.value & 0xff; |
297 | trigger = ((a.value >> 8) & 0x1) ? ACPI_EDGE_SENSITIVE |
298 | : ACPI_LEVEL_SENSITIVE; |
299 | polarity = ((a.value >> 8) & 0x2) ? ACPI_ACTIVE_LOW |
300 | : ACPI_ACTIVE_HIGH; |
301 | xen_events_irq = acpi_register_gsi(NULL, gsi: interrupt, triggering: trigger, polarity); |
302 | #endif |
303 | } |
304 | |
305 | #ifdef CONFIG_XEN_UNPOPULATED_ALLOC |
306 | /* |
307 | * A type-less specific Xen resource which contains extended regions |
308 | * (unused regions of guest physical address space provided by the hypervisor). |
309 | */ |
310 | static struct resource xen_resource = { |
311 | .name = "Xen unused space" , |
312 | }; |
313 | |
314 | int __init arch_xen_unpopulated_init(struct resource **res) |
315 | { |
316 | struct device_node *np; |
317 | struct resource *regs, *tmp_res; |
318 | uint64_t min_gpaddr = -1, max_gpaddr = 0; |
319 | unsigned int i, nr_reg = 0; |
320 | int rc; |
321 | |
322 | if (!xen_domain()) |
323 | return -ENODEV; |
324 | |
325 | if (!acpi_disabled) |
326 | return -ENODEV; |
327 | |
328 | np = of_find_compatible_node(NULL, NULL, compat: "xen,xen" ); |
329 | if (WARN_ON(!np)) |
330 | return -ENODEV; |
331 | |
332 | /* Skip region 0 which is reserved for grant table space */ |
333 | while (of_get_address(dev: np, index: nr_reg + EXT_REGION_INDEX, NULL, NULL)) |
334 | nr_reg++; |
335 | |
336 | if (!nr_reg) { |
337 | pr_err("No extended regions are found\n" ); |
338 | of_node_put(node: np); |
339 | return -EINVAL; |
340 | } |
341 | |
342 | regs = kcalloc(n: nr_reg, size: sizeof(*regs), GFP_KERNEL); |
343 | if (!regs) { |
344 | of_node_put(node: np); |
345 | return -ENOMEM; |
346 | } |
347 | |
348 | /* |
349 | * Create resource from extended regions provided by the hypervisor to be |
350 | * used as unused address space for Xen scratch pages. |
351 | */ |
352 | for (i = 0; i < nr_reg; i++) { |
353 | rc = of_address_to_resource(dev: np, index: i + EXT_REGION_INDEX, r: ®s[i]); |
354 | if (rc) |
355 | goto err; |
356 | |
357 | if (max_gpaddr < regs[i].end) |
358 | max_gpaddr = regs[i].end; |
359 | if (min_gpaddr > regs[i].start) |
360 | min_gpaddr = regs[i].start; |
361 | } |
362 | |
363 | xen_resource.start = min_gpaddr; |
364 | xen_resource.end = max_gpaddr; |
365 | |
366 | /* |
367 | * Mark holes between extended regions as unavailable. The rest of that |
368 | * address space will be available for the allocation. |
369 | */ |
370 | for (i = 1; i < nr_reg; i++) { |
371 | resource_size_t start, end; |
372 | |
373 | /* There is an overlap between regions */ |
374 | if (regs[i - 1].end + 1 > regs[i].start) { |
375 | rc = -EINVAL; |
376 | goto err; |
377 | } |
378 | |
379 | /* There is no hole between regions */ |
380 | if (regs[i - 1].end + 1 == regs[i].start) |
381 | continue; |
382 | |
383 | start = regs[i - 1].end + 1; |
384 | end = regs[i].start - 1; |
385 | |
386 | tmp_res = kzalloc(size: sizeof(*tmp_res), GFP_KERNEL); |
387 | if (!tmp_res) { |
388 | rc = -ENOMEM; |
389 | goto err; |
390 | } |
391 | |
392 | tmp_res->name = "Unavailable space" ; |
393 | tmp_res->start = start; |
394 | tmp_res->end = end; |
395 | |
396 | rc = insert_resource(parent: &xen_resource, new: tmp_res); |
397 | if (rc) { |
398 | pr_err("Cannot insert resource %pR (%d)\n" , tmp_res, rc); |
399 | kfree(objp: tmp_res); |
400 | goto err; |
401 | } |
402 | } |
403 | |
404 | *res = &xen_resource; |
405 | |
406 | err: |
407 | of_node_put(node: np); |
408 | kfree(objp: regs); |
409 | return rc; |
410 | } |
411 | #endif |
412 | |
413 | static void __init xen_dt_guest_init(void) |
414 | { |
415 | struct device_node *xen_node; |
416 | struct resource res; |
417 | |
418 | xen_node = of_find_compatible_node(NULL, NULL, compat: "xen,xen" ); |
419 | if (!xen_node) { |
420 | pr_err("Xen support was detected before, but it has disappeared\n" ); |
421 | return; |
422 | } |
423 | |
424 | xen_events_irq = irq_of_parse_and_map(node: xen_node, index: 0); |
425 | |
426 | if (of_address_to_resource(dev: xen_node, GRANT_TABLE_INDEX, r: &res)) { |
427 | pr_err("Xen grant table region is not found\n" ); |
428 | of_node_put(node: xen_node); |
429 | return; |
430 | } |
431 | of_node_put(node: xen_node); |
432 | xen_grant_frames = res.start; |
433 | } |
434 | |
435 | static int __init xen_guest_init(void) |
436 | { |
437 | struct xen_add_to_physmap xatp; |
438 | struct shared_info *shared_info_page = NULL; |
439 | int rc, cpu; |
440 | |
441 | if (!xen_domain()) |
442 | return 0; |
443 | |
444 | if (IS_ENABLED(CONFIG_XEN_VIRTIO)) |
445 | virtio_set_mem_acc_cb(func: xen_virtio_restricted_mem_acc); |
446 | |
447 | if (!acpi_disabled) |
448 | xen_acpi_guest_init(); |
449 | else |
450 | xen_dt_guest_init(); |
451 | |
452 | if (!xen_events_irq) { |
453 | pr_err("Xen event channel interrupt not found\n" ); |
454 | return -ENODEV; |
455 | } |
456 | |
457 | /* |
458 | * The fdt parsing codes have set EFI_RUNTIME_SERVICES if Xen EFI |
459 | * parameters are found. Force enable runtime services. |
460 | */ |
461 | if (efi_enabled(EFI_RUNTIME_SERVICES)) |
462 | xen_efi_runtime_setup(); |
463 | |
464 | shared_info_page = (struct shared_info *)get_zeroed_page(GFP_KERNEL); |
465 | |
466 | if (!shared_info_page) { |
467 | pr_err("not enough memory\n" ); |
468 | return -ENOMEM; |
469 | } |
470 | xatp.domid = DOMID_SELF; |
471 | xatp.idx = 0; |
472 | xatp.space = XENMAPSPACE_shared_info; |
473 | xatp.gpfn = virt_to_gfn(shared_info_page); |
474 | if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, arg: &xatp)) |
475 | BUG(); |
476 | |
477 | HYPERVISOR_shared_info = (struct shared_info *)shared_info_page; |
478 | |
479 | /* xen_vcpu is a pointer to the vcpu_info struct in the shared_info |
480 | * page, we use it in the event channel upcall and in some pvclock |
481 | * related functions. |
482 | * The shared info contains exactly 1 CPU (the boot CPU). The guest |
483 | * is required to use VCPUOP_register_vcpu_info to place vcpu info |
484 | * for secondary CPUs as they are brought up. |
485 | * For uniformity we use VCPUOP_register_vcpu_info even on cpu0. |
486 | */ |
487 | xen_vcpu_info = alloc_percpu(struct vcpu_info); |
488 | if (xen_vcpu_info == NULL) |
489 | return -ENOMEM; |
490 | |
491 | /* Direct vCPU id mapping for ARM guests. */ |
492 | for_each_possible_cpu(cpu) |
493 | per_cpu(xen_vcpu_id, cpu) = cpu; |
494 | |
495 | if (!xen_grant_frames) { |
496 | xen_auto_xlat_grant_frames.count = gnttab_max_grant_frames(); |
497 | rc = xen_xlate_map_ballooned_pages(pfns: &xen_auto_xlat_grant_frames.pfn, |
498 | vaddr: &xen_auto_xlat_grant_frames.vaddr, |
499 | nr_grant_frames: xen_auto_xlat_grant_frames.count); |
500 | } else |
501 | rc = gnttab_setup_auto_xlat_frames(addr: xen_grant_frames); |
502 | if (rc) { |
503 | free_percpu(pdata: xen_vcpu_info); |
504 | return rc; |
505 | } |
506 | gnttab_init(); |
507 | |
508 | /* |
509 | * Making sure board specific code will not set up ops for |
510 | * cpu idle and cpu freq. |
511 | */ |
512 | disable_cpuidle(); |
513 | disable_cpufreq(); |
514 | |
515 | xen_init_IRQ(); |
516 | |
517 | if (request_percpu_irq(irq: xen_events_irq, handler: xen_arm_callback, |
518 | devname: "events" , percpu_dev_id: &xen_vcpu)) { |
519 | pr_err("Error request IRQ %d\n" , xen_events_irq); |
520 | return -EINVAL; |
521 | } |
522 | |
523 | if (xen_initial_domain()) |
524 | pvclock_gtod_register_notifier(nb: &xen_pvclock_gtod_notifier); |
525 | |
526 | return cpuhp_setup_state(state: CPUHP_AP_ARM_XEN_STARTING, |
527 | name: "arm/xen:starting" , startup: xen_starting_cpu, |
528 | teardown: xen_dying_cpu); |
529 | } |
530 | early_initcall(xen_guest_init); |
531 | |
532 | static int xen_starting_runstate_cpu(unsigned int cpu) |
533 | { |
534 | xen_setup_runstate_info(cpu); |
535 | return 0; |
536 | } |
537 | |
538 | static int __init xen_late_init(void) |
539 | { |
540 | if (!xen_domain()) |
541 | return -ENODEV; |
542 | |
543 | pm_power_off = xen_power_off; |
544 | register_restart_handler(&xen_restart_nb); |
545 | if (!xen_initial_domain()) { |
546 | struct timespec64 ts; |
547 | xen_read_wallclock(ts: &ts); |
548 | do_settimeofday64(ts: &ts); |
549 | } |
550 | |
551 | if (xen_kernel_unmapped_at_usr()) |
552 | return 0; |
553 | |
554 | xen_time_setup_guest(); |
555 | |
556 | return cpuhp_setup_state(state: CPUHP_AP_ARM_XEN_RUNSTATE_STARTING, |
557 | name: "arm/xen_runstate:starting" , |
558 | startup: xen_starting_runstate_cpu, NULL); |
559 | } |
560 | late_initcall(xen_late_init); |
561 | |
562 | |
563 | /* empty stubs */ |
564 | void xen_arch_pre_suspend(void) { } |
565 | void xen_arch_post_suspend(int suspend_cancelled) { } |
566 | void xen_timer_resume(void) { } |
567 | void xen_arch_resume(void) { } |
568 | void xen_arch_suspend(void) { } |
569 | |
570 | |
571 | /* In the hypercall.S file. */ |
572 | EXPORT_SYMBOL_GPL(HYPERVISOR_event_channel_op); |
573 | EXPORT_SYMBOL_GPL(HYPERVISOR_grant_table_op); |
574 | EXPORT_SYMBOL_GPL(HYPERVISOR_xen_version); |
575 | EXPORT_SYMBOL_GPL(HYPERVISOR_console_io); |
576 | EXPORT_SYMBOL_GPL(HYPERVISOR_sched_op); |
577 | EXPORT_SYMBOL_GPL(HYPERVISOR_hvm_op); |
578 | EXPORT_SYMBOL_GPL(HYPERVISOR_memory_op); |
579 | EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op); |
580 | EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op); |
581 | EXPORT_SYMBOL_GPL(HYPERVISOR_platform_op_raw); |
582 | EXPORT_SYMBOL_GPL(HYPERVISOR_multicall); |
583 | EXPORT_SYMBOL_GPL(HYPERVISOR_vm_assist); |
584 | EXPORT_SYMBOL_GPL(HYPERVISOR_dm_op); |
585 | EXPORT_SYMBOL_GPL(privcmd_call); |
586 | |