From nobody Fri Dec 19 00:34:30 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) (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 012483A1B6 for ; Sun, 8 Sep 2024 04:57:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771473; cv=none; b=qyp4+ChjdDX7ZWYFjc84wVqzgjH1jUIxG7j/tiw1Ia7LUIHmzS11wnWptUGDr/tu99wgj2KDLftiklocwignzlJU999jY+JiVb7nhjC3AvS3IRFWBEYComAuSzlwfoQSW/sdayfi/PusCXqJYPa5bQA+EIJ1byGqDK/h0EPp3e0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771473; c=relaxed/simple; bh=xD/sz73bewgKRorRh4MBtS0BXrpfPei7LS5GZqG3Qnc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QXok5e5J4gOQG9CERuE4dTgYyCpmBifsXyeZxFNAIiA+3Xt6ugyNwFbNue7ER5paWpNHATexDsHJEzuEyaoZCPc14F1YQA+jIlNdd04ziJH2DQKKSZC1d15pyOo1RqjJ/8O2Y7UGF0Q5OPmpQOhsWSsQIzOqsXBwIme2UBA32TA= 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=VHITgpZm; arc=none smtp.client-ip=192.198.163.9 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="VHITgpZm" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1725771471; x=1757307471; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=xD/sz73bewgKRorRh4MBtS0BXrpfPei7LS5GZqG3Qnc=; b=VHITgpZmNZ8cZWCKnZ9wl9WjxT3SQqooV/dzCLK21M0CfPZGULj0r228 vExgRz0rZwgiu9ScPmXFM0BtFiNAy8z+CZVEu3iSQOlGhGc3fnAMRhBHe Gdmkw6PmvE5vX6XrQjfTUTmtnTy68CZ74ghquQu20y1Q/N5q4HNEU3PvD vSjnkBjXTvY5A8kMckWKxovH4t2UyA0jM41WH+CFrRuodlFOc9prkfsoe JPEN7+0iJnnSAJPWuBruLdyUNqJQ5X4KtGe17Pjw7C3Y4UDNy7isZOAW+ KKSuVvFMTRdWE1vTCUABP/6kgtb+B7gDe3p9gdkuUL0b6Vlw5+Kh4hylD A==; X-CSE-ConnectionGUID: EtC11KrwSgqLrj2qC4duzQ== X-CSE-MsgGUID: 1r/Ggk4EQIi/Cf8LvnaFlQ== X-IronPort-AV: E=McAfee;i="6700,10204,11188"; a="35152166" X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="35152166" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:49 -0700 X-CSE-ConnectionGUID: j/a+WePbQnm4kUd7hfMlsg== X-CSE-MsgGUID: k7Y5I/V7Slmbl7UuTRPgNQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="70924657" Received: from ibarbour-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.116.49]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:49 -0700 From: Cedric Xing Date: Sat, 07 Sep 2024 23:56:19 -0500 Subject: [PATCH RFC 1/3] 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: <20240907-tsm-rtmr-v1-1-12fc4d43d4e7@intel.com> References: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> In-Reply-To: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> To: Dan Williams , Samuel Ortiz , James Bottomley , Lukas Wunner , Dionna Amalie Glaze , Qinkun Bao , Mikko Ylinen , Kuppuswamy Sathyanarayanan Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev X-Mailer: b4 0.13.0 This commit extends the TSM core with support for CC measurement registers (MRs). The newly added APIs are: - `tsm_register_measurement_provider(struct tsm_measurement_provider *)` This function allows a CC guest driver to register a set of measurement registers with the TSM core. - `tsm_unregister_measurement_provider(struct tsm_measurement_provider *)`: This function enables a CC guest driver to unregister a previously regist= ered set of measurement registers. The `struct tsm_measurement_provider` has been defined to encapsulate the details of CC-specific MRs. It includes an array of `struct tsm_measurement_register`s and provides operations for reading and updating these registers. For a comprehensive understanding of the structure and its usage, refer to the detailed comments added in `include/linux/tsm.h= `. Upon successful registration of a measurement provider, the TSM core exposes the MRs through a directory tree in the sysfs filesystem. The root of this = tree is located at `/sys/kernel/tsm//`, with `` being = the name of the measurement provider. Each static MR is made accessible as a read-only file, named after the MR itself. Runtime MRs (RTMRs) are represen= ted as directories named after the RTMR, containing two files: `digest` and `hash_algo`. The purpose and content of these files are self-explanatory. Signed-off-by: Cedric Xing --- drivers/virt/coco/Kconfig | 4 +- drivers/virt/coco/tsm.c | 390 ++++++++++++++++++++++++++++++++++++++++++= ++-- include/linux/tsm.h | 62 ++++++++ 3 files changed, 446 insertions(+), 10 deletions(-) diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig index 87d142c1f932..0ce23c8d5854 100644 --- a/drivers/virt/coco/Kconfig +++ b/drivers/virt/coco/Kconfig @@ -5,7 +5,9 @@ =20 config TSM_REPORTS select CONFIGFS_FS - tristate + select SYSFS + select CRYPTO_HASH_INFO + tristate "Trusted Security Module (TSM) sysfs/configfs support" =20 source "drivers/virt/coco/efi_secret/Kconfig" =20 diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm.c index 9432d4e303f1..e83143f22fad 100644 --- a/drivers/virt/coco/tsm.c +++ b/drivers/virt/coco/tsm.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2023 Intel Corporation. All rights reserved. */ =20 -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include @@ -11,6 +9,8 @@ #include #include #include +#include +#include =20 static struct tsm_provider { const struct tsm_ops *ops; @@ -166,8 +166,9 @@ static ssize_t tsm_report_service_guid_store(struct con= fig_item *cfg, } CONFIGFS_ATTR_WO(tsm_report_, service_guid); =20 -static ssize_t tsm_report_service_manifest_version_store(struct config_ite= m *cfg, - const char *buf, size_t len) +static ssize_t +tsm_report_service_manifest_version_store(struct config_item *cfg, + const char *buf, size_t len) { struct tsm_report *report =3D to_tsm_report(cfg); unsigned int val; @@ -187,8 +188,8 @@ static ssize_t tsm_report_service_manifest_version_stor= e(struct config_item *cfg } CONFIGFS_ATTR_WO(tsm_report_, service_manifest_version); =20 -static ssize_t tsm_report_inblob_write(struct config_item *cfg, - const void *buf, size_t count) +static ssize_t tsm_report_inblob_write(struct config_item *cfg, const void= *buf, + size_t count) { struct tsm_report *report =3D to_tsm_report(cfg); int rc; @@ -341,7 +342,8 @@ static struct configfs_attribute *tsm_report_attrs[] = =3D { [TSM_REPORT_PRIVLEVEL_FLOOR] =3D &tsm_report_attr_privlevel_floor, [TSM_REPORT_SERVICE_PROVIDER] =3D &tsm_report_attr_service_provider, [TSM_REPORT_SERVICE_GUID] =3D &tsm_report_attr_service_guid, - [TSM_REPORT_SERVICE_MANIFEST_VER] =3D &tsm_report_attr_service_manifest_v= ersion, + [TSM_REPORT_SERVICE_MANIFEST_VER] =3D + &tsm_report_attr_service_manifest_version, NULL, }; =20 @@ -383,7 +385,8 @@ static bool tsm_report_is_visible(struct config_item *i= tem, } =20 static bool tsm_report_is_bin_visible(struct config_item *item, - struct configfs_bin_attribute *attr, int n) + struct configfs_bin_attribute *attr, + int n) { guard(rwsem_read)(&tsm_rwsem); if (!provider.ops) @@ -476,7 +479,370 @@ int tsm_unregister(const struct tsm_ops *ops) } EXPORT_SYMBOL_GPL(tsm_unregister); =20 +enum _rtmr_bin_attr_index { + _RTMR_BATTR_DIGEST, + _RTMR_BATTR__COUNT, +}; + +struct _rtmr { + struct kobject kobj; + struct bin_attribute battrs[_RTMR_BATTR__COUNT]; +}; + +struct _mr_provider { + struct kset kset; + struct rw_semaphore rwsem; + struct bin_attribute *static_mrs; + struct tsm_measurement_provider *provider; + bool in_sync; +}; + +static inline const struct tsm_measurement_register * +_rtmr_mr(const struct _rtmr *rtmr) +{ + return (struct tsm_measurement_register *)rtmr + ->battrs[_RTMR_BATTR_DIGEST] + .private; +} + +static inline struct _mr_provider * +_mr_to_group(const struct tsm_measurement_register *mr, struct kobject *ko= bj) +{ + if (!(mr->mr_flags & TSM_MR_F_X)) + return container_of(kobj, struct _mr_provider, kset.kobj); + else + return container_of(kobj->kset, struct _mr_provider, kset); +} + +static inline int _call_refresh(struct _mr_provider *pvd, + const struct tsm_measurement_register *mr) +{ + int rc =3D pvd->provider->refresh(pvd->provider, mr); + if (rc) + pr_warn(KBUILD_MODNAME ": %s.extend(%s) failed %d\n", + kobject_name(&pvd->kset.kobj), mr->mr_name, rc); + return rc; +} + +static inline int _call_extend(struct _mr_provider *pvd, + const struct tsm_measurement_register *mr, + const u8 *data) +{ + int rc =3D pvd->provider->extend(pvd->provider, mr, data); + if (rc) + pr_warn(KBUILD_MODNAME ": %s.extend(%s) failed %d\n", + kobject_name(&pvd->kset.kobj), mr->mr_name, rc); + return rc; +} + +static ssize_t hash_algo_show(struct kobject *kobj, struct kobj_attribute = *attr, + char *page) +{ + struct _rtmr *rtmr; + rtmr =3D container_of(kobj, typeof(*rtmr), kobj); + return sysfs_emit(page, "%s", hash_algo_name[_rtmr_mr(rtmr)->mr_hash]); +} + +static ssize_t _mr_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *page, loff_t off, + size_t count) +{ + const struct tsm_measurement_register *mr; + struct _mr_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 (typeof(mr))attr->private; + BUG_ON(mr->mr_size !=3D attr->size); + + pvd =3D _mr_to_group(mr, kobj); + rc =3D down_read_interruptible(&pvd->rwsem); + if (rc) + return rc; + + 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 _call_refresh(pvd, mr); + pvd->in_sync =3D !rc; + } + + downgrade_write(&pvd->rwsem); + } + + if (!rc) + memcpy(page, mr->mr_value + off, count); + else + pr_debug(KBUILD_MODNAME ": refresh(%s,%s)=3D%d\n", + kobject_name(&pvd->kset.kobj), mr->mr_name, rc); + + up_read(&pvd->rwsem); + return rc ?: count; +} + +static inline size_t snprint_hex(char *sbuf, size_t size, const u8 *data, + size_t len) +{ + size_t ret =3D 0; + for (size_t i =3D 0; i < len; ++i) + ret +=3D snprintf(sbuf + ret, size - ret, "%02x", data[i]); + return ret; +} + +static ssize_t _mr_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *page, loff_t off, + size_t count) +{ + const struct tsm_measurement_register *mr; + struct _mr_provider *pvd; + ssize_t rc; + + if (off !=3D 0 || count !=3D attr->size) + return -EINVAL; + + mr =3D (typeof(mr))attr->private; + BUG_ON(mr->mr_size !=3D attr->size); + + pvd =3D _mr_to_group(mr, kobj); + rc =3D down_write_killable(&pvd->rwsem); + if (rc) + return rc; + + if (mr->mr_flags & TSM_MR_F_X) + rc =3D pvd->provider->extend(pvd->provider, mr, (u8 *)page); + else { + BUG_ON(!(mr->mr_flags & TSM_MR_F_W)); + memcpy(mr->mr_value, page, count); + } + + if (!rc) + pvd->in_sync =3D false; + else + pr_warn(KBUILD_MODNAME ": extending %s/%s failed with %ld\n", + kobject_name(&pvd->kset.kobj), mr->mr_name, rc); + + up_write(&pvd->rwsem); + return rc ?: count; +} + +static void _rtmr_release(struct kobject *kobj) +{ + struct _rtmr *rtmr; + rtmr =3D container_of(kobj, typeof(*rtmr), kobj); + pr_debug("%s(%s)\n", __func__, kobject_name(kobj)); + kfree(rtmr); +} + +static struct kobj_type _rtmr_ktype =3D { + .release =3D _rtmr_release, + .sysfs_ops =3D &kobj_sysfs_ops, +}; + +static struct _rtmr *_rtmr_create(const struct tsm_measurement_register *m= r, + struct _mr_provider *pvd) +{ + struct _rtmr *rtmr __free(kfree); + int rc; + + BUG_ON(!(mr->mr_flags & TSM_MR_F_X)); + rtmr =3D kzalloc(sizeof(*rtmr), GFP_KERNEL); + if (!rtmr) + return ERR_PTR(-ENOMEM); + + sysfs_bin_attr_init(&rtmr->battrs[_RTMR_BATTR_DIGEST]); + rtmr->battrs[_RTMR_BATTR_DIGEST].attr.name =3D "digest"; + if (mr->mr_flags & TSM_MR_F_W) + rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IWUSR; + if (mr->mr_flags & TSM_MR_F_R) + rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IRUGO; + + rtmr->battrs[_RTMR_BATTR_DIGEST].size =3D mr->mr_size; + rtmr->battrs[_RTMR_BATTR_DIGEST].read =3D _mr_read; + rtmr->battrs[_RTMR_BATTR_DIGEST].write =3D _mr_write; + rtmr->battrs[_RTMR_BATTR_DIGEST].private =3D (void *)mr; + + rtmr->kobj.kset =3D &pvd->kset; + rc =3D kobject_init_and_add(&rtmr->kobj, &_rtmr_ktype, NULL, "%s", + mr->mr_name); + if (rc) + return ERR_PTR(rc); + + return_ptr(rtmr); +} + +static void _mr_provider_release(struct kobject *kobj) +{ + struct _mr_provider *pvd; + pvd =3D container_of(kobj, typeof(*pvd), kset.kobj); + pr_debug("%s(%s)\n", __func__, kobject_name(kobj)); + BUG_ON(!list_empty(&pvd->kset.list)); + kfree(pvd->static_mrs); + kfree(pvd); +} + +static struct kobj_type _mr_provider_ktype =3D { + .release =3D _mr_provider_release, + .sysfs_ops =3D &kobj_sysfs_ops, +}; + static struct config_group *tsm_report_group; +static struct kset *_sysfs_tsm; + +static struct _mr_provider * +_mr_provider_create(struct tsm_measurement_provider *tpvd) +{ + struct _mr_provider *pvd __free(kfree); + int rc; + + pvd =3D kzalloc(sizeof(*pvd), GFP_KERNEL); + if (!pvd) + return ERR_PTR(-ENOMEM); + + if (!tpvd->name || !tpvd->mrs || !tpvd->refresh || !tpvd->extend) + return ERR_PTR(-EINVAL); + + rc =3D kobject_set_name(&pvd->kset.kobj, "%s", tpvd->name); + if (rc) + return ERR_PTR(rc); + + pvd->kset.kobj.kset =3D _sysfs_tsm; + pvd->kset.kobj.ktype =3D &_mr_provider_ktype; + pvd->provider =3D tpvd; + + rc =3D kset_register(&pvd->kset); + if (rc) + return ERR_PTR(rc); + + return_ptr(pvd); +} + +DEFINE_FREE(_unregister_measurement_provider, struct _mr_provider *, + if (!IS_ERR_OR_NULL(_T)) + tsm_unregister_measurement_provider(_T->provider)); + +int tsm_register_measurement_provider(struct tsm_measurement_provider *tpv= d) +{ + static struct kobj_attribute _attr_hash =3D __ATTR_RO(hash_algo); + + struct _mr_provider *pvd __free(_unregister_measurement_provider); + int rc, nr; + + pvd =3D _mr_provider_create(tpvd); + if (IS_ERR(pvd)) + return PTR_ERR(pvd); + + nr =3D 0; + for (int i =3D 0; tpvd->mrs[i].mr_name; ++i) { + if (!(tpvd->mrs[i].mr_flags & TSM_MR_F_X)) { + ++nr; + continue; + } + + struct _rtmr *rtmr =3D _rtmr_create(&tpvd->mrs[i], pvd); + if (IS_ERR(rtmr)) + return PTR_ERR(rtmr); + + struct attribute *attrs[] =3D { + &_attr_hash.attr, + NULL, + }; + struct bin_attribute + *battrs[_RTMR_BATTR__COUNT + 1] =3D {}; + for (int j =3D 0; j < _RTMR_BATTR__COUNT; ++j) + battrs[j] =3D &rtmr->battrs[j]; + struct attribute_group agrp =3D { + .attrs =3D attrs, + .bin_attrs =3D battrs, + }; + rc =3D sysfs_create_group(&rtmr->kobj, &agrp); + if (rc) + return rc; + } + + if (nr > 0) { + struct bin_attribute *static_mrs __free(kfree); + struct bin_attribute **battrs __free(kfree); + + static_mrs =3D kcalloc(sizeof(*static_mrs), nr, GFP_KERNEL); + battrs =3D kcalloc(sizeof(*battrs), nr + 1, GFP_KERNEL); + if (!battrs || !static_mrs) + return -ENOMEM; + + for (int i =3D 0, j =3D 0; tpvd->mrs[i].mr_name; ++i) { + if (tpvd->mrs[i].mr_flags & TSM_MR_F_X) + continue; + + static_mrs[j].attr.name =3D tpvd->mrs[i].mr_name; + if (tpvd->mrs[i].mr_flags & TSM_MR_F_R) { + static_mrs[j].attr.mode |=3D S_IRUGO; + static_mrs[j].read =3D _mr_read; + } + if (tpvd->mrs[i].mr_flags & TSM_MR_F_W) { + static_mrs[j].attr.mode |=3D S_IWUSR; + static_mrs[j].write =3D _mr_write; + } + static_mrs[j].size =3D tpvd->mrs[i].mr_size; + static_mrs[j].private =3D (void *)&tpvd->mrs[i]; + + battrs[j] =3D &static_mrs[j]; + ++j; + + BUG_ON(j > nr); + } + + struct attribute_group agrp =3D { + .bin_attrs =3D battrs, + }; + rc =3D sysfs_create_group(&pvd->kset.kobj, &agrp); + if (rc) + return rc; + + pvd->static_mrs =3D no_free_ptr(static_mrs); + } + + pvd =3D NULL; + return 0; +} +EXPORT_SYMBOL_GPL(tsm_register_measurement_provider); + +static void _kset_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_provider(struct tsm_measurement_provider *t= pvd) +{ + struct kobject *kobj =3D kset_find_obj(_sysfs_tsm, tpvd->name); + if (!kobj) + return -ENOENT; + + struct _mr_provider *pvd =3D container_of(kobj, typeof(*pvd), kset.kobj); + BUG_ON(pvd->provider !=3D tpvd); + + _kset_put_children(&pvd->kset); + kset_unregister(&pvd->kset); + kobject_put(kobj); + return 0; +} +EXPORT_SYMBOL_GPL(tsm_unregister_measurement_provider); =20 static int __init tsm_init(void) { @@ -497,16 +863,22 @@ static int __init tsm_init(void) } tsm_report_group =3D tsm; =20 + _sysfs_tsm =3D kset_create_and_add("tsm", NULL, kernel_kobj); + if (!_sysfs_tsm) + return -ENOMEM; + return 0; } module_init(tsm_init); =20 static void __exit tsm_exit(void) { + kset_unregister(_sysfs_tsm); configfs_unregister_default_group(tsm_report_group); configfs_unregister_subsystem(&tsm_configfs); } module_exit(tsm_exit); =20 MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports vi= a configfs"); +MODULE_DESCRIPTION( + "Provide Trusted Security Module attestation reports via configfs"); diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 11b0c525be30..9fd7a2f0208e 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,65 @@ 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 measurement + * 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 + */ +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 is extensible, should be set for RTMRs + * @TSM_MR_F_W: this MR is writable, should be set if direct extension to = an + * RTMR is allowed + * @TSM_MR_F_R: this MR is readable. All MRs should have this flag set + * @TSM_MR_F_L: this MR is live - writes to other MRs may change this MR + * @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_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_(h, f) .mr_size =3D h##_DIGEST_SIZE, .mr_flags =3D (f) +#define TSM_MR(h) TSM_MR_(h, TSM_MR_F_R) +#define TSM_RTMR(h) TSM_MR_(h, TSM_MR_F_RTMR), .mr_hash =3D HASH_ALGO_##h + +/** + * struct tsm_measurement_provider - define CC specific MRs and methods for + * updating them + * @name: name of the measurement provider + * @mrs: array of MR definitions ending with mr_name set to %NULL + * @refresh: invoked to update the specified MR + * @extend: invoked to extend the specified MR with mr_size bytes + */ +struct tsm_measurement_provider { + const char *name; + const struct tsm_measurement_register *mrs; + int (*refresh)(struct tsm_measurement_provider *, + const struct tsm_measurement_register *); + int (*extend)(struct tsm_measurement_provider *, + const struct tsm_measurement_register *, const u8 *); +}; + +int tsm_register_measurement_provider(struct tsm_measurement_provider *); +int tsm_unregister_measurement_provider(struct tsm_measurement_provider *); + #endif /* __TSM_H */ --=20 2.43.0 From nobody Fri Dec 19 00:34:30 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) (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 C8DEA4595B for ; Sun, 8 Sep 2024 04:57:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771474; cv=none; b=Hr5QC/48Y08EOl14HvLJ+zMogKA9U/F9fk78koiShMeLMvJpLUFmUb4Dql+f7PlGuH9VBIcSmlmZSfOhLFJae1kIyHwqrwiM+Ccb4lL37Lx9d7O6VMhJsmv9O0QK4Sk65ZbUgJSbhH/Ek44nkwBpVagpo+zth7F5T/jXFOezO88= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771474; c=relaxed/simple; bh=g5TC1j1p9H5K2ejsFtwRR8SX6cZRBTzpKxHuFtvAmkY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cMYVDlKYMf7g5f67PsV/m88/nPNW6DKY9DL3+z/UdCvG0iBOJjtGXIYJ7dlkqmc/7Ru3ppmlvjF6UGN7jvM4qrGA+ZK08AfkpvMx9GRMudETb0gMiiCYrrtMut93YwkvlEIUJxzSI1TpPiJzuZlOx9DRj1Lo+0vzQXBKAFdfqBY= 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=jobAqU28; arc=none smtp.client-ip=192.198.163.9 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="jobAqU28" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1725771473; x=1757307473; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=g5TC1j1p9H5K2ejsFtwRR8SX6cZRBTzpKxHuFtvAmkY=; b=jobAqU28Mix9be4x5J3EP+haJPWvM0VA74yFR1rTY1wFgI8I61CKXrD6 lcy0kruE4nX2ZjbX7FscNhtbRzjQNTr8ZTQfUZyOuTYDH41GGZh3QEejN yF7gsl/d6qSkaBw4Qxm0tTw/Wzgf6S/WukFfcD5QbSg1E+YhXTMHWpiar E251s3BQG2nf5STh4447CTtgZ2wEORLyAu40P2JfkK9zMTiMDk7OmDWhB 2goCMlpfXOm5W7EcVfmWW+wD1yxKu+eUpqinHDp8Mc77WpKJ+fVOZDX3s yvrCgaDVGN/Y6Pl/Ml9jY+7auWfFXL/rxNrPW+ubz4dKdgtgkezz+1h2o Q==; X-CSE-ConnectionGUID: IjotRfg6SqeBf9T+s7nlfw== X-CSE-MsgGUID: iG5NM4mKQK6O+5xUUDPjdA== X-IronPort-AV: E=McAfee;i="6700,10204,11188"; a="35152172" X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="35152172" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:50 -0700 X-CSE-ConnectionGUID: b8UHpuTQR9qtokyKo+WlsQ== X-CSE-MsgGUID: E9V5oA57Riy5M9VW0v5ohA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="70924660" Received: from ibarbour-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.116.49]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:49 -0700 From: Cedric Xing Date: Sat, 07 Sep 2024 23:56:20 -0500 Subject: [PATCH RFC 2/3] tsm: Add RTMR event logging 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: <20240907-tsm-rtmr-v1-2-12fc4d43d4e7@intel.com> References: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> In-Reply-To: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> To: Dan Williams , Samuel Ortiz , James Bottomley , Lukas Wunner , Dionna Amalie Glaze , Qinkun Bao , Mikko Ylinen , Kuppuswamy Sathyanarayanan Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev X-Mailer: b4 0.13.0 An RTMR typically accumulates measurements from multiple software component= s, making its value alone less informative without an accompanying log. The log format design distinguishes between the meaning (semantics) of even= ts and their storage. - **Storage**: Specifies how to delineate and hash event records, allowing = the kernel to accurately maintain logs without interpreting their contents. - **Semantics**: The internal structure and meaning of records, defined by = the agreements between applications and verifiers, are not processed by the kernel. Event Log Format: - Records are lines ending with `\n`. - Each record (line) is hashed in its entirety (excluding the trailing `\n`) and extended to the RTMR. - The log for an RTMR is stored at `/sys/kernel/tsm///event_log` and consists of t= hese delineated records. - Lines that are empty (containing only `\n`) or start with `#` are skipped (not hashed or extended). This patch adds two more files to every RTMR directory: - `event_log`: A read-only file containing the full event log. - `append_log`: A write-only file for appending new event records to the lo= g. Records will be processed (hashed/extended) according to the format descr= ibed above. Multiple records can be appended in a single write, provided their total size doesn't exceed the size limit (PAGE_SIZE). Partial records are= not permitted - the last line written will always be treated as complete, eve= n if not terminated by `\n`. Special Event Records/Lines: - A line starting with `SYNC` captures the RTMR value prior to the inclusio= n of that line, providing verifiers with the starting value of the RTMR. This = line must be hashed/extended to prevent front-end log truncation. - A line beginning with `# .EXTEND` indicates a direct extension to the RTM= R by writing to `digest`. The remainder of the line specifies the value extend= ed. Direct extensions cause the log to become out of sync; therefore, a `SYNC` line will be automatically generated at the next `append_event` write. Signed-off-by: Cedric Xing --- drivers/virt/coco/tsm.c | 230 ++++++++++++++++++++++++++++++++++++++++++++= +--- 1 file changed, 219 insertions(+), 11 deletions(-) diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm.c index e83143f22fad..28a10330912c 100644 --- a/drivers/virt/coco/tsm.c +++ b/drivers/virt/coco/tsm.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include =20 @@ -481,12 +482,14 @@ EXPORT_SYMBOL_GPL(tsm_unregister); =20 enum _rtmr_bin_attr_index { _RTMR_BATTR_DIGEST, + _RTMR_BATTR_LOG, _RTMR_BATTR__COUNT, }; =20 struct _rtmr { struct kobject kobj; struct bin_attribute battrs[_RTMR_BATTR__COUNT]; + bool log_in_sync; }; =20 struct _mr_provider { @@ -505,8 +508,40 @@ _rtmr_mr(const struct _rtmr *rtmr) .private; } =20 +static inline char *_rtmr_log(const struct _rtmr *rtmr) +{ + return (char *)rtmr->battrs[_RTMR_BATTR_LOG].private; +} + +static inline size_t _rtmr_log_size(const struct _rtmr *rtmr) +{ + return rtmr->battrs[_RTMR_BATTR_LOG].size; +} + +static inline void _rtmr_log_set_buf(struct _rtmr *rtmr, char *log) +{ + rtmr->battrs[_RTMR_BATTR_LOG].private =3D log; +} + +static inline void _rtmr_log_inc_size(struct _rtmr *rtmr, size_t size) +{ + rtmr->battrs[_RTMR_BATTR_LOG].size +=3D size; +} + +static inline int _rtmr_log_update_attribute(struct _rtmr *rtmr) +{ + struct bin_attribute *attrs_to_update[] =3D { + &rtmr->battrs[_RTMR_BATTR_LOG], + NULL, + }; + struct attribute_group agrp =3D { + .bin_attrs =3D attrs_to_update, + }; + return sysfs_update_group(&rtmr->kobj, &agrp); +} + static inline struct _mr_provider * -_mr_to_group(const struct tsm_measurement_register *mr, struct kobject *ko= bj) +_mr_to_provider(const struct tsm_measurement_register *mr, struct kobject = *kobj) { if (!(mr->mr_flags & TSM_MR_F_X)) return container_of(kobj, struct _mr_provider, kset.kobj); @@ -561,7 +596,7 @@ static ssize_t _mr_read(struct file *filp, struct kobje= ct *kobj, mr =3D (typeof(mr))attr->private; BUG_ON(mr->mr_size !=3D attr->size); =20 - pvd =3D _mr_to_group(mr, kobj); + pvd =3D _mr_to_provider(mr, kobj); rc =3D down_read_interruptible(&pvd->rwsem); if (rc) return rc; @@ -591,9 +626,66 @@ static ssize_t _mr_read(struct file *filp, struct kobj= ect *kobj, return rc ?: count; } =20 -static inline size_t snprint_hex(char *sbuf, size_t size, const u8 *data, +#define _EVENTLOG_GRANULARITY HPAGE_SIZE + +static ssize_t _log_extend_line(struct _rtmr *rtmr, const char *line, + const char *end, int newlines, + struct crypto_shash *tfm) +{ + struct _mr_provider *pvd; + pvd =3D container_of(rtmr->kobj.kset, typeof(*pvd), kset); + lockdep_assert_held_write(&pvd->rwsem); + + BUG_ON(line > end); + + while (line < end && isspace(line[0])) + ++line; + while (line < end && isspace(end[-1])) + --end; + if (line =3D=3D end) + return 0; + + ssize_t count =3D end - line; + char *log =3D _rtmr_log(rtmr); + size_t needed =3D _rtmr_log_size(rtmr) + count + newlines; + if (ksize(log) < needed) { + log =3D krealloc(log, + ALIGN(needed + _EVENTLOG_GRANULARITY / 2, + _EVENTLOG_GRANULARITY), + GFP_KERNEL); + if (!log) + return -ENOMEM; + + _rtmr_log_set_buf(rtmr, log); + } + + log +=3D _rtmr_log_size(rtmr); + for (int i =3D 0; i < newlines; ++i) + *log++ =3D '\n'; + + if (*line !=3D '#') { + u8 digest[SHA512_DIGEST_SIZE]; + BUG_ON(tfm =3D=3D NULL); + BUG_ON(sizeof(digest) < crypto_shash_digestsize(tfm)); + + int rc =3D crypto_shash_tfm_digest(tfm, line, count, digest); + if (!rc) + rc =3D _call_extend(pvd, _rtmr_mr(rtmr), digest); + if (rc) + return rc; + } + + memcpy(log, line, count); + log[count] =3D '\n'; + _rtmr_log_inc_size(rtmr, count +=3D newlines + 1); + + return _rtmr_log_update_attribute(rtmr) ?: count; +} + +static inline size_t snprint_hex(char *sbuf, ssize_t size, const u8 *data, size_t len) { + BUG_ON(size < len * 2); size_t ret =3D 0; for (size_t i =3D 0; i < len; ++i) ret +=3D snprintf(sbuf + ret, size - ret, "%02x", data[i]); @@ -614,15 +706,26 @@ static ssize_t _mr_write(struct file *filp, struct ko= bject *kobj, mr =3D (typeof(mr))attr->private; BUG_ON(mr->mr_size !=3D attr->size); =20 - pvd =3D _mr_to_group(mr, kobj); + pvd =3D _mr_to_provider(mr, kobj); rc =3D down_write_killable(&pvd->rwsem); if (rc) return rc; =20 - if (mr->mr_flags & TSM_MR_F_X) - rc =3D pvd->provider->extend(pvd->provider, mr, (u8 *)page); - else { - BUG_ON(!(mr->mr_flags & TSM_MR_F_W)); + if (mr->mr_flags & TSM_MR_F_X) { + struct _rtmr *rtmr; + rtmr =3D container_of(kobj, typeof(*rtmr), kobj); + + char ext_line[0x100] =3D "# .EXTEND "; + size_t len =3D strnlen(ext_line, sizeof(ext_line)); + len +=3D snprint_hex(ext_line + len, sizeof(ext_line) - len, page, + count); + rc =3D _log_extend_line(rtmr, ext_line, ext_line + len, + rtmr->log_in_sync, NULL); + if (!IS_ERR_VALUE(rc)) + rc =3D _call_extend(pvd, mr, page); + if (!rc) + rtmr->log_in_sync =3D false; + } else { memcpy(mr->mr_value, page, count); } =20 @@ -636,11 +739,110 @@ static ssize_t _mr_write(struct file *filp, struct k= object *kobj, return rc ?: count; } =20 +static ssize_t _log_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *page, loff_t off, + size_t count) +{ + struct _mr_provider *pvd; + int rc; + + if (unlikely(off < 0)) + return -EINVAL; + + if (unlikely(off > attr->size)) + return 0; + + count =3D min(count, attr->size - off); + if (likely(count > 0)) { + pvd =3D container_of(kobj->kset, typeof(*pvd), kset); + rc =3D down_read_interruptible(&pvd->rwsem); + if (rc) + return rc; + + memcpy(page, (char *)attr->private + off, count); + + up_read(&pvd->rwsem); + } + + return count; +} + +static ssize_t _log_extend(struct _rtmr *rtmr, const char *page, size_t co= unt, + struct crypto_shash *tfm) +{ + ssize_t rc =3D 0, sz =3D 0; + for (size_t i =3D 0; i < count && !IS_ERR_VALUE(rc);) { + size_t j; + for (j =3D i; j < count && (page[j] !=3D '\n' && page[j] !=3D '\r');) + ++j; + + rc =3D _log_extend_line(rtmr, &page[i], &page[j], sz =3D=3D 0, tfm); + sz +=3D rc; + + for (i =3D j; i < count && (page[i] =3D=3D '\n' || page[i] =3D=3D '\r');) + ++i; + } + + return IS_ERR_VALUE(rc) ? rc : sz; +} + +DEFINE_FREE(shash, struct crypto_shash *, + if (!IS_ERR(_T)) crypto_free_shash(_T)); + +static ssize_t append_event_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *page, + size_t count) +{ + struct _rtmr *rtmr; + rtmr =3D container_of(kobj, typeof(*rtmr), kobj); + + const struct tsm_measurement_register *mr; + mr =3D _rtmr_mr(rtmr); + + struct crypto_shash *tfm __free(shash) =3D + crypto_alloc_shash(hash_algo_name[mr->mr_hash], 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + struct _mr_provider *pvd; + pvd =3D container_of(kobj->kset, typeof(*pvd), kset); + + ssize_t rc =3D down_write_killable(&pvd->rwsem); + if (rc) + return rc; + + if (!rtmr->log_in_sync) { + if (mr->mr_flags & TSM_MR_F_L) + rc =3D _call_refresh(pvd, mr); + + if (!IS_ERR_VALUE(rc)) { + char sync[0x100] =3D "SYNC "; + strncat(sync, hash_algo_name[mr->mr_hash], + sizeof(sync)); + size_t len =3D strnlen(sync, sizeof(sync)); + sync[len++] =3D '/'; + len +=3D snprint_hex(sync + len, sizeof(sync) - len, + mr->mr_value, mr->mr_size); + rc =3D _log_extend_line(rtmr, sync, sync + len, + _rtmr_log_size(rtmr) > 0, tfm); + } + } + + if (!IS_ERR_VALUE(rc)) { + rtmr->log_in_sync =3D true; + rc =3D _log_extend(rtmr, page, count, tfm); + } + + up_write(&pvd->rwsem); + return IS_ERR_VALUE(rc) ? rc : count; +} + static void _rtmr_release(struct kobject *kobj) { struct _rtmr *rtmr; rtmr =3D container_of(kobj, typeof(*rtmr), kobj); pr_debug("%s(%s)\n", __func__, kobject_name(kobj)); + kfree(_rtmr_log(rtmr)); kfree(rtmr); } =20 @@ -663,7 +865,7 @@ static struct _rtmr *_rtmr_create(const struct tsm_meas= urement_register *mr, sysfs_bin_attr_init(&rtmr->battrs[_RTMR_BATTR_DIGEST]); rtmr->battrs[_RTMR_BATTR_DIGEST].attr.name =3D "digest"; if (mr->mr_flags & TSM_MR_F_W) - rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IWUSR; + rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IWUSR; if (mr->mr_flags & TSM_MR_F_R) rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IRUGO; =20 @@ -672,6 +874,11 @@ static struct _rtmr *_rtmr_create(const struct tsm_mea= surement_register *mr, rtmr->battrs[_RTMR_BATTR_DIGEST].write =3D _mr_write; rtmr->battrs[_RTMR_BATTR_DIGEST].private =3D (void *)mr; =20 + sysfs_bin_attr_init(&rtmr->battrs[_RTMR_BATTR_LOG]); + rtmr->battrs[_RTMR_BATTR_LOG].attr.name =3D "event_log"; + rtmr->battrs[_RTMR_BATTR_LOG].attr.mode =3D S_IRUGO; + rtmr->battrs[_RTMR_BATTR_LOG].read =3D _log_read; + rtmr->kobj.kset =3D &pvd->kset; rc =3D kobject_init_and_add(&rtmr->kobj, &_rtmr_ktype, NULL, "%s", mr->mr_name); @@ -734,6 +941,7 @@ DEFINE_FREE(_unregister_measurement_provider, struct _m= r_provider *, int tsm_register_measurement_provider(struct tsm_measurement_provider *tpv= d) { static struct kobj_attribute _attr_hash =3D __ATTR_RO(hash_algo); + static struct kobj_attribute _attr_append =3D __ATTR_WO(append_event); =20 struct _mr_provider *pvd __free(_unregister_measurement_provider); int rc, nr; @@ -754,11 +962,11 @@ int tsm_register_measurement_provider(struct tsm_meas= urement_provider *tpvd) return PTR_ERR(rtmr); =20 struct attribute *attrs[] =3D { + &_attr_append.attr, &_attr_hash.attr, NULL, }; - struct bin_attribute - *battrs[_RTMR_BATTR__COUNT + 1] =3D {}; + struct bin_attribute *battrs[_RTMR_BATTR__COUNT + 1] =3D {}; for (int j =3D 0; j < _RTMR_BATTR__COUNT; ++j) battrs[j] =3D &rtmr->battrs[j]; struct attribute_group agrp =3D { --=20 2.43.0 From nobody Fri Dec 19 00:34:30 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) (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 4D52449625 for ; Sun, 8 Sep 2024 04:57:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771475; cv=none; b=qZWeLAkig9F9IxKJ1PNKjuOcdYsYc5jVwNQAyYSEgBgvjULZSslmvDng8geUxBq+dVAQt2cICoXI1/e06wBZyC/2REUZ9NTQWY8Vm0cSz9MCGaU0AeWxL+GyMJm6C+7WQcvbw096Fr77mI4My8P/IxjHNKaPPRqBjJmAN2+Diss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771475; c=relaxed/simple; bh=TTfU1gIn373WQAv+wSjs1LLHvBRpR9JaaQ4A3DvKcak=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MndBmh3qPQYm6UWlp+HYd3rEC7zm3q6C6LpOmYk9rPe8vIaB0n4Rrg+E42cIsbGVEV/Zhmxj0dcMpmTAUmcgodLxL0S7wBiit0HKGUSYpYW44vqlfYGszF7nvKwcvCfCVq1fYWUN1iO9AFWPYlkocPIUyEJYOlSg4C6atzjNECo= 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=Qe30FMro; arc=none smtp.client-ip=192.198.163.9 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="Qe30FMro" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1725771473; x=1757307473; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=TTfU1gIn373WQAv+wSjs1LLHvBRpR9JaaQ4A3DvKcak=; b=Qe30FMro1OD7s4VEeZ4YEJ6ZqyIQZt2DZLJUcV5mebvObCz/DW3/uOER 620oCHrsrcpnvGByDnD+1DhjUR/WLYLxvIl8M9qdLl7e/KkQBivPJ6PT0 mN7ch8B4lBqm9TPivyNwrOOg+8C5HqPiSFZu0hbIOkAmhgXIo4n8iYQpR ZgHfYBiEBLUY8X3YwBHMMeebplBoR+ctydUN796sf/2qQPhbCkGY5UiKw nDBV3q2aZiN8cS7DKJU1RLnWZEBvD4ehMAQOneFI/+m8RA2br+d919lXP 4+vvYhWCI09XmOEwsNy8/MUKg+/bOAIbKj8wHmeG5NSFpu5pYQmpwENEz g==; X-CSE-ConnectionGUID: Sme3FyfkSNuIeEMj93Pchw== X-CSE-MsgGUID: x5YujcNcQpWiPmbJ08EIsQ== X-IronPort-AV: E=McAfee;i="6700,10204,11188"; a="35152178" X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="35152178" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:51 -0700 X-CSE-ConnectionGUID: 07id3TQkQD6aR7ZdNXOj0w== X-CSE-MsgGUID: B/75QyiQSmya8HRsgEZMJg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="70924663" Received: from ibarbour-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.116.49]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:50 -0700 From: Cedric Xing Date: Sat, 07 Sep 2024 23:56:21 -0500 Subject: [PATCH RFC 3/3] tsm: Add TVM 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: <20240907-tsm-rtmr-v1-3-12fc4d43d4e7@intel.com> References: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> In-Reply-To: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> To: Dan Williams , Samuel Ortiz , James Bottomley , Lukas Wunner , Dionna Amalie Glaze , Qinkun Bao , Mikko Ylinen , Kuppuswamy Sathyanarayanan Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev X-Mailer: b4 0.13.0 This sample kernel module demonstrates how to make MRs accessible to user m= ode 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 measurement-example =E2=94=9C=E2=94=80=E2=94=80 config_mr =E2=94=9C=E2=94=80=E2=94=80 full_report =E2=94=9C=E2=94=80=E2=94=80 report_digest =E2=94=9C=E2=94=80=E2=94=80 rtmr0 =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 append_event =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 digest =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 event_log =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 hash_algo =E2=94=9C=E2=94=80=E2=94=80 rtmr1 =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 append_event =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 digest =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 event_log =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 hash_algo =E2=94=9C=E2=94=80=E2=94=80 static_mr =E2=94=94=E2=94=80=E2=94=80 user_data Among the MRs in this example: - `static_mr` and `config_mr` are static MRs. - `full_report` illustrates that a complete architecture-dependent attestat= ion report structure (for instance, the `TDREPORT` structure in Intel TDX) ca= n be presented as an MR. - `user_data` represents the data provided by the software to be incorporat= ed into the attestation report. Writing to this MR and then reading from `full_report` effectively triggers a request for an attestation report fr= om the underlying CC hardware. - `report_digest` serves as an example MR to demonstrate the use of the `TSM_MR_F_L` flag. - `rtmr0` is an RTMR with `TSM_MR_F_W` **cleared**, preventing direct extensions; as a result, `rtmr0/digest` is read-only, and the sole method= to extend this RTMR is by writing to `rtmr0/append_event`. - `rtmr1` is an RTMR with `TSM_MR_F_W` **set**, permitting direct extension= s; thus, `rtmr1/digest` is writable. See comments in `samples/tsm/measurement-example.c` for more details. Signed-off-by: Cedric Xing --- samples/Kconfig | 4 ++ samples/Makefile | 1 + samples/tsm/Makefile | 2 + samples/tsm/measurement-example.c | 116 ++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 123 insertions(+) diff --git a/samples/Kconfig b/samples/Kconfig index b288d9991d27..8159d3ca6487 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -184,6 +184,10 @@ config SAMPLE_TIMER bool "Timer sample" depends on CC_CAN_LINK && HEADERS_INSTALL =20 +config SAMPLE_TSM + tristate "TSM measurement sample" + depends on TSM_REPORTS + config SAMPLE_UHID bool "UHID sample" depends on CC_CAN_LINK && HEADERS_INSTALL diff --git a/samples/Makefile b/samples/Makefile index b85fa64390c5..891f5c12cd39 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_SAMPLE_KMEMLEAK) +=3D kmemleak/ obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) +=3D coresight/ obj-$(CONFIG_SAMPLE_FPROBE) +=3D fprobe/ obj-$(CONFIG_SAMPLES_RUST) +=3D rust/ +obj-$(CONFIG_SAMPLE_TSM) +=3D tsm/ diff --git a/samples/tsm/Makefile b/samples/tsm/Makefile new file mode 100644 index 000000000000..3969a59221e9 --- /dev/null +++ b/samples/tsm/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_SAMPLE_TSM) +=3D measurement-example.o diff --git a/samples/tsm/measurement-example.c b/samples/tsm/measurement-ex= ample.c new file mode 100644 index 000000000000..b10bda4ba1ee --- /dev/null +++ b/samples/tsm/measurement-example.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */ + +#include +#include +#include +#include + +struct __aligned(16) +{ + 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]; +} +example_report =3D { + .static_mr =3D "STATIC MR", + .config_mr =3D "CONFIG MR", +}; + +DEFINE_FREE(shash, struct crypto_shash *, + if (!IS_ERR(_T)) crypto_free_shash(_T)); + +static int _refresh_report(struct tsm_measurement_provider *pvd, + const struct tsm_measurement_register *mr) +{ + pr_debug(KBUILD_MODNAME ": %s(%s,%s)\n", __func__, pvd->name, + 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 *)&example_report, + offsetof(typeof(example_report), + report_digest), + example_report.report_digest); +} + +static int _extend_mr(struct tsm_measurement_provider *pvd, + const struct tsm_measurement_register *mr, const u8 *data) +{ + SHASH_DESC_ON_STACK(desc, 0); + int rc; + + pr_debug(KBUILD_MODNAME ": %s(%s,%s)\n", __func__, pvd->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); + + BUG_ON(crypto_shash_digestsize(desc->tfm) !=3D mr->mr_size); + + 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) #mr, &example_report.mr, sizeof(example_report.mr), TSM_MR= _F_R +static const struct tsm_measurement_register example_mrs[] =3D { + /* the entire report can be considered as a LIVE MR */ + { "full_report", &example_report, sizeof(example_report), + TSM_MR_F_LIVE }, + /* static MR, read-only */ + { MR_(static_mr) }, + /* config MR, read-only */ + { MR_(config_mr) }, + /* RTMR, direct extension prohibited */ + { MR_(rtmr0) | TSM_MR_F_RTMR, HASH_ALGO_SHA256 }, + /* RTMR, direct extension allowed */ + { MR_(rtmr1) | TSM_MR_F_RTMR | TSM_MR_F_W, HASH_ALGO_SHA384 }, + /* most CC archs allow including user data in attestation */ + { MR_(user_data) | TSM_MR_F_W }, + /* LIVE MR example, usually doesn't exist in real CC arch */ + { MR_(report_digest) | TSM_MR_F_L }, + /* terminating NULL entry */ + {} +}; +#undef MR_ + +static struct tsm_measurement_provider example_measurement_provider =3D { + .name =3D "measurement-example", + .mrs =3D example_mrs, + .refresh =3D _refresh_report, + .extend =3D _extend_mr, +}; + +static int __init measurement_example_init(void) +{ + int rc =3D tsm_register_measurement_provider( + &example_measurement_provider); + pr_debug(KBUILD_MODNAME ": tsm_register_measurement_provider(%p)=3D%d\n", + &example_measurement_provider, rc); + return rc; +} + +static void __exit measurement_example_exit(void) +{ + int rc =3D tsm_unregister_measurement_provider( + &example_measurement_provider); + pr_debug(KBUILD_MODNAME + ": tsm_unregister_measurement_provider(%p)=3D%d\n", + &example_measurement_provider, rc); +} + +module_init(measurement_example_init); +module_exit(measurement_example_exit); + +MODULE_LICENSE("GPL"); --=20 2.43.0