From nobody Wed Dec 17 02:55:20 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 78B5B157A5C for ; Mon, 24 Feb 2025 03:21:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367321; cv=none; b=oLHrfUFIQCehHuFPPtCfzobN5keQdsPsoewqxgvPxXSp62q6q2Ce0xBe8U10YTp34Jho0/8kJznSz/xmP2b7VvuMSUsnCtbVOkFo1LhuDX6wyP/HAvauSwOA90I1oXg3ReX2zL8NT/Rq+Xk9Fwqir6Qcn+Q5wOVqTlUY4DGqq/M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367321; c=relaxed/simple; bh=JfXnBns1Fax1qPA3yPKSlFeVnjd9t6yOBfyrq6kQx6g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=J6a2dxKl8hs2w1Te6VzFSzGuKumwU++ThDyKxMnGXXDGawM0aqzlbnZW1rAlWbMeBbG8s0O0PegvNWKDYNpfK9jVBIeAOVk3yaeu9CYXJ9qjvNGISce1E4hHIZ+1TXMti18+KINBSe/KxsAZG67HPJ2O6X9K5C7IgPE6bT8zXWY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=P9ujbI08; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="P9ujbI08" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1740367320; x=1771903320; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=JfXnBns1Fax1qPA3yPKSlFeVnjd9t6yOBfyrq6kQx6g=; b=P9ujbI08fXt3YTemgzTROXHY7nA6bm27dWd6JCujP0hvMpe1i10TTy3+ By/hICMafAr0eb7H4sqK9olPWoSTtfXrRtyY7ZCPVgiQc0c5hPWur8AHd plGoKjq6BRbXk6URCN7Iavk/PsvrQmke7sW5d4Zz0/PTGMtYOfzBtsbBY UgFgCsHhVOByrf5DYyZ1xCf/6acR4PVrJJ1mMXyJD4YBW9cp+YoWaaREu tidBtgnTYdjrWb3WIVeN7SxRN7WyArPwdfWY6IWi71dadVgDFIAIlklhi CafzaxDcoCLxCp3IEHQ2yeZ2vipWkmUWpoDP6Qvn0CySVTFAvAN+/IgLV Q==; X-CSE-ConnectionGUID: iPx0YTCSRkayXNDdxJSwdw== X-CSE-MsgGUID: vMsTBKJXQ0GicKAQ65KOXQ== X-IronPort-AV: E=McAfee;i="6700,10204,11354"; a="28706864" X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="28706864" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:56 -0800 X-CSE-ConnectionGUID: AYbk5T1iRLGtQP54D+Ck3g== X-CSE-MsgGUID: 4OrWKWCaQSy1Mx+jhdCcBA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="116441900" Received: from shanagud-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.117.251]) by fmviesa010-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:14 -0800 From: Cedric Xing Date: Sun, 23 Feb 2025 21:20:12 -0600 Subject: [PATCH v2 1/4] tsm: Add TVM Measurement Register support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250223-tdx-rtmr-v2-1-f2d85b0a5f94@intel.com> References: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> In-Reply-To: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> To: Dan Williams , "Kirill A. Shutemov" , Dave Hansen , Thomas Gleixner , Ingo Molnar , Borislav Petkov , x86@kernel.org, "H. Peter Anvin" Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, Dionna Amalie Glaze , James Bottomley , Dan Middleton , Mikko Ylinen , Sathyanarayanan Kuppuswamy X-Mailer: b4 0.13.0 Add new TSM APIs for TVM guest drivers to register and expose measurement registers (MRs) as sysfs attributes (files). New TSM APIs: - `tsm_register_measurement(struct tsm_measurement *)`: Register a set of MRs with the TSM core. - `tsm_unregister_measurement(struct tsm_measurement *)`: Unregister a previously registered set of MRs. These APIs are centered around `struct tsm_measurement`, which includes an array of `struct tsm_measurement_register`s with properties (`tsm_measurement_register::mr_flags`) like *Readable* (`TSM_MR_F_R`) and *Extensible* (`TSM_MR_F_X`). For details, see include/linux/tsm.h. Upon successful registration, the TSM core exposes MRs in sysfs at /sys/kernel/tsm/MR_PROVIDER/, where MR_PROVIDER is the measurement provider's name (`tsm_measurement::name`). Each MR is accessible either as a file (/sys/kernel/tsm/MR_PROVIDER/MR_NAME contains the MR value) or a directory (/sys/kernel/tsm/MR_PROVIDER/MR_NAME/HASH/digest contains the MR value) depending on whether `TSM_MR_F_F` is set or cleared (in `tsm_measurement_register::mr_flags`). MR_NAME is the name (`tsm_measurement_register::mr_name`) of the MR, while HASH is the hash algorithm (`tsm_measurement_register::mr_hash`) name in the latter case. *Crypto Agility* is supported by merging independent MRs with a common name into a single directory, each represented by its HASH/digest file. Note that HASH must be distinct or behavior is undefined. Signed-off-by: Cedric Xing Tested-by: Jianxiong Gao --- Documentation/ABI/testing/sysfs-kernel-tsm | 20 ++ MAINTAINERS | 2 +- drivers/virt/coco/Kconfig | 17 +- drivers/virt/coco/Makefile | 2 + drivers/virt/coco/{tsm.c =3D> tsm-core.c} | 6 +- drivers/virt/coco/tsm-mr.c | 383 +++++++++++++++++++++++++= ++++ include/linux/tsm.h | 65 +++++ 7 files changed, 492 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-kernel-tsm b/Documentation/ABI= /testing/sysfs-kernel-tsm new file mode 100644 index 000000000000..99735cf4da5c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-tsm @@ -0,0 +1,20 @@ +What: /sys/kernel/tsm// +Date: February 2025 +Contact: Cedric Xing . +Description: + This file contains the value of the measurement register + . Depending on the CC architecture, this file may be + writable, in which case the value written will be the new value + of . Each write must start at the beginning and be of + the same size as the file. Partial writes are not permitted. + +What: /sys/kernel/tsm////digest +Date: February 2025 +Contact: Cedric Xing . +Description: + This file contains the value of the measurement register + . Depending on the CC architecture, this file may be + writable, in which case any value written may be extended to + using . Each write must start at the beginning + and be of the same size as the file. Partial writes are not + permitted. diff --git a/MAINTAINERS b/MAINTAINERS index 4ff26fa94895..a5eef4c7234c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24104,7 +24104,7 @@ M: Dan Williams L: linux-coco@lists.linux.dev S: Maintained F: Documentation/ABI/testing/configfs-tsm -F: drivers/virt/coco/tsm.c +F: drivers/virt/coco/tsm*.c F: include/linux/tsm.h =20 TRUSTED SERVICES TEE DRIVER diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig index ff869d883d95..3fa38fd731b9 100644 --- a/drivers/virt/coco/Kconfig +++ b/drivers/virt/coco/Kconfig @@ -5,7 +5,22 @@ =20 config TSM_REPORTS select CONFIGFS_FS - tristate + select CRYPTO_HASH_INFO + select CRYPTO + tristate "Trusted Security Module (TSM) support" + +if TSM_REPORTS + +config TSM_MR_MAXBANKS + int "Max number of banks of Measurement Registers" + range 1 8 + default 1 + help + A "bank" is a group of MRs that use the same hash algorithm. This + option specifies the maximal number of banks each Measurement + Register (MR) can support. + +endif =20 source "drivers/virt/coco/efi_secret/Kconfig" =20 diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile index c3d07cfc087e..4b108d8df1bd 100644 --- a/drivers/virt/coco/Makefile +++ b/drivers/virt/coco/Makefile @@ -2,6 +2,8 @@ # # Confidential computing related collateral # +tsm-y +=3D tsm-core.o tsm-mr.o + obj-$(CONFIG_TSM_REPORTS) +=3D tsm.o obj-$(CONFIG_EFI_SECRET) +=3D efi_secret/ obj-$(CONFIG_ARM_PKVM_GUEST) +=3D pkvm-guest/ diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm-core.c similarity index 99% rename from drivers/virt/coco/tsm.c rename to drivers/virt/coco/tsm-core.c index 9432d4e303f1..ab5269db9c13 100644 --- a/drivers/virt/coco/tsm.c +++ b/drivers/virt/coco/tsm-core.c @@ -476,6 +476,9 @@ int tsm_unregister(const struct tsm_ops *ops) } EXPORT_SYMBOL_GPL(tsm_unregister); =20 +int tsm_mr_init(void); +void tsm_mr_exit(void); + static struct config_group *tsm_report_group; =20 static int __init tsm_init(void) @@ -497,12 +500,13 @@ static int __init tsm_init(void) } tsm_report_group =3D tsm; =20 - return 0; + return tsm_mr_init(); } module_init(tsm_init); =20 static void __exit tsm_exit(void) { + tsm_mr_exit(); configfs_unregister_default_group(tsm_report_group); configfs_unregister_subsystem(&tsm_configfs); } diff --git a/drivers/virt/coco/tsm-mr.c b/drivers/virt/coco/tsm-mr.c new file mode 100644 index 000000000000..8a96b2a78869 --- /dev/null +++ b/drivers/virt/coco/tsm-mr.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +int tsm_mr_init(void); +void tsm_mr_exit(void); + +enum tmr_dir_battr_index { + TMR_DIR_BA_DIGEST, + TMR_DIR_BA__COUNT, +}; + +struct tmr_dir { + struct kobject kobj; + struct bin_attribute battrs[CONFIG_TSM_MR_MAXBANKS][TMR_DIR_BA__COUNT]; + int algo; +}; + +struct tmr_provider { + struct kset kset; + struct rw_semaphore rwsem; + struct bin_attribute *mrfiles; + struct tsm_measurement *tmr; + bool in_sync; +}; + +static inline struct tmr_provider *tmr_mr_to_provider(const struct tsm_mea= surement_register *mr, + struct kobject *kobj) +{ + if (mr->mr_flags & TSM_MR_F_F) + return container_of(kobj, struct tmr_provider, kset.kobj); + else + return container_of(kobj->kset, struct tmr_provider, kset); +} + +static inline int tmr_call_refresh(struct tmr_provider *pvd, + const struct tsm_measurement_register *mr) +{ + int rc; + + rc =3D pvd->tmr->refresh(pvd->tmr, mr); + if (rc) + pr_warn("%s.refresh(%s) failed %d\n", kobject_name(&pvd->kset.kobj), mr-= >mr_name, + rc); + return rc; +} + +static inline int tmr_call_extend(struct tmr_provider *pvd, + const struct tsm_measurement_register *mr, const u8 *data) +{ + int rc; + + rc =3D pvd->tmr->extend(pvd->tmr, mr, data); + if (rc) + pr_warn("%s.extend(%s) failed %d\n", kobject_name(&pvd->kset.kobj), mr->= mr_name, + rc); + return rc; +} + +static ssize_t tmr_digest_read(struct file *filp, struct kobject *kobj, st= ruct bin_attribute *attr, + char *buffer, loff_t off, size_t count) +{ + const struct tsm_measurement_register *mr; + struct tmr_provider *pvd; + int rc; + + if (off < 0 || off > attr->size) + return -EINVAL; + + count =3D min(count, attr->size - (size_t)off); + if (!count) + return count; + + mr =3D attr->private; + pvd =3D tmr_mr_to_provider(mr, kobj); + rc =3D down_read_interruptible(&pvd->rwsem); + if (rc) + return rc; + + /* + * pvd->in_sync indicates if any MRs have been written/extended since the= last + * pvd->refresh() call. When pvd->in_sync is false, pvd->refresh() is nec= essary to sync the + * cached values of all live (L) MRs with the underlying hardware. + */ + if ((mr->mr_flags & TSM_MR_F_L) && !pvd->in_sync) { + up_read(&pvd->rwsem); + + rc =3D down_write_killable(&pvd->rwsem); + if (rc) + return rc; + + if (!pvd->in_sync) { + rc =3D tmr_call_refresh(pvd, mr); + pvd->in_sync =3D !rc; + } + + downgrade_write(&pvd->rwsem); + } + + if (!rc) + memcpy(buffer, mr->mr_value + off, count); + + up_read(&pvd->rwsem); + return rc ?: count; +} + +static ssize_t tmr_digest_write(struct file *filp, struct kobject *kobj, s= truct bin_attribute *attr, + char *buffer, loff_t off, size_t count) +{ + const struct tsm_measurement_register *mr; + struct tmr_provider *pvd; + ssize_t rc; + + if (off !=3D 0 || count !=3D attr->size) + return -EINVAL; + + mr =3D attr->private; + pvd =3D tmr_mr_to_provider(mr, kobj); + rc =3D down_write_killable(&pvd->rwsem); + if (rc) + return rc; + + if (mr->mr_flags & TSM_MR_F_X) + rc =3D tmr_call_extend(pvd, mr, buffer); + else + memcpy(mr->mr_value, buffer, count); + + // clear pvd->in_sync so the next read from any live (L) MR will trigger = pvd->refresh() + if (!rc) + pvd->in_sync =3D false; + + up_write(&pvd->rwsem); + return rc ?: count; +} + +static void tmr_dir_release(struct kobject *kobj) +{ + struct tmr_dir *mrd; + + mrd =3D container_of(kobj, typeof(*mrd), kobj); + kfree(mrd); +} + +static const struct kobj_type tmr_dir_ktype =3D { + .release =3D tmr_dir_release, + .sysfs_ops =3D &kobj_sysfs_ops, +}; + +static struct tmr_dir *tmr_dir_create(const struct tsm_measurement_registe= r *mr, + struct tmr_provider *pvd) +{ + struct kobject *kobj; + struct tmr_dir *mrd; + + kobj =3D kset_find_obj(&pvd->kset, mr->mr_name); + if (kobj) { + mrd =3D container_of(kobj, typeof(*mrd), kobj); + kobject_put(kobj); + if (++mrd->algo >=3D CONFIG_TSM_MR_MAXBANKS) { + --mrd->algo; + return ERR_PTR(-ENOSPC); + } + } else { + int rc; + + mrd =3D kzalloc(sizeof(*mrd), GFP_KERNEL); + if (!mrd) + return ERR_PTR(-ENOMEM); + + mrd->kobj.kset =3D &pvd->kset; + rc =3D kobject_init_and_add(&mrd->kobj, &tmr_dir_ktype, NULL, "%s", mr->= mr_name); + if (rc) { + kfree(mrd); + return ERR_PTR(rc); + } + } + + sysfs_bin_attr_init(&mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST]); + mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST].attr.name =3D "digest"; + if (mr->mr_flags & TSM_MR_F_W) + mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST].attr.mode |=3D S_IWUSR | S_IWG= RP; + if (mr->mr_flags & TSM_MR_F_R) + mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST].attr.mode |=3D S_IRUGO; + + mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST].size =3D mr->mr_size; + mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST].read =3D tmr_digest_read; + mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST].write =3D tmr_digest_write; + mrd->battrs[mrd->algo][TMR_DIR_BA_DIGEST].private =3D (void *)mr; + + return mrd; +} + +static void tmr_provider_release(struct kobject *kobj) +{ + struct tmr_provider *pvd; + + pvd =3D container_of(kobj, typeof(*pvd), kset.kobj); + if (!WARN_ON(!list_empty(&pvd->kset.list))) { + kfree(pvd->mrfiles); + kfree(pvd); + } +} + +static const struct kobj_type _mr_provider_ktype =3D { + .release =3D tmr_provider_release, + .sysfs_ops =3D &kobj_sysfs_ops, +}; + +static struct kset *tmr_sysfs_root; + +static struct tmr_provider *tmr_provider_create(struct tsm_measurement *tm= r) +{ + struct tmr_provider *pvd __free(kfree); + int rc; + + pvd =3D kzalloc(sizeof(*pvd), GFP_KERNEL); + if (!pvd) + return ERR_PTR(-ENOMEM); + + if (!tmr->name || !tmr->mrs || !tmr->refresh || !tmr->extend) + return ERR_PTR(-EINVAL); + + rc =3D kobject_set_name(&pvd->kset.kobj, "%s", tmr->name); + if (rc) + return ERR_PTR(rc); + + pvd->kset.kobj.kset =3D tmr_sysfs_root; + pvd->kset.kobj.ktype =3D &_mr_provider_ktype; + pvd->tmr =3D tmr; + + init_rwsem(&pvd->rwsem); + + rc =3D kset_register(&pvd->kset); + if (rc) + return ERR_PTR(rc); + + return_ptr(pvd); +} + +DEFINE_FREE(_unregister_measurement, struct tmr_provider *, + if (!IS_ERR_OR_NULL(_T)) tsm_unregister_measurement(_T->tmr)); + +int tsm_register_measurement(struct tsm_measurement *tmr) +{ + struct tmr_provider *pvd __free(_unregister_measurement); + int rc, nr; + + pvd =3D tmr_provider_create(tmr); + if (IS_ERR(pvd)) + return PTR_ERR(pvd); + + nr =3D 0; + for (int i =3D 0; tmr->mrs[i].mr_name; ++i) { + // flat files are counted and skipped + if (tmr->mrs[i].mr_flags & TSM_MR_F_F) { + ++nr; + continue; + } + + struct tmr_dir *mrd; + struct bin_attribute *battrs[TMR_DIR_BA__COUNT + 1] =3D {}; + struct attribute_group agrp =3D { + .name =3D hash_algo_name[tmr->mrs[i].mr_hash], + .bin_attrs =3D battrs, + }; + + mrd =3D tmr_dir_create(&tmr->mrs[i], pvd); + if (IS_ERR(mrd)) { + if (WARN_ONCE(PTR_ERR(mrd) =3D=3D -ENOSPC, "too many banks")) + continue; + + return PTR_ERR(mrd); + } + + for (int j =3D 0; j < TMR_DIR_BA__COUNT; ++j) + battrs[j] =3D &mrd->battrs[mrd->algo][j]; + + rc =3D sysfs_create_group(&mrd->kobj, &agrp); + if (rc) + return rc; + } + + if (nr > 0) { + struct bin_attribute *mrfiles __free(kfree); + struct bin_attribute **battrs __free(kfree); + + mrfiles =3D kcalloc(nr, sizeof(*mrfiles), GFP_KERNEL); + battrs =3D kcalloc(nr + 1, sizeof(*battrs), GFP_KERNEL); + if (!battrs || !mrfiles) + return -ENOMEM; + + for (int i =3D 0, j =3D 0; tmr->mrs[i].mr_name; ++i) { + if (!(tmr->mrs[i].mr_flags & TSM_MR_F_F)) + continue; + + mrfiles[j].attr.name =3D tmr->mrs[i].mr_name; + mrfiles[j].read =3D tmr_digest_read; + mrfiles[j].write =3D tmr_digest_write; + mrfiles[j].size =3D tmr->mrs[i].mr_size; + mrfiles[j].private =3D (void *)&tmr->mrs[i]; + if (tmr->mrs[i].mr_flags & TSM_MR_F_R) + mrfiles[j].attr.mode |=3D S_IRUGO; + if (tmr->mrs[i].mr_flags & TSM_MR_F_W) + mrfiles[j].attr.mode |=3D S_IWUSR | S_IWGRP; + + battrs[j] =3D &mrfiles[j]; + ++j; + } + + struct attribute_group agrp =3D { + .bin_attrs =3D battrs, + }; + rc =3D sysfs_create_group(&pvd->kset.kobj, &agrp); + if (rc) + return rc; + + pvd->mrfiles =3D no_free_ptr(mrfiles); + } + + // initial refresh of MRs + rc =3D tmr_call_refresh(pvd, NULL); + pvd->in_sync =3D !rc; + + pvd =3D NULL; // to avoid being freed automatically + return 0; +} +EXPORT_SYMBOL_GPL(tsm_register_measurement); + +static void tmr_put_children(struct kset *kset) +{ + struct kobject *p, *n; + + spin_lock(&kset->list_lock); + list_for_each_entry_safe(p, n, &kset->list, entry) { + spin_unlock(&kset->list_lock); + kobject_put(p); + spin_lock(&kset->list_lock); + } + spin_unlock(&kset->list_lock); +} + +int tsm_unregister_measurement(struct tsm_measurement *tmr) +{ + struct kobject *kobj; + struct tmr_provider *pvd; + + kobj =3D kset_find_obj(tmr_sysfs_root, tmr->name); + if (!kobj) + return -ENOENT; + + pvd =3D container_of(kobj, typeof(*pvd), kset.kobj); + if (pvd->tmr !=3D tmr) + return -EINVAL; + + tmr_put_children(&pvd->kset); + kset_unregister(&pvd->kset); + kobject_put(kobj); + return 0; +} +EXPORT_SYMBOL_GPL(tsm_unregister_measurement); + +int tsm_mr_init(void) +{ + tmr_sysfs_root =3D kset_create_and_add("tsm", NULL, kernel_kobj); + if (!tmr_sysfs_root) + return -ENOMEM; + return 0; +} + +void tsm_mr_exit(void) +{ + kset_unregister(tmr_sysfs_root); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Provide Trusted Security Module measurements via sysfs= "); diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 11b0c525be30..312965d45001 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -5,6 +5,7 @@ #include #include #include +#include =20 #define TSM_INBLOB_MAX 64 #define TSM_OUTBLOB_MAX SZ_32K @@ -109,4 +110,68 @@ struct tsm_ops { =20 int tsm_register(const struct tsm_ops *ops, void *priv); int tsm_unregister(const struct tsm_ops *ops); + +/** + * struct tsm_measurement_register - describes an architectural measuremen= t register (MR) + * @mr_name: name of the MR + * @mr_value: buffer containing the current value of the MR + * @mr_size: size of the MR - typically the digest size of @mr_hash + * @mr_flags: bitwise OR of flags defined in enum tsm_measurement_register= _flag + * @mr_hash: optional hash identifier defined in include/uapi/linux/hash_i= nfo.h + * + * A CC guest driver provides this structure to detail the measurement fac= ility supported by the + * underlying CC hardware. After registration via `tsm_register_measuremen= t`, the CC guest driver + * must retain this structure until it is unregistered using `tsm_unregist= er_measurement`. + */ +struct tsm_measurement_register { + const char *mr_name; + void *mr_value; + u32 mr_size; + u32 mr_flags; + enum hash_algo mr_hash; +}; + +/** + * enum tsm_measurement_register_flag - properties of an MR + * @TSM_MR_F_X: this MR supports the extension semantics + * @TSM_MR_F_W: the sysfs attribute corresponding to this MR is writable + * @TSM_MR_F_R: the sysfs attribute corresponding to this MR is readable + * @TSM_MR_F_L: this MR is live - the current value may differ from the la= st value written so must + * be loaded back from TVM hardware/firmware on read + * @TSM_MR_F_F: present this MR as a file (instead of a directory) + * @TSM_MR_F_LIVE: shorthand for L (live) and R (readable) + * @TSM_MR_F_RTMR: shorthand for LIVE and X (extensible) + */ +enum tsm_measurement_register_flag { + TSM_MR_F_X =3D 1, + TSM_MR_F_W =3D 2, + TSM_MR_F_R =3D 4, + TSM_MR_F_L =3D 8, + TSM_MR_F_F =3D 16, + TSM_MR_F_LIVE =3D TSM_MR_F_L | TSM_MR_F_R, + TSM_MR_F_RTMR =3D TSM_MR_F_LIVE | TSM_MR_F_X, +}; + +#define TSM_MR_(mr, hash) = \ + .mr_name =3D #mr, .mr_size =3D hash##_DIGEST_SIZE, .mr_hash =3D HASH_ALGO= _##hash, \ + .mr_flags =3D TSM_MR_F_R + +/** + * struct tsm_measurement - define CC specific MRs and methods for updatin= g them + * @name: name of the measurement provider + * @mrs: array of MR definitions ending with mr_name set to %NULL + * @refresh: sync/reload MR values in TVM hardware/firmware into the kerne= l buffers + * @extend: extend the specified MR with mr->mr_size bytes stored in mr->m= r_value + */ +struct tsm_measurement { + const char *name; + const struct tsm_measurement_register *mrs; + int (*refresh)(struct tsm_measurement *tmr, const struct tsm_measurement_= register *mr); + int (*extend)(struct tsm_measurement *tmr, const struct tsm_measurement_r= egister *mr, + const u8 *data); +}; + +int tsm_register_measurement(struct tsm_measurement *tmr); +int tsm_unregister_measurement(struct tsm_measurement *tmr); + #endif /* __TSM_H */ --=20 2.43.0 From nobody Wed Dec 17 02:55:20 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B2C71F60A for ; Mon, 24 Feb 2025 03:21:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367319; cv=none; b=eWOqgeozaZAw7gUhsvWB/dAX7mol9RnpctxhIroB3WykvDEROAeq7+X5UD1wJxLHv0J6U2iNNwy1IWwxEbbriKLCtRQ2wp30nlwimNWL5QkzprSqsLjU/WWHRHC0l12IXixAWY55UEKobFiDSf6UQnM1KkFOE8rRQUGR4cnW6zk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367319; c=relaxed/simple; bh=GiULTZ2ipk+wIUbKL/FpRBpeW8mvkTFvKGmACFzv7hI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ucWkGXUixdTAVWBWQx4khJ9i1mFtLH5mMC3VSx2v+pGpuLuhbPro2CqB/hJZRntrM2Gcy7R/1oHEpoK+UKYqC39fo8Nv2GWjXPtarL4zqJYIHOb6Edv2v9GPCF22MoISA3yOtCqF7S5ksEDI/glXHJPGRbVV9T9hG7T0AusQL8k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=UNxRa3o3; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="UNxRa3o3" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1740367317; x=1771903317; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=GiULTZ2ipk+wIUbKL/FpRBpeW8mvkTFvKGmACFzv7hI=; b=UNxRa3o3LR275L3Zr0rb855DVgiT6D8JBMCgmqF1ggHEzva5AHmc8bfF ePmytQeEMqakWvmnA2In+DuYDvTIVrvRCcbjeNjU+Usljbly4rKyzF5jK e1oxcgFWCqeGm8+9T5cqfp38nPAzlBnBiaLnaGeARcrfmQlI9RgR9uD8E e616fTkPq8mp+NoQXrZYL7ZW9HZDZJC93ywrCOO7P2mBcBetjEv8BoOzj LpweVffm8yEC/id1J80eB3n6ZK9ZVSwefpuoWjl7zzQ7aquopnubdR0Br PKf4IeDBBAls6HMnq8H6AgOCzp0BLuc3ReKY/wRvELm92D8YezjzV8MOR g==; X-CSE-ConnectionGUID: YW+kXZPiTnixN0jksf+fPg== X-CSE-MsgGUID: xrg2+Ca8Q0OCDS2SCbOC7A== X-IronPort-AV: E=McAfee;i="6700,10204,11354"; a="28706874" X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="28706874" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:56 -0800 X-CSE-ConnectionGUID: RDRNehVqQZ6Gak3lT0KySw== X-CSE-MsgGUID: HbGLO86BTV2ZKeoSFt+WEw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="116441902" Received: from shanagud-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.117.251]) by fmviesa010-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:15 -0800 From: Cedric Xing Date: Sun, 23 Feb 2025 21:20:13 -0600 Subject: [PATCH v2 2/4] tsm: Add TSM measurement sample code Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250223-tdx-rtmr-v2-2-f2d85b0a5f94@intel.com> References: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> In-Reply-To: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> To: Dan Williams , "Kirill A. Shutemov" , Dave Hansen , Thomas Gleixner , Ingo Molnar , Borislav Petkov , x86@kernel.org, "H. Peter Anvin" Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, Dionna Amalie Glaze , James Bottomley , Dan Middleton , Mikko Ylinen , Sathyanarayanan Kuppuswamy X-Mailer: b4 0.13.0 This sample kernel module demonstrates how to make MRs accessible to user mode through TSM. Once loaded, this module registers a virtual measurement provider with the TSM core and will result in the directory tree below. /sys/kernel/tsm/ =E2=94=94=E2=94=80=E2=94=80 tsm_mr_sample =E2=94=9C=E2=94=80=E2=94=80 config_mr =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha512 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 report_digest =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha512 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 rtmr0 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha256 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 rtmr1 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 rtmr_crypto_agile =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 sha256 =E2=94=82 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=94=E2=94=80=E2=94=80 static_mr =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=94=E2=94=80=E2=94=80 digest Among the MRs in this example: - `static_mr` and `config_mr` are *Readonly* (static) MRs. - `rtmr0` is an RTMR with `TSM_MR_F_W` **cleared**, preventing direct extensions; as a result, the attribute rtmr0/sha256/digest is read-only. - `rtmr1` is an RTMR with `TSM_MR_F_W` **set**, permitting direct extensions; thus, the attribute rtmr1/sha384/digest is writable. - `rtmr_crypto_agile` demonstrates a "single" MR that supports multiple hash algorithms. Each supported algorithm has a corresponding digest, usually referred to as a "bank" in TCG terminology. In this specific sample, the 2 banks are aliased to `rtmr0` and `rtmr1`, respectively. - `report_digest` contains the digest of the internal report structure living in this sample module's memory. It is to demonstrate the use of the `TSM_MR_F_L` flag. Its value changes each time an RTMR is extended. More details are available in samples/tsm/tsm_mr_sample.c. Signed-off-by: Cedric Xing Tested-by: Jianxiong Gao --- MAINTAINERS | 1 + samples/Kconfig | 13 ++++++ samples/Makefile | 1 + samples/tsm/Makefile | 2 + samples/tsm/tsm_mr_sample.c | 107 ++++++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 124 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a5eef4c7234c..1d4232f5269e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24106,6 +24106,7 @@ S: Maintained F: Documentation/ABI/testing/configfs-tsm F: drivers/virt/coco/tsm*.c F: include/linux/tsm.h +F: samples/tsm/* =20 TRUSTED SERVICES TEE DRIVER M: Balint Dobszay diff --git a/samples/Kconfig b/samples/Kconfig index 820e00b2ed68..0b4592581648 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -184,6 +184,19 @@ config SAMPLE_TIMER bool "Timer sample" depends on CC_CAN_LINK && HEADERS_INSTALL =20 +config SAMPLE_TSM_MR + tristate "TSM measurement sample" + depends on TSM_REPORTS + help + Build a sample module that emulates MRs (Measurement Registers) and + exposes them to user mode applications through the TSM sysfs + interface (/sys/kernel/tsm/tsm_mr_sample/). + + The module name will be tsm-mr-sample when built as a module. + + Note: TSM_MR_MAXBANKS must be at least 2 for this sample to work + properly. + config SAMPLE_UHID bool "UHID sample" depends on CC_CAN_LINK && HEADERS_INSTALL diff --git a/samples/Makefile b/samples/Makefile index f24cd0d72dd0..c4b6dcc81df6 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_SAMPLE_FPROBE) +=3D fprobe/ obj-$(CONFIG_SAMPLES_RUST) +=3D rust/ obj-$(CONFIG_SAMPLE_DAMON_WSSE) +=3D damon/ obj-$(CONFIG_SAMPLE_DAMON_PRCL) +=3D damon/ +obj-$(CONFIG_SAMPLE_TSM_MR) +=3D tsm/ diff --git a/samples/tsm/Makefile b/samples/tsm/Makefile new file mode 100644 index 000000000000..587c3947b3a7 --- /dev/null +++ b/samples/tsm/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_SAMPLE_TSM_MR) +=3D tsm_mr_sample.o diff --git a/samples/tsm/tsm_mr_sample.c b/samples/tsm/tsm_mr_sample.c new file mode 100644 index 000000000000..4c2c6cde36e1 --- /dev/null +++ b/samples/tsm/tsm_mr_sample.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */ + +#define DEBUG +#define pr_fmt(x) KBUILD_MODNAME ": " x + +#include +#include +#include +#include + +struct { + u8 static_mr[SHA384_DIGEST_SIZE]; + u8 config_mr[SHA512_DIGEST_SIZE]; + u8 rtmr0[SHA256_DIGEST_SIZE]; + u8 rtmr1[SHA384_DIGEST_SIZE]; + u8 user_data[SHA512_DIGEST_SIZE]; + u8 report_digest[SHA512_DIGEST_SIZE]; +} sample_report =3D { + .static_mr =3D "static_mr", + .config_mr =3D "config_mr", + .rtmr0 =3D "rtmr0", + .rtmr1 =3D "rtmr1", + .user_data =3D "user_data", +}; + +DEFINE_FREE(shash, struct crypto_shash *, if (!IS_ERR(_T)) crypto_free_sha= sh(_T)); + +static int sample_report_refresh(struct tsm_measurement *tmr, + const struct tsm_measurement_register *mr) +{ + pr_debug("%s(%s,%s) is called\n", __func__, tmr->name, mr ? mr->mr_name := ""); + struct crypto_shash *tfm __free(shash) =3D + crypto_alloc_shash(hash_algo_name[HASH_ALGO_SHA512], 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + return crypto_shash_tfm_digest(tfm, (u8 *)&sample_report, + offsetof(typeof(sample_report), report_digest), + sample_report.report_digest); +} + +static int sample_report_extend_mr(struct tsm_measurement *tmr, + const struct tsm_measurement_register *mr, const u8 *data) +{ + SHASH_DESC_ON_STACK(desc, 0); + int rc; + + pr_debug("%s(%s,%s) is called\n", __func__, tmr->name, mr->mr_name); + + desc->tfm =3D crypto_alloc_shash(hash_algo_name[mr->mr_hash], 0, 0); + if (IS_ERR(desc->tfm)) + return PTR_ERR(desc->tfm); + + rc =3D crypto_shash_init(desc); + if (!rc) + rc =3D crypto_shash_update(desc, mr->mr_value, mr->mr_size); + if (!rc) + rc =3D crypto_shash_finup(desc, data, mr->mr_size, mr->mr_value); + + crypto_free_shash(desc->tfm); + return rc; +} + +#define MR_(mr, hash) .mr_value =3D &sample_report.mr, TSM_MR_(mr, hash) +static const struct tsm_measurement_register sample_mrs[] =3D { + /* static MR, read-only */ + { MR_(static_mr, SHA384) }, + /* config MR, read-only */ + { MR_(config_mr, SHA512) }, + /* RTMR, direct extension prohibited */ + { MR_(rtmr0, SHA256) | TSM_MR_F_RTMR }, + /* RTMR, direct extension allowed */ + { MR_(rtmr1, SHA384) | TSM_MR_F_RTMR | TSM_MR_F_W }, + /* RTMR, crypto agile, alaised to rtmr0 and rtmr1, respectively */ + { .mr_value =3D &sample_report.rtmr0, + TSM_MR_(rtmr_crypto_agile, SHA256) | TSM_MR_F_RTMR | TSM_MR_F_W }, + { .mr_value =3D &sample_report.rtmr1, + TSM_MR_(rtmr_crypto_agile, SHA384) | TSM_MR_F_RTMR | TSM_MR_F_W }, + /* most CC archs allow including user data in attestation */ + { MR_(report_digest, SHA512) | TSM_MR_F_L }, + /* terminating NULL entry */ + {} +}; +#undef MR_ + +static struct tsm_measurement sample_mr_provider =3D { + .name =3D KBUILD_MODNAME, + .mrs =3D sample_mrs, + .refresh =3D sample_report_refresh, + .extend =3D sample_report_extend_mr, +}; + +static int __init tsm_mr_sample_init(void) +{ + return tsm_register_measurement(&sample_mr_provider); +} + +static void __exit tsm_mr_sample_exit(void) +{ + tsm_unregister_measurement(&sample_mr_provider); +} + +module_init(tsm_mr_sample_init); +module_exit(tsm_mr_sample_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Sample tsm_measurement implementation"); --=20 2.43.0 From nobody Wed Dec 17 02:55:20 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4716A1A3176 for ; Mon, 24 Feb 2025 03:21:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367321; cv=none; b=gxhAkKPiGYnQkJ2DOV/NY+w5GcPWx9Mw860515NOUUMO54RUVX36gL7luUQ7vH2ui1D7oTZ3qtanNQMKz7/Vptf0yE6TwbmP+igI3oh3bmYuHGw0k/dbF/3YICo/FYZrQQZQYO5fzWs6Tke5k+yxyW5K13j4jnVfefOwliayBFQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367321; c=relaxed/simple; bh=taadowUR5/58ys41IAsM9k+P+FmsnUdHVJhEU6NXOls=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cncd0EsEqCk5KkJ+RNTVOptiRX/xFYYpGCJ3FHo0PjhOkH4jlKDe6XLCFXz/xktTgwg1qG3PhWM0XJ3gL791kB9KZJqxMyJdBpcPM3V5OslvdeYYseXT5KRwS7pu1jGx5VfWR++DsPgOPz3YH9lbVzsq2sqSTNFa2aajyk/jzXg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=VuyPAp2I; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="VuyPAp2I" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1740367320; x=1771903320; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=taadowUR5/58ys41IAsM9k+P+FmsnUdHVJhEU6NXOls=; b=VuyPAp2IzNF7GYY13qCV4I3JRmy+FlE/21kozeQKq4vG/bwaEahNPV+o IdvRScHqwCYRSkFJxmlQuGEedbfHl5iiRKwntd0d9AomRJWu2GUeUKTaF 3wouNWIrKUWQnwAp3Yhru+VevYVZdVztNqOQ0qyc/sWP1RgrE91ymdPuR eVb+QRmmsT8MI4EdvJ3lDPd0BGMF4yxFQkOqee58b75LGz1pGdNsB7F6W WD6wkTVUZnghk73536qGHDFYd9nv2zMH89ghXDJA8aaf/UcQj5WJwHX1l qO8cLLix+8opmnKsnjZwO/pP5wHuXRBUW6hTWBJkVTcpEZ5A/Znii8fvz w==; X-CSE-ConnectionGUID: 91UgikEZSomvbyzWP1Z+sw== X-CSE-MsgGUID: plqFTYT3TACzm5PylDoWCg== X-IronPort-AV: E=McAfee;i="6700,10204,11354"; a="28706884" X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="28706884" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:56 -0800 X-CSE-ConnectionGUID: dtwl4QAgROmEkAs/LCc1xQ== X-CSE-MsgGUID: DM3mpKcgSsygBmtDyZkTgQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="116441904" Received: from shanagud-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.117.251]) by fmviesa010-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:16 -0800 From: Cedric Xing Date: Sun, 23 Feb 2025 21:20:14 -0600 Subject: [PATCH v2 3/4] x86/tdx: Add tdx_mcall_extend_rtmr() interface Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250223-tdx-rtmr-v2-3-f2d85b0a5f94@intel.com> References: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> In-Reply-To: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> To: Dan Williams , "Kirill A. Shutemov" , Dave Hansen , Thomas Gleixner , Ingo Molnar , Borislav Petkov , x86@kernel.org, "H. Peter Anvin" Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, Dionna Amalie Glaze , James Bottomley , Dan Middleton , Mikko Ylinen , Sathyanarayanan Kuppuswamy X-Mailer: b4 0.13.0 The TDX guest exposes one MRTD (Build-time Measurement Register) and four RTMR (Run-time Measurement Register) registers to record the build and boot measurements of a virtual machine (VM). These registers are similar to PCR (Platform Configuration Register) registers in the TPM (Trusted Platform Module) space. This measurement data is used to implement security features like attestation and trusted boot. To facilitate updating the RTMR registers, the TDX module provides support for the `TDG.MR.RTMR.EXTEND` TDCALL which can be used to securely extend the RTMR registers. Add helper function to update RTMR registers. It will be used by the TDX guest driver in enabling RTMR extension support. Signed-off-by: Kuppuswamy Sathyanarayanan Signed-off-by: Cedric Xing Tested-by: Jianxiong Gao --- arch/x86/coco/tdx/tdx.c | 36 ++++++++++++++++++++++++++++++++++++ arch/x86/include/asm/shared/tdx.h | 1 + arch/x86/include/asm/tdx.h | 2 ++ 3 files changed, 39 insertions(+) diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 32809a06dab4..f88e249e339a 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -35,6 +35,7 @@ /* TDX Module call error codes */ #define TDCALL_RETURN_CODE(a) ((a) >> 32) #define TDCALL_INVALID_OPERAND 0xc0000100 +#define TDCALL_OPERAND_BUSY 0x80000200 =20 #define TDREPORT_SUBTYPE_0 0 =20 @@ -135,6 +136,41 @@ int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport) } EXPORT_SYMBOL_GPL(tdx_mcall_get_report0); =20 +/** + * tdx_mcall_extend_rtmr() - Wrapper to extend RTMR registers using + * TDG.MR.RTMR.EXTEND TDCALL. + * @index: Index of RTMR register to be extended. + * @data: Address of the input buffer with RTMR register extend data. + * + * Refer to section titled "TDG.MR.RTMR.EXTEND leaf" in the TDX Module + * v1.0 specification for more information on TDG.MR.RTMR.EXTEND TDCALL. + * It is used in the TDX guest driver module to allow user extend the + * RTMR registers (index > 1). + * + * Return 0 on success, -EINVAL for invalid operands, -EBUSY for busy + * operation or -EIO on other TDCALL failures. + */ +int tdx_mcall_extend_rtmr(u8 index, u8 *data) +{ + struct tdx_module_args args =3D { + .rcx =3D virt_to_phys(data), + .rdx =3D index, + }; + u64 ret; + + ret =3D __tdcall(TDG_MR_RTMR_EXTEND, &args); + if (ret) { + if (TDCALL_RETURN_CODE(ret) =3D=3D TDCALL_INVALID_OPERAND) + return -EINVAL; + if (TDCALL_RETURN_CODE(ret) =3D=3D TDCALL_OPERAND_BUSY) + return -EBUSY; + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(tdx_mcall_extend_rtmr); + /** * tdx_hcall_get_quote() - Wrapper to request TD Quote using GetQuote * hypercall. diff --git a/arch/x86/include/asm/shared/tdx.h b/arch/x86/include/asm/share= d/tdx.h index fcbbef484a78..d0760c6160cc 100644 --- a/arch/x86/include/asm/shared/tdx.h +++ b/arch/x86/include/asm/shared/tdx.h @@ -13,6 +13,7 @@ /* TDX module Call Leaf IDs */ #define TDG_VP_VMCALL 0 #define TDG_VP_INFO 1 +#define TDG_MR_RTMR_EXTEND 2 #define TDG_VP_VEINFO_GET 3 #define TDG_MR_REPORT 4 #define TDG_MEM_PAGE_ACCEPT 6 diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index b4b16dafd55e..6b98d7dc5207 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -64,6 +64,8 @@ bool tdx_early_handle_ve(struct pt_regs *regs); =20 int tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport); =20 +int tdx_mcall_extend_rtmr(u8 index, u8 *data); + u64 tdx_hcall_get_quote(u8 *buf, size_t size); =20 void __init tdx_dump_attributes(u64 td_attr); --=20 2.43.0 From nobody Wed Dec 17 02:55:20 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CAE051A5B9A for ; Mon, 24 Feb 2025 03:22:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367323; cv=none; b=KJUPmAb0jCRLzbfIYH3SYUS9qEhS5JtXXAY8S5pGCz2kD0KqKMlDmOijzSdWReUHXRJV44UO3QlAE7Acg+Y2qukspyg0hgtlZKM/xvDSozIu84DmLWwwzKOQAlzyEm6dn4VxeaHObqtPsuefVNkzz63QGytQXsmtTwLoiOFaFJM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740367323; c=relaxed/simple; bh=gtvQmFMZq96hBKeNoF2V0rliu4QKfGTnrxvFeoQp8Ss=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QI+xgzH6ksZtcgePTXv/FgRlZQ1zitl2Hpa/Us7AXLD+lsURdpBaKM1g5jEooiIfw/ZsavRtKyxyIfGUPikxl1FvSbQqKpn0IGYxuqnFj2Df6TBVexhi4NWF34W5poTLZmWxVoVdkHAGeJiYwHHkXOEgbIaHn3dZIP/rdoxSkBg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=ATD+jVa7; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="ATD+jVa7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1740367322; x=1771903322; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=gtvQmFMZq96hBKeNoF2V0rliu4QKfGTnrxvFeoQp8Ss=; b=ATD+jVa784JgKNPocwP0DvSWeip4DRsKwNbdcgZibnkllG3GKR8lXOjC ZQ4sG7BysksbCamkvDxtFHxyKaNYrZX71IUokDkV+RWujy/2UAGsaQP6w sqJ6EUb44R/SwlZnLa0iry8Vx3BnOFGCEzMurrBXLkougZOqIXTDXQt4K eZHMXAWS/KZwE5SEbhUly8Gd9wfLhz7+yzVAkro4lOQUfUwz0EJqWa/av d4afg4ymr/9dYjnWitXCS4GgJGOwmFBLuFNndzUGy6OnIIF5U1W8Hiex+ j02sY7BbrczEcKxFtoFTlRmeJldqh7xW498W7ZKYI1xV6zL5JDGoROT2i A==; X-CSE-ConnectionGUID: mjqVxB5qToStJx0d01EFPg== X-CSE-MsgGUID: H/3XUxImQJ+kI3rnIxIS9Q== X-IronPort-AV: E=McAfee;i="6700,10204,11354"; a="28706895" X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="28706895" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:56 -0800 X-CSE-ConnectionGUID: 98GZGlJlThOh0sx475/aTw== X-CSE-MsgGUID: /pY/rAzpTNOzD/pMmjuJvQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.13,309,1732608000"; d="scan'208";a="116441906" Received: from shanagud-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.117.251]) by fmviesa010-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Feb 2025 19:21:17 -0800 From: Cedric Xing Date: Sun, 23 Feb 2025 21:20:15 -0600 Subject: [PATCH v2 4/4] x86/tdx: Expose TDX MRs through TSM sysfs interface Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250223-tdx-rtmr-v2-4-f2d85b0a5f94@intel.com> References: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> In-Reply-To: <20250223-tdx-rtmr-v2-0-f2d85b0a5f94@intel.com> To: Dan Williams , "Kirill A. Shutemov" , Dave Hansen , Thomas Gleixner , Ingo Molnar , Borislav Petkov , x86@kernel.org, "H. Peter Anvin" Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, Dionna Amalie Glaze , James Bottomley , Dan Middleton , Mikko Ylinen , Sathyanarayanan Kuppuswamy X-Mailer: b4 0.13.0 TDX MRs are made accessible to user mode as files (attributes) in sysfs. Below shows the directory structure of TDX MRs inside a TDVM. /sys/kernel/tsm =E2=94=94=E2=94=80=E2=94=80 tdx =E2=94=9C=E2=94=80=E2=94=80 mrconfigid =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 mrowner =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 mrownerconfig =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 mrtd =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 report0 =E2=94=9C=E2=94=80=E2=94=80 reportdata =E2=94=9C=E2=94=80=E2=94=80 rtmr0 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 rtmr1 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 rtmr2 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=9C=E2=94=80=E2=94=80 rtmr3 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 digest =E2=94=94=E2=94=80=E2=94=80 servtd_hash =E2=94=94=E2=94=80=E2=94=80 sha384 =E2=94=94=E2=94=80=E2=94=80 digest The digest attribute/file of each MR contains the MR's current value. Writing to the digest attribute/file of an RTMR extends the written value to that RTMR. The report0 and reportdata attributes offer a simple interface for user mode applications to request TDREPORTs. These 2 attributes can be enabled/disabled by setting TDX_GUEST_DRIVER_TSM_REPORT to Y/n. Signed-off-by: Cedric Xing Tested-by: Jianxiong Gao --- drivers/virt/coco/tdx-guest/Kconfig | 24 +++++-- drivers/virt/coco/tdx-guest/tdx-guest.c | 115 ++++++++++++++++++++++++++++= ++++ 2 files changed, 134 insertions(+), 5 deletions(-) diff --git a/drivers/virt/coco/tdx-guest/Kconfig b/drivers/virt/coco/tdx-gu= est/Kconfig index 22dd59e19431..a1c5e8fdd511 100644 --- a/drivers/virt/coco/tdx-guest/Kconfig +++ b/drivers/virt/coco/tdx-guest/Kconfig @@ -3,9 +3,23 @@ config TDX_GUEST_DRIVER depends on INTEL_TDX_GUEST select TSM_REPORTS help - The driver provides userspace interface to communicate with - the TDX module to request the TDX guest details like attestation - report. + The driver provides userspace interface to communicate with the TDX + module to request the TDX guest details like attestation report. =20 - To compile this driver as module, choose M here. The module will - be called tdx-guest. + To compile this driver as module, choose M here. The module will be + called tdx-guest. + +if TDX_GUEST_DRIVER + +config TDX_GUEST_DRIVER_TSM_REPORT + bool "tdx-guest: Enable TSM raw TDREPORT interface" + default y + help + This option adds 2 files, namely report0 and reportdata, to the TSM + sysfs directory tree (/sys/kernel/tsm/tdx/). + + To request a TDREPORT, set REPORTDATA by writing to + /sys/kernel/tsm/tdx/reportdata, then read + /sys/kernel/tsm/tdx/report0. + +endif diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/td= x-guest/tdx-guest.c index 224e7dde9cde..a31fe2098901 100644 --- a/drivers/virt/coco/tdx-guest/tdx-guest.c +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -5,6 +5,8 @@ * Copyright (C) 2022 Intel Corporation */ =20 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -18,6 +20,8 @@ #include #include =20 +#include + #include =20 #include @@ -304,6 +308,110 @@ static const struct tsm_ops tdx_tsm_ops =3D { .report_bin_attr_visible =3D tdx_report_bin_attr_visible, }; =20 +enum { + TDREPORT_MRSIZE =3D SHA384_DIGEST_SIZE, + + TDREPORT_reportdata =3D 128, + TDREPORT_tdinfo =3D 512, + TDREPORT_mrtd =3D TDREPORT_tdinfo + 16, + TDREPORT_mrconfigid =3D TDREPORT_mrtd + TDREPORT_MRSIZE, + TDREPORT_mrowner =3D TDREPORT_mrconfigid + TDREPORT_MRSIZE, + TDREPORT_mrownerconfig =3D TDREPORT_mrowner + TDREPORT_MRSIZE, + TDREPORT_rtmr0 =3D TDREPORT_mrownerconfig + TDREPORT_MRSIZE, + TDREPORT_rtmr1 =3D TDREPORT_rtmr0 + TDREPORT_MRSIZE, + TDREPORT_rtmr2 =3D TDREPORT_rtmr1 + TDREPORT_MRSIZE, + TDREPORT_rtmr3 =3D TDREPORT_rtmr2 + TDREPORT_MRSIZE, + TDREPORT_servtd_hash =3D TDREPORT_rtmr3 + TDREPORT_MRSIZE, +}; + +static u8 tdx_mr_report[TDX_REPORT_LEN] __aligned(TDX_REPORT_LEN); + +#define TDX_MR_(r) .mr_value =3D tdx_mr_report + TDREPORT_##r, TSM_MR_(r, = SHA384) +static const struct tsm_measurement_register tdx_mrs[] =3D { + { TDX_MR_(rtmr0) | TSM_MR_F_RTMR | TSM_MR_F_W }, + { TDX_MR_(rtmr1) | TSM_MR_F_RTMR | TSM_MR_F_W }, + { TDX_MR_(rtmr2) | TSM_MR_F_RTMR | TSM_MR_F_W }, + { TDX_MR_(rtmr3) | TSM_MR_F_RTMR | TSM_MR_F_W }, + { TDX_MR_(mrtd) }, + { TDX_MR_(mrconfigid) }, + { TDX_MR_(mrowner) }, + { TDX_MR_(mrownerconfig) }, + { TDX_MR_(servtd_hash) }, +#if IS_ENABLED(CONFIG_TDX_GUEST_DRIVER_TSM_REPORT) + { .mr_value =3D tdx_mr_report, .mr_size =3D sizeof(tdx_mr_report), + .mr_name =3D "report0", .mr_flags =3D TSM_MR_F_LIVE | TSM_MR_F_F }, + { .mr_value =3D tdx_mr_report + TDREPORT_reportdata, + TSM_MR_(reportdata, SHA512) | TSM_MR_F_W | TSM_MR_F_F }, +#endif + {} +}; +#undef TDX_MR_ + +static int tdx_mr_refresh(struct tsm_measurement *tmr, + const struct tsm_measurement_register *mr) +{ + u8 *reportdata, *tdreport; + int ret; + + reportdata =3D tdx_mr_report + TDREPORT_reportdata; + + /* + * TDCALL requires a GPA as input. Depending on whether this module is + * built as a built-in (Y) or a module (M), tdx_mr_report may or may + * not be converted to a GPA using virt_to_phys. If not, a directly + * mapped buffer must be allocated using kmalloc and used as an + * intermediary. + */ +#if IS_BUILTIN(CONFIG_TDX_GUEST_DRIVER) + tdreport =3D tdx_mr_report; +#else + tdreport =3D kmalloc(sizeof(tdx_mr_report), GFP_KERNEL); + if (!tdreport) + return -ENOMEM; + + reportdata =3D memcpy(tdreport + TDREPORT_reportdata, reportdata, + TDX_REPORTDATA_LEN); +#endif + + ret =3D tdx_mcall_get_report0(reportdata, tdreport); + if (ret) + pr_err("GetReport call failed\n"); + +#if !IS_BUILTIN(CONFIG_TDX_GUEST_DRIVER) + memcpy(tdx_mr_report, tdreport, sizeof(tdx_mr_report)); + kfree(tdreport); +#endif + + return ret; +} + +static int tdx_mr_extend(struct tsm_measurement *tmr, + const struct tsm_measurement_register *mr, const u8 *data) +{ + u8 *buf; + int ret; + + buf =3D kmalloc(64, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, data, TDREPORT_MRSIZE); + + ret =3D tdx_mcall_extend_rtmr((u8)(mr - tmr->mrs), buf); + if (ret) + pr_err("Extending RTMR%ld failed\n", mr - tmr->mrs); + + kfree(buf); + return ret; +} + +static struct tsm_measurement tdx_measurement =3D { + .name =3D "tdx", + .mrs =3D tdx_mrs, + .refresh =3D tdx_mr_refresh, + .extend =3D tdx_mr_extend, +}; + static int __init tdx_guest_init(void) { int ret; @@ -326,8 +434,14 @@ static int __init tdx_guest_init(void) if (ret) goto free_quote; =20 + ret =3D tsm_register_measurement(&tdx_measurement); + if (ret) + goto unregister_tsm; + return 0; =20 +unregister_tsm: + tsm_unregister(&tdx_tsm_ops); free_quote: free_quote_buf(quote_data); free_misc: @@ -339,6 +453,7 @@ module_init(tdx_guest_init); =20 static void __exit tdx_guest_exit(void) { + tsm_unregister_measurement(&tdx_measurement); tsm_unregister(&tdx_tsm_ops); free_quote_buf(quote_data); misc_deregister(&tdx_misc_dev); --=20 2.43.0