From nobody Fri Nov 14 16:37:28 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=siemens.com ARC-Seal: i=1; a=rsa-sha256; t=1762262098; cv=none; d=zohomail.com; s=zohoarc; b=QFsqRLpySia4tiQqsxltXXk8eJOxPKaWFjdVmIV0ersqN2UBVBYg2Vsg2FtHWuPTBZXu/h0Tv/lz7hevL40OFbbxnt1OimJAe+McozhKI9aU2fZWtSK31ocUkoZs3uw0gH9jS9+yM31NCCxdgUlGLAdtKmSWIOa258N2V/bn2Xg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762262098; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=cyHNINQrX0HyoRirhBGd3VHoxG1788Ib/R5yBwqDbvA=; b=f6QtfzdEq0cIqwD56N7rCi1KPRmBhxZPYp6okziHFuFnI9AmvaO5joBoyMtmvhU3e5n2D5HEy9I5TR7jqDcCwv3BjgVEfloY0zINDHYYnBz4+yCgPXfFnpHZWScsnsa/8SozO1DLDB2wLKNWyXysALaDJxdkmElh72DJObZW06k= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762262097111281.32801831448694; Tue, 4 Nov 2025 05:14:57 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGGrQ-0002sG-B1; Tue, 04 Nov 2025 08:14:09 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrL-0002qF-1E for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:03 -0500 Received: from mta-65-227.siemens.flowmailer.net ([185.136.65.227]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrG-0005VQ-TF for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:02 -0500 Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 20251104130351d4476d700200020729 for ; Tue, 04 Nov 2025 14:03:51 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=jan.kiszka@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=cyHNINQrX0HyoRirhBGd3VHoxG1788Ib/R5yBwqDbvA=; b=hgWmtENKHAc5Gb8GGlQt0I6OZwhewHp0Od3FXoyUy1DzjSEpSSy52mmACFSyvSiKvASNuF +VAKkF35OpZEUbgQ7dRLfGLXz5I3cYasyuJ5u0OPndSRYFLogDkcT3G0jzxNK8faPsUFTwL7 nSs1TRJgwiFIU5I6zAOVvnOmhs/GLHuDyQkw5RuU4NvfnQ6GVUhraK36jK7ZXRtTk4X9knz5 JSgrAe/JSFJdwvgvUmdkFGDB0juhjHkG/qyTQIfJG29rGs8QU7/8Ta7DOZzPKUCcWJsxCXM6 NJO/lZyD3UI4rvUg7RFZtx58ndeBFD6QsxIaqJpABR8uGbkZkY/N60nQ==; From: Jan Kiszka To: qemu-devel Cc: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Ilias Apalodimas , =?UTF-8?q?Alex=20Benn=C3=A9e?= , =?UTF-8?q?Jan=20L=C3=BCbbe?= , Jerome Forissier , Warner Losh , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , Joel Stanley , Alistair Francis , Alexander Bulekov Subject: [PATCH v6 1/6] hw/sd/sdcard: Fix size check for backing block image Date: Tue, 4 Nov 2025 14:03:45 +0100 Message-ID: <591f6d8a9dc86428723cb6876df6e717cc41a70e.1762261430.git.jan.kiszka@siemens.com> In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-294854:519-21489:flowmailer Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.136.65.227; envelope-from=fm-294854-20251104130351d4476d700200020729-7qk_u5@rts-flowmailer.siemens.com; helo=mta-65-227.siemens.flowmailer.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity jan.kiszka@siemens.com) X-ZM-MESSAGEID: 1762262101045158500 From: Jan Kiszka Alignment rules apply the the individual partitions (user, boot, later on also RPMB) and depend both on the size of the image and the type of the device. Up to and including 2GB, the power-of-2 rule applies to the user data area. For larger images, multiples of 512 sectors must be used for eMMC and multiples of 512K for SD-cards. Fix the check accordingly and also detect if the image is too small to even hold the boot partitions. Signed-off-by: Jan Kiszka Reviewed-by: C=C3=A9dric Le Goater Reviewed-by: Philippe Mathieu-Daud=C3=A9 Reviewed-by: Warner Losh Tested-by: C=C3=A9dric Le Goater Tested-by: Ilias Apalodimas --- CC: Warner Losh CC: C=C3=A9dric Le Goater CC: Joel Stanley CC: Alistair Francis CC: Alexander Bulekov --- hw/sd/sd.c | 69 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d7a496d77c..76e915e190 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2759,9 +2759,32 @@ static void sd_instance_finalize(Object *obj) timer_free(sd->ocr_power_timer); } =20 +static void sd_blk_size_error(SDState *sd, int64_t blk_size, + int64_t blk_size_aligned, const char *rule, + Error **errp) +{ + const char *dev_type =3D sd_is_emmc(sd) ? "eMMC" : "SD card"; + char *blk_size_str; + + blk_size_str =3D size_to_str(blk_size); + error_setg(errp, "Invalid %s size: %s", dev_type, blk_size_str); + g_free(blk_size_str); + + blk_size_str =3D size_to_str(blk_size_aligned); + error_append_hint(errp, + "%s size has to be %s, e.g. %s.\n" + "You can resize disk images with" + " 'qemu-img resize '\n" + "(note that this will lose data if you make the" + " image smaller than it currently is).\n", + dev_type, rule, blk_size_str); + g_free(blk_size_str); +} + static void sd_realize(DeviceState *dev, Error **errp) { SDState *sd =3D SDMMC_COMMON(dev); + int64_t blk_size =3D -ENOMEDIUM; int ret; =20 switch (sd->spec_version) { @@ -2774,32 +2797,36 @@ static void sd_realize(DeviceState *dev, Error **er= rp) } =20 if (sd->blk) { - int64_t blk_size; - if (!blk_supports_write_perm(sd->blk)) { error_setg(errp, "Cannot use read-only drive as SD card"); return; } =20 blk_size =3D blk_getlength(sd->blk); - if (blk_size > 0 && !is_power_of_2(blk_size)) { - int64_t blk_size_aligned =3D pow2ceil(blk_size); - char *blk_size_str; - - blk_size_str =3D size_to_str(blk_size); - error_setg(errp, "Invalid SD card size: %s", blk_size_str); - g_free(blk_size_str); - - blk_size_str =3D size_to_str(blk_size_aligned); - error_append_hint(errp, - "SD card size has to be a power of 2, e.g. %= s.\n" - "You can resize disk images with" - " 'qemu-img resize '\n" - "(note that this will lose data if you make = the" - " image smaller than it currently is).\n", - blk_size_str); - g_free(blk_size_str); - + } + if (blk_size >=3D 0) { + blk_size -=3D sd->boot_part_size * 2; + if (blk_size > SDSC_MAX_CAPACITY) { + if (sd_is_emmc(sd) && + !QEMU_IS_ALIGNED(blk_size, 1 << HWBLOCK_SHIFT)) { + int64_t blk_size_aligned =3D + ((blk_size >> HWBLOCK_SHIFT) + 1) << HWBLOCK_SHIFT; + sd_blk_size_error(sd, blk_size, blk_size_aligned, + "multiples of 512", errp); + return; + } else if (!sd_is_emmc(sd) && + !QEMU_IS_ALIGNED(blk_size, 512 * KiB)) { + int64_t blk_size_aligned =3D ((blk_size >> 19) + 1) << 19; + sd_blk_size_error(sd, blk_size, blk_size_aligned, + "multiples of 512K", errp); + return; + } + } else if (blk_size > 0 && !is_power_of_2(blk_size)) { + sd_blk_size_error(sd, blk_size, pow2ceil(blk_size), "a power o= f 2", + errp); + return; + } else if (blk_size < 0) { + error_setg(errp, "eMMC image smaller than boot partitions"); return; } =20 @@ -2810,7 +2837,7 @@ static void sd_realize(DeviceState *dev, Error **errp) } blk_set_dev_ops(sd->blk, &sd_block_ops, sd); } - if (sd->boot_part_size % (128 * KiB) || + if (!QEMU_IS_ALIGNED(sd->boot_part_size, 128 * KiB) || sd->boot_part_size > 255 * 128 * KiB) { g_autofree char *size_str =3D size_to_str(sd->boot_part_size); =20 --=20 2.51.0 From nobody Fri Nov 14 16:37:28 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=siemens.com ARC-Seal: i=1; a=rsa-sha256; t=1762262157; cv=none; d=zohomail.com; s=zohoarc; b=fNWQ9npsXoI1GRtWDftU3sjP4CKFEK71HiIIo7KYwBdAsPODPBf8m8D2OkTmXg23RmdawEoWbMGkMRpx58BY6a1+FLGIr9oUSd6ZcTfZ67f3bjSk1lryFR98Y2EgZOX/vgzy8lISRwbmWy8eNZgnKAWfjogEGBkIHkYfipaVcy8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762262157; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Vy09HTh3JYnvcuwY5IAEqh0Cuyp2/EvXPk0j44vcsDg=; b=Tw0uCVbOgvzaSRRTx0X4BwKmbC87qjon1Klf80utQkvmgzd3J1vDoax6rv/OEry6kHe2+zmkt01em2IyuqI2IauH5Ve/5pmggXNA7vm+pbmCWVhV4xrZd/9XmbZNytzKYf23iqueMKDaa6f4QSzLCx+1tOPDgLd6C+HJqOEO7Dg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17622621573771010.0162945029754; Tue, 4 Nov 2025 05:15:57 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGGrT-0002uO-2G; Tue, 04 Nov 2025 08:14:11 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrM-0002qi-0a for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:04 -0500 Received: from mta-65-227.siemens.flowmailer.net ([185.136.65.227]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrG-0005VU-SB for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:03 -0500 Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 202511041303528b9198a251000207db for ; Tue, 04 Nov 2025 14:03:52 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=jan.kiszka@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=Vy09HTh3JYnvcuwY5IAEqh0Cuyp2/EvXPk0j44vcsDg=; b=hGbtOt5y0V7tvI9SnCflQ9iVdDSWpbmTPko1gvz0JPjPf8xQ8EOjBWwHOhloHsvFvssPQw 8wxIrhj63/1TkwmAzLy2PiuMO1ziiWCzRdakkPbHTGd+UBISU+MOhVb87wYF0DRB5PbZnG6q nx9Tp4S7pYm36vaW+dtX60r6T0Q4hEzHhIfTfFcB6ALMREhdG91oKYBQlqXm4Nk0IKCsE/5n sXXE1ctCws8gc05LQBXPPz2Nd634fjvGDgmD/eqhPEKef0BNWg1j/ISXqdVq4ffqkbLrtuXZ afE9aEhK9X7RBduWNUZNEWOb8PG4gWJr9yOJqtt83g6LeeDtr+APHpZw==; From: Jan Kiszka To: qemu-devel Cc: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Ilias Apalodimas , =?UTF-8?q?Alex=20Benn=C3=A9e?= , =?UTF-8?q?Jan=20L=C3=BCbbe?= , Jerome Forissier Subject: [PATCH v6 2/6] hw/sd/sdcard: Allow user-instantiated eMMC Date: Tue, 4 Nov 2025 14:03:46 +0100 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-294854:519-21489:flowmailer Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.136.65.227; envelope-from=fm-294854-202511041303528b9198a251000207db-J5kvXi@rts-flowmailer.siemens.com; helo=mta-65-227.siemens.flowmailer.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity jan.kiszka@siemens.com) X-ZM-MESSAGEID: 1762262161124158500 Content-Type: text/plain; charset="utf-8" From: Jan Kiszka Enable user-instantiation so that eMMCs can be created for PCI-attached SD/MMC host controllers (such as sdhci-pci) on virt machines, for QA purposes for the eMMC model itself and for complex firmware/OS integrations using the upcoming RPMB partition support. Signed-off-by: Jan Kiszka Tested-by: C=C3=A9dric Le Goater Tested-by: Ilias Apalodimas --- hw/sd/sd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 76e915e190..e43e1a10eb 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2930,8 +2930,6 @@ static void emmc_class_init(ObjectClass *klass, const= void *data) dc->desc =3D "eMMC"; dc->realize =3D emmc_realize; device_class_set_props(dc, emmc_properties); - /* Reason: Soldered on board */ - dc->user_creatable =3D false; =20 sc->proto =3D &sd_proto_emmc; =20 --=20 2.51.0 From nobody Fri Nov 14 16:37:28 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=siemens.com ARC-Seal: i=1; a=rsa-sha256; t=1762262148; cv=none; d=zohomail.com; s=zohoarc; b=kZtguOk8tGi1IowV5fCHRnYrBIt0UGQhVzoC2f4MxQgNKfUTvccSI/35OgnSpeOqytgom3kqc/qekFJYyJPtB8vzb3mewGvPZ1cF5rxLJl6/zdky4v1QnZJqOXI4oxMr+02Unnxlkj82++fYbrq9Ny89v92hIh2GX6bp/33ZJyg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762262148; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=75is0p/61m7hsDBcKJ7RJkT/rlOQCimU30xwq72aCQw=; b=l+OtguGCNobhOyN1SVuTCR9eC6EbX3foF7jja0elkbJ795HAOFU+u7KRJK16p812/XWAAvOJ0LvnS5BpIdY72REi7bPKDNT70vRh97AUys74RLFm7Y/42YVXMLJNIXpOer3wJgy+eXfaVLvEMofZDmiI/ED9jD1B466OsCVNZMU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762262148922347.9182363929435; Tue, 4 Nov 2025 05:15:48 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGGrT-0002un-3H; Tue, 04 Nov 2025 08:14:11 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrL-0002qg-L0 for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:04 -0500 Received: from mta-65-226.siemens.flowmailer.net ([185.136.65.226]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrG-0005VT-RX for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:03 -0500 Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 202511041303525cc44cbc2f000207a8 for ; Tue, 04 Nov 2025 14:03:52 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=jan.kiszka@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=75is0p/61m7hsDBcKJ7RJkT/rlOQCimU30xwq72aCQw=; b=CGRHj/eRgbZv/V08nyYWgeuBfu/uXwqEjs+Tu4J+k7ZfqR0oFQOlC/G6dx/sL44gvYcJh5 Ru9B94PPADQUWoscy749+m1PE3Fax8lGm+m4AZCsDjYWtiy1Bdk2XhJ8fNJKP6uOUsfqpRCd ocZiTez+Xj8bSKXWDiCX6x6G8nBLNeEzZLK1T0f9tcySYo4BTMGrTdePJf1KOCZk1p/rx8i4 7qjLnl5WuZ2/TIoBiOP84crvWvwgcj+LjBfscLylSXT1kRB2rotxYgR5JjzPxWHpu3Vn8cQa wUSnMkOtbRfM+jDYyNIDTDcVJ/Pui5XOl4XSMwY/y1ZF++tYXih1UOhg==; From: Jan Kiszka To: qemu-devel Cc: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Ilias Apalodimas , =?UTF-8?q?Alex=20Benn=C3=A9e?= , =?UTF-8?q?Jan=20L=C3=BCbbe?= , Jerome Forissier Subject: [PATCH v6 3/6] hw/sd/sdcard: Add basic support for RPMB partition Date: Tue, 4 Nov 2025 14:03:47 +0100 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-294854:519-21489:flowmailer Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.136.65.226; envelope-from=fm-294854-202511041303525cc44cbc2f000207a8-ZnUcQ5@rts-flowmailer.siemens.com; helo=mta-65-226.siemens.flowmailer.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity jan.kiszka@siemens.com) X-ZM-MESSAGEID: 1762262150967158500 Content-Type: text/plain; charset="utf-8" From: Jan Kiszka The Replay Protected Memory Block (RPMB) is available since eMMC 4.4 which has been obsoleted by 4.41. Therefore lift the provided EXT_CSD_REV to 5 (4.41) and provide the basic logic to implement basic support for it. This allows to set the authentication key, read the write counter and authenticated perform data read and write requests. Those aren't actually authenticated yet, support for that will be added later. The RPMB image needs to be added to backing block images after potential boot partitions and before the user data. It's size is controlled by the rpmb-partition-size property. Also missing in this version (and actually not only for RPMB bits) is persistence of registers that are supposed to survive power cycles. Most prominent are the write counters or the authentication key. This feature can be added later, e.g. by append a state structure to the backing block image. Signed-off-by: Jan Kiszka Reviewed-by: Philippe Mathieu-Daud=C3=A9 Tested-by: C=C3=A9dric Le Goater Tested-by: Ilias Apalodimas --- hw/sd/sd.c | 217 +++++++++++++++++++++++++++++++++++++++-- hw/sd/sdmmc-internal.h | 22 +++++ hw/sd/trace-events | 2 + 3 files changed, 233 insertions(+), 8 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index e43e1a10eb..cee738be11 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -117,6 +117,26 @@ typedef struct SDProto { } cmd[SDMMC_CMD_MAX], acmd[SDMMC_CMD_MAX]; } SDProto; =20 +#define RPMB_STUFF_LEN 196 +#define RPMB_KEY_MAC_LEN 32 +#define RPMB_DATA_LEN 256 /* one RPMB block is half a sector */ +#define RPMB_NONCE_LEN 16 + +typedef struct QEMU_PACKED { + uint8_t stuff_bytes[RPMB_STUFF_LEN]; + uint8_t key_mac[RPMB_KEY_MAC_LEN]; + uint8_t data[RPMB_DATA_LEN]; + uint8_t nonce[RPMB_NONCE_LEN]; + uint32_t write_counter; + uint16_t address; + uint16_t block_count; + uint16_t result; + uint16_t req_resp; +} RPMBDataFrame; + +QEMU_BUILD_BUG_MSG(sizeof(RPMBDataFrame) !=3D 512, + "invalid RPMBDataFrame size"); + struct SDState { DeviceState parent_obj; =20 @@ -140,6 +160,7 @@ struct SDState { =20 uint8_t spec_version; uint64_t boot_part_size; + uint64_t rpmb_part_size; BlockBackend *blk; uint8_t boot_config; =20 @@ -172,6 +193,12 @@ struct SDState { uint32_t data_offset; size_t data_size; uint8_t data[512]; + struct { + uint32_t write_counter; + uint8_t key[RPMB_KEY_MAC_LEN]; + uint8_t key_set; + RPMBDataFrame result; + } rpmb; QEMUTimer *ocr_power_timer; uint8_t dat_lines; bool cmd_line; @@ -506,7 +533,9 @@ static void emmc_set_ext_csd(SDState *sd, uint64_t size) sd->ext_csd[205] =3D 0x46; /* Min read perf for 4bit@26Mhz */ sd->ext_csd[EXT_CSD_CARD_TYPE] =3D 0b11; sd->ext_csd[EXT_CSD_STRUCTURE] =3D 2; - sd->ext_csd[EXT_CSD_REV] =3D 3; + sd->ext_csd[EXT_CSD_REV] =3D 5; + sd->ext_csd[EXT_CSD_RPMB_MULT] =3D sd->rpmb_part_size / (128 * KiB); + sd->ext_csd[EXT_CSD_PARTITION_SUPPORT] =3D 0b111; =20 /* Mode segment (RW) */ sd->ext_csd[EXT_CSD_PART_CONFIG] =3D sd->boot_config; @@ -834,7 +863,8 @@ static uint32_t sd_blk_len(SDState *sd) /* * This requires a disk image that has two boot partitions inserted at the * beginning of it, followed by an RPMB partition. The size of the boot - * partitions is the "boot-partition-size" property. + * partitions is the "boot-partition-size" property, the one of the RPMB + * partition is 'rpmb-partition-size'. */ static uint32_t sd_part_offset(SDState *sd) { @@ -848,11 +878,13 @@ static uint32_t sd_part_offset(SDState *sd) & EXT_CSD_PART_CONFIG_ACC_MASK; switch (partition_access) { case EXT_CSD_PART_CONFIG_ACC_DEFAULT: - return sd->boot_part_size * 2; + return sd->boot_part_size * 2 + sd->rpmb_part_size; case EXT_CSD_PART_CONFIG_ACC_BOOT1: return 0; case EXT_CSD_PART_CONFIG_ACC_BOOT2: return sd->boot_part_size * 1; + case EXT_CSD_PART_CONFIG_ACC_RPMB: + return sd->boot_part_size * 2; default: g_assert_not_reached(); } @@ -891,7 +923,7 @@ static void sd_reset(DeviceState *dev) } size =3D sect << HWBLOCK_SHIFT; if (sd_is_emmc(sd)) { - size -=3D sd->boot_part_size * 2; + size -=3D sd->boot_part_size * 2 + sd->rpmb_part_size; } =20 sect =3D sd_addr_to_wpnum(size) + 1; @@ -979,6 +1011,34 @@ static const VMStateDescription sd_ocr_vmstate =3D { }, }; =20 +static bool vmstate_needed_for_rpmb(void *opaque) +{ + SDState *sd =3D opaque; + + return sd->rpmb_part_size > 0; +} + +static const VMStateDescription emmc_rpmb_vmstate =3D { + .name =3D "sd-card/ext_csd_modes-state", + .version_id =3D 1, + .minimum_version_id =3D 1, + .needed =3D vmstate_needed_for_rpmb, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(rpmb.result.key_mac, SDState, RPMB_KEY_MAC_LEN= ), + VMSTATE_UINT8_ARRAY(rpmb.result.data, SDState, RPMB_DATA_LEN), + VMSTATE_UINT8_ARRAY(rpmb.result.nonce, SDState, RPMB_NONCE_LEN), + VMSTATE_UINT32(rpmb.result.write_counter, SDState), + VMSTATE_UINT16(rpmb.result.address, SDState), + VMSTATE_UINT16(rpmb.result.block_count, SDState), + VMSTATE_UINT16(rpmb.result.result, SDState), + VMSTATE_UINT16(rpmb.result.req_resp, SDState), + VMSTATE_UINT32(rpmb.write_counter, SDState), + VMSTATE_UINT8_ARRAY(rpmb.key, SDState, 32), + VMSTATE_UINT8(rpmb.key_set, SDState), + VMSTATE_END_OF_LIST() + }, +}; + static bool vmstate_needed_for_emmc(void *opaque) { SDState *sd =3D opaque; @@ -1045,6 +1105,7 @@ static const VMStateDescription sd_vmstate =3D { .subsections =3D (const VMStateDescription * const []) { &sd_ocr_vmstate, &emmc_extcsd_vmstate, + &emmc_rpmb_vmstate, NULL }, }; @@ -1067,6 +1128,108 @@ static void sd_blk_write(SDState *sd, uint64_t addr= , uint32_t len) } } =20 +static void emmc_rpmb_blk_read(SDState *sd, uint64_t addr, uint32_t len) +{ + uint16_t resp =3D lduw_be_p(&sd->rpmb.result.req_resp); + uint16_t result =3D lduw_be_p(&sd->rpmb.result.result); + unsigned int curr_block =3D 0; + + if ((result & ~RPMB_RESULT_COUTER_EXPIRED) =3D=3D RPMB_RESULT_OK && + resp =3D=3D RPMB_RESP(RPMB_REQ_AUTH_DATA_READ)) { + curr_block =3D lduw_be_p(&sd->rpmb.result.address); + if (sd->rpmb.result.block_count =3D=3D 0) { + stw_be_p(&sd->rpmb.result.block_count, sd->multi_blk_cnt); + } else { + curr_block +=3D lduw_be_p(&sd->rpmb.result.block_count) - + sd->multi_blk_cnt; + } + addr =3D curr_block * RPMB_DATA_LEN + sd_part_offset(sd); + if (blk_pread(sd->blk, addr, RPMB_DATA_LEN, sd->rpmb.result.data, = 0) < 0) { + error_report("sd_blk_read: read error on host side"); + memset(sd->rpmb.result.data, 0, sizeof(sd->rpmb.result.data)); + stw_be_p(&sd->rpmb.result.result, + RPMB_RESULT_READ_FAILURE | + (result & RPMB_RESULT_COUTER_EXPIRED)); + } + } + memcpy(sd->data, &sd->rpmb.result, sizeof(sd->rpmb.result)); + + trace_sdcard_rpmb_read_block(resp, curr_block, + lduw_be_p(&sd->rpmb.result.result)); +} + +static void emmc_rpmb_blk_write(SDState *sd, uint64_t addr, uint32_t len) +{ + RPMBDataFrame *frame =3D (RPMBDataFrame *)sd->data; + uint16_t req =3D lduw_be_p(&frame->req_resp); + + if (req =3D=3D RPMB_REQ_READ_RESULT) { + /* just return the current result register */ + goto exit; + } + memset(&sd->rpmb.result, 0, sizeof(sd->rpmb.result)); + memcpy(sd->rpmb.result.nonce, frame->nonce, sizeof(sd->rpmb.result.non= ce)); + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_OK); + stw_be_p(&sd->rpmb.result.req_resp, RPMB_RESP(req)); + + if (!sd->rpmb.key_set && req !=3D RPMB_REQ_PROGRAM_AUTH_KEY) { + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_NO_AUTH_KEY); + goto exit; + } + + switch (req) { + case RPMB_REQ_PROGRAM_AUTH_KEY: + if (sd->rpmb.key_set) { + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_WRITE_FAILURE); + break; + } + memcpy(sd->rpmb.key, frame->key_mac, sizeof(sd->rpmb.key)); + sd->rpmb.key_set =3D 1; + break; + case RPMB_REQ_READ_WRITE_COUNTER: + stl_be_p(&sd->rpmb.result.write_counter, sd->rpmb.write_counter); + break; + case RPMB_REQ_AUTH_DATA_WRITE: + /* We only support single-block writes so far */ + if (sd->multi_blk_cnt !=3D 1) { + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_GENERAL_FAILURE); + break; + } + if (sd->rpmb.write_counter =3D=3D 0xffffffff) { + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_WRITE_FAILURE); + break; + } + if (ldl_be_p(&frame->write_counter) !=3D sd->rpmb.write_counter) { + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_COUNTER_FAILURE); + break; + } + sd->rpmb.result.address =3D frame->address; + addr =3D lduw_be_p(&frame->address) * RPMB_DATA_LEN + sd_part_offs= et(sd); + if (blk_pwrite(sd->blk, addr, RPMB_DATA_LEN, frame->data, 0) < 0) { + error_report("sd_blk_write: write error on host side"); + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_WRITE_FAILURE); + } else { + sd->rpmb.write_counter++; + } + stl_be_p(&sd->rpmb.result.write_counter, sd->rpmb.write_counter); + break; + case RPMB_REQ_AUTH_DATA_READ: + sd->rpmb.result.address =3D frame->address; + break; + default: + qemu_log_mask(LOG_UNIMP, "RPMB request %d not implemented\n", req); + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_GENERAL_FAILURE); + break; + } +exit: + if (sd->rpmb.write_counter =3D=3D 0xffffffff) { + stw_be_p(&sd->rpmb.result.result, + lduw_be_p(&sd->rpmb.result.result) | + RPMB_RESULT_COUTER_EXPIRED); + } + trace_sdcard_rpmb_write_block(req, lduw_be_p(&sd->rpmb.result.result)); +} + static void sd_erase(SDState *sd) { uint64_t erase_start =3D sd->erase_start; @@ -1180,6 +1343,19 @@ static void emmc_function_switch(SDState *sd, uint32= _t arg) break; } =20 + if (index =3D=3D EXT_CSD_PART_CONFIG) { + uint8_t part =3D b & EXT_CSD_PART_CONFIG_ACC_MASK; + + if (((part =3D=3D EXT_CSD_PART_CONFIG_ACC_BOOT1 || + part =3D=3D EXT_CSD_PART_CONFIG_ACC_BOOT2) && !sd->boot_part= _size) || + (part =3D=3D EXT_CSD_PART_CONFIG_ACC_RPMB && !sd->rpmb_part_si= ze)) { + qemu_log_mask(LOG_GUEST_ERROR, + "MMC switching to illegal partition\n"); + sd->card_status |=3D R_CSR_SWITCH_ERROR_MASK; + return; + } + } + trace_sdcard_ext_csd_update(index, sd->ext_csd[index], b); sd->ext_csd[index] =3D b; } @@ -2378,6 +2554,7 @@ static bool sd_generic_read_byte(SDState *sd, uint8_t= *value) =20 static void sd_write_byte(SDState *sd, uint8_t value) { + unsigned int partition_access; int i; =20 if (!sd->blk || !blk_is_inserted(sd->blk)) { @@ -2427,7 +2604,13 @@ static void sd_write_byte(SDState *sd, uint8_t value) if (sd->data_offset >=3D sd->blk_len) { /* TODO: Check CRC before committing */ sd->state =3D sd_programming_state; - sd_blk_write(sd, sd->data_start, sd->data_offset); + partition_access =3D sd->ext_csd[EXT_CSD_PART_CONFIG] + & EXT_CSD_PART_CONFIG_ACC_MASK; + if (partition_access =3D=3D EXT_CSD_PART_CONFIG_ACC_RPMB) { + emmc_rpmb_blk_write(sd, sd->data_start, sd->data_offset); + } else { + sd_blk_write(sd, sd->data_start, sd->data_offset); + } sd->blk_written++; sd->data_start +=3D sd->blk_len; sd->data_offset =3D 0; @@ -2510,6 +2693,7 @@ static uint8_t sd_read_byte(SDState *sd) { /* TODO: Append CRCs */ const uint8_t dummy_byte =3D 0x00; + unsigned int partition_access; uint8_t ret; uint32_t io_len; =20 @@ -2553,7 +2737,13 @@ static uint8_t sd_read_byte(SDState *sd) sd->data_start, io_len)) { return dummy_byte; } - sd_blk_read(sd, sd->data_start, io_len); + partition_access =3D sd->ext_csd[EXT_CSD_PART_CONFIG] + & EXT_CSD_PART_CONFIG_ACC_MASK; + if (partition_access =3D=3D EXT_CSD_PART_CONFIG_ACC_RPMB) { + emmc_rpmb_blk_read(sd, sd->data_start, io_len); + } else { + sd_blk_read(sd, sd->data_start, io_len); + } } ret =3D sd->data[sd->data_offset ++]; =20 @@ -2805,7 +2995,7 @@ static void sd_realize(DeviceState *dev, Error **errp) blk_size =3D blk_getlength(sd->blk); } if (blk_size >=3D 0) { - blk_size -=3D sd->boot_part_size * 2; + blk_size -=3D sd->boot_part_size * 2 + sd->rpmb_part_size; if (blk_size > SDSC_MAX_CAPACITY) { if (sd_is_emmc(sd) && !QEMU_IS_ALIGNED(blk_size, 1 << HWBLOCK_SHIFT)) { @@ -2846,13 +3036,23 @@ static void sd_realize(DeviceState *dev, Error **er= rp) "The boot partition size must be multiples of 12= 8K" "and not larger than 32640K.\n"); } + if (!QEMU_IS_ALIGNED(sd->rpmb_part_size, 128 * KiB) || + sd->rpmb_part_size > 128 * 128 * KiB) { + char *size_str =3D size_to_str(sd->boot_part_size); + + error_setg(errp, "Invalid RPMB partition size: %s", size_str); + g_free(size_str); + error_append_hint(errp, + "The RPMB partition size must be multiples of 12= 8K" + "and not larger than 16384K.\n"); + } } =20 static void emmc_realize(DeviceState *dev, Error **errp) { SDState *sd =3D SDMMC_COMMON(dev); =20 - sd->spec_version =3D SD_PHY_SPECv3_01_VERS; /* Actually v4.3 */ + sd->spec_version =3D SD_PHY_SPECv3_01_VERS; /* Actually v4.5 */ =20 sd_realize(dev, errp); } @@ -2869,6 +3069,7 @@ static const Property sd_properties[] =3D { static const Property emmc_properties[] =3D { DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0), DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0), + DEFINE_PROP_UINT64("rpmb-partition-size", SDState, rpmb_part_size, 0), }; =20 static void sdmmc_common_class_init(ObjectClass *klass, const void *data) diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index ce6bc4e6ec..c115f472ef 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -118,9 +118,31 @@ DECLARE_OBJ_CHECKERS(SDState, SDCardClass, SDMMC_COMMO= N, TYPE_SDMMC_COMMON) #define EXT_CSD_PART_CONFIG_ACC_DEFAULT (0x0) #define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x1) #define EXT_CSD_PART_CONFIG_ACC_BOOT2 (0x2) +#define EXT_CSD_PART_CONFIG_ACC_RPMB (0x3) =20 #define EXT_CSD_PART_CONFIG_EN_MASK (0x7 << 3) #define EXT_CSD_PART_CONFIG_EN_BOOT0 (0x1 << 3) #define EXT_CSD_PART_CONFIG_EN_USER (0x7 << 3) =20 +#define RPMB_REQ_PROGRAM_AUTH_KEY (1) +#define RPMB_REQ_READ_WRITE_COUNTER (2) +#define RPMB_REQ_AUTH_DATA_WRITE (3) +#define RPMB_REQ_AUTH_DATA_READ (4) +#define RPMB_REQ_READ_RESULT (5) +#define RPMB_REQ_AUTH_CONFIG_WRITE (6) +#define RPMB_REQ_AUTH_CONFIG_READ (7) + +#define RPMB_RESP(__req__) ((__req__) << 8) + +#define RPMB_RESULT_OK (0) +#define RPMB_RESULT_GENERAL_FAILURE (1) +#define RPMB_RESULT_AUTH_FAILURE (2) +#define RPMB_RESULT_COUNTER_FAILURE (3) +#define RPMB_RESULT_ADDRESS_FAILURE (4) +#define RPMB_RESULT_WRITE_FAILURE (5) +#define RPMB_RESULT_READ_FAILURE (6) +#define RPMB_RESULT_NO_AUTH_KEY (7) + +#define RPMB_RESULT_COUTER_EXPIRED (0x80) + #endif diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 8d49840917..d30daa2143 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -59,6 +59,8 @@ sdcard_read_data(const char *proto, const char *cmd_desc,= uint8_t cmd, uint32_t sdcard_set_voltage(uint16_t millivolts) "%u mV" sdcard_ext_csd_update(unsigned index, uint8_t oval, uint8_t nval) "index %= u: 0x%02x -> 0x%02x" sdcard_switch(unsigned access, unsigned index, unsigned value, unsigned se= t) "SWITCH acc:%u idx:%u val:%u set:%u" +sdcard_rpmb_read_block(uint16_t resp, uint16_t read_addr, uint16_t result)= "resp 0x%x read_addr 0x%x result 0x%x" +sdcard_rpmb_write_block(uint16_t req, uint16_t result) "req 0x%x result 0x= %x" =20 # pl181.c pl181_command_send(uint8_t cmd, uint32_t arg) "sending CMD%02d arg 0x%08" = PRIx32 --=20 2.51.0 From nobody Fri Nov 14 16:37:28 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=siemens.com ARC-Seal: i=1; a=rsa-sha256; t=1762262073; cv=none; d=zohomail.com; s=zohoarc; b=DmtF53CjuuoR7z/eAEn5njEO6yd36oSQPRlKK54xZk/TOGYSnnrPZPt2gL3FhrX1S6N+HUmTSe9JdKnP3lncToHecBVtW3FlcyIfhYHeDyekeiRyFUDxTqdoX+c05kxsHGV2IQfNMwee6VcTFK9BXKPakUA00nqBDPdBMlPh7b4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762262073; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=65LeBGkfIIfi1HTBLnsw67VOKiKIzyfOPCsCx2gtwJI=; b=HEUZjbtZArAILcG2iynE/f2H3DhGQGtUybUs9S0Y3va/2/sOi0uXvehAmhJdHT0JlkTIeAQP58+LIb0yACE7jl01dMbMg2ew46ZcyXM77sfdQ5qtKW5MeAI8GtC4o3IBVyGjrV22aHt3a21g7HEJmMdJQeVhxhaXYjoRYmBlQME= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762262073335140.25449986837964; Tue, 4 Nov 2025 05:14:33 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGGrQ-0002sR-Bu; Tue, 04 Nov 2025 08:14:09 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrK-0002q7-Pa for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:02 -0500 Received: from mta-64-226.siemens.flowmailer.net ([185.136.64.226]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrG-0005VV-Rf for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:02 -0500 Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 202511041303533eeb05fe5500020700 for ; Tue, 04 Nov 2025 14:03:53 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=jan.kiszka@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=65LeBGkfIIfi1HTBLnsw67VOKiKIzyfOPCsCx2gtwJI=; b=lkOhyVyDoxDo/Afltpc+RoitISLlz2Nue57uyUsaJzYpRmH1jkYBpdk1tWE2Nb9gRqu4M9 QvmXCaKEGeqyj68J0NVyEtuCoUvs8x4mgGSgnAP9SegECR9VFgFBYkkVzGX/EDSMGS2Ea0CG YfdwFPaefB37dstAyxtYgCv4sawlKibNuBe+CG5N+UCUd1sYdOe9GMim0fOMn/R8++z8doYJ Uthb7+XoC5FjGOx7iIVFn6DfrB3CkEU/1efaBXQSVqr+PDkw0uGL2Zz45tzRsLCozWfWn1V9 dTQW/kgq3kdscBL7Ro5SJqtRBJzyfHUKs3b8sgT792WJqJM5dI8ncWVg==; From: Jan Kiszka To: qemu-devel Cc: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Ilias Apalodimas , =?UTF-8?q?Alex=20Benn=C3=A9e?= , =?UTF-8?q?Jan=20L=C3=BCbbe?= , Jerome Forissier Subject: [PATCH v6 4/6] hw/sd/sdcard: Handle RPMB MAC field Date: Tue, 4 Nov 2025 14:03:48 +0100 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-294854:519-21489:flowmailer Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.136.64.226; envelope-from=fm-294854-202511041303533eeb05fe5500020700-tN64DA@rts-flowmailer.siemens.com; helo=mta-64-226.siemens.flowmailer.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity jan.kiszka@siemens.com) X-ZM-MESSAGEID: 1762262078486158500 Content-Type: text/plain; charset="utf-8" From: Jan Kiszka Implement correct setting of the MAC field when passing RPMB frames back to the guest. Also check the MAC on authenticated write requests. This depends on HMAC support for QCRYPTO_HASH_ALGO_SHA256 which is always available via glib - assert this, just to be safe. Signed-off-by: Jan Kiszka Reviewed-by: Philippe Mathieu-Daud=C3=A9 Tested-by: C=C3=A9dric Le Goater Tested-by: Ilias Apalodimas --- hw/sd/sd.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index cee738be11..61754bf108 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -51,6 +51,7 @@ #include "qemu/module.h" #include "sdmmc-internal.h" #include "trace.h" +#include "crypto/hmac.h" =20 //#define DEBUG_SD 1 =20 @@ -121,6 +122,7 @@ typedef struct SDProto { #define RPMB_KEY_MAC_LEN 32 #define RPMB_DATA_LEN 256 /* one RPMB block is half a sector */ #define RPMB_NONCE_LEN 16 +#define RPMB_HASH_LEN 284 =20 typedef struct QEMU_PACKED { uint8_t stuff_bytes[RPMB_STUFF_LEN]; @@ -1128,6 +1130,68 @@ static void sd_blk_write(SDState *sd, uint64_t addr,= uint32_t len) } } =20 +static bool rpmb_calc_hmac(SDState *sd, const RPMBDataFrame *frame, + unsigned int num_blocks, uint8_t *mac) +{ + g_autoptr(QCryptoHmac) hmac =3D NULL; + size_t mac_len =3D RPMB_KEY_MAC_LEN; + bool success =3D true; + Error *err =3D NULL; + uint64_t offset; + + hmac =3D qcrypto_hmac_new(QCRYPTO_HASH_ALGO_SHA256, sd->rpmb.key, + RPMB_KEY_MAC_LEN, &err); + if (!hmac) { + error_report_err(err); + return false; + } + + /* + * This implies a read request because we only support single-block wr= ite + * requests so far. + */ + if (num_blocks > 1) { + /* + * Unfortunately, the underlying crypto libraries do not allow us = to + * migrate an active QCryptoHmac state. Therefore, we have to calc= ulate + * the HMAC in one run. To avoid buffering a complete read sequenc= e in + * SDState, reconstruct all frames except for the last one. + */ + void *buf =3D sd->data; + + assert(RPMB_HASH_LEN <=3D sizeof(sd->data)); + + memcpy((uint8_t *)buf + RPMB_DATA_LEN, &frame->data[RPMB_DATA_LEN], + RPMB_HASH_LEN - RPMB_DATA_LEN); + offset =3D lduw_be_p(&frame->address) * RPMB_DATA_LEN + + sd_part_offset(sd); + do { + if (blk_pread(sd->blk, offset, RPMB_DATA_LEN, buf, 0) < 0) { + error_report("sd_blk_read: read error on host side"); + success =3D false; + break; + } + if (qcrypto_hmac_bytes(hmac, buf, RPMB_HASH_LEN, NULL, NULL, + &err) < 0) { + error_report_err(err); + success =3D false; + break; + } + offset +=3D RPMB_DATA_LEN; + } while (--num_blocks > 1); + } + + if (success && + qcrypto_hmac_bytes(hmac, frame->data, RPMB_HASH_LEN, &mac, + &mac_len, &err) < 0) { + error_report_err(err); + success =3D false; + } + assert(!success || mac_len =3D=3D RPMB_KEY_MAC_LEN); + + return success; +} + static void emmc_rpmb_blk_read(SDState *sd, uint64_t addr, uint32_t len) { uint16_t resp =3D lduw_be_p(&sd->rpmb.result.req_resp); @@ -1151,6 +1215,17 @@ static void emmc_rpmb_blk_read(SDState *sd, uint64_t= addr, uint32_t len) RPMB_RESULT_READ_FAILURE | (result & RPMB_RESULT_COUTER_EXPIRED)); } + if (sd->multi_blk_cnt =3D=3D 1 && + !rpmb_calc_hmac(sd, &sd->rpmb.result, + lduw_be_p(&sd->rpmb.result.block_count), + sd->rpmb.result.key_mac)) { + memset(sd->rpmb.result.data, 0, sizeof(sd->rpmb.result.data)); + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_AUTH_FAILURE); + } + } else if (!rpmb_calc_hmac(sd, &sd->rpmb.result, 1, + sd->rpmb.result.key_mac)) { + memset(sd->rpmb.result.data, 0, sizeof(sd->rpmb.result.data)); + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_AUTH_FAILURE); } memcpy(sd->data, &sd->rpmb.result, sizeof(sd->rpmb.result)); =20 @@ -1162,6 +1237,7 @@ static void emmc_rpmb_blk_write(SDState *sd, uint64_t= addr, uint32_t len) { RPMBDataFrame *frame =3D (RPMBDataFrame *)sd->data; uint16_t req =3D lduw_be_p(&frame->req_resp); + uint8_t mac[RPMB_KEY_MAC_LEN]; =20 if (req =3D=3D RPMB_REQ_READ_RESULT) { /* just return the current result register */ @@ -1199,6 +1275,11 @@ static void emmc_rpmb_blk_write(SDState *sd, uint64_= t addr, uint32_t len) stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_WRITE_FAILURE); break; } + if (!rpmb_calc_hmac(sd, frame, 1, mac) || + memcmp(frame->key_mac, mac, RPMB_KEY_MAC_LEN) !=3D 0) { + stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_AUTH_FAILURE); + break; + } if (ldl_be_p(&frame->write_counter) !=3D sd->rpmb.write_counter) { stw_be_p(&sd->rpmb.result.result, RPMB_RESULT_COUNTER_FAILURE); break; @@ -3128,6 +3209,8 @@ static void emmc_class_init(ObjectClass *klass, const= void *data) DeviceClass *dc =3D DEVICE_CLASS(klass); SDCardClass *sc =3D SDMMC_COMMON_CLASS(klass); =20 + assert(qcrypto_hmac_supports(QCRYPTO_HASH_ALGO_SHA256)); + dc->desc =3D "eMMC"; dc->realize =3D emmc_realize; device_class_set_props(dc, emmc_properties); --=20 2.51.0 From nobody Fri Nov 14 16:37:28 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=siemens.com ARC-Seal: i=1; a=rsa-sha256; t=1762262135; cv=none; d=zohomail.com; s=zohoarc; b=NrRz4ZQolgdJ9trmelumT/QT87yVQsa5j9AwF5+++clGsEdfb/NSOuHO9YPnoHw5pq/trnP+erQwLZWOEUEtBXFE1+Lygq41zjLzx9jsMeecQnU/IpjqlFragPANb0IgIDcW9+HRHNdkV11s0EeH3ihE64Zb4rzW5QoJ35bZTFc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762262135; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=qzuERtfVpQ9KnPl0uoWW5kiXEgutyokNa0zOFc5427o=; b=KpZPwZAnNJ1tlytwgAWwMJfJx0D9HAs6asHCledwKvrbh+9QUgmjeFXkevOYeV1lbA6jKPVNFWlOHKrgxb/+/ugLPlZgFmdHwUR1cHMhDoQZYoUYPiNFmH4s55VkrYfdf+EHhjMDY8cUqYblWfVmmfL4KtxRNTlnEMFbqzAyDwI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762262135556132.39929486031303; Tue, 4 Nov 2025 05:15:35 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGGrV-0002vQ-Oy; Tue, 04 Nov 2025 08:14:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrO-0002r0-2X for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:06 -0500 Received: from mta-64-226.siemens.flowmailer.net ([185.136.64.226]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrK-0005VP-CN for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:05 -0500 Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 2025110413035384b4b423dd000207ab for ; Tue, 04 Nov 2025 14:03:53 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=jan.kiszka@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=qzuERtfVpQ9KnPl0uoWW5kiXEgutyokNa0zOFc5427o=; b=STKrJ61wCkrcrPEsvj3pBU0SeZeh+Nj1RBJZKw2b/hWf0Ja1WqbSZ48ug2CL2DmNfBi4t2 VaBPxzYwyKFMAg/USaGiCekzzx1p6vYXV5XrJFiQtOLCPZj4lBEVkQFTCt7QQaARnTk3EHSB SBgYBUQj1OaXDPi+VubeOuK8zbI7IUfHtK9PiWzGTEuImL9y8xH1vz5Ev2aop65azIGENpCq xumvKDxLVg3kBwu01mGMokMdZT+usoU/bHpWZ7YXdUleUv/gvxzoY5lahJsyTJ79sXzhiizj gwQwnSVgrWUuKh7a88edDjCktFqZrZWQ5hunqEF+yl91f0C6oFpv9mcQ==; From: Jan Kiszka To: qemu-devel Cc: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Ilias Apalodimas , =?UTF-8?q?Alex=20Benn=C3=A9e?= , =?UTF-8?q?Jan=20L=C3=BCbbe?= , Jerome Forissier Subject: [PATCH v6 5/6] scripts: Add helper script to generate eMMC block device images Date: Tue, 4 Nov 2025 14:03:49 +0100 Message-ID: <60f265e0b2bea18fe6ef41742600a7e933e3c47f.1762261430.git.jan.kiszka@siemens.com> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-294854:519-21489:flowmailer Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.136.64.226; envelope-from=fm-294854-2025110413035384b4b423dd000207ab-4whYDc@rts-flowmailer.siemens.com; helo=mta-64-226.siemens.flowmailer.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity jan.kiszka@siemens.com) X-ZM-MESSAGEID: 1762262138783158500 Content-Type: text/plain; charset="utf-8" From: Jan Kiszka As an eMMC block device image may consist of more than just the user data partition, provide a helper script that can compose the image from boot partitions, an RPMB partition and the user data image. The script also does the required size validation and/or rounding. Signed-off-by: Jan Kiszka Tested-by: C=C3=A9dric Le Goater Tested-by: Ilias Apalodimas --- scripts/mkemmc.sh | 219 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100755 scripts/mkemmc.sh diff --git a/scripts/mkemmc.sh b/scripts/mkemmc.sh new file mode 100755 index 0000000000..558548ffc7 --- /dev/null +++ b/scripts/mkemmc.sh @@ -0,0 +1,219 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-only +# +# Create eMMC block device image from boot, RPMB and user data images +# +# Copyright (c) Siemens, 2025 +# +# Authors: +# Jan Kiszka +# +# This work is licensed under the terms of the GNU GPL version 2. +# See the COPYING file in the top-level directory. +# + +usage() { + echo "$0 [OPTIONS] USER_IMG[:SIZE] OUTPUT_IMG" + echo "" + echo "SIZE must be a power of 2 up to 2G and multiples of 512 byte fro= m there on." + echo "If no SIZE is specified, the size of USER_ING will be used (roun= ded up)." + echo "" + echo "Supported options:" + echo " -b BOOT1_IMG[:SIZE] Add boot partitions. SIZE must be multip= les of 128K. If" + echo " no SIZE is specified, the size of BOOT= 1_IMG will be" + echo " used (rounded up). BOOT1_IMG will be s= tored in boot" + echo " partition 1, and a boot partition 2 of= the same size" + echo " will be created as empty (all zeros) u= nless -B is" + echo " specified as well." + echo " -B BOOT2_IMG Fill boot partition 2 with BOOT2_IMG. Mu= st be combined" + echo " with -b which is also defining the par= tition size." + echo " -r RPMB_IMG[:SIZE] Add RPMB partition. SIZE must be multipl= es of 128K. If" + echo " no SIZE is specified, the size of RPMB= _IMG will be" + echo " used (rounded up)." + echo " -h, --help This help" + echo "" + echo "All SIZE parameters support the units K, M, G. If SIZE is smalle= r than the" + echo "associated image, it will be truncated in the output image." + exit "$1" +} + +process_size() { + name=3D$1 + image_file=3D$2 + alignment=3D$3 + image_arg=3D$4 + if [ "${image_arg#*:}" =3D "$image_arg" ]; then + if ! size=3D$(stat -L -c %s "$image_file" 2>/dev/null); then + echo "Missing $name image '$image_file'." >&2 + exit 1 + fi + if [ "$alignment" =3D 128 ]; then + size=3D$(( (size + 128 * 1024 - 1) & ~(128 * 1024 - 1) )) + elif [ $size -gt $((2 * 1024 * 1024 * 1024)) ]; then + size=3D$(( (size + 511) & ~511 )) + elif [ $(( size & (size - 1) )) -gt 0 ]; then + n=3D0 + while [ "$size" -gt 0 ]; do + size=3D$((size >> 1)) + n=3D$((n + 1)) + done + size=3D$((1 << n)) + fi + else + value=3D"${image_arg#*:}" + if [ "${value%K}" !=3D "$value" ]; then + size=3D${value%K} + multiplier=3D1024 + elif [ "${value%M}" !=3D "$value" ]; then + size=3D${value%M} + multiplier=3D$((1024 * 1024)) + elif [ "${value%G}" !=3D "$value" ]; then + size=3D${value%G} + multiplier=3D$((1024 * 1024 * 1024)) + else + size=3D$value + multiplier=3D1 + fi + # check if "$size" is a valid integer by doing a self-comparison + if [ "$size" -eq "$size" ] 2>/dev/null; then + size=3D$((size * multiplier)) + else + echo "Invalid value '$value' specified for $image_file image s= ize." >&2 + exit 1 + fi + if [ "$alignment" =3D 128 ]; then + if [ $(( size & (128 * 1024 - 1) )) -ne 0 ]; then + echo "The $name image size must be multiples of 128K." >&2 + exit 1 + fi + elif [ $size -gt $((2 * 1024 * 1024 * 1024)) ]; then + if [ $(( size & 511)) -ne 0 ]; then + echo "The $name image size must be multiples of 512 (if >2= G)." >&2 + exit 1 + fi + elif [ $(( size & (size - 1) )) -gt 0 ]; then + echo "The $name image size must be power of 2 (up to 2G)." >&2 + exit 1 + fi + fi + echo $size +} + +check_truncation() { + image_file=3D$1 + output_size=3D$2 + if [ "$image_file" =3D "/dev/zero" ]; then + return + fi + if ! actual_size=3D$(stat -L -c %s "$image_file" 2>/dev/null); then + echo "Missing image '$image_file'." >&2 + exit 1 + fi + if [ "$actual_size" -gt "$output_size" ]; then + echo "Warning: image '$image_file' will be truncated on output." + fi +} + +userimg=3D +outimg=3D +bootimg1=3D +bootimg2=3D/dev/zero +bootsz=3D0 +rpmbimg=3D +rpmbsz=3D0 + +while [ $# -gt 0 ]; do + case "$1" in + -b) + shift + [ $# -ge 1 ] || usage 1 + bootimg1=3D${1%%:*} + bootsz=3D$(process_size boot "$bootimg1" 128 "$1") + shift + ;; + -B) + shift + [ $# -ge 1 ] || usage 1 + bootimg2=3D$1 + shift + ;; + -r) + shift + [ $# -ge 1 ] || usage 1 + rpmbimg=3D${1%%:*} + rpmbsz=3D$(process_size RPMB "$rpmbimg" 128 "$1") + shift + ;; + -h|--help) + usage 0 + ;; + *) + if [ -z "$userimg" ]; then + userimg=3D${1%%:*} + usersz=3D$(process_size user "$userimg" U "$1") + elif [ -z "$outimg" ]; then + outimg=3D$1 + else + usage 1 + fi + shift + ;; + esac +done + +[ -n "$outimg" ] || usage 1 + +if [ "$bootsz" -gt $((32640 * 1024)) ]; then + echo "Boot image size is larger than 32640K." >&2 + exit 1 +fi +if [ "$rpmbsz" -gt $((16384 * 1024)) ]; then + echo "RPMB image size is larger than 16384K." >&2 + exit 1 +fi + +echo "Creating eMMC image" + +truncate -s 0 "$outimg" +pos=3D0 + +if [ "$bootsz" -gt 0 ]; then + echo " Boot partition 1 and 2: $((bootsz / 1024))K each" + blocks=3D$(( bootsz / (128 * 1024) )) + check_truncation "$bootimg1" "$bootsz" + dd if=3D"$bootimg1" of=3D"$outimg" conv=3Dsparse bs=3D128K count=3D$bl= ocks \ + status=3Dnone + check_truncation "$bootimg2" "$bootsz" + dd if=3D"$bootimg2" of=3D"$outimg" conv=3Dsparse bs=3D128K count=3D$bl= ocks \ + seek=3D$blocks status=3Dnone + pos=3D$((2 * bootsz)) +fi + +if [ "$rpmbsz" -gt 0 ]; then + echo " RPMB partition: $((rpmbsz / 1024))K" + blocks=3D$(( rpmbsz / (128 * 1024) )) + check_truncation "$rpmbimg" "$rpmbsz" + dd if=3D"$rpmbimg" of=3D"$outimg" conv=3Dsparse bs=3D128K count=3D$blo= cks \ + seek=3D$(( pos / (128 * 1024) )) status=3Dnone + pos=3D$((pos + rpmbsz)) +fi + +if [ "$usersz" -lt 1024 ]; then + echo " User data: $usersz bytes" +elif [ "$usersz" -lt $((1024 * 1024)) ]; then + echo " User data: $(( (usersz + 1023) / 1024 ))K ($use= rsz)" +elif [ "$usersz" -lt $((1024 * 1024 * 1024)) ]; then + echo " User data: $(( (usersz + 1048575) / 1048576))M = ($usersz)" +else + echo " User data: $(( (usersz + 1073741823) / 10737418= 24))G ($usersz)" +fi +check_truncation "$userimg" "$usersz" +dd if=3D"$userimg" of=3D"$outimg" conv=3Dsparse bs=3D128K seek=3D$(( pos /= (128 * 1024) )) \ + count=3D$(( (usersz + 128 * 1024 - 1) / (128 * 1024) )) status=3Dnone +pos=3D$((pos + usersz)) +truncate -s $pos "$outimg" + +echo "" +echo "Instantiate by appending to the qemu command line:" +echo " -drive file=3D$outimg,if=3Dnone,format=3Draw,id=3Demmc-img" +echo " -device emmc,boot-partition-size=3D$bootsz,rpmb-partition-size=3D$= rpmbsz,drive=3Demmc-img" --=20 2.51.0 From nobody Fri Nov 14 16:37:28 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=siemens.com ARC-Seal: i=1; a=rsa-sha256; t=1762262107; cv=none; d=zohomail.com; s=zohoarc; b=ibR6TbQP89djeBdJYVJegEly26EN+vrzCs52s8YTs/FCLC/NeuaWgQykC+5Y+ysoIyJrr0bzVa8IejgrS+mpL8CM9JBReI8kZOV8Shw1ML/7oq9sjYiLdW8CfTMKcRb41hxTEhxVhnvzqpgLWMkW/n4U1IxpaHEke6cKztdq08A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762262107; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Aa16IkTRH9wGRMcu8vItbojNNj/gEEQnYnW57vAlPSA=; b=NehDD3GEf+SIXlm/p/D5E7bI0JIG9KelyDqYObuZFePti8OyxtSPa3l6xZpf8YpYwgMxuYMUkYmoaJ6m//HNkJdG48hJG6rweyl9EzZ/cqX24j5YKKa/Ot/xru+OsnDXRaMzHgoqPjVClO++i3i0pcATM2iTZXM665Adj36Us7s= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=jan.kiszka@siemens.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762262107842553.4567254290788; Tue, 4 Nov 2025 05:15:07 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGGrU-0002vK-2q; Tue, 04 Nov 2025 08:14:12 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrN-0002qw-D9 for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:06 -0500 Received: from mta-65-226.siemens.flowmailer.net ([185.136.65.226]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGGrG-0005VS-S0 for qemu-devel@nongnu.org; Tue, 04 Nov 2025 08:14:05 -0500 Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 2025110413035315aaa1fd07000207fc for ; Tue, 04 Nov 2025 14:03:53 +0100 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm1; d=siemens.com; i=jan.kiszka@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=Aa16IkTRH9wGRMcu8vItbojNNj/gEEQnYnW57vAlPSA=; b=H3l6VBrh1DY426vkI8yN4v8MnXz9awskKkbmP5DjNV+gy6cueUMXbNdT/IsjfjTOLR3FZM GNiu5O2i5WMI8hEMe1yBJ05Al60TcfwVJ+MPsb9/cVLJD5DU0CKL/cpyxYrTYnFETAWXV9oS kGFA5QattwXCXRXT9/jqXptRcfxPsMqwq17tyi99AJ3xwCpwXV3bqaiUtzWnSewb44OXEHzX h1biCcd0AsUgJZTfegmAt8GIAgFHDL+1kGvQ52FNOdPcQyhXLIxzk/5G5HJ7OE14k+QskqxD QdigAU77+yDv9JurU2+56JgC0ZVrItMnUHItNy4W5tdzT0rtsptIvVqQ==; From: Jan Kiszka To: qemu-devel Cc: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Bin Meng , qemu-block@nongnu.org, Ilias Apalodimas , =?UTF-8?q?Alex=20Benn=C3=A9e?= , =?UTF-8?q?Jan=20L=C3=BCbbe?= , Jerome Forissier Subject: [PATCH v6 6/6] docs: Add eMMC device model description Date: Tue, 4 Nov 2025 14:03:50 +0100 Message-ID: In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-294854:519-21489:flowmailer Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.136.65.226; envelope-from=fm-294854-2025110413035315aaa1fd07000207fc-ELuPtv@rts-flowmailer.siemens.com; helo=mta-65-226.siemens.flowmailer.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity jan.kiszka@siemens.com) X-ZM-MESSAGEID: 1762262110733158500 From: Jan Kiszka Signed-off-by: Jan Kiszka Reviewed-by: Alex Benn=C3=A9e Reviewed-by: Philippe Mathieu-Daud=C3=A9 Tested-by: C=C3=A9dric Le Goater Tested-by: Ilias Apalodimas --- docs/system/device-emulation.rst | 1 + docs/system/devices/emmc.rst | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 docs/system/devices/emmc.rst diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulatio= n.rst index db714ad47a..971325527a 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -88,6 +88,7 @@ Emulated Devices devices/canokey.rst devices/ccid.rst devices/cxl.rst + devices/emmc.rst devices/igb.rst devices/ivshmem-flat.rst devices/ivshmem.rst diff --git a/docs/system/devices/emmc.rst b/docs/system/devices/emmc.rst new file mode 100644 index 0000000000..7e15b62270 --- /dev/null +++ b/docs/system/devices/emmc.rst @@ -0,0 +1,53 @@ +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +eMMC Emulation +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Besides SD card emulation, QEMU also offers an eMMC model as found on many +embedded boards. An eMMC, just like an SD card, is connected to the machine +via an SDHCI controller. + +Create eMMC Images +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +A recent eMMC consists of 4 partitions: 2 boot partitions, 1 Replay protec= ted +Memory Block (RPMB), and the user data area. QEMU expects backing images f= or +the eMMC to contain those partitions concatenated in exactly that order. +However, the boot partitions as well as the RPMB might be absent if their = sizes +are configured to zero. + +The eMMC specification defines alignment constraints for the partitions. T= he +two boot partitions must be of the same size. Furthermore, boot and RPMB +partitions must be multiples of 128 KB with a maximum of 32640 KB for each +boot partition and 16384K for the RPMB partition. + +The alignment constrain of the user data area depends on its size. Up to 2 +GByte, the size must be a power of 2. From 2 GByte onward, the size has to= be +multiples of 512 byte. + +QEMU is enforcing those alignment rules before instantiating the device. +Therefore, the provided image has to strictly follow them as well. The hel= per +script ``scripts/mkemmc.sh`` can be used to create compliant images, with = or +without pre-filled partitions. E.g., to create an eMMC image from a firmwa= re +image and an OS image with an empty 2 MByte RPMB, use the following comman= d: + +.. code-block:: console + + scripts/mkemmc.sh -b firmware.img -r /dev/zero:2MB os.img emmc.img + +This will take care of rounding up the partition sizes to the next valid v= alue +and will leave the RPMB and the second boot partition empty (zeroed). + +Adding eMMC Devices +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +An eMMC is either automatically created by a machine model (e.g. Aspeed bo= ards) +or can be user-created when using a PCI-attached SDHCI controller. To +instantiate the eMMC image from the example above in a machine without oth= er +SDHCI controllers while assuming that the firmware needs a boot partitions= of +1 MB, use the following options: + +.. code-block:: console + + -drive file=3Demmc.img,if=3Dnone,format=3Draw,id=3Demmc-img + -device sdhci-pci + -device emmc,drive=3Demmc-img,boot-partition-size=3D1048576,rpmb-parti= tion-size=3D2097152 --=20 2.51.0