From nobody Mon Feb 9 23:42:50 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.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 4B8CA1B5ED6 for ; Thu, 31 Oct 2024 16:50:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730393452; cv=none; b=l9jwfEzOM9uap3Nq4ksain7qEcRveJk4hDNPuL2mug8cDv0bW+9+J+jNgDivtA5eCXsFSAQZj4KJu9loiyAvScO2sf6rJCOGtOCvzoMz5Q/D35XXzJpEPZ22HrYmMOCX02Lq/vO6HFJDdBFriuQPyWV2wxe2mUmpOxCwmS9N4Qc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730393452; c=relaxed/simple; bh=q59xsDFESxWB8f85RsK4byfthxqa6imbWXHUx36bksc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=E1Ptplw/ARPyPHG4LOEX8d7tnOyOIlF06LEVoNNGdx7JAAOj9sAqoqR79jB42SzletEbHUxsOt8zCzfP1sThwKq96Ue5m3ppkc3nytcmvDdfqUK9R/sxhlRuYa/eF/qCgc90kVJE4neBfKU1SCWozOXkYP9rtoI1GC7TvMfbmD4= 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=PDXznjOF; arc=none smtp.client-ip=198.175.65.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="PDXznjOF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1730393450; x=1761929450; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=q59xsDFESxWB8f85RsK4byfthxqa6imbWXHUx36bksc=; b=PDXznjOFWskX0r2am0Jmeyo/PZbxofqFzQ9OmXNlknmppgtd9AIZgcWY 90eI25LLIcoWoCWQ2/Zn0PYdzPJVcGm9lB4MIln1DcLoV5S/ts4d7X1Tr mWCUONlx4IKEQaKPP7wzAHEbsehGcJ2dF+5rk4mlEY5VUuF8xSZ6FJY3N RrOaDvitVJHtXS0lMZlmTzXBtpeDTkxm/Q+2ehWpjFplMmeAI8KelIqhK 4hXmDVwvchi+lA2F4ylVndS28I2uFYAj6NilPgI7okqvRJ8mapwSi53Ri 3ZwfTQ9ORqnRjUYyyRazzlWqAOG4FSgowo+tNG2lJvjKbIYU1yIPW3y1+ Q==; X-CSE-ConnectionGUID: HWf0R3/uTcK1gce0dPG96g== X-CSE-MsgGUID: pjTiOS89RmS2uyr/oc8puA== X-IronPort-AV: E=McAfee;i="6700,10204,11222"; a="30312370" X-IronPort-AV: E=Sophos;i="6.11,199,1725346800"; d="scan'208";a="30312370" Received: from fmviesa006.fm.intel.com ([10.60.135.146]) by orvoesa108.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Oct 2024 09:50:48 -0700 X-CSE-ConnectionGUID: jSH/ZRi5R62p/IOS/IrqdQ== X-CSE-MsgGUID: LsCHbWAMTFKXlykmDpN0lg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.11,247,1725346800"; d="scan'208";a="82217188" Received: from eschuber-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.125.64.58]) by fmviesa006-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 31 Oct 2024 09:50:46 -0700 From: Cedric Xing Date: Thu, 31 Oct 2024 11:50:40 -0500 Subject: [PATCH RFC v2 1/2] 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: <20241031-tsm-rtmr-v2-1-1a6762795911@intel.com> References: <20241031-tsm-rtmr-v2-0-1a6762795911@intel.com> In-Reply-To: <20241031-tsm-rtmr-v2-0-1a6762795911@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(struct tsm_measurement *)` This function allows a CC guest driver to register a set of measurement registers with the TSM core. - `tsm_unregister_measurement(struct tsm_measurement *)`: This function enables a CC guest driver to unregister a previously regist= ered set of measurement registers. `struct tsm_measurement` 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 MR is made accessible as either a file or a directory, named after the MR itself. In the former case, the file content is the MR value, while in the latter case there will be two files in the directory: `digest` and `hash_algo`. The purpose and content of these f= iles are self-explanatory. Signed-off-by: Cedric Xing --- drivers/virt/coco/Kconfig | 3 +- drivers/virt/coco/Makefile | 2 + drivers/virt/coco/{tsm.c =3D> tsm-core.c} | 26 ++- drivers/virt/coco/tsm-mr.c | 374 ++++++++++++++++++++++++++++= ++++ 4 files changed, 394 insertions(+), 11 deletions(-) diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig index d9ff676bf48d..0609622cbcb9 100644 --- a/drivers/virt/coco/Kconfig +++ b/drivers/virt/coco/Kconfig @@ -5,7 +5,8 @@ =20 config TSM_REPORTS select CONFIGFS_FS - tristate + 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/Makefile b/drivers/virt/coco/Makefile index b69c30c1c720..8192d78dff61 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 95% rename from drivers/virt/coco/tsm.c rename to drivers/virt/coco/tsm-core.c index 9432d4e303f1..92e961f21507 100644 --- a/drivers/virt/coco/tsm.c +++ b/drivers/virt/coco/tsm-core.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 @@ -166,8 +164,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 +186,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 +340,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 +383,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) @@ -478,6 +479,9 @@ EXPORT_SYMBOL_GPL(tsm_unregister); =20 static struct config_group *tsm_report_group; =20 +extern int tsm_mr_init(void); +extern void tsm_mr_exit(void); + static int __init tsm_init(void) { struct config_group *root =3D &tsm_configfs.su_group; @@ -497,16 +501,18 @@ 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); } 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/drivers/virt/coco/tsm-mr.c b/drivers/virt/coco/tsm-mr.c new file mode 100644 index 000000000000..a84e923a7782 --- /dev/null +++ b/drivers/virt/coco/tsm-mr.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2024 Intel Corporation. All rights reserved. */ + +#include +#include +#include +#include +#include + +int tsm_mr_init(void); +void tsm_mr_exit(void); + +enum _mrdir_bin_attr_index { + _MRDIR_BA_DIGEST, + _MRDIR_BA__COUNT, +}; + +struct _mrdir { + struct kobject kobj; + struct bin_attribute battrs[_MRDIR_BA__COUNT]; +}; + +struct _mr_provider { + struct kset kset; + struct rw_semaphore rwsem; + struct bin_attribute *mrfiles; + struct tsm_measurement *tmr; + bool in_sync; +}; + +static inline const struct tsm_measurement_register * +_mrdir_mr(const struct _mrdir *mrd) +{ + return (struct tsm_measurement_register *)mrd->battrs[_MRDIR_BA_DIGEST] + .private; +} + +static inline struct _mr_provider * +_mr_to_provider(const struct tsm_measurement_register *mr, struct kobject = *kobj) +{ + if (mr->mr_flags & TSM_MR_F_F) + 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->tmr->refresh(pvd->tmr, 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->tmr->extend(pvd->tmr, 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 _mrdir *mrd; + mrd =3D container_of(kobj, typeof(*mrd), kobj); + return sysfs_emit(page, "%s", hash_algo_name[_mrdir_mr(mrd)->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_provider(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 ": %s.refresh(%s)=3D%d\n", + kobject_name(&pvd->kset.kobj), mr->mr_name, rc); + + up_read(&pvd->rwsem); + return rc ?: count; +} + +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_provider(mr, kobj); + rc =3D down_write_killable(&pvd->rwsem); + if (rc) + return rc; + + if (mr->mr_flags & TSM_MR_F_X) + rc =3D _call_extend(pvd, mr, page); + else + memcpy(mr->mr_value, page, count); + + if (!rc) + pvd->in_sync =3D false; + + up_write(&pvd->rwsem); + return rc ?: count; +} + +static void _mrdir_release(struct kobject *kobj) +{ + struct _mrdir *mrd; + mrd =3D container_of(kobj, typeof(*mrd), kobj); + pr_debug("%s(%s)\n", __func__, kobject_name(kobj)); + kfree(mrd); +} + +static struct kobj_type _mrdir_ktype =3D { + .release =3D _mrdir_release, + .sysfs_ops =3D &kobj_sysfs_ops, +}; + +static struct _mrdir *_mrdir_create(const struct tsm_measurement_register = *mr, + struct _mr_provider *pvd) +{ + struct _mrdir *mrd __free(kfree); + int rc; + + BUG_ON(mr->mr_flags & TSM_MR_F_F); + mrd =3D kzalloc(sizeof(*mrd), GFP_KERNEL); + if (!mrd) + return ERR_PTR(-ENOMEM); + + sysfs_bin_attr_init(&mrd->battrs[_MRDIR_BA_DIGEST]); + mrd->battrs[_MRDIR_BA_DIGEST].attr.name =3D "digest"; + if (mr->mr_flags & TSM_MR_F_W) + mrd->battrs[_MRDIR_BA_DIGEST].attr.mode |=3D S_IWUSR | S_IWGRP; + if (mr->mr_flags & TSM_MR_F_R) + mrd->battrs[_MRDIR_BA_DIGEST].attr.mode |=3D S_IRUGO; + + mrd->battrs[_MRDIR_BA_DIGEST].size =3D mr->mr_size; + mrd->battrs[_MRDIR_BA_DIGEST].read =3D _mr_read; + mrd->battrs[_MRDIR_BA_DIGEST].write =3D _mr_write; + mrd->battrs[_MRDIR_BA_DIGEST].private =3D (void *)mr; + + mrd->kobj.kset =3D &pvd->kset; + rc =3D kobject_init_and_add(&mrd->kobj, &_mrdir_ktype, NULL, "%s", + mr->mr_name); + if (rc) + return ERR_PTR(rc); + + return_ptr(mrd); +} + +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->mrfiles); + kfree(pvd); +} + +static struct kobj_type _mr_provider_ktype =3D { + .release =3D _mr_provider_release, + .sysfs_ops =3D &kobj_sysfs_ops, +}; + +static struct kset *_sysfs_tsm; + +static struct _mr_provider *_mr_provider_create(struct tsm_measurement *tm= r) +{ + struct _mr_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 _sysfs_tsm; + 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 _mr_provider *, + if (!IS_ERR_OR_NULL(_T)) tsm_unregister_measurement(_T->tmr)); + +int tsm_register_measurement(struct tsm_measurement *tmr) +{ + static struct kobj_attribute _attr_hash =3D __ATTR_RO(hash_algo); + + struct _mr_provider *pvd __free(_unregister_measurement); + int rc, nr; + + pvd =3D _mr_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 _mrdir *mrd =3D _mrdir_create(&tmr->mrs[i], pvd); + if (IS_ERR(mrd)) + return PTR_ERR(mrd); + + struct attribute *attrs[] =3D { + &_attr_hash.attr, + NULL, + }; + struct bin_attribute *battrs[_MRDIR_BA__COUNT + 1] =3D {}; + for (int j =3D 0; j < _MRDIR_BA__COUNT; ++j) + battrs[j] =3D &mrd->battrs[j]; + struct attribute_group agrp =3D { + .attrs =3D attrs, + .bin_attrs =3D battrs, + }; + 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(sizeof(*mrfiles), nr, GFP_KERNEL); + battrs =3D kcalloc(sizeof(*battrs), nr + 1, 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 _mr_read; + mrfiles[j].write =3D _mr_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; + + 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->mrfiles =3D no_free_ptr(mrfiles); + } + + pvd =3D NULL; + return 0; +} +EXPORT_SYMBOL_GPL(tsm_register_measurement); + +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(struct tsm_measurement *tmr) +{ + struct kobject *kobj =3D kset_find_obj(_sysfs_tsm, tmr->name); + if (!kobj) + return -ENOENT; + + struct _mr_provider *pvd =3D container_of(kobj, typeof(*pvd), kset.kobj); + BUG_ON(pvd->tmr !=3D tmr); + + _kset_put_children(&pvd->kset); + kset_unregister(&pvd->kset); + kobject_put(kobj); + return 0; +} +EXPORT_SYMBOL_GPL(tsm_unregister_measurement); + +int tsm_mr_init(void) +{ + _sysfs_tsm =3D kset_create_and_add("tsm", NULL, kernel_kobj); + if (!_sysfs_tsm) + return -ENOMEM; + return 0; +} + +void tsm_mr_exit(void) +{ + kset_unregister(_sysfs_tsm); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Provide Trusted Security Module measurements via sysfs= "); --=20 2.43.0