[PATCH 11/12] habanalabs/gaudi2: add tpm attestation info uapi

From: Oded Gabbay
Date: Mon Jun 27 2022 - 16:27:26 EST


From: Dani Liberman <dliberman@xxxxxxxxx>

User will provide a nonce via the ioctl, and will retrieve
attestation data of the boot from the tpm, generated using given
nonce.

Signed-off-by: Dani Liberman <dliberman@xxxxxxxxx>
Reviewed-by: Oded Gabbay <ogabbay@xxxxxxxxxx>
Signed-off-by: Oded Gabbay <ogabbay@xxxxxxxxxx>
---
drivers/misc/habanalabs/common/firmware_if.c | 58 +++++++++++++++++++
drivers/misc/habanalabs/common/habanalabs.h | 2 +
.../misc/habanalabs/common/habanalabs_ioctl.c | 52 +++++++++++++++++
.../misc/habanalabs/include/common/cpucp_if.h | 51 +++++++++++++++-
4 files changed, 162 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c
index 9f0a24ee5af4..60093088ba43 100644
--- a/drivers/misc/habanalabs/common/firmware_if.c
+++ b/drivers/misc/habanalabs/common/firmware_if.c
@@ -13,6 +13,14 @@
#include <linux/slab.h>
#include <linux/ctype.h>

+#if (TPM_PCR_DATA_BUF_SZ != HL_TPM_PCR_DATA_BUF_SZ) || \
+ (TPM_PCR_QUOTE_BUF_SZ != HL_TPM_PCR_QUOTE_BUF_SZ) || \
+ (TPM_SIGNATURE_BUF_SZ != HL_TPM_SIGNATURE_BUF_SZ) || \
+ (TPM_PUB_DATA_BUF_SZ != HL_TPM_PUB_DATA_BUF_SZ) || \
+ (TPM_CERTIFICATE_BUF_SZ != HL_TPM_CERTIFICATE_BUF_SZ)
+#error TPM attestation structures are not aligned
+#endif
+
#define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */

struct fw_binning_conf {
@@ -525,6 +533,11 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val,
err_exists = true;
}

+ if (err_val & CPU_BOOT_ERR0_TMP_THRESH_INIT_FAIL) {
+ dev_err(hdev->dev, "Device boot error - Failed to set threshold for temperature sensor\n");
+ err_exists = true;
+ }
+
if (err_val & CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL) {
/* Ignore this bit, don't prevent driver loading */
dev_dbg(hdev->dev, "device unusable status is set\n");
@@ -2905,3 +2918,48 @@ void hl_fw_set_max_power(struct hl_device *hdev)
if (rc)
dev_err(hdev->dev, "Failed to set max power, error %d\n", rc);
}
+
+static int hl_fw_get_tpm_data(struct hl_device *hdev, u32 packet_id,
+ void *data, u32 size, u32 nonce, u32 timeout)
+{
+ struct cpucp_packet pkt = {};
+ dma_addr_t req_dma_addr;
+ void *req_cpu_addr;
+ int rc;
+
+ req_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, size, &req_dma_addr);
+ if (!data) {
+ dev_err(hdev->dev,
+ "Failed to allocate DMA memory for CPU-CP packet %u\n", packet_id);
+ return -ENOMEM;
+ }
+
+ memset(data, 0, size);
+
+ pkt.ctl = cpu_to_le32(packet_id << CPUCP_PKT_CTL_OPCODE_SHIFT);
+ pkt.addr = cpu_to_le64(req_dma_addr);
+ pkt.data_max_size = cpu_to_le32(size);
+ pkt.nonce = cpu_to_le32(nonce);
+
+ rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+ timeout, NULL);
+ if (rc) {
+ dev_err(hdev->dev,
+ "Failed to handle CPU-CP pkt %u, error %d\n", packet_id, rc);
+ goto out;
+ }
+
+ memcpy(data, req_cpu_addr, size);
+
+out:
+ hl_cpu_accessible_dma_pool_free(hdev, size, req_cpu_addr);
+
+ return rc;
+}
+
+int hl_fw_get_tpm_info(struct hl_device *hdev, struct cpucp_tpm_attest_info *tpm_info, u32 nonce)
+{
+ return hl_fw_get_tpm_data(hdev, CPUCP_PACKET_TPM_ATTEST_GET, tpm_info,
+ sizeof(struct cpucp_tpm_attest_info), nonce,
+ HL_CPUCP_TPM_INFO_TINEOUT_USEC);
+}
diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h
index f61389115c26..4537845658f8 100644
--- a/drivers/misc/habanalabs/common/habanalabs.h
+++ b/drivers/misc/habanalabs/common/habanalabs.h
@@ -66,6 +66,7 @@ struct hl_fpriv;
#define HL_CPUCP_INFO_TIMEOUT_USEC 10000000 /* 10s */
#define HL_CPUCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */
#define HL_CPUCP_MON_DUMP_TIMEOUT_USEC 10000000 /* 10s */
+#define HL_CPUCP_TPM_INFO_TINEOUT_USEC 10000000 /* 10s */

