Skip to content

Commit 05bd330

Browse files
dcuiKAGA-KOKO
authored andcommitted
x86/hyperv: Suspend/resume the hypercall page for hibernation
For hibernation the hypercall page must be disabled before the hibernation image is created so that subsequent hypercall operations fail safely. On resume the hypercall page has to be restored and reenabled to ensure proper operation of the resumed kernel. Implement the necessary suspend/resume callbacks. [ tglx: Decrypted changelog ] Signed-off-by: Dexuan Cui <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 6f1a489 commit 05bd330

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

arch/x86/hyperv/hv_init.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@
2121
#include <linux/hyperv.h>
2222
#include <linux/slab.h>
2323
#include <linux/cpuhotplug.h>
24+
#include <linux/syscore_ops.h>
2425
#include <clocksource/hyperv_timer.h>
2526

2627
void *hv_hypercall_pg;
2728
EXPORT_SYMBOL_GPL(hv_hypercall_pg);
2829

30+
/* Storage to save the hypercall page temporarily for hibernation */
31+
static void *hv_hypercall_pg_saved;
32+
2933
u32 *hv_vp_index;
3034
EXPORT_SYMBOL_GPL(hv_vp_index);
3135

@@ -246,6 +250,48 @@ static int __init hv_pci_init(void)
246250
return 1;
247251
}
248252

253+
static int hv_suspend(void)
254+
{
255+
union hv_x64_msr_hypercall_contents hypercall_msr;
256+
257+
/*
258+
* Reset the hypercall page as it is going to be invalidated
259+
* accross hibernation. Setting hv_hypercall_pg to NULL ensures
260+
* that any subsequent hypercall operation fails safely instead of
261+
* crashing due to an access of an invalid page. The hypercall page
262+
* pointer is restored on resume.
263+
*/
264+
hv_hypercall_pg_saved = hv_hypercall_pg;
265+
hv_hypercall_pg = NULL;
266+
267+
/* Disable the hypercall page in the hypervisor */
268+
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
269+
hypercall_msr.enable = 0;
270+
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
271+
272+
return 0;
273+
}
274+
275+
static void hv_resume(void)
276+
{
277+
union hv_x64_msr_hypercall_contents hypercall_msr;
278+
279+
/* Re-enable the hypercall page */
280+
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
281+
hypercall_msr.enable = 1;
282+
hypercall_msr.guest_physical_address =
283+
vmalloc_to_pfn(hv_hypercall_pg_saved);
284+
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
285+
286+
hv_hypercall_pg = hv_hypercall_pg_saved;
287+
hv_hypercall_pg_saved = NULL;
288+
}
289+
290+
static struct syscore_ops hv_syscore_ops = {
291+
.suspend = hv_suspend,
292+
.resume = hv_resume,
293+
};
294+
249295
/*
250296
* This function is to be invoked early in the boot sequence after the
251297
* hypervisor has been detected.
@@ -330,6 +376,8 @@ void __init hyperv_init(void)
330376

331377
x86_init.pci.arch_init = hv_pci_init;
332378

379+
register_syscore_ops(&hv_syscore_ops);
380+
333381
return;
334382

335383
remove_cpuhp_state:
@@ -349,6 +397,8 @@ void hyperv_cleanup(void)
349397
{
350398
union hv_x64_msr_hypercall_contents hypercall_msr;
351399

400+
unregister_syscore_ops(&hv_syscore_ops);
401+
352402
/* Reset our OS id */
353403
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
354404

0 commit comments

Comments
 (0)