From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.221.46 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794586150; b=NUDes1gR6wGfis7TgcvQgwfjW5XuOjicWPKCLwLZMftQkBFqjH/60Kz3k3RJrIcvmtOQ BA4wODAHPKMjtslS8eEMRU/tcbJsCIkNTS79U8MVhRU5jF/nIxYcJBd8TxyLxahHpl/qg Jy3X8ItQ2On1FUb0x6YnIJAiNkQHDtPUBzCEN6j6JRqJ6izhPYpRRA65vYHd96bprwjbi USktnT3dRpWcoVlhqAW/VYfDgQ1uCmc4rgKkYHstIvdrzAtiG0nU7OhSbr0iDpNvoB4Cr 0kmJ2ZULwIx5ux11yf2F/2gGvMS4vlLdSzDTawc0FPbhok+oLZB8dLgcriJDf4QEXpw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794586150; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:To:Cc:Subject: Date:Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=i9bo9BfxaEw7amAwj2fZcNBOwQOKFl/OM4ulegQbiyk=; b=488XVftAVmNDeF9Lsqc4g23Q9hKmkbWBbU0OH4QY94SYJ4p1onro81IQ2w5F564jhnlh 7aW87Oab77FlxoA0wYTtymvoTxFXsTUB4/M1L9338KzjMFPX2N8KVY/qm1nb3VfPbHumt GwzJWtzSPGtv4812Yn24B25jPDi3csNtnYjubUwBL9gVPKp05IlHYHlgTPtHKCQaWT9FE iNlLhQTNhoBigMW+IwaqESw08BRpo2Is2xyHgII5WAFieMXqRfL4j3EJnpYBHokzQN6Pq ZpWd2wRGj96Tgo0wlbF0tzDKobiSetypyzyi7wlML7Zwpd/IBrn44RvGDqvtTr3fE7Q== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=resnulli.us; spf=pass smtp.mailfrom=resnulli.us; dkim=pass header.d=resnulli.us header.i=@resnulli.us header.a=rsa-sha256 header.s=google header.b=MLSXJFSL; arc=none smtp.remote-ip=209.85.221.46 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7138E606D451 for ; Fri, 13 Nov 2026 16:09:10 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 7138E606D451 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=resnulli.us Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=resnulli.us Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=resnulli.us header.i=@resnulli.us header.a=rsa-sha256 header.s=google header.b=MLSXJFSL Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-48e10a70540so544969f8f.1 for ; Fri, 13 Nov 2026 08:09:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=resnulli.us; s=google; t=1794586149; x=1795190949; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=i9bo9BfxaEw7amAwj2fZcNBOwQOKFl/OM4ulegQbiyk=; b=MLSXJFSLknVyIqJNPDUng61UtqOqcxBnSQeC4c5nHraRNdrLeqdxywaSvp2Dlosgdj tR3XiT38vJhgVDpE9qFsV3DQZ+sea+YH8t8Tn/0tK6Aky2ddxTNT+mDBXQmdnP3seyVY DxoDmplrmkxuTLWWxn1Au3BGzf4GjOxfy1h8XXA/XH9VPe87UspuPLRRhk9hV28vRz55 OSLJE116gSjuHAKfVgXyQN0FrHB1Aw1fXV1y7QzAt2tHR4mHRcllqerHWpJzt1Geqrqs 8LofaIKDz+syVMsblreM7ouVC1nu3ZlSVteUq5IvQeMjv4Ozx9XZWLZYbqouBHTpSB/h DpPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794586149; x=1795190949; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=i9bo9BfxaEw7amAwj2fZcNBOwQOKFl/OM4ulegQbiyk=; b=T51vVQM4iNffqHlfYwTqPivy1ceRF3TZi00Ig/7Brt+HVXDflCoGBlwka+iTQafx0y dY57EkMKF+7jowvM27pOBUNmY+H3jO4QWwAMEQYVT8qrksxTTYQLBZ1AuLdSAONXU7Tu 4XElXJSyhu7M6Nh5DlMeSS/C4waxoWgRVPEL6ula4jeqrZaLm1S/aGA7Lta9b3oPH7iG 9mEWYMu/Bp8JTIyCuMl185rOo/OswuY2nPLM9ijVAShKr3JOJpfZw5hAACUAvHuxdPIF QrM09FbDWaWyrnKSfEYXmchWfuEDEcNNX6qg9+y4cehD/8YvuCVwVWGeUObvrG3Vi5HF bk7Q== X-Gm-Message-State: APuU4Z+jzGOdJqvIS6OHZ92ECp/wivb771549f7Sv6ty/EbTsgW6PbeT 3/YpXpocw+Ao15Ip9YNDADWkOFQV1KGYPcnvaGM4nSNE+0BzoayX2ol6huaVSZcLMxU= X-Gm-Gg: AYBFou3tT0f+Ho8Ezc4js2JJTrXeoxgcx7AsAtwNRb/SrCOqk+RxJwabKoGZtENhmqs cCeoexJtvqM/pUdQIu7HxCms5Jyojh6rlZ/AMAqJ6J2pdRM7hYJRJIcHhsNZ1olGlmUbq6ttNkt zTZqZoizFYMkOvNDAwiXM4plsLA1assAScY617DhBGxv+NGZkFQVJEct+s6ybek/y1mHJG3xZ6O pLAmoWv2UCJeSm/b8zeezWGEIF2VJ74zqEpu4Cog1N+rY0cXD6ei4AjIY/sjVg0yybd/3kNGhu2 GxCWrnYPbMoTg02UHQWKg0+ZGVZxKAjRZa11kK2pFLyFIsdT2zL5N/xSymJz0zZars363nk9BVg nCfRrb+WtQkZjfjiJn5I751iP9m8Z+4q5H3rkgPkwmKwuTul404X3p9vxKkYjPhPI54VWZXiUHg 19FSM6v5EXkSDHF/J4cMQz8doAR5eIAD/G/jqKGYQMSaJBqp5IVO5M38hN0tVKZHAv0RlTFtBlk Npi4GnjDNKXZtpuy1QOEgql/efndqrw9aAcqeqAq4dc7hZ+QTxI4K7coMy6HC8YiX58HhN+kNzP Dh7jCI4hE52HP5xPsS7pe3N6BJ3F5vBf8fK/3c4= X-Received: by 2002:a05:6000:992:b0:491:fd47:5df3 with SMTP id ffacd0b85a97d-49c6fc9e6bcmr6236979f8f.7.1794586148759; Fri, 13 Nov 2026 08:09:08 -0800 (PST) Received: from localhost ([140.209.217.212]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-49c5d03b10fsm6898877f8f.1.2026.11.13.08.09.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Nov 2026 08:09:08 -0800 (PST) From: Jiri Pirko To: linux-coco@lists.linux.dev Cc: linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, x86@kernel.org, kvm@vger.kernel.org, linux-doc@vger.kernel.org, jgg@ziepe.ca, david.quigley@intel.com, lukas@wunner.de, ksinyuk@kernel.org, rodrigo.vivi@intel.com, cedric.xing@intel.com, sameo@meta.com, drdeeglaze@gmail.com, James.Bottomley@HansenPartnership.com, aik@amd.com, jpiotrowski@nvidia.com, qinkun@google.com, pgonda@google.com, jic23@kernel.org, alistair.francis@wdc.com, ankita@nvidia.com, suzuki.poulose@arm.com, kas@kernel.org, rick.p.edgecombe@intel.com, elena.reshetova@intel.com, dave.hansen@linux.intel.com, sathyanarayanan.kuppuswamy@linux.intel.com, peter.fang@intel.com, jarkko@kernel.org, zohar@linux.ibm.com, yeoreum.yun@arm.com, aneesh.kumar@kernel.org, dave.jiang@intel.com, nik.borisov@suse.com, sergii.dmytruk@3mdeb.com Subject: [PATCH RFC v2 7/9] tdx-guest: Expose the native attestation service API Date: Fri, 13 Nov 2026 17:08:36 +0100 Message-ID: <20261113160838.2512652-8-jiri@resnulli.us> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261113160838.2512652-1-jiri@resnulli.us> References: <20261113160838.2512652-1-jiri@resnulli.us> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Jiri Pirko Factor the Quote and measurement-register operations of the misc, TSM report and TSM MR interfaces into helpers declared in a driver-internal header, for an attestation provider built into the driver to share. Assisted-by: Cursor: GPT-5.6 Sol Assisted-by: Cursor: Claude Opus 5.5 Signed-off-by: Jiri Pirko --- RFC->RFCv2: - kept the helpers inside tdx-guest, in place of a client API and its header - dropped the MAINTAINERS lines, which N: tdx already covers - sized the TSM report outblob to its Quote - dropped an unreachable return in tdx_do_report() - dropped the stale kernel client section from tdx-guest.rst - numbered every enum member --- drivers/virt/coco/tdx-guest/internal.h | 30 ++++++ drivers/virt/coco/tdx-guest/tdx-guest.c | 130 +++++++++++++++++++----- 2 files changed, 132 insertions(+), 28 deletions(-) create mode 100644 drivers/virt/coco/tdx-guest/internal.h diff --git a/drivers/virt/coco/tdx-guest/internal.h b/drivers/virt/coco/tdx-guest/internal.h new file mode 100644 index 000000000000..d57a8c385aff --- /dev/null +++ b/drivers/virt/coco/tdx-guest/internal.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TDX_GUEST_INTERNAL_H +#define _TDX_GUEST_INTERNAL_H + +#include +#include + +#define TDX_GUEST_QUOTE_MAX_LEN ((128 * 1024) - 24) + +struct tdx_guest_info { + u8 mrconfigid[SHA384_DIGEST_SIZE]; + u8 mrowner[SHA384_DIGEST_SIZE]; + u8 mrownerconfig[SHA384_DIGEST_SIZE]; +}; + +enum tdx_guest_mr { + TDX_GUEST_MR_MRTD = 0, + TDX_GUEST_MR_RTMR0 = 1, + TDX_GUEST_MR_RTMR1 = 2, + TDX_GUEST_MR_RTMR2 = 3, + TDX_GUEST_MR_RTMR3 = 4, +}; + +/* @reportdata is TDX_REPORTDATA_LEN bytes, and @out holds TDX_GUEST_QUOTE_MAX_LEN. */ +int tdx_guest_quote_get(const u8 *reportdata, void *out, size_t *out_length); +int tdx_guest_mr_read(enum tdx_guest_mr mr, u8 value[SHA384_DIGEST_SIZE]); +int tdx_guest_rtmr_extend(unsigned int rtmr, + const u8 value[SHA384_DIGEST_SIZE]); + +#endif /* _TDX_GUEST_INTERNAL_H */ diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/tdx-guest/tdx-guest.c index 11d741da3b35..c49454ec0b18 100644 --- a/drivers/virt/coco/tdx-guest/tdx-guest.c +++ b/drivers/virt/coco/tdx-guest/tdx-guest.c @@ -25,8 +25,11 @@ #include #include +#include "internal.h" + /* TDREPORT buffer */ static u8 *tdx_report_buf; +static struct tdx_guest_info tdx_guest_info; /* Lock to serialize TDG.MR.REPORT and TDG.MR.RTMR.EXTEND TDCALLs */ static DEFINE_MUTEX(mr_lock); @@ -49,27 +52,32 @@ enum { TDREPORT_servtd_hash = TDREPORT_rtmr3 + SHA384_DIGEST_SIZE, }; -static int tdx_do_report(sockptr_t data, sockptr_t tdreport) +static int __tdx_do_report(sockptr_t data, sockptr_t tdreport) { - scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) { - u8 *reportdata = tdx_report_buf + TDREPORT_reportdata; - int ret; + u8 *reportdata = tdx_report_buf + TDREPORT_reportdata; + int ret; - if (!sockptr_is_null(data) && - copy_from_sockptr(reportdata, data, TDX_REPORTDATA_LEN)) - return -EFAULT; + lockdep_assert_held(&mr_lock); + if (!sockptr_is_null(data) && + copy_from_sockptr(reportdata, data, TDX_REPORTDATA_LEN)) + return -EFAULT; - ret = tdx_mcall_get_report0(reportdata, tdx_report_buf); - if (WARN_ONCE(ret, "tdx_mcall_get_report0() failed: %d", ret)) - return ret; + ret = tdx_mcall_get_report0(reportdata, tdx_report_buf); + if (WARN_ONCE(ret, "tdx_mcall_get_report0() failed: %d", ret)) + return ret; - if (!sockptr_is_null(tdreport) && - copy_to_sockptr(tdreport, tdx_report_buf, TDX_REPORT_LEN)) - return -EFAULT; - } + if (!sockptr_is_null(tdreport) && + copy_to_sockptr(tdreport, tdx_report_buf, TDX_REPORT_LEN)) + return -EFAULT; return 0; } +static int tdx_do_report(sockptr_t data, sockptr_t tdreport) +{ + scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) + return __tdx_do_report(data, tdreport); +} + static int tdx_do_extend(u8 mr_ind, const u8 *data) { scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) { @@ -90,6 +98,34 @@ static int tdx_do_extend(u8 mr_ind, const u8 *data) return 0; } +int tdx_guest_mr_read(enum tdx_guest_mr mr, u8 value[SHA384_DIGEST_SIZE]) +{ + static const size_t offsets[] = { + [TDX_GUEST_MR_MRTD] = TDREPORT_mrtd, + [TDX_GUEST_MR_RTMR0] = TDREPORT_rtmr0, + [TDX_GUEST_MR_RTMR1] = TDREPORT_rtmr1, + [TDX_GUEST_MR_RTMR2] = TDREPORT_rtmr2, + [TDX_GUEST_MR_RTMR3] = TDREPORT_rtmr3, + }; + int ret; + + scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) { + ret = __tdx_do_report(KERNEL_SOCKPTR(NULL), + KERNEL_SOCKPTR(NULL)); + if (ret) + return ret; + memcpy(value, tdx_report_buf + offsets[mr], + SHA384_DIGEST_SIZE); + } + return 0; +} + +int tdx_guest_rtmr_extend(unsigned int rtmr, + const u8 value[SHA384_DIGEST_SIZE]) +{ + return tdx_do_extend(rtmr, value); +} + #define TDX_MR_(r) .mr_value = (void *)TDREPORT_##r, TSM_MR_(r, SHA384) static struct tsm_measurement_register tdx_mrs[] = { { TDX_MR_(rtmr0) | TSM_MR_F_RTMR }, @@ -135,6 +171,12 @@ static const struct attribute_group *tdx_mr_init(void) rc = tdx_mr_refresh(&tdx_measurements); if (rc) return ERR_PTR(rc); + memcpy(tdx_guest_info.mrconfigid, + buf + TDREPORT_mrconfigid, SHA384_DIGEST_SIZE); + memcpy(tdx_guest_info.mrowner, + buf + TDREPORT_mrowner, SHA384_DIGEST_SIZE); + memcpy(tdx_guest_info.mrownerconfig, + buf + TDREPORT_mrownerconfig, SHA384_DIGEST_SIZE); /* * @mr_value was initialized with the offset only, while the base @@ -189,6 +231,9 @@ struct tdx_quote_buf { u8 data[]; }; +static_assert(TDX_GUEST_QUOTE_MAX_LEN == + TDX_DEFAULT_QUOTE_SIZE - sizeof(struct tdx_quote_buf)); + static struct tdx_quote_buf *quote_buf; /* Lock to streamline quote requests */ @@ -285,15 +330,18 @@ static int wait_for_quote_completion(struct tdx_quote_buf *buf, u32 timeout) return (i == timeout) ? -ETIMEDOUT : 0; } -static int tdx_report_new_locked(struct tsm_report *report) +/* Leaves the Quote in quote_buf; copy it out before dropping quote_lock. */ +static int __tdx_guest_quote_get(const u8 *reportdata, size_t length, + size_t *out_length) { - u8 *buf; - struct tsm_report_desc *desc = &report->desc; size_t quote_buf_size = get_quote_buf_size(); u32 out_len; int ret; u64 err; + lockdep_assert_held("e_lock); + if (!reportdata || length != TDX_REPORTDATA_LEN) + return -EINVAL; /* * If the previous request is timedout or interrupted, and the * Quote buf status is still in GET_QUOTE_IN_FLIGHT (owned by @@ -302,16 +350,13 @@ static int tdx_report_new_locked(struct tsm_report *report) if (quote_buf->status == GET_QUOTE_IN_FLIGHT) return -EBUSY; - if (desc->inblob_len != TDX_REPORTDATA_LEN) - return -EINVAL; - memset(quote_buf, 0, quote_buf_size); /* Update Quote buffer header */ quote_buf->version = GET_QUOTE_CMD_VER; quote_buf->in_len = TDX_REPORT_LEN; - ret = tdx_do_report(KERNEL_SOCKPTR(desc->inblob), + ret = tdx_do_report(KERNEL_SOCKPTR((void *)reportdata), KERNEL_SOCKPTR(quote_buf->data)); if (ret) return ret; @@ -337,13 +382,7 @@ static int tdx_report_new_locked(struct tsm_report *report) if (struct_size(quote_buf, data, out_len) > quote_buf_size) return -EFBIG; - - buf = kvmemdup(quote_buf->data, out_len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - report->outblob = buf; - report->outblob_len = out_len; + *out_length = out_len; /* * TODO: parse the PEM-formatted cert chain out of the quote buffer when @@ -353,6 +392,41 @@ static int tdx_report_new_locked(struct tsm_report *report) return ret; } +int tdx_guest_quote_get(const u8 *reportdata, void *out, size_t *out_length) +{ + int ret; + + scoped_cond_guard(mutex_intr, return -EINTR, "e_lock) { + ret = __tdx_guest_quote_get(reportdata, TDX_REPORTDATA_LEN, + out_length); + if (ret) + return ret; + /* The TDX module may allow a Quote longer than @out. */ + if (*out_length > TDX_GUEST_QUOTE_MAX_LEN) + return -EFBIG; + memcpy(out, quote_buf->data, *out_length); + } + return 0; +} + +static int tdx_report_new_locked(struct tsm_report *report) +{ + struct tsm_report_desc *desc = &report->desc; + size_t length; + void *buf; + int ret; + + ret = __tdx_guest_quote_get(desc->inblob, desc->inblob_len, &length); + if (ret) + return ret; + buf = kvmemdup(quote_buf->data, length, GFP_KERNEL); + if (!buf) + return -ENOMEM; + report->outblob = buf; + report->outblob_len = length; + return 0; +} + static int tdx_report_new(struct tsm_report *report, void *unused) { scoped_cond_guard(mutex_intr, return -EINTR, "e_lock) -- 2.56.0