From nobody Fri Apr 3 12:44:42 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (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 7FA2C38F227 for ; Tue, 24 Mar 2026 18:13:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774375984; cv=none; b=jubqVyVlSU6BqJf44H0R4ae8KWlgaTjej6PUqAd2gjYttPX01mk8tmAY+fxtaVsDVG58AFeJEYxUtgPYXumKPutjNoTk3jpVY/GCyVzrzhOFmzyfsXBZhOeOTR08v0HPQV+wbrDqoKT7DngaWLz/IaO1uGV+eS0kASlKmPx7I64= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774375984; c=relaxed/simple; bh=orXh+Es5W797mVKPf+s7K9KOo+YnHs2Q86rzO0qqpVY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YPf5YaLbz/B4Wf0Edd55EXMLfzfTPRGxDk8o4/AnBLm1dEKxc8OBsHRH34wbzHqNxiOYuKe7KlccsAhSkhyIYtlhczAXInFCx9D+CwgWgIxXJYo7vJZQGad4FSXot4lLFTCLH3aC3ShQ2z+ywgyRPuHIssk9G/cP6wapPr/qKHk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=WiesThDs; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b=Nf7s45Cz; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="WiesThDs"; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b="Nf7s45Cz" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774375980; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fxd0G1I4kWf+JoknGoSmowxDRTwU4glRvJ0760l4ojQ=; b=WiesThDs+ekKUp0x5xuwjXIw9CCkOmqsjynfeO1bvx/gNG6j3qJ+2lCW0nZVUXWPsC0A1b AHTr860KCQk/EAxSwcIL1q5k5ZjOmwUNGOZa+XF1F0hOnqPwi56a9Y37yV4cZaZdPt9yaw PpsbBFiNfDNx1eOTYjZc7t32VveJlis= Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-627-dk8MHrdKOM6ZqsUQFejJ6w-1; Tue, 24 Mar 2026 14:12:59 -0400 X-MC-Unique: dk8MHrdKOM6ZqsUQFejJ6w-1 X-Mimecast-MFC-AGG-ID: dk8MHrdKOM6ZqsUQFejJ6w_1774375978 Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-35a032cdd78so6297398a91.1 for ; Tue, 24 Mar 2026 11:12:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1774375978; x=1774980778; 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; bh=fxd0G1I4kWf+JoknGoSmowxDRTwU4glRvJ0760l4ojQ=; b=Nf7s45Cz/dxOgZ5TRLMzdayaJU2mwpgvN7Qb3kauNEHW6A3szDvKRyxLONY85ZqOAh 87FzJ2TUYZ53l9z76KpvHCCFPULHC28KOCWhDE5m/Lg/4VAfNa29gTOocxWBvjDZ+4m+ b9o17SPMSCiCXzaY/lJl0yl1LTeNi0Tbcb5fTlZ/NV0EDSfwKy+0LLt/F8btjqe414sT o/hLK9op76TMPXROmMRWW3BTY008ByqtblmlnxV10bfvBem5bkpA0rCky3eCGX931XeM H1u8PCf4Kx337L8jp4gMU255Ry74XA8KNzHOo5RyRCnHE8y6mglWCQeFjwFs81uLpCTD XkWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774375978; x=1774980778; 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; bh=fxd0G1I4kWf+JoknGoSmowxDRTwU4glRvJ0760l4ojQ=; b=JuI55AZ2Ok/BIq9ngFhVryEhMr5QiQj53kaZCHAIo9rKHP+SuBnNpBK6PAfo7mu6WZ aKFJgRHsNkvch5E0epEUIJoUyP0/2EAQx/EyK/kIwBzAG7o7ysH3r8Coo97xCE/9bUTt DNOeaeVanl2sB978U2zgs3m7hvUs2Tb/JxBPRwDHjQq8vkElwMcwZNzuUYomwVnuUJhd FYUAsYRiekW+oNwe+4ydKZW6NF663XXrAa5ZBOjB/jmrcabtDYsDoV1zXh/wAZLWUZgg 9fuBk5cfAtmFa1BLVBDgPe036EF8eu6QAbOA5xrM0pws+GgSy9qUWwy3vnmVDFW8f6XJ X6NA== X-Gm-Message-State: AOJu0Yx732qirJD9dnES+nzT6NbuFmG6AB4WJ90EPH4NBLaEOXG+l0YX vtm0RHKzxwA1S6sgTnFhBIK0Z2H+6jGKDAKRqRvrjiCPvoYFofbUqWw9Uy9T5Ti9szFEtmZ8peH zBrRFezTtqtRB5Ysza0K4Tj6e5mB0OvgzvlekYBQM4mgSXctsts1DV6u5hwXwEa7kA/2Drz5E1L PPmMT5O3lwRVqC/uOux2JHsGw/vnHym5dAvbYEmHCbsMJN1Zxn X-Gm-Gg: ATEYQzykTrMEyggkaAh9L4MHcSnZg+nzj+Bz59o39MMRQ2BsWDYAkv09vMxYKIKo4UK pxG2V9mDr0/7Zj9yG7e03cfz7aeODfsPRYoyG4c2UNEJC0k+0FyanduPWoOUbBvHaAvmz+6Gfic AsHVb8UqCAOWzsZqj2qOl8QeWVkZGUdeeBrl0n2gqEma+SuLO3i1pzRcfuc7QpaRZNn3BVPYCm6 sijKnw4Txgeffs72TGEvtGHll4zyLBBQQL/bNefLeN8vzDsCbmCLELVwu/oLUI1kuaIIJgFRCLl 1rQwzAHfUAAVdE5Gw4aEH6ONVmEOIxeT8PX0SY59LimWPBlkeM0DAGzMES5yZuu0CLHwqsQMws1 cpVteQ7o4kbokQ1t+dKxs7jFDHbs1rIutE5gSx2I8OVZvh6lhNDS/6ZjqXVHy2g== X-Received: by 2002:a05:6a20:158a:b0:38b:eadd:449a with SMTP id adf61e73a8af0-39c4ad52483mr738123637.38.1774375977942; Tue, 24 Mar 2026 11:12:57 -0700 (PDT) X-Received: by 2002:a05:6a20:158a:b0:38b:eadd:449a with SMTP id adf61e73a8af0-39c4ad52483mr738086637.38.1774375977199; Tue, 24 Mar 2026 11:12:57 -0700 (PDT) Received: from fedora.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.108.16]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82b0410b1bdsm16654229b3a.57.2026.03.24.11.12.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Mar 2026 11:12:56 -0700 (PDT) From: Arun Menon To: linux-kernel@vger.kernel.org Cc: Jarkko Sakkinen , linux-integrity@vger.kernel.org, Peter Huewe , Jason Gunthorpe , Arun Menon Subject: [RFC v2 3/5] tpm_crb: Implement command and response chunking logic Date: Tue, 24 Mar 2026 23:42:42 +0530 Message-ID: <20260324181244.17741-4-armenon@redhat.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260324181244.17741-1-armenon@redhat.com> References: <20260324181244.17741-1-armenon@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" With the introduction of support for Post Quantum Cryptography algorithms in TPM, the commands and responses will grow in size. Some TPMs have a physical hardware memory window (MMIO) that is smaller than the commands we need to send. Therefore this commit implements the core logic of sending/receiving data in chunks. Instead of sending the whole command at once, the driver now sends it in small chunks. After each chunk, it signals the TPM using a nextChunk signal, and waits for the TPM to consume the data. Once the final piece is delivered, the driver signals the TPM to begin execution by toggling the start invoke bit. We use the same logic in reverse to read large responses from the TPM. This allows the driver to handle large payloads even when the hardware interface has limited memory. This kernel-side support corresponds to the backend implementation in QEMU [1]. QEMU reassembles the chunks before passing them to the TPM emulator. [1] https://lore.kernel.org/qemu-devel/20260319135316.37412-1-armenon@redha= t.com/ Signed-off-by: Arun Menon --- drivers/char/tpm/tpm_crb.c | 155 +++++++++++++++++++++++++++---------- 1 file changed, 114 insertions(+), 41 deletions(-) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 922bcf7a69ad5..a97fc5e9927e3 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -104,11 +104,13 @@ struct crb_priv { u8 __iomem *cmd; u8 __iomem *rsp; u32 cmd_size; + u32 rsp_size; u32 smc_func_id; u32 __iomem *pluton_start_addr; u32 __iomem *pluton_reply_addr; u8 ffa_flags; u8 ffa_attributes; + bool chunking_supported; }; =20 struct tpm2_crb_smc { @@ -368,38 +370,6 @@ static u8 crb_status(struct tpm_chip *chip) return sts; } =20 -static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) -{ - struct crb_priv *priv =3D dev_get_drvdata(&chip->dev); - unsigned int expected; - - /* A sanity check that the upper layer wants to get at least the header - * as that is the minimum size for any TPM response. - */ - if (count < TPM_HEADER_SIZE) - return -EIO; - - /* If this bit is set, according to the spec, the TPM is in - * unrecoverable condition. - */ - if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR) - return -EIO; - - /* Read the first 8 bytes in order to get the length of the response. - * We read exactly a quad word in order to make sure that the remaining - * reads will be aligned. - */ - memcpy_fromio(buf, priv->rsp, 8); - - expected =3D be32_to_cpup((__be32 *)&buf[2]); - if (expected > count || expected < TPM_HEADER_SIZE) - return -EIO; - - memcpy_fromio(&buf[8], &priv->rsp[8], expected - 8); - - return expected; -} - static int crb_do_acpi_start(struct tpm_chip *chip) { union acpi_object *obj; @@ -474,6 +444,8 @@ static int crb_trigger_tpm(struct tpm_chip *chip, u32 s= tart_cmd) static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t = len) { struct crb_priv *priv =3D dev_get_drvdata(&chip->dev); + size_t offset =3D 0; + size_t chunk_size; int rc =3D 0; =20 /* Zero the cancel register so that the next command will not get @@ -481,7 +453,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, siz= e_t bufsiz, size_t len) */ iowrite32(0, &priv->regs_t->ctrl_cancel); =20 - if (len > priv->cmd_size) { + if (len > priv->cmd_size && !priv->chunking_supported) { dev_err(&chip->dev, "invalid command count value %zd %d\n", len, priv->cmd_size); return -E2BIG; @@ -491,18 +463,108 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, = size_t bufsiz, size_t len) if (priv->sm =3D=3D ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) __crb_cmd_ready(&chip->dev, priv, chip->locality); =20 - memcpy_toio(priv->cmd, buf, len); + while (offset < len) { + chunk_size =3D min_t(size_t, len - offset, priv->cmd_size); =20 - /* Make sure that cmd is populated before issuing start. */ - wmb(); - - rc =3D crb_trigger_tpm(chip, CRB_START_INVOKE); - if (rc) - return rc; + if (chunk_size =3D=3D 0) + break; =20 + memcpy_toio(priv->cmd, buf + offset, chunk_size); + offset +=3D chunk_size; + + /* Make sure that cmd is populated before issuing start. */ + wmb(); + if (offset < len) { + rc =3D crb_trigger_tpm(chip, CRB_START_NEXT_CHUNK); + if (rc) + return rc; + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_start, + CRB_START_NEXT_CHUNK, 0, TPM2_TIMEOUT_C)) { + dev_err(&chip->dev, + "Timeout waiting for backend to consume chunk\n"); + return -ETIME; + } + } else { + rc =3D crb_trigger_tpm(chip, CRB_START_INVOKE); + if (rc) + return rc; + } + } return crb_try_pluton_doorbell(priv, false); } =20 +static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct crb_priv *priv =3D dev_get_drvdata(&chip->dev); + unsigned int expected; + size_t offset =3D 0; + size_t chunk_size; + size_t first_read; + int rc; + + /* A sanity check that the upper layer wants to get at least the header + * as that is the minimum size for any TPM response. + */ + if (count < TPM_HEADER_SIZE) + return -EIO; + + /* If this bit is set, according to the spec, the TPM is in + * unrecoverable condition. + */ + if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR) + return -EIO; + + /* Read the first 8 bytes in order to get the length of the response. + * We read exactly a quad word in order to make sure that the remaining + * reads will be aligned. + */ + memcpy_fromio(buf, priv->rsp, 8); + + expected =3D be32_to_cpup((__be32 *)&buf[2]); + if (expected > count || expected < TPM_HEADER_SIZE) + return -EIO; + + /* + * Set chunk_size by comparing the size of the buffer that the upper laye= r has + * allocated (count) to the hardware tpm limit (priv->rsp_size). + * This is to prevent buffer overflow while writing to buf. + */ + chunk_size =3D min_t(size_t, count, priv->rsp_size); + if (chunk_size < 8) + return -EIO; + + /* + * Compare the actual size of the response we found in the header to the = chunk_size. + */ + first_read =3D min_t(size_t, expected, chunk_size); + + memcpy_fromio(&buf[8], &priv->rsp[8], first_read - 8); + offset =3D first_read; + + while (offset < expected) { + if (!priv->chunking_supported) { + dev_err(&chip->dev, "Response larger than MMIO and chunking not support= ed\n"); + return -EIO; + } + + rc =3D crb_trigger_tpm(chip, CRB_START_NEXT_CHUNK); + if (rc) + return rc; + + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_start, + CRB_START_NEXT_CHUNK, 0, TPM2_TIMEOUT_C)) { + dev_err(&chip->dev, "Timeout waiting for backend response\n"); + return -ETIME; + } + + chunk_size =3D min_t(size_t, expected - offset, priv->rsp_size); + memcpy_fromio(buf + offset, priv->rsp, chunk_size); + offset +=3D chunk_size; + } + + return expected; +} + static void crb_cancel(struct tpm_chip *chip) { struct crb_priv *priv =3D dev_get_drvdata(&chip->dev); @@ -727,6 +789,15 @@ static int crb_map_io(struct acpi_device *device, stru= ct crb_priv *priv, goto out; } =20 + if (priv->regs_h) { + u32 intf_id =3D ioread32((u32 __iomem *)&priv->regs_h->intf_id); + + if (intf_id & CRB_INTF_CAP_CRB_CHUNK) { + priv->chunking_supported =3D true; + dev_info(dev, "CRB Chunking is supported by backend\n"); + } + } + memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); rsp_pa =3D le64_to_cpu(__rsp_pa); rsp_size =3D ioread32(&priv->regs_t->ctrl_rsp_size); @@ -764,8 +835,10 @@ static int crb_map_io(struct acpi_device *device, stru= ct crb_priv *priv, priv->rsp =3D priv->cmd; =20 out: - if (!ret) + if (!ret) { priv->cmd_size =3D cmd_size; + priv->rsp_size =3D rsp_size; + } =20 __crb_go_idle(dev, priv, 0); =20 --=20 2.53.0