Skip to content

Commit 22daa42

Browse files
kirylbp3tk0v
authored andcommitted
x86/mm: Add callbacks to prepare encrypted memory for kexec
AMD SEV and Intel TDX guests allocate shared buffers for performing I/O. This is done by allocating pages normally from the buddy allocator and then converting them to shared using set_memory_decrypted(). On kexec, the second kernel is unaware of which memory has been converted in this manner. It only sees E820_TYPE_RAM. Accessing shared memory as private is fatal. Therefore, the memory state must be reset to its original state before starting the new kernel with kexec. The process of converting shared memory back to private occurs in two steps: - enc_kexec_begin() stops new conversions. - enc_kexec_finish() unshares all existing shared memory, reverting it back to private. Signed-off-by: Kirill A. Shutemov <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Nikolay Borisov <[email protected]> Reviewed-by: Kai Huang <[email protected]> Tested-by: Tao Liu <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent c3abbf1 commit 22daa42

File tree

4 files changed

+38
-0
lines changed

4 files changed

+38
-0
lines changed

arch/x86/include/asm/x86_init.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,22 @@ struct x86_init_acpi {
149149
* @enc_status_change_finish Notify HV after the encryption status of a range is changed
150150
* @enc_tlb_flush_required Returns true if a TLB flush is needed before changing page encryption status
151151
* @enc_cache_flush_required Returns true if a cache flush is needed before changing page encryption status
152+
* @enc_kexec_begin Begin the two-step process of converting shared memory back
153+
* to private. It stops the new conversions from being started
154+
* and waits in-flight conversions to finish, if possible.
155+
* @enc_kexec_finish Finish the two-step process of converting shared memory to
156+
* private. All memory is private after the call when
157+
* the function returns.
158+
* It is called on only one CPU while the others are shut down
159+
* and with interrupts disabled.
152160
*/
153161
struct x86_guest {
154162
int (*enc_status_change_prepare)(unsigned long vaddr, int npages, bool enc);
155163
int (*enc_status_change_finish)(unsigned long vaddr, int npages, bool enc);
156164
bool (*enc_tlb_flush_required)(bool enc);
157165
bool (*enc_cache_flush_required)(void);
166+
void (*enc_kexec_begin)(void);
167+
void (*enc_kexec_finish)(void);
158168
};
159169

160170
/**

arch/x86/kernel/crash.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
128128
#ifdef CONFIG_HPET_TIMER
129129
hpet_disable();
130130
#endif
131+
132+
/*
133+
* Non-crash kexec calls enc_kexec_begin() while scheduling is still
134+
* active. This allows the callback to wait until all in-flight
135+
* shared<->private conversions are complete. In a crash scenario,
136+
* enc_kexec_begin() gets called after all but one CPU have been shut
137+
* down and interrupts have been disabled. This allows the callback to
138+
* detect a race with the conversion and report it.
139+
*/
140+
x86_platform.guest.enc_kexec_begin();
141+
x86_platform.guest.enc_kexec_finish();
142+
131143
crash_save_cpu(regs, safe_smp_processor_id());
132144
}
133145

arch/x86/kernel/reboot.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/delay.h>
1313
#include <linux/objtool.h>
1414
#include <linux/pgtable.h>
15+
#include <linux/kexec.h>
1516
#include <acpi/reboot.h>
1617
#include <asm/io.h>
1718
#include <asm/apic.h>
@@ -716,6 +717,14 @@ static void native_machine_emergency_restart(void)
716717

717718
void native_machine_shutdown(void)
718719
{
720+
/*
721+
* Call enc_kexec_begin() while all CPUs are still active and
722+
* interrupts are enabled. This will allow all in-flight memory
723+
* conversions to finish cleanly.
724+
*/
725+
if (kexec_in_progress)
726+
x86_platform.guest.enc_kexec_begin();
727+
719728
/* Stop the cpus and apics */
720729
#ifdef CONFIG_X86_IO_APIC
721730
/*
@@ -752,6 +761,9 @@ void native_machine_shutdown(void)
752761
#ifdef CONFIG_X86_64
753762
x86_platform.iommu_shutdown();
754763
#endif
764+
765+
if (kexec_in_progress)
766+
x86_platform.guest.enc_kexec_finish();
755767
}
756768

757769
static void __machine_emergency_restart(int emergency)

arch/x86/kernel/x86_init.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ static int enc_status_change_prepare_noop(unsigned long vaddr, int npages, bool
138138
static int enc_status_change_finish_noop(unsigned long vaddr, int npages, bool enc) { return 0; }
139139
static bool enc_tlb_flush_required_noop(bool enc) { return false; }
140140
static bool enc_cache_flush_required_noop(void) { return false; }
141+
static void enc_kexec_begin_noop(void) {}
142+
static void enc_kexec_finish_noop(void) {}
141143
static bool is_private_mmio_noop(u64 addr) {return false; }
142144

143145
struct x86_platform_ops x86_platform __ro_after_init = {
@@ -161,6 +163,8 @@ struct x86_platform_ops x86_platform __ro_after_init = {
161163
.enc_status_change_finish = enc_status_change_finish_noop,
162164
.enc_tlb_flush_required = enc_tlb_flush_required_noop,
163165
.enc_cache_flush_required = enc_cache_flush_required_noop,
166+
.enc_kexec_begin = enc_kexec_begin_noop,
167+
.enc_kexec_finish = enc_kexec_finish_noop,
164168
},
165169
};
166170

0 commit comments

Comments
 (0)