Skip to content

Commit 8ef9795

Browse files
ashkalrabp3tk0v
authored andcommitted
crypto: ccp: Add panic notifier for SEV/SNP firmware shutdown on kdump
Add a kdump safe version of sev_firmware_shutdown() and register it as a crash_kexec_post_notifier so it will be invoked during panic/crash to do SEV/SNP shutdown. This is required for transitioning all IOMMU pages to reclaim/hypervisor state, otherwise re-init of IOMMU pages during crashdump kernel boot fails and panics the crashdump kernel. This panic notifier runs in atomic context, hence it ensures not to acquire any locks/mutexes and polls for PSP command completion instead of depending on PSP command completion interrupt. [ mdr: Remove use of "we" in comments. ] Signed-off-by: Ashish Kalra <[email protected]> Signed-off-by: Michael Roth <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent f366a8d commit 8ef9795

File tree

5 files changed

+102
-30
lines changed

5 files changed

+102
-30
lines changed

arch/x86/include/asm/sev.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
227227
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
228228
u64 snp_get_unsupported_features(u64 status);
229229
u64 sev_get_status(void);
230+
void kdump_sev_callback(void);
230231
#else
231232
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
232233
static inline void sev_es_ist_exit(void) { }
@@ -255,6 +256,7 @@ static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *in
255256
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
256257
static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
257258
static inline u64 sev_get_status(void) { return 0; }
259+
static inline void kdump_sev_callback(void) { }
258260
#endif
259261

260262
#ifdef CONFIG_KVM_AMD_SEV

arch/x86/kernel/crash.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <asm/intel_pt.h>
4141
#include <asm/crash.h>
4242
#include <asm/cmdline.h>
43+
#include <asm/sev.h>
4344

4445
/* Used while preparing memory map entries for second kernel */
4546
struct crash_memmap_data {
@@ -59,6 +60,8 @@ static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
5960
*/
6061
cpu_emergency_stop_pt();
6162

63+
kdump_sev_callback();
64+
6265
disable_local_APIC();
6366
}
6467

arch/x86/kernel/sev.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,3 +2265,13 @@ static int __init snp_init_platform_device(void)
22652265
return 0;
22662266
}
22672267
device_initcall(snp_init_platform_device);
2268+
2269+
void kdump_sev_callback(void)
2270+
{
2271+
/*
2272+
* Do wbinvd() on remote CPUs when SNP is enabled in order to
2273+
* safely do SNP_SHUTDOWN on the local CPU.
2274+
*/
2275+
if (cpu_feature_enabled(X86_FEATURE_SEV_SNP))
2276+
wbinvd();
2277+
}

arch/x86/virt/svm/sev.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,12 @@ static int __init snp_rmptable_init(void)
216216

217217
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/rmptable_init:online", __snp_enable, NULL);
218218

219+
/*
220+
* Setting crash_kexec_post_notifiers to 'true' to ensure that SNP panic
221+
* notifier is invoked to do SNP IOMMU shutdown before kdump.
222+
*/
223+
crash_kexec_post_notifiers = true;
224+
219225
return 0;
220226

221227
nosnp:

drivers/crypto/ccp/sev-dev.c

Lines changed: 81 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/hw_random.h>
2222
#include <linux/ccp.h>
2323
#include <linux/firmware.h>
24+
#include <linux/panic_notifier.h>
2425
#include <linux/gfp.h>
2526
#include <linux/cpufeature.h>
2627
#include <linux/fs.h>
@@ -143,6 +144,25 @@ static int sev_wait_cmd_ioc(struct sev_device *sev,
143144
{
144145
int ret;
145146

147+
/*
148+
* If invoked during panic handling, local interrupts are disabled,
149+
* so the PSP command completion interrupt can't be used. Poll for
150+
* PSP command completion instead.
151+
*/
152+
if (irqs_disabled()) {
153+
unsigned long timeout_usecs = (timeout * USEC_PER_SEC) / 10;
154+
155+
/* Poll for SEV command completion: */
156+
while (timeout_usecs--) {
157+
*reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg);
158+
if (*reg & PSP_CMDRESP_RESP)
159+
return 0;
160+
161+
udelay(10);
162+
}
163+
return -ETIMEDOUT;
164+
}
165+
146166
ret = wait_event_timeout(sev->int_queue,
147167
sev->int_rcvd, timeout * HZ);
148168
if (!ret)
@@ -1338,17 +1358,6 @@ static int __sev_platform_shutdown_locked(int *error)
13381358
return ret;
13391359
}
13401360

1341-
static int sev_platform_shutdown(int *error)
1342-
{
1343-
int rc;
1344-
1345-
mutex_lock(&sev_cmd_mutex);
1346-
rc = __sev_platform_shutdown_locked(NULL);
1347-
mutex_unlock(&sev_cmd_mutex);
1348-
1349-
return rc;
1350-
}
1351-
13521361
static int sev_get_platform_state(int *state, int *error)
13531362
{
13541363
struct sev_user_data_status data;
@@ -1624,7 +1633,7 @@ static int sev_update_firmware(struct device *dev)
16241633
return ret;
16251634
}
16261635

