diff --git a/Documentation/ABI/testing/sysfs-firmware-sev-vulnerabilities b/Documentation/ABI/testing/sysfs-firmware-sev-vulnerabilities new file mode 100644 index 000000000000..964362558bb2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-sev-vulnerabilities @@ -0,0 +1,19 @@ +What: /sys/firmware/sev/vulnerabilities/supported_mitigations +Date: June 2026 +Contact: linux-crypto@vger.kernel.org +Description: + Read-only interface that reports the vector of SEV-SNP + firmware vulnerability mitigations supported by the firmware. + +What: /sys/firmware/sev/vulnerabilities/verified_mitigations +Date: June 2026 +Contact: linux-crypto@vger.kernel.org +Description: + Read/write interface that reports the vector of SEV-SNP + firmware vulnerability mitigations already verified by the + firmware. Writing a vector value requests the firmware to + VERIFY the corresponding mitigation bit(s). + + The list of supported mitigations and the meaning of each + vector bit are both platform- and bug-specific and are + published as part of the AMD Security Bulletin. diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index ca473ca198b8..8be4dab05cbb 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -245,6 +245,7 @@ static int sev_cmd_buffer_len(int cmd) case SEV_CMD_SNP_LAUNCH_FINISH: return sizeof(struct sev_data_snp_launch_finish); case SEV_CMD_SNP_DBG_DECRYPT: return sizeof(struct sev_data_snp_dbg); case SEV_CMD_SNP_DBG_ENCRYPT: return sizeof(struct sev_data_snp_dbg); + case SEV_CMD_SNP_VERIFY_MITIGATION: return sizeof(struct sev_data_snp_verify_mitigation); case SEV_CMD_SNP_PAGE_UNSMASH: return sizeof(struct sev_data_snp_page_unsmash); case SEV_CMD_SNP_PLATFORM_STATUS: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_GUEST_REQUEST: return sizeof(struct sev_data_snp_guest_request); @@ -1352,6 +1353,162 @@ static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg) return 0; } +#ifdef CONFIG_SYSFS +static int snp_verify_mitigation(u16 command, u64 vector, + struct sev_data_snp_verify_mitigation_dst *dst) +{ + struct sev_data_snp_verify_mitigation_dst *mit_dst = NULL; + struct sev_data_snp_verify_mitigation data = {0}; + struct sev_device *sev = psp_master->sev_data; + int ret, error = 0; + + mit_dst = snp_alloc_firmware_page(GFP_KERNEL | __GFP_ZERO); + if (!mit_dst) + return -ENOMEM; + + data.length = sizeof(data); + data.subcommand = command; + data.vector = vector; + data.dst_paddr = __psp_pa(mit_dst); + data.dst_paddr_en = true; + + ret = sev_do_cmd(SEV_CMD_SNP_VERIFY_MITIGATION, &data, &error); + if (!ret) + memcpy(dst, mit_dst, sizeof(*mit_dst)); + else + dev_err(sev->dev, "SNP_VERIFY_MITIGATION command failed, ret = %d, error = %#x\n", + ret, error); + + snp_free_firmware_page(mit_dst); + + return ret; +} + +static ssize_t supported_mitigations_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct sev_data_snp_verify_mitigation_dst dst; + int ret; + + ret = snp_verify_mitigation(SNP_MIT_SUBCMD_REQ_STATUS, 0, &dst); + if (ret) + return ret; + + return sysfs_emit(buf, "0x%llx\n", dst.mit_supported_vector); +} + +static struct kobj_attribute supported_attr = + __ATTR_RO_MODE(supported_mitigations, 0400); + +static ssize_t verified_mitigations_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct sev_data_snp_verify_mitigation_dst dst; + int ret; + + ret = snp_verify_mitigation(SNP_MIT_SUBCMD_REQ_STATUS, 0, &dst); + if (ret) + return ret; + + return sysfs_emit(buf, "0x%llx\n", dst.mit_verified_vector); +} + +static ssize_t verified_mitigations_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct sev_data_snp_verify_mitigation_dst dst; + struct sev_device *sev = psp_master->sev_data; + u64 vector; + int ret; + + ret = kstrtoull(buf, 0, &vector); + if (ret) + return ret; + + /* + * The firmware verifies a single mitigation per call. Reject vectors + * with more than one bit set early to avoid a guaranteed-to-fail call + */ + if (hweight64(vector) != 1) + return -EINVAL; + + ret = snp_verify_mitigation(SNP_MIT_SUBCMD_REQ_VERIFY, vector, &dst); + if (ret) + return ret; + + if (dst.mit_failure_status) { + dev_err(sev->dev, "Verify Mitigation - failure status: 0x%x\n", + dst.mit_failure_status); + return -EINVAL; + } + + return count; +} + +static struct kobj_attribute verified_attr = + __ATTR_RW_MODE(verified_mitigations, 0600); + +static struct attribute *mitigation_attrs[] = { + &supported_attr.attr, + &verified_attr.attr, + NULL +}; + +static const struct attribute_group mit_attr_group = { + .attrs = mitigation_attrs, +}; + +static void sev_snp_register_verify_mitigation(struct sev_device *sev) +{ + int rc; + + if (!(sev->snp_feat_info_0.ecx & SNP_VERIFY_MITIGATION_SUPPORTED) || + sev->verify_mit) + return; + + if (!sev->sev_kobj) { + sev->sev_kobj = kobject_create_and_add("sev", firmware_kobj); + if (!sev->sev_kobj) + return; + } + + sev->verify_mit = kobject_create_and_add("vulnerabilities", sev->sev_kobj); + if (!sev->verify_mit) + goto err_sev_kobj; + + rc = sysfs_create_group(sev->verify_mit, &mit_attr_group); + if (rc) + goto err_verify_mit; + + return; + +err_verify_mit: + kobject_put(sev->verify_mit); + sev->verify_mit = NULL; +err_sev_kobj: + kobject_put(sev->sev_kobj); + sev->sev_kobj = NULL; +} + +static void sev_snp_unregister_verify_mitigation(struct sev_device *sev) +{ + if (sev->verify_mit) { + sysfs_remove_group(sev->verify_mit, &mit_attr_group); + kobject_put(sev->verify_mit); + sev->verify_mit = NULL; + } + + if (sev->sev_kobj) { + kobject_put(sev->sev_kobj); + sev->sev_kobj = NULL; + } +} +#else // CONFIG_SYSFS +static void sev_snp_register_verify_mitigation(struct sev_device *sev) { } +static void sev_snp_unregister_verify_mitigation(struct sev_device *sev) { } +#endif // CONFIG_SYSFS + static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid) { struct sev_data_range_list *snp_range_list __free(kfree) = NULL; @@ -1675,6 +1832,17 @@ int sev_platform_init(struct sev_platform_init_args *args) rc = _sev_platform_init_locked(args); mutex_unlock(&sev_cmd_mutex); + /* + * Register the sysfs interface outside the sev_cmd_mutex. The + * _show()/_store() handlers issue SEV commands that acquire the + * sev_cmd_mutex, so creating (and on the shutdown path, removing) the + * sysfs group must stay outside that lock. sysfs provides its own + * synchronization between group creation/removal and concurrent + * attribute access. + */ + if (!rc) + sev_snp_register_verify_mitigation(psp_master->sev_data); + return rc; } EXPORT_SYMBOL_GPL(sev_platform_init); @@ -2769,6 +2937,15 @@ static void sev_firmware_shutdown(struct sev_device *sev) if (sev->tio_status) sev_tsm_uninit(sev); + /* + * Remove the sysfs interface before taking the sev_cmd_mutex. + * sysfs_remove_group() waits for in-flight _show()/_store() handlers + * to drain, and those handlers issue SNP_VERIFY_MITIGATION via + * sev_do_cmd() which acquires the sev_cmd_mutex. Removing the group + * while holding the mutex could therefore deadlock. + */ + sev_snp_unregister_verify_mitigation(sev); + mutex_lock(&sev_cmd_mutex); __sev_firmware_shutdown(sev, false); diff --git a/drivers/crypto/ccp/sev-dev.h b/drivers/crypto/ccp/sev-dev.h index b1cd556bbbf6..d5e596606def 100644 --- a/drivers/crypto/ccp/sev-dev.h +++ b/drivers/crypto/ccp/sev-dev.h @@ -59,6 +59,9 @@ struct sev_device { bool snp_initialized; + struct kobject *sev_kobj; + struct kobject *verify_mit; + struct sev_user_data_status sev_plat_status; struct sev_user_data_snp_status snp_plat_status; diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h index ce16bbc0b308..03a79786df1d 100644 --- a/include/linux/psp-sev.h +++ b/include/linux/psp-sev.h @@ -129,6 +129,7 @@ enum sev_cmd { SEV_CMD_SNP_LAUNCH_FINISH = 0x0A2, SEV_CMD_SNP_DBG_DECRYPT = 0x0B0, SEV_CMD_SNP_DBG_ENCRYPT = 0x0B1, + SEV_CMD_SNP_VERIFY_MITIGATION = 0x0B2, SEV_CMD_SNP_PAGE_SWAP_OUT = 0x0C0, SEV_CMD_SNP_PAGE_SWAP_IN = 0x0C1, SEV_CMD_SNP_PAGE_MOVE = 0x0C2, @@ -898,10 +899,60 @@ struct snp_feature_info { #define SNP_CIPHER_TEXT_HIDING_SUPPORTED BIT(3) #define SNP_AES_256_XTS_POLICY_SUPPORTED BIT(4) #define SNP_CXL_ALLOW_POLICY_SUPPORTED BIT(5) +#define SNP_VERIFY_MITIGATION_SUPPORTED BIT(13) /* Feature bits in EBX */ #define SNP_SEV_TIO_SUPPORTED BIT(1) +#define SNP_MIT_SUBCMD_REQ_STATUS 0x0 +#define SNP_MIT_SUBCMD_REQ_VERIFY 0x1 + +/** + * struct sev_data_snp_verify_mitigation - SNP_VERIFY_MITIGATION command params + * + * @length: Length of the command buffer read by the PSP + * @subcommand: Mitigation sub-command for the firmware to execute. + * REQ_STATUS: 0x0 - Request status about currently supported and + * verified mitigations + * REQ_VERIFY: 0x1 - Request to initiate verification mitigation + * operation on a specific mitigation + * @rsvd: Reserved + * @vector: Bit specifying the vulnerability mitigation to process + * @dst_paddr_en: Destination paddr enabled + * @src_paddr_en: Source paddr enabled + * @rsvd1: Reserved + * @rsvd2: Reserved + * @src_paddr: Source address for optional input data + * @dst_paddr: Destination address to write the result + * @rsvd3: Reserved + */ +struct sev_data_snp_verify_mitigation { + u32 length; + u16 subcommand; + u16 rsvd; + u64 vector; + u32 dst_paddr_en : 1, + src_paddr_en : 1, + rsvd1 : 30; + u8 rsvd2[4]; + u64 src_paddr; + u64 dst_paddr; + u8 rsvd3[24]; +} __packed; + +/** + * struct sev_data_snp_verify_mitigation_dst - mitigation result vectors + * + * @mit_verified_vector: Bit vector of vulnerability mitigations verified + * @mit_supported_vector: Bit vector of vulnerability mitigations supported + * @mit_failure_status: Status of the verification operation + */ +struct sev_data_snp_verify_mitigation_dst { + u64 mit_verified_vector; /* OUT */ + u64 mit_supported_vector; /* OUT */ + u32 mit_failure_status; /* OUT */ +} __packed; + /** * struct sev_snp_tcb_version_genoa_milan *