#define HL_FW_STATUS_POLL_INTERVAL_USEC 10000 /* 10ms */
#define HL_FW_COMMS_STATUS_PLDM_POLL_INTERVAL_USEC 1000000 /* 1s */
@@ -3604,6 +3605,7 @@ int hl_get_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long *va
void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long value);
long hl_fw_get_max_power(struct hl_device *hdev);
void hl_fw_set_max_power(struct hl_device *hdev);
+int hl_fw_get_tpm_info(struct hl_device *hdev, struct cpucp_tpm_attest_info *tpm_info, u32 nonce);
int hl_set_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long value);
int hl_set_current(struct hl_device *hdev, int sensor_index, u32 attr, long value);
int hl_set_power(struct hl_device *hdev, int sensor_index, u32 attr, long value);
diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c
index 96e12ab7a924..9db5167978e5 100644
--- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c
+++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c
@@ -660,6 +660,55 @@ static int dev_mem_alloc_page_sizes_info(struct hl_fpriv *hpriv, struct hl_info_
return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
}

+static int tpm_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
+{
+ void __user *out = (void __user *) (uintptr_t) args->return_pointer;
+ struct cpucp_tpm_attest_info *tpm_info;
+ struct hl_info_tpm *info;
+ u32 max_size = args->return_size;
+ int rc;
+
+ if ((!max_size) || (!out))
+ return -EINVAL;
+
+ tpm_info = kmalloc(sizeof(*tpm_info), GFP_KERNEL);
+ if (!tpm_info)
+ return -ENOMEM;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ rc = -ENOMEM;
+ goto free_tpm_info;
+ }
+
+ rc = hl_fw_get_tpm_info(hpriv->hdev, tpm_info, args->tpm_nonce);
+ if (rc)
+ goto free_info;
+
+ info->nonce = le32_to_cpu(tpm_info->nonce);
+ info->pcr_quote_len = le16_to_cpu(tpm_info->pcr_quote_len);
+ info->pub_data_len = le16_to_cpu(tpm_info->pub_data_len);
+ info->certificate_len = le16_to_cpu(tpm_info->certificate_len);
+ info->pcr_num_reg = tpm_info->pcr_num_reg;
+ info->pcr_reg_len = tpm_info->pcr_reg_len;
+ info->quote_sig_len = tpm_info->quote_sig_len;
+ memcpy(&info->pcr_data, &tpm_info->pcr_data, sizeof(info->pcr_data));
+ memcpy(&info->pcr_quote, &tpm_info->pcr_quote, sizeof(info->pcr_quote));
+ memcpy(&info->public_data, &tpm_info->public_data, sizeof(info->public_data));
+ memcpy(&info->certificate, &tpm_info->certificate, sizeof(info->certificate));
+ memcpy(&info->quote_sig, &tpm_info->quote_sig, sizeof(info->quote_sig));
+
+ rc = copy_to_user(out, info,
+ min_t(size_t, max_size, sizeof(*info))) ? -EFAULT : 0;
+
+free_info:
+ kfree(info);
+free_tpm_info:
+ kfree(tpm_info);
+
+ return rc;
+}
+
static int eventfd_register(struct hl_fpriv *hpriv, struct hl_info_args *args)
{
int rc;
@@ -753,6 +802,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
case HL_INFO_DEV_MEM_ALLOC_PAGE_SIZES:
return dev_mem_alloc_page_sizes_info(hpriv, args);

+ case HL_INFO_TPM:
+ return tpm_info(hpriv, args);
+
case HL_INFO_GET_EVENTS:
return events_info(hpriv, args);

diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h
index 719b2ff80985..a97d9f03915d 100644
--- a/drivers/misc/habanalabs/include/common/cpucp_if.h
+++ b/drivers/misc/habanalabs/include/common/cpucp_if.h
@@ -629,6 +629,13 @@ enum pq_init_status {
* CPUCP_PACKET_ENGINE_CORE_ASID_SET -
* Packet to perform engine core ASID configuration
*
+ * CPUCP_PACKET_TPM_ATTEST_GET -
+ * Get the attestaion data that is collected by the Trusted Platform
+ * device during various stages of the boot sequence. the attestation data
+ * is also hashed with some unique number (nonce) provided by the host to
+ * prevent replay attacks. public key and certificate also provided as
+ * part of the FW response.
+ *
* CPUCP_PACKET_MONITOR_DUMP_GET -
* Get monitors registers dump from the CpuCP kernel.
* The CPU will put the registers dump in the a buffer allocated by the driver
@@ -687,7 +694,7 @@ enum cpucp_packet_id {
CPUCP_PACKET_RESERVED, /* not used */
CPUCP_PACKET_ENGINE_CORE_ASID_SET, /* internal */
CPUCP_PACKET_RESERVED2, /* not used */
- CPUCP_PACKET_RESERVED3, /* not used */
+ CPUCP_PACKET_TPM_ATTEST_GET, /* internal */
CPUCP_PACKET_RESERVED4, /* not used */
CPUCP_PACKET_RESERVED5, /* not used */
CPUCP_PACKET_MONITOR_DUMP_GET, /* debugfs */
@@ -783,6 +790,9 @@ struct cpucp_packet {
* result cannot be used to hold general purpose data.
*/
__le32 status_mask;
+
+ /* random, used once number, for tpm secured packets */
+ __le32 nonce;
};

/* For NIC requests */
@@ -1193,6 +1203,45 @@ enum cpu_reset_status {
CPU_RST_STATUS_SOFT_RST_DONE = 1,
};

+#define TPM_PCR_DATA_BUF_SZ 256
+#define TPM_PCR_QUOTE_BUF_SZ (512 - 2) /* 2 bytes used for size */
+#define TPM_SIGNATURE_BUF_SZ (256 - 1) /* 1 byte used for size */
+#define TPM_PUB_DATA_BUF_SZ (512 - 2) /* 2 bytes used for size */
+#define TPM_CERTIFICATE_BUF_SZ (2048 - 2) /* 2 bytes used for size */
+
+/*
+ * struct cpucp_tpm_attest_info - attestation data of the boot from the TPM
+ * @pcr_data: raw values of the PCR registers from the TPM
+ * @pcr_num_reg: number of PCR registers in the pcr_data array
+ * @pcr_reg_len: length of each PCR register in the pcr_data array (bytes)
+ * @nonce: number only used once. random number provided by host. this also
+ * passed to the quote command as a qualifying data.
+ * @pcr_quote_len: length of the attestation quote data (bytes)
+ * @pcr_quote: attestation data structure (TPM2B_ATTEST) from the TPM
+ * @quote_sig_len: length of the attestation signature (bytes)
+ * @quote_sig: signature structure (TPMT_SIGNATURE) of the attestation data
+ * @pub_data_len: length of the public data (bytes)
+ * @public_data: public key and certificate info from the TPM
+ * (outPublic + name + qualifiedName)
+ * @certificate_len: length of the certificate (bytes)
+ * @certificate: certificate for the attestation data, read from the TPM NV mem
+ */
+struct cpucp_tpm_attest_info {
+ __u8 pcr_data[TPM_PCR_DATA_BUF_SZ];
+ __u8 pcr_num_reg;
+ __u8 pcr_reg_len;
+ __le16 pad0;
+ __le32 nonce;
+ __le16 pcr_quote_len;
+ __u8 pcr_quote[TPM_PCR_QUOTE_BUF_SZ];
+ __u8 quote_sig_len;
+ __u8 quote_sig[TPM_SIGNATURE_BUF_SZ];
+ __le16 pub_data_len;
+ __u8 public_data[TPM_PUB_DATA_BUF_SZ];
+ __le16 certificate_len;
+ __u8 certificate[TPM_CERTIFICATE_BUF_SZ];
+};
+
/*
* struct dcore_monitor_regs_data - DCORE monitor regs data.
* the structure follows sync manager block layout. relevant only to Gaudi.
--
2.25.1