1627-
static int __sev_snp_shutdown_locked(int *error)
1636+
static int __sev_snp_shutdown_locked(int *error, bool panic)
16281637
{
16291638
struct sev_device *sev = psp_master->sev_data;
16301639
struct sev_data_snp_shutdown_ex data;
@@ -1637,7 +1646,16 @@ static int __sev_snp_shutdown_locked(int *error)
16371646
data.len = sizeof(data);
16381647
data.iommu_snp_shutdown = 1;
16391648

1640-
wbinvd_on_all_cpus();
1649+
/*
1650+
* If invoked during panic handling, local interrupts are disabled
1651+
* and all CPUs are stopped, so wbinvd_on_all_cpus() can't be called.
1652+
* In that case, a wbinvd() is done on remote CPUs via the NMI
1653+
* callback, so only a local wbinvd() is needed here.
1654+
*/
1655+
if (!panic)
1656+
wbinvd_on_all_cpus();
1657+
else
1658+
wbinvd();
16411659

16421660
ret = __sev_do_cmd_locked(SEV_CMD_SNP_SHUTDOWN_EX, &data, error);
16431661
/* SHUTDOWN may require DF_FLUSH */
@@ -1681,17 +1699,6 @@ static int __sev_snp_shutdown_locked(int *error)
16811699
return ret;
16821700
}
16831701

1684-
static int sev_snp_shutdown(int *error)
1685-
{
1686-
int rc;
1687-
1688-
mutex_lock(&sev_cmd_mutex);
1689-
rc = __sev_snp_shutdown_locked(error);
1690-
mutex_unlock(&sev_cmd_mutex);
1691-
1692-
return rc;
1693-
}
1694-
16951702
static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)
16961703
{
16971704
struct sev_device *sev = psp_master->sev_data;
@@ -2139,19 +2146,28 @@ int sev_dev_init(struct psp_device *psp)
21392146
return ret;
21402147
}
21412148

2142-
static void sev_firmware_shutdown(struct sev_device *sev)
2149+
static void __sev_firmware_shutdown(struct sev_device *sev, bool panic)
21432150
{
21442151
int error;
21452152

2146-
sev_platform_shutdown(NULL);
2153+
__sev_platform_shutdown_locked(NULL);
21472154

21482155
if (sev_es_tmr) {
2149-
/* The TMR area was encrypted, flush it from the cache */
2150-
wbinvd_on_all_cpus();
2156+
/*
2157+
* The TMR area was encrypted, flush it from the cache.
2158+
*
2159+
* If invoked during panic handling, local interrupts are
2160+
* disabled and all CPUs are stopped, so wbinvd_on_all_cpus()
2161+
* can't be used. In that case, wbinvd() is done on remote CPUs
2162+
* via the NMI callback, and done for this CPU later during
2163+
* SNP shutdown, so wbinvd_on_all_cpus() can be skipped.
2164+
*/
2165+
if (!panic)
2166+
wbinvd_on_all_cpus();
21512167

21522168
__snp_free_firmware_pages(virt_to_page(sev_es_tmr),
21532169
get_order(sev_es_tmr_size),
2154-
false);
2170+
true);
21552171
sev_es_tmr = NULL;
21562172
}
21572173

@@ -2167,7 +2183,14 @@ static void sev_firmware_shutdown(struct sev_device *sev)
21672183
snp_range_list = NULL;
21682184
}
21692185

2170-
sev_snp_shutdown(&error);
2186+
__sev_snp_shutdown_locked(&error, panic);
2187+
}
2188+
2189+
static void sev_firmware_shutdown(struct sev_device *sev)
2190+
{
2191+
mutex_lock(&sev_cmd_mutex);
2192+
__sev_firmware_shutdown(sev, false);
2193+
mutex_unlock(&sev_cmd_mutex);
21712194
}
21722195

21732196
void sev_dev_destroy(struct psp_device *psp)
@@ -2185,6 +2208,29 @@ void sev_dev_destroy(struct psp_device *psp)
21852208
psp_clear_sev_irq_handler(psp);
21862209
}
21872210

2211+
static int snp_shutdown_on_panic(struct notifier_block *nb,
2212+
unsigned long reason, void *arg)
2213+
{
2214+
struct sev_device *sev = psp_master->sev_data;
2215+
2216+
/*
2217+
* If sev_cmd_mutex is already acquired, then it's likely
2218+
* another PSP command is in flight and issuing a shutdown
2219+
* would fail in unexpected ways. Rather than create even
2220+
* more confusion during a panic, just bail out here.
2221+
*/
2222+
if (mutex_is_locked(&sev_cmd_mutex))
2223+
return NOTIFY_DONE;
2224+
2225+
__sev_firmware_shutdown(sev, true);
2226+
2227+
return NOTIFY_DONE;
2228+
}
2229+
2230+
static struct notifier_block snp_panic_notifier = {
2231+
.notifier_call = snp_shutdown_on_panic,
2232+
};
2233+
21882234
int sev_issue_cmd_external_user(struct file *filep, unsigned int cmd,
21892235
void *data, int *error)
21902236
{
@@ -2222,6 +2268,8 @@ void sev_pci_init(void)
22222268
dev_info(sev->dev, "SEV%s API:%d.%d build:%d\n", sev->snp_initialized ?
22232269
"-SNP" : "", sev->api_major, sev->api_minor, sev->build);
22242270

2271+
atomic_notifier_chain_register(&panic_notifier_list,
2272+
&snp_panic_notifier);
22252273
return;
22262274

22272275
err:
@@ -2236,4 +2284,7 @@ void sev_pci_exit(void)
22362284
return;
22372285

22382286
sev_firmware_shutdown(sev);
2287+
2288+
atomic_notifier_chain_unregister(&panic_notifier_list,
2289+
&snp_panic_notifier);
22392290
}

0 commit comments

Comments
 (0)