RSI_RDEV_GET_INFO returns different digest hash values, which can be compared with host cached values to ensure the host didn't tamper with the cached data. Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@xxxxxxxxxx> --- arch/arm64/include/asm/rsi_cmds.h | 12 +++ arch/arm64/include/asm/rsi_smc.h | 24 +++++ drivers/virt/coco/arm-cca-guest/Kconfig | 2 + drivers/virt/coco/arm-cca-guest/rsi-da.c | 128 +++++++++++++++++++++++ drivers/virt/coco/arm-cca-guest/rsi-da.h | 13 +++ 5 files changed, 179 insertions(+) diff --git a/arch/arm64/include/asm/rsi_cmds.h b/arch/arm64/include/asm/rsi_cmds.h index 42b998f44a0e..6b90a6cdd7fb 100644 --- a/arch/arm64/include/asm/rsi_cmds.h +++ b/arch/arm64/include/asm/rsi_cmds.h @@ -276,4 +276,16 @@ static inline unsigned long __rsi_rdev_get_measurements(unsigned long vdev_id, return res.a0; } +static inline unsigned long rsi_rdev_get_info(unsigned long vdev_id, + unsigned long inst_id, + unsigned long digest_phys) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC_RSI_RDEV_GET_INFO, + vdev_id, inst_id, digest_phys, &res); + + return res.a0; +} + #endif /* __ASM_RSI_CMDS_H */ diff --git a/arch/arm64/include/asm/rsi_smc.h b/arch/arm64/include/asm/rsi_smc.h index f051db54cdc3..e2b51cf58bd4 100644 --- a/arch/arm64/include/asm/rsi_smc.h +++ b/arch/arm64/include/asm/rsi_smc.h @@ -125,6 +125,9 @@ #ifndef __ASSEMBLY__ +#define RSI_HASH_SHA_256 0 +#define RSI_HASH_SHA_512 1 + struct realm_config { union { struct { @@ -200,6 +203,27 @@ struct rsi_host_call { #define SMC_RSI_RDEV_GET_INSTANCE_ID SMC_RSI_FID(0x19c) #define SMC_RSI_RDEV_CONTINUE SMC_RSI_FID(0x1a4) +struct rsi_device_info { + union { + struct { + u64 flags; + u64 attest_type; + u64 cert_id; + u8 hash_algo; + }; + u8 padding[0x40]; + }; + union { /* 0x40 */ + struct { + u8 cert_digest[0x40]; + u8 meas_digest[0x40]; + u8 report_digest[0x40]; + }; + u8 padding2[0x200 - 0x40]; + }; +}; + +#define SMC_RSI_RDEV_GET_INFO SMC_RSI_FID(0x1a5) #define SMC_RSI_RDEV_GET_INTERFACE_REPORT SMC_RSI_FID(0x1a6) #define RSI_DEV_MEASURE_ALL BIT(0) diff --git a/drivers/virt/coco/arm-cca-guest/Kconfig b/drivers/virt/coco/arm-cca-guest/Kconfig index 410d9c3fb2b3..6fc86c1f3900 100644 --- a/drivers/virt/coco/arm-cca-guest/Kconfig +++ b/drivers/virt/coco/arm-cca-guest/Kconfig @@ -5,6 +5,8 @@ config ARM_CCA_GUEST tristate "Arm CCA Guest driver" depends on ARM64 depends on PCI_TSM + select CRYPTO_SHA256 + select CRYPTO_SHA512 select TSM_REPORTS select TSM help diff --git a/drivers/virt/coco/arm-cca-guest/rsi-da.c b/drivers/virt/coco/arm-cca-guest/rsi-da.c index 6222b10964ee..a1bb225adb4c 100644 --- a/drivers/virt/coco/arm-cca-guest/rsi-da.c +++ b/drivers/virt/coco/arm-cca-guest/rsi-da.c @@ -6,6 +6,7 @@ #include <linux/pci.h> #include <linux/mem_encrypt.h> #include <asm/rsi_cmds.h> +#include <crypto/hash.h> #include "rsi-da.h" @@ -186,11 +187,102 @@ rsi_rdev_get_measurements(struct pci_dev *pdev, unsigned long vdev_id, return RSI_SUCCESS; } +static int verify_digests(struct cca_guest_dsc *dsm) +{ + int i; + int ret; + u8 digest[SHA512_DIGEST_SIZE]; + int sdesc_size; + size_t digest_size; + char *hash_alg_name; + struct shash_desc *shash; + struct crypto_shash *alg; + struct pci_dev *pdev = dsm->pci.tsm.pdev; + struct { + uint8_t *report; + size_t size; + uint8_t *digest; + } reports[] = { + { + dsm->interface_report, + dsm->interface_report_size, + dsm->dev_info.report_digest + }, + { + dsm->certificate, + dsm->certificate_size, + dsm->dev_info.cert_digest + }, + { + dsm->measurements, + dsm->measurements_size, + dsm->dev_info.meas_digest + } + }; + + + if (dsm->dev_info.hash_algo == RSI_HASH_SHA_256) { + hash_alg_name = "sha256"; + digest_size = SHA256_DIGEST_SIZE; + } else if (dsm->dev_info.hash_algo == RSI_HASH_SHA_512) { + hash_alg_name = "sha512"; + digest_size = SHA512_DIGEST_SIZE; + } else { + pci_err(pdev, "unknown realm hash algorithm!\n"); + ret = -EINVAL; + goto err_out; + } + + alg = crypto_alloc_shash(hash_alg_name, 0, 0); + if (IS_ERR(alg)) { + pci_err(pdev, "cannot allocate %s\n", hash_alg_name); + return PTR_ERR(alg); + } + + sdesc_size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); + shash = kzalloc(sdesc_size, GFP_KERNEL); + if (!shash) { + pci_err(pdev, "cannot allocate sdesc\n"); + ret = -ENOMEM; + goto err_free_shash; + } + shash->tfm = alg; + + for (i = 0; i < ARRAY_SIZE(reports); i++) { + ret = crypto_shash_digest(shash, reports[i].report, + reports[i].size, digest); + if (ret) { + pci_err(pdev, "failed to compute digest, %d\n", ret); + goto err_free_sdesc; + } + + if (memcmp(reports[i].digest, digest, digest_size)) { + pci_err(pdev, "invalid digest\n"); + ret = -EINVAL; + goto err_free_sdesc; + } + } + + kfree(shash); + crypto_free_shash(alg); + + pci_info(pdev, "Successfully verified the digests\n"); + return 0; + +err_free_sdesc: + kfree(shash); +err_free_shash: + crypto_free_shash(alg); +err_out: + return ret; +} + int rsi_device_lock(struct pci_dev *pdev) { unsigned long ret; unsigned long tdisp_version; struct rsi_device_measurements_params *rsi_dev_meas; + struct rsi_device_info *dev_info; struct cca_guest_dsc *dsm = to_cca_guest_dsc(pdev); int vdev_id = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn); @@ -252,8 +344,44 @@ int rsi_device_lock(struct pci_dev *pdev) return -EIO; } + /* RMM expects sizeof(dev_info) (512 bytes) aligned address */ + dev_info = kmalloc(sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) { + ret = -ENOMEM; + goto err_out; + } + + ret = rsi_rdev_get_info(vdev_id, dsm->instance_id, virt_to_phys(dev_info)); + if (ret != RSI_SUCCESS) { + pci_err(pdev, "failed to get device digests (%lu)\n", ret); + ret = -EIO; + kfree(dev_info); + goto err_out; + } + + dsm->dev_info.attest_type = dev_info->attest_type; + dsm->dev_info.cert_id = dev_info->cert_id; + dsm->dev_info.hash_algo = dev_info->hash_algo; + memcpy(dsm->dev_info.cert_digest, dev_info->cert_digest, SHA512_DIGEST_SIZE); + memcpy(dsm->dev_info.meas_digest, dev_info->meas_digest, SHA512_DIGEST_SIZE); + memcpy(dsm->dev_info.report_digest, dev_info->report_digest, SHA512_DIGEST_SIZE); + + kfree(dev_info); + /* + * Verify that the digests of the provided reports match with the + * digests from RMM + */ + ret = verify_digests(dsm); + if (ret) { + pci_err(pdev, "device digest validation failed (%ld)\n", ret); + return ret; + } + + return 0; +err_out: return ret; } + static inline unsigned long rsi_rdev_start(struct pci_dev *pdev, unsigned long vdev_id, unsigned long inst_id) { diff --git a/drivers/virt/coco/arm-cca-guest/rsi-da.h b/drivers/virt/coco/arm-cca-guest/rsi-da.h index f26156d9be81..e8953b8e85a3 100644 --- a/drivers/virt/coco/arm-cca-guest/rsi-da.h +++ b/drivers/virt/coco/arm-cca-guest/rsi-da.h @@ -10,6 +10,7 @@ #include <linux/pci-tsm.h> #include <asm/rsi_smc.h> #include <asm/rhi.h> +#include <crypto/sha2.h> struct pci_tdisp_device_interface_report { u16 interface_info; @@ -33,6 +34,17 @@ struct pci_tdisp_mmio_range { #define TSM_INTF_REPORT_MMIO_RESERVED GENMASK(15, 4) #define TSM_INTF_REPORT_MMIO_RANGE_ID GENMASK(31, 16) +struct dsm_device_info { + u64 flags; + u64 attest_type; + u64 cert_id; + u64 hash_algo; + u8 cert_digest[SHA512_DIGEST_SIZE]; + u8 meas_digest[SHA512_DIGEST_SIZE]; + u8 report_digest[SHA512_DIGEST_SIZE]; +}; + + struct cca_guest_dsc { struct pci_tsm_pf0 pci; unsigned long instance_id; @@ -42,6 +54,7 @@ struct cca_guest_dsc { int certificate_size; void *measurements; int measurements_size; + struct dsm_device_info dev_info; }; static inline struct cca_guest_dsc *to_cca_guest_dsc(struct pci_dev *pdev) -- 2.43.0