Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iommu/amd: Clean up RMP entries for IOMMU pages during SNP shutdown

Add a new IOMMU API interface amd_iommu_snp_disable() to transition
IOMMU pages to Hypervisor state from Reclaim state after SNP_SHUTDOWN_EX
command. Invoke this API from the CCP driver after SNP_SHUTDOWN_EX
command.

Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
Signed-off-by: Michael Roth <michael.roth@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20240126041126.1927228-20-michael.roth@amd.com

authored by

Ashish Kalra and committed by
Borislav Petkov (AMD)
f366a8da a867ad6b

+105
+20
drivers/crypto/ccp/sev-dev.c
··· 26 26 #include <linux/fs.h> 27 27 #include <linux/fs_struct.h> 28 28 #include <linux/psp.h> 29 + #include <linux/amd-iommu.h> 29 30 30 31 #include <asm/smp.h> 31 32 #include <asm/cacheflush.h> ··· 1653 1652 } 1654 1653 if (ret) { 1655 1654 dev_err(sev->dev, "SEV-SNP firmware shutdown failed\n"); 1655 + return ret; 1656 + } 1657 + 1658 + /* 1659 + * SNP_SHUTDOWN_EX with IOMMU_SNP_SHUTDOWN set to 1 disables SNP 1660 + * enforcement by the IOMMU and also transitions all pages 1661 + * associated with the IOMMU to the Reclaim state. 1662 + * Firmware was transitioning the IOMMU pages to Hypervisor state 1663 + * before version 1.53. But, accounting for the number of assigned 1664 + * 4kB pages in a 2M page was done incorrectly by not transitioning 1665 + * to the Reclaim state. This resulted in RMP #PF when later accessing 1666 + * the 2M page containing those pages during kexec boot. Hence, the 1667 + * firmware now transitions these pages to Reclaim state and hypervisor 1668 + * needs to transition these pages to shared state. SNP Firmware 1669 + * version 1.53 and above are needed for kexec boot. 1670 + */ 1671 + ret = amd_iommu_snp_disable(); 1672 + if (ret) { 1673 + dev_err(sev->dev, "SNP IOMMU shutdown failed\n"); 1656 1674 return ret; 1657 1675 } 1658 1676
+79
drivers/iommu/amd/init.c
··· 30 30 #include <asm/io_apic.h> 31 31 #include <asm/irq_remapping.h> 32 32 #include <asm/set_memory.h> 33 + #include <asm/sev.h> 33 34 34 35 #include <linux/crash_dump.h> 35 36 ··· 3798 3797 3799 3798 return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, true); 3800 3799 } 3800 + 3801 + #ifdef CONFIG_KVM_AMD_SEV 3802 + static int iommu_page_make_shared(void *page) 3803 + { 3804 + unsigned long paddr, pfn; 3805 + 3806 + paddr = iommu_virt_to_phys(page); 3807 + /* Cbit maybe set in the paddr */ 3808 + pfn = __sme_clr(paddr) >> PAGE_SHIFT; 3809 + 3810 + if (!(pfn % PTRS_PER_PMD)) { 3811 + int ret, level; 3812 + bool assigned; 3813 + 3814 + ret = snp_lookup_rmpentry(pfn, &assigned, &level); 3815 + if (ret) 3816 + pr_warn("IOMMU PFN %lx RMP lookup failed, ret %d\n", 3817 + pfn, ret); 3818 + 3819 + if (!assigned) 3820 + pr_warn("IOMMU PFN %lx not assigned in RMP table\n", 3821 + pfn); 3822 + 3823 + if (level > PG_LEVEL_4K) { 3824 + ret = psmash(pfn); 3825 + if (ret) { 3826 + pr_warn("IOMMU PFN %lx had a huge RMP entry, but attempted psmash failed, ret: %d, level: %d\n", 3827 + pfn, ret, level); 3828 + } 3829 + } 3830 + } 3831 + 3832 + return rmp_make_shared(pfn, PG_LEVEL_4K); 3833 + } 3834 + 3835 + static int iommu_make_shared(void *va, size_t size) 3836 + { 3837 + void *page; 3838 + int ret; 3839 + 3840 + if (!va) 3841 + return 0; 3842 + 3843 + for (page = va; page < (va + size); page += PAGE_SIZE) { 3844 + ret = iommu_page_make_shared(page); 3845 + if (ret) 3846 + return ret; 3847 + } 3848 + 3849 + return 0; 3850 + } 3851 + 3852 + int amd_iommu_snp_disable(void) 3853 + { 3854 + struct amd_iommu *iommu; 3855 + int ret; 3856 + 3857 + if (!amd_iommu_snp_en) 3858 + return 0; 3859 + 3860 + for_each_iommu(iommu) { 3861 + ret = iommu_make_shared(iommu->evt_buf, EVT_BUFFER_SIZE); 3862 + if (ret) 3863 + return ret; 3864 + 3865 + ret = iommu_make_shared(iommu->ppr_log, PPR_LOG_SIZE); 3866 + if (ret) 3867 + return ret; 3868 + 3869 + ret = iommu_make_shared((void *)iommu->cmd_sem, PAGE_SIZE); 3870 + if (ret) 3871 + return ret; 3872 + } 3873 + 3874 + return 0; 3875 + } 3876 + EXPORT_SYMBOL_GPL(amd_iommu_snp_disable); 3877 + #endif
+6
include/linux/amd-iommu.h
··· 85 85 u64 *value); 86 86 struct amd_iommu *get_amd_iommu(unsigned int idx); 87 87 88 + #ifdef CONFIG_KVM_AMD_SEV 89 + int amd_iommu_snp_disable(void); 90 + #else 91 + static inline int amd_iommu_snp_disable(void) { return 0; } 92 + #endif 93 + 88 94 #endif /* _ASM_X86_AMD_IOMMU_H */