Add host kdump support for SNP
Subsystems (6)
Ashish Kalra
maintainerv6 cover Add host kdump support for SNP
From: Ashish Kalra <[email protected]>
When a crash is triggered the kernel attempts to shut down SEV-SNP using the SNP_SHUTDOWN_EX command. If active SNP VMs are present, SNP_SHUTDOWN_EX fails as firmware checks all encryption-capable ASIDs to ensure none are in use and that a DF_FLUSH is not required.
This casues the kdump kernel to boot with IOMMU SNP enforcement still enabled and IOMMU completion wait buffers (CWBs), command buffers, device tables and event buffer registers remain locked and exclusive to the previous kernel. Attempts to allocate and use new buffers in the kdump kernel fail, as the hardware ignores writes to the locked MMIO registers (per AMD IOMMU spec Section 2.12.2.1).
As a result, the kdump kernel cannot initialize the IOMMU or enable IRQ remapping which is required for proper operation.
This results in repeated "Completion-Wait loop timed out" errors and a second kernel panic: "Kernel panic - not syncing: timer doesn't work through Interrupt-remapped IO-APIC"
The list of MMIO registers locked and which ignore writes after failed SNP shutdown are mentioned in the AMD IOMMU specifications below:
Section 2.12.2.1.
https://docs.amd.com/v/u/en-US/48882_3.10_PUB
Instead of allocating new buffers, re-use the previous kernel’s pages for completion wait buffers, command buffers, event buffers and device tables and operate with the already enabled SNP configuration and existing data structures.
This approach is now used for kdump boot regardless of whether SNP is enabled during kdump.
The patch-series enables successful crashkernel/kdump operation on SNP hosts even when SNP_SHUTDOWN_EX fails.
v6:
- Fix commit logs and inline comments.
- Add Reviewed-by tags.
v5:
- Fix sparse build warnings, use (__force void *) for
fixing cast return of (void __iomem *) to (void *) from ioremap_encrypted() in iommu_memremap().
- Add Tested-by tags.
v4:
- Fix commit logs.
- Explicitly call ioremap_encrypted() if SME is enabled and memremap() otherwise if SME is not enabled in iommu_memremap().
- Rename remap_cwwb_sem() to remap_or_alloc_cwwb_sem().
- Fix inline comments.
- Skip both SEV and SNP INIT for kdump boot.
- Add a BUG_ON() if reuse_device_table() fails in case of SNP enabled.
- Drop "Fixes:" tag as this patch-series enables host kdump for SNP.
v3:
- Moving to AMD IOMMU driver fix so that there is no need to do SNP_DECOMMISSION during panic() and kdump kernel boot will be more agnostic to
whether or not SNP_SHUTDOWN is done properly (or even done at all), i.e., even with active SNP guests. Fixing crashkernel/kdump boot with IOMMU SNP/RMP enforcement still enabled prior to kdump boot by reusing the pages of the previous kernel for IOMMU completion wait buffers, command buffer and device table and memremap them during kdump boot.
- Rebased on linux-next.
- Split the original patch into smaller patches and prepare separate patches for adding iommu_memremap() helper and remapping/unmapping of IOMMU buffers for kdump, Reusing device table for kdump and skip the enabling of IOMMU buffers for kdump.
- Add new functions for remapping/unmapping IOMMU buffers and call them from alloc_iommu_buffers/free_iommu_buffers in case of kdump boot else call the exisiting alloc/free variants of CWB, command and event buffers.
- Skip SNP INIT in case of kdump boot.
- The final patch skips enabling IOMMU command buffer and event buffer for kdump boot which fixes kdump on SNP host.
- Add comment that completion wait buffers are only re-used when SNP is enabled.
Ashish Kalra (4):
iommu/amd: Add support to remap/unmap IOMMU buffers for kdump iommu/amd: Reuse device table for kdump
crypto: ccp: Skip SEV and SNP INIT for kdump boot iommu/amd: Skip enabling command/event buffers for kdump
drivers/crypto/ccp/sev-dev.c | 10 + drivers/iommu/amd/amd_iommu_types.h | 5 + drivers/iommu/amd/init.c | 284 +++++++++++++++++++--------- drivers/iommu/amd/iommu.c | 2 +- 4 files changed, 209 insertions(+), 92 deletions(-)
Signature · 1 line
2.34.1
Ashish Kalra
maintainerPATCH v6 1/4 iommu/amd: Add support to remap/unmap IOMMU buffers for kdump
From: Ashish Kalra <[email protected]>
After a panic if SNP is enabled in the previous kernel then the kdump kernel boots with IOMMU SNP enforcement still enabled.
IOMMU completion wait buffers (CWBs), command buffers and event buffer registers remain locked and exclusive to the previous kernel. Attempts to allocate and use new buffers in the kdump kernel fail, as hardware ignores writes to the locked MMIO registers as per AMD IOMMU spec Section 2.12.2.1.
This results in repeated "Completion-Wait loop timed out" errors and a second kernel panic: "Kernel panic - not syncing: timer doesn't work through Interrupt-remapped IO-APIC"
The list of MMIO registers locked and which ignore writes after failed SNP shutdown are mentioned in the AMD IOMMU specifications below:
Section 2.12.2.1.
https://docs.amd.com/v/u/en-US/48882_3.10_PUB
Reuse the pages of the previous kernel for completion wait buffers, command buffers, event buffers and memremap them during kdump boot and essentially work with an already enabled IOMMU configuration and re-using the previous kernel’s data structures.
Reusing of command buffers and event buffers is now done for kdump boot irrespective of SNP being enabled during kdump.
Re-use of completion wait buffers is only done when SNP is enabled as the exclusion base register is used for the completion wait buffer (CWB) address only when SNP is enabled.
--- drivers/iommu/amd/amd_iommu_types.h | 5 + drivers/iommu/amd/init.c | 152 +++++++++++++++++++++++++--- drivers/iommu/amd/iommu.c | 2 +- 3 files changed, 146 insertions(+), 13 deletions(-)
drivers/iommu/amd/amd_iommu_types.h +5 -0
@@ -791,6 +791,11 @@ struct amd_iommu {u32 flags;volatile u64 *cmd_sem;atomic64_t cmd_sem_val; +/* +* Track physical address to directly use it in build_completion_wait() +* and avoid adding any special checks and handling for kdump. +*/ +u64 cmd_sem_paddr;#ifdef CONFIG_AMD_IOMMU_DEBUGFS /* DebugFS Info */
drivers/iommu/amd/init.c +140 -12
183 lines not on this page · raw message
drivers/iommu/amd/iommu.c +1 -1
@@ -1195,7 +1195,7 @@ static void build_completion_wait(struct iommu_cmd *cmd, struct amd_iommu *iommu, u64 data) { -u64 paddr =iommu_virt_to_phys ((void *)iommu->cmd_sem); +u64 paddr = iommu->cmd_sem_paddr;memset (cmd,0 ,sizeof (*cmd)); cmd->data[0 ] =lower_32_bits (paddr) |CMD_COMPL_WAIT_STORE_MASK ;
Signature · 1 line
2.34.1
Ashish Kalra
maintainerPATCH v6 2/4 iommu/amd: Reuse device table for kdump
In reply to Ashish KalraFrom: Ashish Kalra <[email protected]>
After a panic if SNP is enabled in the previous kernel then the kdump kernel boots with IOMMU SNP enforcement still enabled.
IOMMU device table register is locked and exclusive to the previous kernel. Attempts to copy old device table from the previous kernel fails in kdump kernel as hardware ignores writes to the locked device table base address register as per AMD IOMMU spec Section 2.12.2.1.
This causes the IOMMU driver (OS) and the hardware to reference different memory locations. As a result, the IOMMU hardware cannot process the command which results in repeated "Completion-Wait loop timed out" errors and a second kernel panic: "Kernel panic - not
syncing: timer doesn't work through Interrupt-remapped IO-APIC".
Reuse device table instead of copying device table in case of kdump boot and remove all copying device table code.
--- drivers/iommu/amd/init.c | 104 +++++++++++++-------------------------- 1 file changed, 34 insertions(+), 70 deletions(-)
drivers/iommu/amd/init.c +34 -70
@@ -406,6 +406,9 @@ static void iommu_set_device_table(struct amd_iommu *iommu)BUG_ON (iommu->mmio_base ==NULL ); +if (is_kdump_kernel ()) +return ; + entry =iommu_virt_to_phys (dev_table); entry |= (dev_table_size >>12 ) -1 ; memcpy_toio(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET,
@@ -646,7 +649,10 @@ static inline int __init alloc_dev_table(struct amd_iommu_pci_seg *pci_seg)static inline void free_dev_table (struct amd_iommu_pci_seg *pci_seg) { -iommu_free_pages (pci_seg->dev_table); +if (is_kdump_kernel ()) +memunmap ((void *)pci_seg->dev_table); +else +iommu_free_pages (pci_seg->dev_table); pci_seg->dev_table =NULL ; }
@@ -1117,15 +1123,12 @@ static void set_dte_bit(struct dev_table_entry *dte, u8 bit) dte->data[i] |= (1UL << _bit); } -static bool __copy_device_table (struct amd_iommu *iommu) +static bool __reuse_device_table (struct amd_iommu *iommu) { -u64 int_ctl, int_tab_len, entry =0 ;struct amd_iommu_pci_seg *pci_seg = iommu->pci_seg; -struct dev_table_entry *old_devtb =NULL ; -u32 lo, hi, devid, old_devtb_size; +u32 lo, hi, old_devtb_size;phys_addr_t old_devtb_phys; -u16 dom_id, dte_v, irq_v; -u64 tmp; +u64 entry;/* Each IOMMU use separate device table with the same size */ lo =readl (iommu->mmio_base +MMIO_DEV_TABLE_OFFSET );
@@ -1150,66 +1153,20 @@ static bool __copy_device_table(struct amd_iommu *iommu)pr_err ("The address of old device table is above 4G, not trustworthy!\n" );return false; } - old_devtb = (cc_platform_has (CC_ATTR_HOST_MEM_ENCRYPT ) &&is_kdump_kernel ()) - ? (__force void *)ioremap_encrypted (old_devtb_phys, - pci_seg->dev_table_size) - :memremap (old_devtb_phys, pci_seg->dev_table_size,MEMREMAP_WB ); - -if (!old_devtb) -return false; - pci_seg->old_dev_tbl_cpy =iommu_alloc_pages_sz ( -GFP_KERNEL |GFP_DMA32 , pci_seg->dev_table_size); +/* +* Re-use the previous kernel's device table for kdump. +*/ + pci_seg->old_dev_tbl_cpy =iommu_memremap (old_devtb_phys, pci_seg->dev_table_size);if (pci_seg->old_dev_tbl_cpy ==NULL ) { -pr_err ("Failed to allocate memory for copying old device table!\n" ); -memunmap (old_devtb); +pr_err ("Failed to remap memory for reusing old device table!\n" );return false; } -for (devid =0 ; devid <= pci_seg->last_bdf; ++devid) { - pci_seg->old_dev_tbl_cpy[devid] = old_devtb[devid]; - dom_id = old_devtb[devid].data[1 ] &DEV_DOMID_MASK ; - dte_v = old_devtb[devid].data[0 ] &DTE_FLAG_V ; - -if (dte_v && dom_id) { - pci_seg->old_dev_tbl_cpy[devid].data[0 ] = old_devtb[devid].data[0 ]; - pci_seg->old_dev_tbl_cpy[devid].data[1 ] = old_devtb[devid].data[1 ]; -/* Reserve the Domain IDs used by previous kernel */ -if (ida_alloc_range (&pdom_ids, dom_id, dom_id,GFP_ATOMIC ) != dom_id) { -pr_err ("Failed to reserve domain ID 0x%x\n" , dom_id); -memunmap (old_devtb); -return false; - } -/* If gcr3 table existed, mask it out */ -if (old_devtb[devid].data[0 ] &DTE_FLAG_GV ) { - tmp = (DTE_GCR3_30_15 |DTE_GCR3_51_31 ); - pci_seg->old_dev_tbl_cpy[devid].data[1 ] &= ~tmp; - tmp = (DTE_GCR3_14_12 |DTE_FLAG_GV ); - pci_seg->old_dev_tbl_cpy[devid].data[0 ] &= ~tmp; - } - } - - irq_v = old_devtb[devid].data[2 ] &DTE_IRQ_REMAP_ENABLE ; - int_ctl = old_devtb[devid].data[2 ] &DTE_IRQ_REMAP_INTCTL_MASK ; - int_tab_len = old_devtb[devid].data[2 ] &DTE_INTTABLEN_MASK ; -if (irq_v && (int_ctl || int_tab_len)) { -if ((int_ctl !=DTE_IRQ_REMAP_INTCTL ) || - (int_tab_len !=DTE_INTTABLEN_512 && - int_tab_len !=DTE_INTTABLEN_2K )) { -pr_err ("Wrong old irq remapping flag: %#x\n" , devid); -memunmap (old_devtb); -return false; - } - - pci_seg->old_dev_tbl_cpy[devid].data[2 ] = old_devtb[devid].data[2 ]; - } - } -memunmap (old_devtb); -return true; } -static bool copy_device_table (void ) +static bool reuse_device_table (void ) {struct amd_iommu *iommu;struct amd_iommu_pci_seg *pci_seg;
@@ -1217,17 +1174,17 @@ static bool copy_device_table(void)if (!amd_iommu_pre_enabled)return false; -pr_warn ("Translation is already enabled - trying to copy translation structures\n" ); +pr_warn ("Translation is already enabled - trying to reuse translation structures\n" );/* * All IOMMUs within PCI segment shares common device table. -* Hence copy device table only once per PCI segment. +* Hence reuse device table only once per PCI segment. */ for_each_pci_segment (pci_seg) {for_each_iommu (iommu) {if (pci_seg->id != iommu->pci_seg->id)continue ; -if (!__copy_device_table (iommu)) +if (!__reuse_device_table (iommu))return false;break ; }
@@ -2906,8 +2863,8 @@ static void early_enable_iommu(struct amd_iommu *iommu)* This function finally enables all IOMMUs found in the system after * they have been initialized. * -* Or if in kdump kernel and IOMMUs are all pre-enabled, try to copy -* the old content of device table entries. Not this case or copy failed, +* Or if in kdump kernel and IOMMUs are all pre-enabled, try to reuse +* the old content of device table entries. Not this case or reuse failed, * just continue as normal kernel does. */ static void early_enable_iommus (void )
@@ -2915,18 +2872,25 @@ static void early_enable_iommus(void)struct amd_iommu *iommu;struct amd_iommu_pci_seg *pci_seg; -if (!copy_device_table ()) { +if (!reuse_device_table ()) {/* -* If come here because of failure in copying device table from old +* If come here because of failure in reusing device table from old * kernel with all IOMMUs enabled, print error message and try to * free allocated old_dev_tbl_cpy. */ -if (amd_iommu_pre_enabled) -pr_err ("Failed to copy DEV table from previous kernel.\n" ); +if (amd_iommu_pre_enabled) { +pr_err ("Failed to reuse DEV table from previous kernel.\n" ); +/* +* Bail out early if unable to remap/reuse DEV table from +* previous kernel if SNP enabled as IOMMU commands will +* time out without DEV table and cause kdump boot panic. +*/ +BUG_ON (check_feature (FEATURE_SNP )); + }for_each_pci_segment (pci_seg) {if (pci_seg->old_dev_tbl_cpy !=NULL ) { -iommu_free_pages (pci_seg->old_dev_tbl_cpy); +memunmap ((void *)pci_seg->old_dev_tbl_cpy); pci_seg->old_dev_tbl_cpy =NULL ; } }
@@ -2936,7 +2900,7 @@ static void early_enable_iommus(void)early_enable_iommu (iommu); } }else { -pr_info ("Copied DEV table from previous kernel.\n" ); +pr_info ("Reused DEV table from previous kernel.\n" );for_each_pci_segment (pci_seg) {iommu_free_pages (pci_seg->dev_table);
Signature · 1 line
2.34.1
Ashish Kalra
maintainerPATCH v6 3/4 crypto: ccp: Skip SEV and SNP INIT for kdump boot
In reply to Ashish KalraFrom: Ashish Kalra <[email protected]>
Since SEV or SNP may already be initialized in the previous kernel, attempting to initialize them again in the kdump kernel can result in SNP initialization failures, which in turn lead to IOMMU initialization failures. Moreover, SNP/SEV guests are not run under a kdump kernel, so there is no need to initialize SEV or SNP during kdump boot.
Skip SNP and SEV INIT if doing kdump boot.
--- drivers/crypto/ccp/sev-dev.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/crypto/ccp/sev-dev.c +10 -0
@@ -28,6 +28,7 @@#include <linux/fs_struct.h> #include <linux/psp.h> #include <linux/amd-iommu.h> +#include <linux/crash_dump.h> #include <asm/smp.h> #include <asm/cacheflush.h>
@@ -1374,6 +1375,15 @@ static int __sev_platform_init_locked(int *error)if (!psp_master || !psp_master->sev_data)return -ENODEV ; +/* +* Skip SNP/SEV initialization under a kdump kernel as SEV/SNP +* may already be initialized in the previous kernel. Since no +* SNP/SEV guests are run under a kdump kernel, there is no +* need to initialize SNP or SEV during kdump boot. +*/ +if (is_kdump_kernel ()) +return 0 ; + sev = psp_master->sev_data;if (sev->sev_plat_status.state ==SEV_STATE_INIT )
Signature · 1 line
2.34.1
Re: [PATCH v6 3/4] crypto: ccp: Skip SEV and SNP INIT for kdump boot
Hi,
these changes seem to have been overwritten after 459daec42ea0c("crypto: ccp - Cache SEV platform status and platform state") has been merged upstream.
I can send a patch if that's not been done already. Please let me know.
Thanks,
Vasant
Kalra, Ashish
maintainerRe: [PATCH v6 3/4] crypto: ccp: Skip SEV and SNP INIT for kdump boot
Quoted 8 lines · On 10/29/2025 5:43 AM, [email protected] wrote:
Hi,
these changes seem to have been overwritten after 459daec42ea0c("crypto: ccp - Cache SEV platform status and platform state") has been merged upstream.
I can send a patch if that's not been done already. Please let me know.
Hi,
I believe that the IOMMU updates for 6.18 (containing this patch) were merged after this commit:
commit 459daec42ea0cf5e276dfb82e90ed91e2db45d9e("crypto: ccp - Cache SEV platform status and platform state")
Thanks,
Ashish
Quoted 3 lines
Thanks,
Vasant
Ashish Kalra
maintainerPATCH v6 4/4 iommu/amd: Skip enabling command/event buffers for kdump
In reply to Ashish KalraFrom: Ashish Kalra <[email protected]>
After a panic if SNP is enabled in the previous kernel then the kdump kernel boots with IOMMU SNP enforcement still enabled.
IOMMU command buffers and event buffer registers remain locked and exclusive to the previous kernel. Attempts to enable command and event buffers in the kdump kernel will fail, as hardware ignores writes to the locked MMIO registers as per AMD IOMMU spec Section 2.12.2.1.
Skip enabling command buffers and event buffers for kdump boot as they are already enabled in the previous kernel.
--- drivers/iommu/amd/init.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
drivers/iommu/amd/init.c +19 -9
@@ -821,11 +821,16 @@ static void iommu_enable_command_buffer(struct amd_iommu *iommu)BUG_ON (iommu->cmd_buf ==NULL ); - entry =iommu_virt_to_phys (iommu->cmd_buf); - entry |=MMIO_CMD_SIZE_512 ; - -memcpy_toio (iommu->mmio_base +MMIO_CMD_BUF_OFFSET , - &entry,sizeof (entry)); +if (!is_kdump_kernel ()) { +/* +* Command buffer is re-used for kdump kernel and setting +* of MMIO register is not required. +*/ + entry =iommu_virt_to_phys (iommu->cmd_buf); + entry |=MMIO_CMD_SIZE_512 ; +memcpy_toio (iommu->mmio_base +MMIO_CMD_BUF_OFFSET , + &entry,sizeof (entry)); + }amd_iommu_reset_cmd_buffer (iommu); }
@@ -876,10 +881,15 @@ static void iommu_enable_event_buffer(struct amd_iommu *iommu)BUG_ON (iommu->evt_buf ==NULL ); - entry =iommu_virt_to_phys (iommu->evt_buf) |EVT_LEN_MASK ; - -memcpy_toio (iommu->mmio_base +MMIO_EVT_BUF_OFFSET , - &entry,sizeof (entry)); +if (!is_kdump_kernel ()) { +/* +* Event buffer is re-used for kdump kernel and setting +* of MMIO register is not required. +*/ + entry =iommu_virt_to_phys (iommu->evt_buf) |EVT_LEN_MASK ; +memcpy_toio (iommu->mmio_base +MMIO_EVT_BUF_OFFSET , + &entry,sizeof (entry)); + }/* set head and tail to zero manually */ writel (0x00 , iommu->mmio_base +MMIO_EVT_HEAD_OFFSET );
Signature · 1 line
2.34.1
Quoted 11 lines · Ashish Kalra
Ashish Kalra (4):
iommu/amd: Add support to remap/unmap IOMMU buffers for kdump iommu/amd: Reuse device table for kdump
crypto: ccp: Skip SEV and SNP INIT for kdump boot iommu/amd: Skip enabling command/event buffers for kdumpdrivers/crypto/ccp/sev-dev.c | 10 + drivers/iommu/amd/amd_iommu_types.h | 5 + drivers/iommu/amd/init.c | 284 +++++++++++++++++++--------- drivers/iommu/amd/iommu.c | 2 +- 4 files changed, 209 insertions(+), 92 deletions(-)
Applied, thanks.