From patchwork Mon Mar 10 09:35:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 58541 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 22C74C28B30 for ; Mon, 10 Mar 2025 09:37:06 +0000 (UTC) Received: from mail-wr1-f51.google.com (mail-wr1-f51.google.com [209.85.221.51]) by mx.groups.io with SMTP id smtpd.web10.33757.1741599421977782903 for ; Mon, 10 Mar 2025 02:37:02 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=Fev3Tmqb; spf=pass (domain: gmail.com, ip: 209.85.221.51, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wr1-f51.google.com with SMTP id ffacd0b85a97d-39141ffa9fcso1018397f8f.0 for ; Mon, 10 Mar 2025 02:37:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741599420; x=1742204220; darn=lists.openembedded.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=2cV1KZvQcLmft8s8Flz+qvXl+PgT+4/juUwwBlFFDi0=; b=Fev3TmqbTn95+StjF9dRblcApxtpKRIK4AJAy+42/HHMddRVmv3guE1aZxQQ720QVd IgxRzitDBbtB7EaWOYQAbWoP/R4FQg9Wtdyg4fn0V80i0LrRE6KIRuG/vGfA7u5epHC8 hR1vkqjC8x+Zt2YMqjX6DeIiKnEY6WOPyFcG9R11uHHDZCCcKJKzppdLHMbqWn7cwEwW aiU4H1IOIxxbYoQie9CRMmbvlUrYEUKdnS1nmIQ5IdKkXiHgZiY5Xe/Jgbjh50qIgSyn 9LTgMuyviizvV80xEdPg4+fGGiVRnBBjtkpxex+Y/eq0Q6lLqmdnLxHwrYvVb5wKwpBd juWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741599420; x=1742204220; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2cV1KZvQcLmft8s8Flz+qvXl+PgT+4/juUwwBlFFDi0=; b=WnOHjNgBrYYYUKIvtF0zT9Bc3jxICdXy+w8Mm4ZTNWr4d25Cve1oYziWeGnktLdydx CJP7WXw/AIjJUvZOaBTcXFrKujsSV4quae/5dGwchtWp8WJ+DOi+pvCo1IDB1+NB7Xgs esCbPWK1UASp6sS+KpCb6gnqCDGKWJunsTeALlMwjkXcsZAXXBtOyzksOe2ld8hT8UX+ 1vf5xqswxrHd3vu/cD8hqiSsXDv73c4c6gJWBrtXrZH7jTH8kYorbF/ZL3puL8wS+vg1 PyINibHvVea5OybQqg9LQVSz9XVh5rzAgx90/dQxeYVBn56TVY4HsR/zcHEVZEkou5lk YeiQ== X-Gm-Message-State: AOJu0YxkGoU/bg8GU75mMs/gCaPguKHgImWZEra4rz5NjirUW5FsmWHF dRzE8rji2Bt/s3e/zFhCUMVFPpHKSvSo/Y+yTgDXpfb/YJSR6wrAX0LfVQ== X-Gm-Gg: ASbGnctXq8Bh3mJW/U5aA3iWrkt0tU7D1v5djXRyI8TSmhrN99c8WiTxHnkhEyF/+o9 VZxQ2dYvJW0pAoywnZQHHCnpy410Rxz5LWB9gjaW/ogTFpkuPRcrLQeLDdgvDJqBnPUgFc+fA9x PV0m8lVzgZUXlUs3ncdslhpoEb2jIRPAUKaDC9ympkverpl4tHkuv1jcr5gNLolOFo9b0791yit c4aBmuspgjr7TV0iLtmvKBChpj+xIYBhf4qoW8uHmnSjpio8hI5Hjln0G3gVcTwcmFOJHnFX22j SCREl43wMZ+4MaNpurMG8nv8/qxI0CUWicMuhXN9hu/Ga9LYQQhMNU4oHurMvOtzofs= X-Google-Smtp-Source: AGHT+IEKJHH4fw1ybJ6WT/zvW1Ij34DNPzidCphapaeGfM+lC85GJgJKdwvu7S64YfvsExRu1BJpHQ== X-Received: by 2002:a5d:6d8a:0:b0:390:ff25:79c8 with SMTP id ffacd0b85a97d-39132d292e3mr8125831f8f.20.1741599420072; Mon, 10 Mar 2025 02:37:00 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3912bfb79fbsm14554454f8f.13.2025.03.10.02.36.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 02:36:59 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 1/7] oe-selftest: drop redundant imports Date: Mon, 10 Mar 2025 10:35:43 +0100 Message-ID: <20250310093641.1983560-2-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250310093641.1983560-1-adrian.freihofer@siemens.com> References: <20250310093641.1983560-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 10 Mar 2025 09:37:06 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/212497 Signed-off-by: Adrian Freihofer --- scripts/oe-selftest | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/oe-selftest b/scripts/oe-selftest index 18ac0f5869c..afc48d9905c 100755 --- a/scripts/oe-selftest +++ b/scripts/oe-selftest @@ -18,8 +18,6 @@ import os import sys -import argparse -import logging scripts_path = os.path.dirname(os.path.realpath(__file__)) lib_path = scripts_path + '/lib' From patchwork Mon Mar 10 09:35:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 58539 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 12153C35FF3 for ; Mon, 10 Mar 2025 09:37:06 +0000 (UTC) Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) by mx.groups.io with SMTP id smtpd.web10.33759.1741599422595402946 for ; Mon, 10 Mar 2025 02:37:02 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=aCg2YDYa; spf=pass (domain: gmail.com, ip: 209.85.128.44, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-43bc4b16135so22747605e9.1 for ; Mon, 10 Mar 2025 02:37:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741599421; x=1742204221; darn=lists.openembedded.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=4I4KIMMiKKqldH8nrGzmHRpYjOhYLNHgXI02WSZw5Dg=; b=aCg2YDYa033dCWgAbjCaHqWJdGjL3wQtu9gYem7im6QHCwhhfjwEroW7yRAg+K+H0+ DPlx/6VRqzAifbKcnFqKbiCqnqpvxmryHREHskrnxrQ04Fk1J8BaYnSFihq3iReubOCh rbklfdPxKrt7J/YQCmwhpbxeOC+vWiD2Yya5m8NiPB6a+ZCh7Nyv6v51fjRng41pMBHB fpHCD8tQa1yBKY6fQYAH5y7EMyRzMbwI60YOkAD4e92DmqgJJqhZNp2ZRKbqbpGjXApW vAe8hkMSkoc+7z4O8nfmtKYCpoO1RF+RC/vFGL3NAY9tCILRcaZ4vBltJdcedrx3Ltej H1gA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741599421; x=1742204221; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4I4KIMMiKKqldH8nrGzmHRpYjOhYLNHgXI02WSZw5Dg=; b=vNkqlN9P4IEyHXQdTgV1Yn/soB82IKZmBPT9QP/+RAga0QFUlEJlaoQYIl1WxRmzbC IOWCl3coIfVE0xRHK7f3EAlGo0U7pzmoaQh7F1aLH/ji0fuQyCJHvH5/0ZC65DIdpTl7 H5CsmJFP6zso0NECvAJbYlKTgF0fW70HKyDQ2x8LLE2N/5K1CWHC4ErGI9I5ZOqiQ1cS YeauSmdA2QsZ5Gc64e+7Z+PhlMzDc/jsrLN9ZclyXTw6YYVD9h3qNXG1fkrLwd0l78Wu Tsy8EU9lyr0vZ0S4OVDEWsr6wcDljklPWvab7qq7UUiU3MHIDqEu2tm2Dwk5+VPmVp3p Xb9w== X-Gm-Message-State: AOJu0Ywgz5lTRyvwDhTCbiP4r+8RdRaNKa45+xflFyfySBOiw2fsy50o KfyZFe7quWn0d0WCG6OWl/g1KBRZtD4k7WqTG1Tm1tmaxFyjJCCO7K6a3A== X-Gm-Gg: ASbGncvZ+GSdmNdNaxabpbI9tjK0ernsJNjrWYMham2VHbcI9QLgdXGmp0/Qf1o9iw+ GtkSiOVLagTWwe68KWAwo2OkpR1QFjov6VpECUF1dTegikcoCpqa3MR/0EkqZm9IANrXXKdNvh3 Jie4eaypcunajYTc7s4mnVONsmyPozXUc38e5r5RbA5VxWeMUy+10x2MKpHrJL5P5NjAy5K9CsC 8FvMt9hGkf+ZILH8Eh22SmXFQsAW3WMtSFSnSwjEu5niUGiYUaDjibG9pUTQmnr1LucM/pqQ08g LFlau/Rq3teZSeV0+nSq+DhsRxkuFHrPDu7j3dsJWviqRTQvq00Vzq1KTRLIdBQ19ks= X-Google-Smtp-Source: AGHT+IEOvnAlkzoJqpf88F9IDSIS5u0QGdagMO9lGQFjCc+rkBZTK5ZVQZNwdqCOvQj7jilchV2kNA== X-Received: by 2002:a05:6000:402a:b0:391:2c0c:126b with SMTP id ffacd0b85a97d-39132d531c3mr8654624f8f.23.1741599420693; Mon, 10 Mar 2025 02:37:00 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3912bfb79fbsm14554454f8f.13.2025.03.10.02.37.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 02:37:00 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 2/7] linux-fitimage: sign setup sections Date: Mon, 10 Mar 2025 10:35:44 +0100 Message-ID: <20250310093641.1983560-3-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250310093641.1983560-1-adrian.freihofer@siemens.com> References: <20250310093641.1983560-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 10 Mar 2025 09:37:06 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/212498 If FIT_SIGN_INDIVIDUAL is set to “1”, a signature section is added to all screen sections, but not to the setup section. To match the setup section with all other sections, the signature is also added. This also helps to implement the associated tests generically. This change is intended to make the code more consistent. However, it is not intended to make the FIT_SIGN_INDIVIDUAL function more popular. Technically, it would be better to remove the signature from all other image sections and discard the FIT_SIGN_INDIVIDUAL function, the use of which is no longer recommended anyway. Signed-off-by: Adrian Freihofer --- meta/classes-recipe/kernel-fitimage.bbclass | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/meta/classes-recipe/kernel-fitimage.bbclass b/meta/classes-recipe/kernel-fitimage.bbclass index f41509d308a..07786647e19 100644 --- a/meta/classes-recipe/kernel-fitimage.bbclass +++ b/meta/classes-recipe/kernel-fitimage.bbclass @@ -243,6 +243,8 @@ EOF fitimage_emit_section_setup() { setup_csum="${FIT_HASH_ALG}" + setup_sign_algo="${FIT_SIGN_ALG}" + setup_sign_keyname="${UBOOT_SIGN_IMG_KEYNAME}" cat << EOF >> $1 setup-$2 { @@ -259,6 +261,17 @@ fitimage_emit_section_setup() { }; }; EOF + + if [ "${UBOOT_SIGN_ENABLE}" = "1" -a "${FIT_SIGN_INDIVIDUAL}" = "1" -a -n "$setup_sign_keyname" ] ; then + sed -i '$ d' $1 + cat << EOF >> $1 + signature-1 { + algo = "$setup_csum,$setup_sign_algo"; + key-name-hint = "$setup_sign_keyname"; + }; + }; +EOF + fi } # From patchwork Mon Mar 10 09:35:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 58540 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 11619C35FF2 for ; Mon, 10 Mar 2025 09:37:06 +0000 (UTC) Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) by mx.groups.io with SMTP id smtpd.web10.33760.1741599423298034618 for ; Mon, 10 Mar 2025 02:37:03 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=YEmUTv9P; spf=pass (domain: gmail.com, ip: 209.85.128.52, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-43cf05f0c3eso6066455e9.0 for ; Mon, 10 Mar 2025 02:37:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741599421; x=1742204221; darn=lists.openembedded.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=ZdvZpLc0vrNAyN+gi2/CAoglnDS+R5W2oCKs4ti5QC0=; b=YEmUTv9PAMwAh6HziCmP5wg4osk9ggv+dUaONOQDZvVvsRe7GLrvXY6yCTpxGhjYko oEvgJ4tC/8N7eOqTmWOEQXas5QgLtfnlDwgjt7lDIec/4D0Yf05a6x5SwsWnkkvv0JaE d+yd7HT/Rf5sWcC70xA7dYy0MhMsRvlv6MBfAI+vMRp3ZPK93RjlcttQ064kI7rtVRtF syKLxx6qmVYIwpBsD+8fMmT3JM7OQTMC5o59Qh/ocM2W1glLibMfre5+Y1UfhRZYcJHG WnY6aYFe1fqMqk3NA69KSu9NG1OqX8UC8eaHf81ywZKIx2cwHnyDFsTPPXhvZ8Hc5sfC Fisw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741599421; x=1742204221; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ZdvZpLc0vrNAyN+gi2/CAoglnDS+R5W2oCKs4ti5QC0=; b=VnRjDtw+BsQ1rl8LGaxSdHD0I9EKaqEi7/ZiDSxKZMUnukjcMAMFjP8sQ6bd+XOMK0 W+fFDT9qn+7avkt5VDx+alYQYNqn1LOGS1YidzUfBOHDo6ote2gbZew90IHeC77/FfbM h841iqkbdKlb/8PQ5EOD3Ed0KA0mdsIA1saSUDoPOqVubJhvpaA4ec0TOf1PsJqrFpPV jXDpNmo2KmYPkfBXJI9cdnpP2c7C7X1WAFPJVQzCN5WHWtgW0yEVGDUWNONgxhPm7UrT SXqfS19Mqr0iHsJeQuNszDErZICr6+UbdY2zD6Lo+GsTilBdhuLG7hqAaa9NnhHJu+Ok ef1Q== X-Gm-Message-State: AOJu0Ywc1c80uwSqvRYKlI+ucIbdGd4Z9AIik+AlmWnGNjFEevXCIKRd TkhMIM1pXMYCsPNGOGoieasa7dtzfKGFzKpJ6KKqlktNdzuoW5aPA5GUSw== X-Gm-Gg: ASbGncsyNUzcDDoN+cRE7MmtHYuiWOGKVuxS27hIpNxYDw9YWMOc8KEmUvHrJr+hZCH JDgBwq1CG/wfrwTyTRrVD4f6wSnNK2eoHCJ068bYQe8U5PfmOaS7eeV4GKFQWozhw/dZyZ1vNDo cAoMQ1xGSos+Ow5k7DLc3CbT6/SlHC5qvbkslAjSODmQOSVtSJY0S5kXcDil1Z33hQvzZWVueXU BYumCvP7lFUJn5QoV17S+DUJ+DmfC7B6k+1PMvRVKLKxejifiv0RCTOTdTLxj4LqqOLnVpkO2AK atxVH8PKZ6ME0MR16shYAgRh6x8HtDhqDgXq0DCpyBgIKccPNgVlDRasoFj5S78M8o8= X-Google-Smtp-Source: AGHT+IHNRJFnekkuvZZ/nSR2kjfjwsOi51lNbfxjok5/6rClRoWETuPYUkJeUo3LC8Otc7jx4BMXhA== X-Received: by 2002:a7b:ca4c:0:b0:43c:eb00:50eb with SMTP id 5b1f17b1804b1-43ceb0054a9mr46222145e9.5.1741599421255; Mon, 10 Mar 2025 02:37:01 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3912bfb79fbsm14554454f8f.13.2025.03.10.02.37.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 02:37:00 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 3/7] uboot, kernel: use hex address for UBOOT_ENTRYPOINT Date: Mon, 10 Mar 2025 10:35:45 +0100 Message-ID: <20250310093641.1983560-4-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250310093641.1983560-1-adrian.freihofer@siemens.com> References: <20250310093641.1983560-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 10 Mar 2025 09:37:06 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/212499 Compiling a FIT image with this default values and dump it with dumpimage shows decimal converted values. For example the default value 20008000 looks like this: Image 0 (kernel-1) ... Load Address: 0x01314c40 Entry Point: 0x01314c40 With this change the expected value is printed by dumpimage. Signed-off-by: Adrian Freihofer --- meta/classes-recipe/kernel.bbclass | 2 +- meta/classes-recipe/uboot-config.bbclass | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass index 64a685a964d..36ce659762e 100644 --- a/meta/classes-recipe/kernel.bbclass +++ b/meta/classes-recipe/kernel.bbclass @@ -233,7 +233,7 @@ KERNEL_VERSION = "${@get_kernelversion_headers('${B}')}" PACKAGE_ARCH = "${MACHINE_ARCH}" # U-Boot support -UBOOT_ENTRYPOINT ?= "20008000" +UBOOT_ENTRYPOINT ?= "0x20008000" UBOOT_LOADADDRESS ?= "${UBOOT_ENTRYPOINT}" # Some Linux kernel configurations need additional parameters on the command line diff --git a/meta/classes-recipe/uboot-config.bbclass b/meta/classes-recipe/uboot-config.bbclass index 74992182c36..f44605cb6ae 100644 --- a/meta/classes-recipe/uboot-config.bbclass +++ b/meta/classes-recipe/uboot-config.bbclass @@ -19,7 +19,7 @@ def removesuffix(s, suffix): return s[:-len(suffix)] return s -UBOOT_ENTRYPOINT ?= "20008000" +UBOOT_ENTRYPOINT ?= "0x20008000" UBOOT_LOADADDRESS ?= "${UBOOT_ENTRYPOINT}" # Some versions of u-boot use .bin and others use .img. By default use .bin From patchwork Mon Mar 10 09:35:46 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 58538 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id F227FC282DE for ; Mon, 10 Mar 2025 09:37:05 +0000 (UTC) Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) by mx.groups.io with SMTP id smtpd.web11.33424.1741599423829766422 for ; Mon, 10 Mar 2025 02:37:04 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=S35U5ejE; spf=pass (domain: gmail.com, ip: 209.85.221.50, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-390fdaf2897so3722694f8f.0 for ; Mon, 10 Mar 2025 02:37:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741599422; x=1742204222; darn=lists.openembedded.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=JNGOFdcTWFoNH4B0+vOal7LcuNg/Xj16EvNXrZYDmdA=; b=S35U5ejE6BBQC0aZKX4+bxkNT1PPzviQowxZK6a6TWv/S1y4N9jXQbX6JQEmNmz+4b vgzFzSaqjVE9Y+GaaZ5VzrwP/HD3ShD2ZjpnTZUFt+g3eMOMuQ4im1lEMjeE0zAGj0ku qECj5M/tMHLBDfuYup7P92ZK5fRGjqINsyu7uQF00yzTpKnmxZyOwVOoKu4k/LwZtVLP FwTMppHzt3nMlns6ayiIAcGIBHzUTJC8TB+cu2b6vtLNwbHiAS49LK3Y6UD0yNhZFVSi 91j3/QaXXki6O6f5OV5XcV7oc1y9gQIEAbaUex+cclp9XQ8AHJdZ4FT/C4vUzlGKioxM A0Hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741599422; x=1742204222; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JNGOFdcTWFoNH4B0+vOal7LcuNg/Xj16EvNXrZYDmdA=; b=of72cN3yBHQMZkVRqRsXAZbLGGoI+WCs8FfIRJU77Nst4lIJvS8O68L/pEAlYgKHhk SA5NmKMpyMh17++Fb0nJ621SkvVW60Y8NGHwCN+MHSVM7XMSxvLqbcWj7IO4wg7HP5C2 1+QW7gCT8lgxl2TcAx9I8azJ6Us1AcR3hgmME1R1k92UMGxL9p+1/Xt3CISIZitQI6Qv oEG9VUUf86oAuF0hj3+E+COS3rZ6ASdX8O0ow1DBURSvONq8ezVJYIDJbVddaODr7V89 Q0OaUdG52fhtyQyfDvropJ/F165X9fLawxjY2G5Y6wA85i7i6396WqSw5m9FzTD6SmmM SGHg== X-Gm-Message-State: AOJu0YwNq1ld/hHiTeM8RxOsiJw/glEZiBK58CqdQJ61lh6/DKekcrjE VGF7nctIgwbNxlHsq8bT2wLwf15vybDTNprl2oSEuYkwIUKNYHG+UXbZQg== X-Gm-Gg: ASbGncu4JoDuXaBZJ6Z9domwFVdIcvuQvEmRA4BOdZyM8A+Nz3+teDox/Wu5YFJThZ2 8sho0U/sK+eY7lttsL/20hnc/23RG7xwCAOMywVON5OF6xiSCknVcCnzC+u7Am8gEhJj7q5BiLh VuMB7sQiB6ClLP2G0hpYb7//eGmnevewapR/KvoDOLqPbp/1Ajd5RXejUt4ajey51ZnRLjLAPM/ HYIKKRpp/dju7mfp44j5leI7En9YzyVPzNAacKnjYBq+AIvSFv6W/hetNCeh0+F8NQovkWea715 s9WAn223Vf8/AXUgHXJtJXEPKKXbeJpw8H9hISb38pQBY/sjqPlDBcNJHMaEIEgQGlM= X-Google-Smtp-Source: AGHT+IGirYN76j5zngY36rj7BOuMadtC7TEbQl0SzpWBu5u6SCu/8GX6gFVQfHAbprW26CozcsZFgA== X-Received: by 2002:a05:6000:1fa9:b0:391:212:459a with SMTP id ffacd0b85a97d-39132d3b46fmr10939032f8f.22.1741599421844; Mon, 10 Mar 2025 02:37:01 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3912bfb79fbsm14554454f8f.13.2025.03.10.02.37.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 02:37:01 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 4/7] oe-selftest: adapt u-boot tests to latest changes Date: Mon, 10 Mar 2025 10:35:46 +0100 Message-ID: <20250310093641.1983560-5-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250310093641.1983560-1-adrian.freihofer@siemens.com> References: <20250310093641.1983560-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 10 Mar 2025 09:37:05 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/212500 For u-boot test cases (bitbake virtual/bootloader) inheriting the kernel-fitimage.bbclass is no longer needed. Also setting any variable which is evaluated by the kernel-fitimage.bbclass but not by uboot-sign.bbclass is pointless since: * Commit OE-Core rev: 5e12dc911d0c541f43aa6d0c046fb87e8b7c1f7e changed the test case from bitbake virtual/kernel to bitbake virtual/bootloader * Commit OE-Core rev: 259bfa86f384206f0d0a96a5b84887186c5f689e has finally removed the dependency of uboot-sign.bbclass on the kernel-fitimage.bbclass completely. Remove the related lines of code which are now without any effect. The two test cases test_uboot_fit_image and test_uboot_sign_fit_image do the exact same test. Both generate a binary equal its file: /dts-v1/; / { description = "A model description"; #address-cells = <1>; images { uboot { description = "U-Boot image"; data = /incbin/("u-boot-nodtb.bin"); type = "standalone"; os = "u-boot"; arch = "arm"; compression = "none"; load = <0x80080000>; entry = <0x80080000>; }; fdt { description = "U-Boot FDT"; data = /incbin/("u-boot.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; }; }; configurations { default = "conf"; conf { description = "Boot with signed U-Boot FIT"; loadables = "uboot"; fdt = "fdt"; }; }; }; The code diff between the two equal test cases looks like: @@ -1,8 +1,9 @@ - def test_uboot_fit_image(self): + def test_uboot_sign_fit_image(self): """ Summary: Check if Uboot FIT image and Image Tree Source (its) are built and the Image Tree Source has the - correct fields. + correct fields, in the scenario where the Kernel + is also creating/signing it's fitImage. Expected: 1. u-boot-fitImage and u-boot-its can be built 2. The type, load address, entrypoint address and default values of U-boot image are correct in the @@ -26,16 +27,15 @@ UBOOT_LOADADDRESS = "0x80080000" UBOOT_ENTRYPOINT = "0x80080000" UBOOT_FIT_DESC = "A model description" - -# Enable creation of Kernel fitImage KERNEL_IMAGETYPES += " fitImage " -KERNEL_CLASSES = " kernel-fitimage" +KERNEL_CLASSES = " kernel-fitimage " UBOOT_SIGN_ENABLE = "1" FIT_GENERATE_KEYS = "1" UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" FIT_SIGN_INDIVIDUAL = "1" +UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" """ self.write_config(config) Conclusion: The test case test_uboot_sign_fit_image looks redundant. Contrary to its name, it does not insert any signature nodes into the its-file and therefore does not test any type of signature. Code history: - Commit OE-Core rev: e71e4c617568496ae3bd6bb678f97b4f73cb43d8 introduces both test cases. - Commit OE-Core rev: 5e12dc911d0c541f43aa6d0c046fb87e8b7c1f7e changes both test cases like this: - bitbake("virtual/kernel") + bitbake("virtual/bootloader") It looks like the original implementation of test_uboot_sign_fit_image was supposed to test the interaction between the kernel-fitimage.bbclass and uboot-sign.bbclass which does not longer work like that. When compiling u-boot, the variable that is relevant for creating an its file with signature nodes is: SPL_SIGN_ENABLE. This is what the test case test_sign_standalone_uboot_fit_image verifies. Lets just delete the now obsolete test_uboot_sign_fit_image test case. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/fitimage.py | 101 ----------------------- 1 file changed, 101 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 5af9ea8afc0..dd177e0b048 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -342,16 +342,6 @@ UBOOT_FITIMAGE_ENABLE = "1" UBOOT_LOADADDRESS = "0x80080000" UBOOT_ENTRYPOINT = "0x80080000" UBOOT_FIT_DESC = "A model description" - -# Enable creation of Kernel fitImage -KERNEL_IMAGETYPES += " fitImage " -KERNEL_CLASSES = " kernel-fitimage" -UBOOT_SIGN_ENABLE = "1" -FIT_GENERATE_KEYS = "1" -UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" -UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" -UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" -FIT_SIGN_INDIVIDUAL = "1" """ self.write_config(config) @@ -396,89 +386,6 @@ FIT_SIGN_INDIVIDUAL = "1" "Fields in Image Tree Source File %s did not match, error in finding %s" % (fitimage_its_path, its_field_check[field_index])) - def test_uboot_sign_fit_image(self): - """ - Summary: Check if Uboot FIT image and Image Tree Source - (its) are built and the Image Tree Source has the - correct fields, in the scenario where the Kernel - is also creating/signing it's fitImage. - Expected: 1. u-boot-fitImage and u-boot-its can be built - 2. The type, load address, entrypoint address and - default values of U-boot image are correct in the - Image Tree Source. Not all the fields are tested, - only the key fields that wont vary between - different architectures. - Product: oe-core - Author: Klaus Heinrich Kiwi - based on work by Usama Arif - """ - config = """ -# We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set -MACHINE = "qemuarm" -UBOOT_MACHINE = "am57xx_evm_defconfig" -SPL_BINARY = "MLO" - -# Enable creation of the U-Boot fitImage -UBOOT_FITIMAGE_ENABLE = "1" - -# (U-boot) fitImage properties -UBOOT_LOADADDRESS = "0x80080000" -UBOOT_ENTRYPOINT = "0x80080000" -UBOOT_FIT_DESC = "A model description" -KERNEL_IMAGETYPES += " fitImage " -KERNEL_CLASSES = " kernel-fitimage " -UBOOT_SIGN_ENABLE = "1" -FIT_GENERATE_KEYS = "1" -UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" -UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" -UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" -FIT_SIGN_INDIVIDUAL = "1" -UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" -""" - self.write_config(config) - - # The U-Boot fitImage is created as part of the U-Boot recipe - bitbake("virtual/bootloader") - - deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') - machine = get_bb_var('MACHINE') - fitimage_its_path = os.path.join(deploy_dir_image, - "u-boot-its-%s" % (machine,)) - fitimage_path = os.path.join(deploy_dir_image, - "u-boot-fitImage-%s" % (machine,)) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - # Check that the type, load address, entrypoint address and default - # values for kernel and ramdisk in Image Tree Source are as expected. - # The order of fields in the below array is important. Not all the - # fields are tested, only the key fields that wont vary between - # different architectures. - its_field_check = [ - 'description = "A model description";', - 'type = "standalone";', - 'load = <0x80080000>;', - 'entry = <0x80080000>;', - 'default = "conf";', - 'loadables = "uboot";', - 'fdt = "fdt";' - ] - - with open(fitimage_its_path) as its_file: - field_index = 0 - for line in its_file: - if field_index == len(its_field_check): - break - if its_field_check[field_index] in line: - field_index +=1 - - if field_index != len(its_field_check): # if its equal, the test passed - self.assertTrue(field_index == len(its_field_check), - "Fields in Image Tree Source File %s did not match, error in finding %s" - % (fitimage_its_path, its_field_check[field_index])) - - def test_sign_standalone_uboot_fit_image(self): """ Summary: Check if U-Boot FIT image and Image Tree Source (its) are @@ -505,9 +412,6 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" MACHINE = "qemuarm" UBOOT_MACHINE = "am57xx_evm_defconfig" SPL_BINARY = "MLO" -# The kernel-fitimage class is a dependency even if we're only -# creating/signing the U-Boot fitImage -KERNEL_CLASSES = " kernel-fitimage" # Enable creation and signing of the U-Boot fitImage UBOOT_FITIMAGE_ENABLE = "1" SPL_SIGN_ENABLE = "1" @@ -663,8 +567,6 @@ SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'" UBOOT_EXTLINUX = "0" UBOOT_FIT_GENERATE_KEYS = "1" UBOOT_FIT_HASH_ALG = "sha256" -KERNEL_IMAGETYPES += " fitImage " -KERNEL_CLASSES = " kernel-fitimage " UBOOT_SIGN_ENABLE = "1" FIT_GENERATE_KEYS = "1" UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" @@ -1030,9 +932,6 @@ UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" MACHINE = "qemuarm" UBOOT_MACHINE = "am57xx_evm_defconfig" SPL_BINARY = "MLO" -# The kernel-fitimage class is a dependency even if we're only -# creating/signing the U-Boot fitImage -KERNEL_CLASSES = " kernel-fitimage" # Enable creation and signing of the U-Boot fitImage UBOOT_FITIMAGE_ENABLE = "1" SPL_SIGN_ENABLE = "1" From patchwork Mon Mar 10 09:35:47 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 58542 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 02DAAC2BA1B for ; Mon, 10 Mar 2025 09:37:06 +0000 (UTC) Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) by mx.groups.io with SMTP id smtpd.web10.33761.1741599424588086674 for ; Mon, 10 Mar 2025 02:37:04 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=ja6MGedn; spf=pass (domain: gmail.com, ip: 209.85.221.48, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-3913cf69784so1794610f8f.1 for ; Mon, 10 Mar 2025 02:37:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741599423; x=1742204223; darn=lists.openembedded.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=vOaOaUweWnBG+7Umg8SVMHdJnNFr5xPtcL5KxPK3Gw8=; b=ja6MGednT642LGL7u9/A0cOOUFeUVZ10bAjTgqWlC632YnBTw11jzEebuM4kxU23PB 3e8Ud1Cra2Vc02AjrSz9cmqNPuwVEi125urF3zkDAZLUP1KEN30VoD+pNWRqcXvP0RX3 6DWgZMfHCiRBuv6B9Jh43y8UjUNtGcD0rg2d++tM4D3+mNZDkZ+T3CEO8IhEzb1+ZaYW mhUrZJ2xO8nCSEl0WV+quO+NZFepZaWU3X0NnxTvurfeCL2mIv5NuMpugBHAQ9a4mnHm /vajClajWuUeyg6RFlQ2xBWdtnF1WhaMyGe9UyNnAyU86/ZuD/2+AyaZ3MoeZl20O/bz MTRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741599423; x=1742204223; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vOaOaUweWnBG+7Umg8SVMHdJnNFr5xPtcL5KxPK3Gw8=; b=lBsv8cvzCIh1QyGNyxICaDjIkqzzdbfC2WWJKN9GoszbWt1dxblPD9lrcJNMLFFC4c AgVioAFMF5ji8DCpFGyQvHa0z94AqIjlu+zyYTdqoD+vGWNlq9XJOOHSnkgwtcVvW0Fl eASObgn7eD29nRcAU1hjg2AX591wsqNzhIWEFRQRyK6wq2LF71+rXeQIkF0sVIZB9wbm JIl4iABuKf/bIDRwB/WO+kxCzJRww0I1LOjTQLNRAiO8u+miuPTspOUcxYHpYHqDeuan L0JWQjQfGKkj1J/wvVTzi50Yxy65yn0dx0kxDx6bb09BrcQwrB9mzdNd+7AzZYqzP8Jz D4+Q== X-Gm-Message-State: AOJu0Yx2jFd6Vk2V+GK9JQca+Oqbyy3o1l8ZU3G5PaiG4ucRfox6/J/q Sz/2wHh4BNnhNeoVgEoYlgtXoJrEh6SaXAUT24hny9tN8VcRjg9gqtiWNw== X-Gm-Gg: ASbGnctRR3yJXKqOQkgQeY4yvSpi4B0OHQMs4t1BfHVM3OXb6mld07iwkAP1hb4tuac jIL9u72Qs5lHmUzoROx0KLKkooboXjH+jtohC86P54tlow/HNBem4wLjBkDtx1ZWvNlqsjzT6qm bVZsbtBep+Pax4FtxBq+AqnAmT3sC9PNp7N5CoOiiRRYDubC0dfFpvJZvJbn7ZrXLrNEsfKrUPk yoeAG/gsJYICMif7yLnCAL1hEODtxjr7fXVjxwNeZTdZJOxlAbkRaAZ5QHZ2TLSZTusHpRq39OL sudrJkRVQGXtL+sjezoHk+HsyVAgDS8gedBiIim/vur9wr1mPME/JAiKjWq35TYhwXw= X-Google-Smtp-Source: AGHT+IFf1XU369Sui6Q0CbJB6dU1SV21WOLHEfFQKdfuZghTB+9eJfxu5/Fobmq0tNwhyHUq0qxgmg== X-Received: by 2002:a5d:47cc:0:b0:391:3124:f287 with SMTP id ffacd0b85a97d-39132d20b5emr11049488f8f.16.1741599422490; Mon, 10 Mar 2025 02:37:02 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3912bfb79fbsm14554454f8f.13.2025.03.10.02.37.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 02:37:02 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 5/7] oe-selftest: fitimage sort tests Date: Mon, 10 Mar 2025 10:35:47 +0100 Message-ID: <20250310093641.1983560-6-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250310093641.1983560-1-adrian.freihofer@siemens.com> References: <20250310093641.1983560-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 10 Mar 2025 09:37:05 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/212501 This is a trivial refactoring. The goal is to have all tests for kernel-fitimage.bbclass and all tests for uboot-sign.bbclass together. This refactoring greatly simplifies the diff of the next commit. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/fitimage.py | 262 +++++++++++------------ 1 file changed, 129 insertions(+), 133 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index dd177e0b048..00769443e80 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -168,7 +168,6 @@ FIT_DESC = "A model description" dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) self._verify_fitimage_uboot_env(dumpimage_result) - def test_sign_fit_image(self): """ Summary: Check if FIT image and Image Tree Source (its) are created @@ -314,6 +313,135 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c '%s'" self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], dtb), 'conf-' + dtb) + def test_initramfs_bundle(self): + """ + Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its) + The FIT settings are set by the test case. + The machine used is beaglebone-yocto. + Expected: 1. The ITS is generated with initramfs bundle support + 2. All the fields in the kernel node are as expected (matching the + conf settings) + 3. The kernel is included in all the available configurations and + its hash is included in the configuration signature + + Product: oe-core + Author: Abdellatif El Khlifi + """ + + config = """ +DISTRO="poky" +MACHINE = "beaglebone-yocto" +INITRAMFS_IMAGE_BUNDLE = "1" +INITRAMFS_IMAGE = "core-image-minimal-initramfs" +INITRAMFS_SCRIPTS = "" +UBOOT_MACHINE = "am335x_evm_defconfig" +KERNEL_CLASSES = " kernel-fitimage " +KERNEL_IMAGETYPES = "fitImage" +UBOOT_SIGN_ENABLE = "1" +UBOOT_SIGN_KEYNAME = "beaglebonekey" +UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" +UBOOT_DTB_BINARY = "u-boot.dtb" +UBOOT_ENTRYPOINT = "0x80000000" +UBOOT_LOADADDRESS = "0x80000000" +UBOOT_DTB_LOADADDRESS = "0x82000000" +UBOOT_ARCH = "arm" +UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" +UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" +UBOOT_EXTLINUX = "0" +FIT_GENERATE_KEYS = "1" +KERNEL_IMAGETYPE_REPLACEMENT = "zImage" +FIT_KERNEL_COMP_ALG = "none" +FIT_HASH_ALG = "sha256" +""" + config = self._config_add_uboot_env(config) + self.write_config(config) + + # fitImage is created as part of linux recipe + bitbake("virtual/kernel") + + bb_vars = get_bb_vars([ + 'DEPLOY_DIR_IMAGE', + 'FIT_HASH_ALG', + 'FIT_KERNEL_COMP_ALG', + 'INITRAMFS_IMAGE', + 'MACHINE', + 'UBOOT_ARCH', + 'UBOOT_ENTRYPOINT', + 'UBOOT_LOADADDRESS', + 'UBOOT_MKIMAGE_KERNEL_TYPE' + ], + 'virtual/kernel') + fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], + "fitImage-its-%s-%s-%s" % (bb_vars['INITRAMFS_IMAGE'], bb_vars['MACHINE'], bb_vars['MACHINE'])) + fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],"fitImage") + + self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) + self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) + + its_file = open(fitimage_its_path) + + its_lines = [line.strip() for line in its_file.readlines()] + + exp_node_lines = [ + 'kernel-1 {', + 'description = "Linux kernel";', + 'data = /incbin/("linux.bin");', + 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";', + 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";', + 'os = "linux";', + 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', + 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;', + 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;', + 'hash-1 {', + 'algo = "' + str(bb_vars['FIT_HASH_ALG']) +'";', + '};', + '};' + ] + + node_str = exp_node_lines[0] + + print ("checking kernel node\n") + self.assertIn(node_str, its_lines) + + node_start_idx = its_lines.index(node_str) + node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))] + + # Remove the absolute path. This refers to WORKDIR which is not always predictable. + re_data = re.compile(r'^data = /incbin/\(.*/linux\.bin"\);$') + node = [re.sub(re_data, 'data = /incbin/("linux.bin");', cfg_str) for cfg_str in node] + + self.assertEqual(node, exp_node_lines, "kernel node does not match expectation") + + rx_configs = re.compile("^conf-.*") + its_configs = list(filter(rx_configs.match, its_lines)) + + for cfg_str in its_configs: + cfg_start_idx = its_lines.index(cfg_str) + line_idx = cfg_start_idx + 2 + node_end = False + while node_end == False: + if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" : + node_end = True + line_idx = line_idx + 1 + + node = its_lines[cfg_start_idx:line_idx] + print("checking configuration " + cfg_str.rstrip(" {")) + rx_desc_line = re.compile(r'^description = ".*Linux kernel.*') + self.assertEqual(len(list(filter(rx_desc_line.match, node))), 1, "kernel keyword not found in the description line") + + self.assertIn('kernel = "kernel-1";', node) + + rx_sign_line = re.compile(r'^sign-images = .*kernel.*') + self.assertEqual(len(list(filter(rx_sign_line.match, node))), 1, "kernel hash not signed") + + # Verify the signature + uboot_tools_bindir = self._setup_uboot_tools_native() + self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 'am335x-bone.dtb')) + + # Check if the u-boot boot.scr script is in the fitImage + dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) + self._verify_fitimage_uboot_env(dumpimage_result) + def test_uboot_fit_image(self): """ Summary: Check if Uboot FIT image and Image Tree Source @@ -522,7 +650,6 @@ UBOOT_FIT_HASH_ALG = "sha256" self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, os.path.join(deploy_dir_image, 'u-boot-spl.dtb')) - def test_sign_cascaded_uboot_fit_image(self): """ Summary: Check if U-Boot FIT image and Image Tree Source (its) are @@ -667,137 +794,6 @@ FIT_SIGN_INDIVIDUAL = "1" self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, os.path.join(deploy_dir_image, 'u-boot-spl.dtb')) - - def test_initramfs_bundle(self): - """ - Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its) - The FIT settings are set by the test case. - The machine used is beaglebone-yocto. - Expected: 1. The ITS is generated with initramfs bundle support - 2. All the fields in the kernel node are as expected (matching the - conf settings) - 3. The kernel is included in all the available configurations and - its hash is included in the configuration signature - - Product: oe-core - Author: Abdellatif El Khlifi - """ - - config = """ -DISTRO="poky" -MACHINE = "beaglebone-yocto" -INITRAMFS_IMAGE_BUNDLE = "1" -INITRAMFS_IMAGE = "core-image-minimal-initramfs" -INITRAMFS_SCRIPTS = "" -UBOOT_MACHINE = "am335x_evm_defconfig" -KERNEL_CLASSES = " kernel-fitimage " -KERNEL_IMAGETYPES = "fitImage" -UBOOT_SIGN_ENABLE = "1" -UBOOT_SIGN_KEYNAME = "beaglebonekey" -UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" -UBOOT_DTB_BINARY = "u-boot.dtb" -UBOOT_ENTRYPOINT = "0x80000000" -UBOOT_LOADADDRESS = "0x80000000" -UBOOT_DTB_LOADADDRESS = "0x82000000" -UBOOT_ARCH = "arm" -UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" -UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" -UBOOT_EXTLINUX = "0" -FIT_GENERATE_KEYS = "1" -KERNEL_IMAGETYPE_REPLACEMENT = "zImage" -FIT_KERNEL_COMP_ALG = "none" -FIT_HASH_ALG = "sha256" -""" - config = self._config_add_uboot_env(config) - self.write_config(config) - - # fitImage is created as part of linux recipe - bitbake("virtual/kernel") - - bb_vars = get_bb_vars([ - 'DEPLOY_DIR_IMAGE', - 'FIT_HASH_ALG', - 'FIT_KERNEL_COMP_ALG', - 'INITRAMFS_IMAGE', - 'MACHINE', - 'UBOOT_ARCH', - 'UBOOT_ENTRYPOINT', - 'UBOOT_LOADADDRESS', - 'UBOOT_MKIMAGE_KERNEL_TYPE' - ], - 'virtual/kernel') - fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], - "fitImage-its-%s-%s-%s" % (bb_vars['INITRAMFS_IMAGE'], bb_vars['MACHINE'], bb_vars['MACHINE'])) - fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],"fitImage") - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - its_file = open(fitimage_its_path) - - its_lines = [line.strip() for line in its_file.readlines()] - - exp_node_lines = [ - 'kernel-1 {', - 'description = "Linux kernel";', - 'data = /incbin/("linux.bin");', - 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";', - 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";', - 'os = "linux";', - 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', - 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;', - 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;', - 'hash-1 {', - 'algo = "' + str(bb_vars['FIT_HASH_ALG']) +'";', - '};', - '};' - ] - - node_str = exp_node_lines[0] - - print ("checking kernel node\n") - self.assertIn(node_str, its_lines) - - node_start_idx = its_lines.index(node_str) - node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))] - - # Remove the absolute path. This refers to WORKDIR which is not always predictable. - re_data = re.compile(r'^data = /incbin/\(.*/linux\.bin"\);$') - node = [re.sub(re_data, 'data = /incbin/("linux.bin");', cfg_str) for cfg_str in node] - - self.assertEqual(node, exp_node_lines, "kernel node does not match expectation") - - rx_configs = re.compile("^conf-.*") - its_configs = list(filter(rx_configs.match, its_lines)) - - for cfg_str in its_configs: - cfg_start_idx = its_lines.index(cfg_str) - line_idx = cfg_start_idx + 2 - node_end = False - while node_end == False: - if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" : - node_end = True - line_idx = line_idx + 1 - - node = its_lines[cfg_start_idx:line_idx] - print("checking configuration " + cfg_str.rstrip(" {")) - rx_desc_line = re.compile(r'^description = ".*Linux kernel.*') - self.assertEqual(len(list(filter(rx_desc_line.match, node))), 1, "kernel keyword not found in the description line") - - self.assertIn('kernel = "kernel-1";', node) - - rx_sign_line = re.compile(r'^sign-images = .*kernel.*') - self.assertEqual(len(list(filter(rx_sign_line.match, node))), 1, "kernel hash not signed") - - # Verify the signature - uboot_tools_bindir = self._setup_uboot_tools_native() - self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 'am335x-bone.dtb')) - - # Check if the u-boot boot.scr script is in the fitImage - dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) - self._verify_fitimage_uboot_env(dumpimage_result) - - def test_uboot_atf_tee_fit_image(self): """ Summary: Check if U-boot FIT image and Image Tree Source From patchwork Mon Mar 10 09:35:48 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 58544 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 28039C28B30 for ; Mon, 10 Mar 2025 09:37:16 +0000 (UTC) Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) by mx.groups.io with SMTP id smtpd.web10.33763.1741599425871947499 for ; Mon, 10 Mar 2025 02:37:06 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=bD+7ifM7; spf=pass (domain: gmail.com, ip: 209.85.221.47, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-3914bc3e01aso296093f8f.2 for ; Mon, 10 Mar 2025 02:37:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741599424; x=1742204224; darn=lists.openembedded.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=bPSpVhMvXGxfG617kIqy5nIDIFNjaQyB6hXpeMxbWDI=; b=bD+7ifM7+usP5IQPkOGRMU7d44w+6A6iQS5eAIE00dcj4HG8Xg4EYZ8s/aFh4DJhem 4poVaa7C9VL7xZ0SyzQLHiH+LtLDZA5q1ji+5by5zcFgjUI0vVJJu2oMI3+T88LPFSOg 6jmyOCYnvWYMVH1sI5B4R/GmTtxfsPngLg47raWIraIpg2txfpnHbO8efNbQQVOJDFzA 8AoB//miyL4kqXUhvdQQHnjFJtCFS1deYE3OWofaekaA7BPAjQDXXIvGAptq1q2/bqIn nRCJ/TL7cq4uGFm1L/3pYrYA6/zlnsEMbA/NSePVuFIc+VIErwTTwEWOEUNZt3ZDYHgr supA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741599424; x=1742204224; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bPSpVhMvXGxfG617kIqy5nIDIFNjaQyB6hXpeMxbWDI=; b=Onuyl0oNueyEV17Cd85YN2n/2gg+r+7ehREgz8haXmv47EqsqUzL1yulHizkmgj6Sy Wp/5esJEhAIdH4jSbgxq4FrZjJ4ICAkMoiEL7UV3uG+2uvZ+aRW/x+f94enmiCoNNMpv 9f47k26JPXMnZVgQiw7fwszOINQkZT/vVgO7Q5ciYdLF6KG+jsgYLQ9fB0ljn4ZFaPdO yBuDN4dI5eQefvsH70LCb8BykplhlUfuXYSGdRX8rGDbLDKKx7xpWRarhzljP3gylI51 LceU5NlSNfzIRIOZKimF3t1z17M1ZfcH/VkxQEgcgMJNRkpa5+X4dtByN8qkXMjAXJbn 743g== X-Gm-Message-State: AOJu0Ywrm20EWOx/BmRs5VnhGkahLKCh7529nAQLL5B3PyR13WrgModx tpWmnWcjrwfAvzesGZdIpTXRYl+Jf8pHHBR9n4mrdkLvvLnFTYr1rUipYA== X-Gm-Gg: ASbGncu7v4MWo5iJTG3xioQLmHxukcyp0je7/ETiCjIA+QI5sFTWCU2W8mUMDfVdRJh VHpA4duVoFNav5WbRsj+NSj/nhrEnAhcOVhwAFBf4qg+jA769G3QVTtr7m6JwEH4hWP/oFWVVO/ fN23bxSx/Ty2EM138+DEPZ1RN/5YY/4/+nEcm8oErY6qfqf/2zA9y6rnXbPhgaiUnAGIxIGnKYj 4U/vOsKRAj+bZ00uX61JDd33OLWBy4R5JVIoLb7bk8mVE+7U/AW+R8Va5jEstsaIakSxw7t7Wtr TJMrT4F9bpcHMZp8dSJL+UxmL/9Gscur8Qq09prgC0MYV9TUNuMweoqo7jVs2LawGEs= X-Google-Smtp-Source: AGHT+IH9eTxNY65CSJix4sFv4NYq9ZQdSIoImC6BnstiPDRHVYtXl1BLyXRCwWx/Mgs5vi8w8H7U/Q== X-Received: by 2002:a5d:6482:0:b0:390:f88c:a6a2 with SMTP id ffacd0b85a97d-39132d986f8mr10127495f8f.39.1741599423323; Mon, 10 Mar 2025 02:37:03 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3912bfb79fbsm14554454f8f.13.2025.03.10.02.37.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 02:37:02 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 6/7] oe-selftest: fitimage cleanup Date: Mon, 10 Mar 2025 10:35:48 +0100 Message-ID: <20250310093641.1983560-7-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250310093641.1983560-1-adrian.freihofer@siemens.com> References: <20250310093641.1983560-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 10 Mar 2025 09:37:16 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/212503 This is a comprehensive cleanup of the fitImage related test cases. The existing test cases were essentially the same code copied and pasted 9 times. All 9 test cases contained the code to parse an its file and to parse the output of the dumpimage utility in slightly different variants. Changing the kernel-fitimage.bbclass or the uboot-sign.bbclass would mean changing 9 test cases individually. This is no longer maintainable. This cleanup converts the code into reusable functions. The new test code is more like a reverse implementation of the bbclasses to be tested than a collection of straightforward test sequences. This also means that the test code evaluates the same bitbake variables as the implementation. This makes it much easier to add new test cases, as a test case is basically just another local.conf file. The code is not yet complete. But it can now be improved step by step in this direction. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/fitimage.py | 1454 ++++++++++++---------- 1 file changed, 793 insertions(+), 661 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 00769443e80..6f3bf296d56 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -5,27 +5,58 @@ # from oeqa.selftest.case import OESelftestTestCase -from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars +from oeqa.utils.commands import runCmd, bitbake, get_bb_vars import os import re +import shlex +import logging +import pprint -class FitImageTests(OESelftestTestCase): +class FitImageTestCase(OESelftestTestCase): + """Test functions usable for testing kernel-fitimage.bbclass and uboot-sign.bbclass - def _setup_uboot_tools_native(self): - """build u-boot-tools-native and return ${RECIPE_SYSROOT_NATIVE}/${bindir}""" - bitbake("u-boot-tools-native -c addto_recipe_sysroot") - vars = get_bb_vars(['RECIPE_SYSROOT_NATIVE', 'bindir'], 'u-boot-tools-native') + A brief summary showing the structure of a test case: + + self._test_fitimage() + # Generate a local.conf file and bitbake the bootloader or the kernel + self._bitbake_fit_image() + + # Check if the its file contains the expected paths and attributes. + # The _get_req_* functions are implemented by more specific chield classes. + self._check_its_file() + req_its_paths = self._get_req_its_paths() + req_sigvalues_config = self._get_req_sigvalues_config() + req_sigvalues_image = self._get_req_sigvalues_image() + # Compare the its file against req_its_paths, req_sigvalues_config, req_sigvalues_image + + # Call the dumpimage utiliy and check that it prints all the expected paths and attributes + # The _get_req_* functions are implemented by more specific chield classes. + self._check_fitimage() + self._get_req_sections() + # Compare the output of the dumpimage utility against + """ + + MKIMAGE_HASH_LENGTHS = { 'sha256': 64, 'sha384': 96, 'sha512': 128 } + MKIMAGE_SIGNATURE_LENGTHS = { 'rsa2048': 512 } + + @staticmethod + def _gen_random_file(file_path, num_bytes=65536): + with open(file_path, 'wb') as file_out: + file_out.write(os.urandom(num_bytes)) + + @staticmethod + def _setup_native(native_recipe): + """Build a native recipe and return the path to its bindir in RECIPE_SYSROOT_NATIVE""" + bitbake(native_recipe + " -c addto_recipe_sysroot") + vars = get_bb_vars(['RECIPE_SYSROOT_NATIVE', 'bindir'], native_recipe) return os.path.join(vars['RECIPE_SYSROOT_NATIVE'], vars['bindir']) - def _run_dumpimage(self, fitimage_path, uboot_tools_bindir): - dumpimage_path = os.path.join(uboot_tools_bindir, 'dumpimage') - return runCmd('%s -l %s' % (dumpimage_path, fitimage_path)) - def _verify_fit_image_signature(self, uboot_tools_bindir, fitimage_path, dtb_path, conf_name=None): - """Verify the signature of a fit contfiguration + """Verify the signature of a fit configuration The fit_check_sign utility from u-boot-tools-native is called. - uboot-fit_check_sign -f fitImage -k $dtb_name -c conf-$dtb_name + uboot-fit_check_sign -f fitImage -k $dtb_path -c conf-$dtb_name + dtb_path refers to a binary device tree containing the public key. """ fit_check_sign_path = os.path.join(uboot_tools_bindir, 'uboot-fit_check_sign') cmd = '%s -f %s -k %s' % (fit_check_sign_path, fitimage_path, dtb_path) @@ -37,33 +68,276 @@ class FitImageTests(OESelftestTestCase): @staticmethod def _find_string_in_bin_file(file_path, search_string): - """find stings in a binary file + """find strings in a binary file Shell equivalent: strings "$1" | grep "$2" | wc -l return number of matches """ found_positions = 0 with open(file_path, 'rb') as file: - byte = file.read(1) - current_position = 0 - current_match = 0 - while byte: - char = byte.decode('ascii', errors='ignore') - if char == search_string[current_match]: - current_match += 1 - if current_match == len(search_string): - found_positions += 1 - current_match = 0 - else: - current_match = 0 - current_position += 1 - byte = file.read(1) + content = file.read().decode('ascii', errors='ignore') + found_positions = content.count(search_string) return found_positions + @staticmethod + def _get_uboot_mkimage_sign_args(uboot_mkimage_sign_args): + """Retrive the string passed via -c to the mkimage command + + Example: If a build configutation defines + UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" + this function returns "a smart comment" + """ + a_comment = None + if uboot_mkimage_sign_args: + mkimage_args = shlex.split(uboot_mkimage_sign_args) + try: + c_index = mkimage_args.index('-c') + a_comment = mkimage_args[c_index+1] + except ValueError: + pass + return a_comment + + @staticmethod + def _get_dtb_files(bb_vars): + kernel_devicetree = bb_vars['KERNEL_DEVICETREE'] or "" + if kernel_devicetree: + return [os.path.basename(dtb) for dtb in kernel_devicetree.split()] + return [] + + def _is_req_dict_in_dict(self, found_dict, req_dict): + """ + Check if all key-value pairs in the required dictionary are present in the found dictionary. + + This function recursively checks if the required dictionary (`req_dict`) is a subset of the found dictionary (`found_dict`). + It supports nested dictionaries, strings, lists, and sets as values. + + Args: + found_dict (dict): The dictionary to search within. + req_dict (dict): The dictionary containing the required key-value pairs. + """ + for key, value in req_dict.items(): + self.assertIn(key, found_dict) + if isinstance(value, dict): + self._is_req_dict_in_dict(found_dict[key], value) + elif isinstance(value, str): + self.assertIn(value, found_dict[key]) + elif isinstance(value, list): + self.assertLessEqual(set(value), set(found_dict[key])) + elif isinstance(value, set): + self.assertLessEqual(value, found_dict[key]) + else: + self.assertEqual(value, found_dict[key]) + + def _check_its_file(self, bb_vars, its_file_path): + """Check if the its file contains the expected sections and fields""" + # print the its file for debugging + if logging.DEBUG >= self.logger.level: + with open(its_file_path) as its_file: + self.logger.debug("its file: %s" % its_file.read()) + + # Generate a list of expected paths in the its file + req_its_paths = self._get_req_its_paths(bb_vars) + self.logger.debug("req_its_paths:\n%s\n" % pprint.pformat(req_its_paths, indent=4)) + + # Generate a dict of expected configuration signature nodes + req_sigvalues_config = self._get_req_sigvalues_config(bb_vars) + self.logger.debug("req_sigvalues_config:\n%s\n" % pprint.pformat(req_sigvalues_config, indent=4)) + + # Generate a dict of expected image signature nodes + req_sigvalues_image = self._get_req_sigvalues_image(bb_vars) + self.logger.debug("req_sigvalues_image:\n%s\n" % pprint.pformat(req_sigvalues_image, indent=4)) + + # Parse the its file for paths and signatures + its_path = [] + its_paths = [] + linect = 0 + sigs = {} + with open(its_file_path) as its_file: + for line in its_file: + linect += 1 + line = line.strip() + if line.endswith('};'): + its_path.pop() + elif line.endswith('{'): + its_path.append(line[:-1].strip()) + its_paths.append(its_path[:]) + # kernel-fitimage uses signature-1, uboot-sign uses signature + elif its_path and (its_path[-1] == 'signature-1' or its_path[-1] == 'signature'): + itsdotpath = '.'.join(its_path) + if not itsdotpath in sigs: + sigs[itsdotpath] = {} + if not '=' in line or not line.endswith(';'): + self.fail('Unexpected formatting in %s sigs section line %d:%s' % (its_file_path, linect, line)) + key, value = line.split('=', 1) + sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') + + # Check if all expected paths are found in the its file + self.logger.debug("itspaths:\n%s\n" % pprint.pformat(its_paths, indent=4)) + for req_path in req_its_paths: + if not req_path in its_paths: + self.fail('Missing path in its file: %s (%s)' % (req_path, its_file_path)) + + # Check if all the expected singnature nodes (images and configurations) are found + self.logger.debug("sigs:\n%s\n" % pprint.pformat(sigs, indent=4)) + if req_sigvalues_config or req_sigvalues_image: + for its_path, values in sigs.items(): + if 'conf-' in its_path: + reqsigvalues = req_sigvalues_config + else: + reqsigvalues = req_sigvalues_image + for reqkey, reqvalue in reqsigvalues.items(): + value = values.get(reqkey, None) + if value is None: + self.fail('Missing key "%s" in its file signature section %s (%s)' % (reqkey, its_path, its_file_path)) + self.assertEqual(value, reqvalue) + + # Generate a list of expected fields in the its file + req_its_fields = self._get_req_its_fields(bb_vars) + self.logger.debug("req_its_fields:\n%s\n" % pprint.pformat(req_its_fields, indent=4)) + + # Check if all expected fields are in the its file + if req_its_fields: + field_index = 0 + field_index_last = len(req_its_fields) - 1 + with open(its_file_path) as its_file: + for line in its_file: + if req_its_fields[field_index] in line: + if field_index < field_index_last: + field_index +=1 + else: + break + self.assertEqual(field_index, field_index_last, + "Fields in Image Tree Source File %s did not match, error in finding %s" + % (its_file_path, req_its_fields[field_index])) + + def _check_fitimage(self, bb_vars, fitimage_path, uboot_tools_bindir): + """Run dumpimage on the final FIT image and parse the output into a dict""" + dumpimage_path = os.path.join(uboot_tools_bindir, 'dumpimage') + cmd = '%s -l %s' % (dumpimage_path, fitimage_path) + self.logger.debug("Analyzing output from dumpimage: %s" % cmd) + dumpimage_result = runCmd(cmd) + in_section = None + sections = {} + self.logger.debug("dumpimage output: %s" % dumpimage_result.output) + for line in dumpimage_result.output.splitlines(): + # Find potentially hashed and signed sections + if line.startswith((' Configuration', ' Image')): + in_section = re.search(r'\((.*)\)', line).groups()[0] + # Key value lines start with two spaces otherwise the section ended + elif not line.startswith(" "): + in_section = None + # Handle key value lines of this section + elif in_section: + if not in_section in sections: + sections[in_section] = {} + try: + key, value = line.split(':', 1) + key = key.strip() + value = value.strip() + except ValueError as val_err: + self.logger.debug("dumpimage debug: %s = %s" % (key, line)) + # Handle multiple entries as e.g. for Loadables as a list + if key and line.startswith(" "): + value = sections[in_section][key] + "," + line.strip() + else: + raise ValueError(f"Error processing line: '{line}'. Original error: {val_err}") + sections[in_section][key] = value + + # Check if the requested dictionary is a subset of the parsed dictionary + req_sections, num_signatures = self._get_req_sections(bb_vars) + self.logger.debug("req_sections: \n%s\n" % pprint.pformat(req_sections, indent=4)) + self.logger.debug("dumpimage sections: \n%s\n" % pprint.pformat(sections, indent=4)) + self._is_req_dict_in_dict(sections, req_sections) + + # Call the signing related checks if the function is provided by a inherited class + self._check_signing(bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path) + + def _get_req_its_paths(self, bb_vars): + self.logger.error("This function needs to be implemented") + return [] + + def _get_req_its_fields(self, bb_vars): + self.logger.error("This function needs to be implemented") + return [] + + def _get_req_sigvalues_config(self, bb_vars): + self.logger.error("This function needs to be implemented") + return {} + + def _get_req_sigvalues_image(self, bb_vars): + self.logger.error("This function needs to be implemented") + return {} + + def _get_req_sections(self, bb_vars): + self.logger.error("This function needs to be implemented") + return ({}, 0) + + def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): + """Verify the signatures in the FIT image.""" + self.fail("Function needs to be implemented by inheriting classes") + + def _bitbake_fit_image(self, bb_vars): + """Bitbake the FIT image and return the paths to the its file and the FIT image""" + self.fail("Function needs to be implemented by inheriting classes") + + def _test_fitimage(self, bb_vars): + """Check if the its file and the FIT image are created and signed correctly""" + fitimage_its_path, fitimage_path = self._bitbake_fit_image(bb_vars) + self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) + self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) + + self.logger.debug("Checking its: %s" % fitimage_its_path) + self._check_its_file(bb_vars, fitimage_its_path) + + # Setup u-boot-tools-native + uboot_tools_bindir = FitImageTestCase._setup_native('u-boot-tools-native') + + # Verify the FIT image + self._check_fitimage(bb_vars, fitimage_path, uboot_tools_bindir) + + +class KernelFitImageTests(FitImageTestCase): + """Test cases for the kernel-fitimage bbclass""" + + def _fit_get_bb_vars(self, additional_vars=[]): + """Retrieve BitBake variables specific to the test case. + + Call the get_bb_vars function once and get all variables needed by the test case. + """ + internal_used = { + 'DEPLOY_DIR_IMAGE', + 'FIT_DESC', + 'FIT_HASH_ALG', + 'FIT_KERNEL_COMP_ALG', + 'FIT_SIGN_ALG', + 'FIT_SIGN_INDIVIDUAL', + 'FIT_UBOOT_ENV', + 'INITRAMFS_IMAGE_BUNDLE', + 'INITRAMFS_IMAGE_NAME', + 'INITRAMFS_IMAGE', + 'KERNEL_DEVICETREE', + 'KERNEL_FIT_LINK_NAME', + 'MACHINE', + 'UBOOT_ARCH', + 'UBOOT_ENTRYPOINT', + 'UBOOT_LOADADDRESS', + 'UBOOT_MKIMAGE_KERNEL_TYPE', + 'UBOOT_MKIMAGE_SIGN_ARGS', + 'UBOOT_RD_ENTRYPOINT', + 'UBOOT_RD_LOADADDRESS', + 'UBOOT_SIGN_ENABLE', + 'UBOOT_SIGN_IMG_KEYNAME', + 'UBOOT_SIGN_KEYDIR', + 'UBOOT_SIGN_KEYNAME', + } + bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/kernel") + return bb_vars + def _config_add_uboot_env(self, config): """Generate an u-boot environment - Create a boot.cmd file that is packed into the FitImage as a source-able text file. + Create a boot.cmd file that is packed into the FIT image as a source-able text file. + Updates the configuration to include the boot.cmd file. """ fit_uenv_file = "boot.cmd" test_files_dir = "test-files" @@ -75,17 +349,253 @@ class FitImageTests(OESelftestTestCase): config += 'SRC_URI:append:pn-linux-yocto = " file://${FIT_UBOOT_ENV}"' + os.linesep if not os.path.isdir(test_files_dir): - os.mkdir(test_files_dir) + os.makedirs(test_files_dir) self.logger.debug("Writing to: %s" % fit_uenv_path) with open(fit_uenv_path, "w") as f: f.write('echo "hello world"') return config - def _verify_fitimage_uboot_env(self, dumpimage_result): - """Check if the boot.cmd script is part of the fitImage""" - num_scr_images = len(re.findall(r"^ *Image +[0-9]+ +\(bootscr-boot\.cmd\)$", dumpimage_result.output, re.MULTILINE)) - self.assertEqual(1, num_scr_images, msg="Expected exactly 1 bootscr-boot.cmd image section in the fitImage") + def _bitbake_fit_image(self, bb_vars): + """Bitbake the kernel and return the paths to the its file and the FIT image""" + bitbake("virtual/kernel") + + # Find the right its file and the final fitImage and check if both files are available + deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] + initramfs_image = bb_vars['INITRAMFS_IMAGE'] + initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] + initramfs_image_name = bb_vars['INITRAMFS_IMAGE_NAME'] + kernel_fit_link_name = bb_vars['KERNEL_FIT_LINK_NAME'] + if not initramfs_image and initramfs_image_bundle != "1": + fitimage_its_name = "fitImage-its-%s" % kernel_fit_link_name + fitimage_name = "fitImage" + elif initramfs_image and initramfs_image_bundle != "1": + fitimage_its_name = "fitImage-its-%s-%s" % (initramfs_image_name, kernel_fit_link_name) + fitimage_name = "fitImage-%s-%s" % (initramfs_image_name, kernel_fit_link_name) + elif initramfs_image and initramfs_image_bundle == "1": + fitimage_its_name = "fitImage-its-%s-%s" % (initramfs_image_name, kernel_fit_link_name) + fitimage_name = "fitImage" # or fitImage-${KERNEL_IMAGE_LINK_NAME}${KERNEL_IMAGE_BIN_EXT} + else: + self.fail('Invalid configuration: INITRAMFS_IMAGE_BUNDLE = "1" and not INITRAMFS_IMAGE') + fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_its_name)) + fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_name)) + return (fitimage_its_path, fitimage_path) + + def _get_req_its_paths(self, bb_vars): + """Generate a list of expected paths in the its file + + Example: + [ + ['/', 'images', 'kernel-1', 'hash-1'], + ['/', 'images', 'kernel-1', 'signature-1'], + ] + """ + dtb_files = FitImageTestCase._get_dtb_files(bb_vars) + fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] + fit_uboot_env = bb_vars['FIT_UBOOT_ENV'] + initramfs_image = bb_vars['INITRAMFS_IMAGE'] + initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] + uboot_sign_enable = bb_vars['UBOOT_SIGN_ENABLE'] + + # image nodes + images = [ 'kernel-1' ] + if dtb_files: + images += [ 'fdt-' + dtb for dtb in dtb_files ] + if fit_uboot_env: + images.append('bootscr-' + fit_uboot_env) + if bb_vars['MACHINE'] == "qemux86-64": # Not really the right if + images.append('setup-1') + if initramfs_image and initramfs_image_bundle != "1": + images.append('ramdisk-1') + + # configuration nodes + if dtb_files: + configurations = [ 'conf-' + conf for conf in dtb_files ] + else: + configurations = [ 'conf-1' ] + + # Create a list of paths for all image and configuration nodes + req_its_paths = [] + for image in images: + req_its_paths.append(['/', 'images', image, 'hash-1']) + if uboot_sign_enable == "1" and fit_sign_individual == "1": + req_its_paths.append(['/', 'images', image, 'signature-1']) + for configuration in configurations: + req_its_paths.append(['/', 'configurations', configuration, 'hash-1']) + if uboot_sign_enable == "1": + req_its_paths.append(['/', 'configurations', configuration, 'signature-1']) + return req_its_paths + + def _get_req_its_fields(self, bb_vars): + initramfs_image = bb_vars['INITRAMFS_IMAGE'] + initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] + uboot_rd_loadaddress = bb_vars['UBOOT_RD_LOADADDRESS'] + uboot_rd_entrypoint = bb_vars['UBOOT_RD_ENTRYPOINT'] + + its_field_check = [ + 'description = "%s";' % bb_vars['FIT_DESC'], + 'description = "Linux kernel";', + 'data = /incbin/("linux.bin");', + 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";', + 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";', + 'os = "linux";', + # 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', defined based on files in TMPDIR, not ideal... + 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;', + 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;', + ] + if initramfs_image and initramfs_image_bundle != "1": + its_field_check.append('type = "ramdisk";') + if uboot_rd_loadaddress: + its_field_check.append("load = <%s>;" % uboot_rd_loadaddress) + if uboot_rd_entrypoint: + its_field_check.append("entry = <%s>;" % uboot_rd_entrypoint) + its_field_check += [ + # 'default = "conf-1";', needs more work + 'kernel = "kernel-1";', + ] + if initramfs_image and initramfs_image_bundle != "1": + its_field_check.append('ramdisk = "ramdisk-1";') + + return its_field_check + + def _get_req_sigvalues_config(self, bb_vars): + """Generate a dictionary of expected configuration signature nodes""" + sign_images = '"kernel", "fdt"' + if bb_vars['INITRAMFS_IMAGE'] and bb_vars['INITRAMFS_IMAGE_BUNDLE'] != "1": + sign_images += ', "ramdisk"' + if bb_vars['FIT_UBOOT_ENV']: + sign_images += ', "bootscr"' + req_sigvalues_config = { + 'algo': '"%s,%s"' % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']), + 'key-name-hint': '"%s"' % bb_vars['UBOOT_SIGN_KEYNAME'], + 'sign-images': sign_images, + } + return req_sigvalues_config + + def _get_req_sigvalues_image(self, bb_vars): + """Generate a dictionary of expected image signature nodes""" + if bb_vars['FIT_SIGN_INDIVIDUAL'] != "1": + return {} + req_sigvalues_image = { + 'algo': '"%s,%s"' % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']), + 'key-name-hint': '"%s"' % bb_vars['UBOOT_SIGN_IMG_KEYNAME'], + } + return req_sigvalues_image + + def _get_req_sections(self, bb_vars): + """Generate a dictionary of expected sections in the output of dumpimage""" + dtb_files = FitImageTestCase._get_dtb_files(bb_vars) + fit_hash_alg = bb_vars['FIT_HASH_ALG'] + fit_sign_alg = bb_vars['FIT_SIGN_ALG'] + fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] + fit_uboot_env = bb_vars['FIT_UBOOT_ENV'] + initramfs_image = bb_vars['INITRAMFS_IMAGE'] + initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] + uboot_sign_enable = bb_vars['UBOOT_SIGN_ENABLE'] + uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] + uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] + num_signatures = 0 + req_sections = { + "kernel-1": { + "Type": "Kernel Image", + "OS": "Linux", + "Load Address": bb_vars['UBOOT_LOADADDRESS'], + "Entry Point": bb_vars['UBOOT_ENTRYPOINT'], + } + } + # Create one section per DTB + for dtb in dtb_files: + req_sections['fdt-' + dtb] = { + "Type": "Flat Device Tree", + } + # Add a script section if there is a script + if fit_uboot_env: + req_sections['bootscr-' + fit_uboot_env] = { "Type": "Script" } + # Add the initramfs + if initramfs_image and initramfs_image_bundle != "1": + req_sections['ramdisk-1'] = { + "Type": "RAMDisk Image", + "Load Address": bb_vars['UBOOT_RD_LOADADDRESS'], + "Entry Point": bb_vars['UBOOT_RD_ENTRYPOINT'] + } + # Create a configuration section for each DTB + if dtb_files: + for dtb in dtb_files: + req_sections['conf-' + dtb] = { + "Kernel": "kernel-1", + "FDT": 'fdt-' + dtb, + } + if initramfs_image and initramfs_image_bundle != "1": + req_sections['conf-' + dtb]['Init Ramdisk'] = "ramdisk-1" + else: + req_sections['conf-1'] = { + "Kernel": "kernel-1" + } + if initramfs_image and initramfs_image_bundle != "1": + req_sections['conf-1']['Init Ramdisk'] = "ramdisk-1" + + # Add signing related properties if needed + if uboot_sign_enable == "1": + for section in req_sections: + req_sections[section]['Hash algo'] = fit_hash_alg + if section.startswith('conf-'): + req_sections[section]['Hash value'] = "unavailable" + req_sections[section]['Sign algo'] = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname) + num_signatures += 1 + elif fit_sign_individual == "1": + req_sections[section]['Sign algo'] = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_img_keyname) + num_signatures += 1 + return (req_sections, num_signatures) + + def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): + """Verify the signature nodes in the FIT image""" + if bb_vars['UBOOT_SIGN_ENABLE'] == "1": + self.logger.debug("Verifying signatures in the FIT image") + else: + self.logger.debug("FIT image is not signed. Signature verification is not needed.") + return + + fit_hash_alg = bb_vars['FIT_HASH_ALG'] + fit_sign_alg = bb_vars['FIT_SIGN_ALG'] + uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] + uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] + deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] + fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] + fit_hash_alg_len = FitImageTestCase.MKIMAGE_HASH_LENGTHS[fit_hash_alg] + fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[fit_sign_alg] + for section, values in sections.items(): + # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1") + if section.startswith("conf"): + sign_algo = values.get('Sign algo', None) + req_sign_algo = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname) + self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) + sign_value = values.get('Sign value', None) + self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) + dtb_path = os.path.join(deploy_dir_image, section.replace('conf-', '')) + self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, dtb_path, section) + else: + # Image nodes always need a hash which gets indirectly signed by the config signature + hash_algo = values.get('Hash algo', None) + self.assertEqual(hash_algo, fit_hash_alg) + hash_value = values.get('Hash value', None) + self.assertEqual(len(hash_value), fit_hash_alg_len, 'Hash value for section %s not expected length' % section) + # Optionally, if FIT_SIGN_INDIVIDUAL = 1 also the image nodes have a signature (which is redundant but possible) + if fit_sign_individual == "1": + sign_algo = values.get('Sign algo', None) + req_sign_algo = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_img_keyname) + self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) + sign_value = values.get('Sign value', None) + self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) + + # Search for the string passed to mkimage in each signed section of the FIT image. + # Looks like mkimage supports to add a comment but does not support to read it back. + a_comment = FitImageTestCase._get_uboot_mkimage_sign_args(bb_vars['UBOOT_MKIMAGE_SIGN_ARGS']) + self.logger.debug("a_comment: %s" % a_comment) + if a_comment: + found_comments = FitImageTestCase._find_string_in_bin_file(fitimage_path, a_comment) + self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % + (num_signatures, a_comment)) + def test_fit_image(self): """ @@ -117,76 +627,34 @@ UBOOT_LOADADDRESS = "0x80080000" UBOOT_ENTRYPOINT = "0x80080000" FIT_DESC = "A model description" """ - config = self._config_add_uboot_env(config) self.write_config(config) + bb_vars = self._fit_get_bb_vars() + self._test_fitimage(bb_vars) - # fitImage is created as part of linux recipe - image = "virtual/kernel" - bitbake(image) - bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'INITRAMFS_IMAGE_NAME', 'KERNEL_FIT_LINK_NAME'], image) - - fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], - "fitImage-its-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME'])) - fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], - "fitImage-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME'])) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - # Check that the type, load address, entrypoint address and default - # values for kernel and ramdisk in Image Tree Source are as expected. - # The order of fields in the below array is important. Not all the - # fields are tested, only the key fields that wont vary between - # different architectures. - its_field_check = [ - 'description = "A model description";', - 'type = "kernel";', - 'load = <0x80080000>;', - 'entry = <0x80080000>;', - 'type = "ramdisk";', - 'load = <0x88000000>;', - 'entry = <0x88000000>;', - 'default = "conf-1";', - 'kernel = "kernel-1";', - 'ramdisk = "ramdisk-1";' - ] - - with open(fitimage_its_path) as its_file: - field_index = 0 - for line in its_file: - if field_index == len(its_field_check): - break - if its_field_check[field_index] in line: - field_index +=1 - - if field_index != len(its_field_check): # if its equal, the test passed - self.assertTrue(field_index == len(its_field_check), - "Fields in Image Tree Source File %s did not match, error in finding %s" - % (fitimage_its_path, its_field_check[field_index])) - - uboot_tools_bindir = self._setup_uboot_tools_native() - dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) - self._verify_fitimage_uboot_env(dumpimage_result) def test_sign_fit_image(self): """ Summary: Check if FIT image and Image Tree Source (its) are created - and signed correctly. + and all nodes are signed correctly. Expected: 1) its and FIT image are built successfully 2) Scanning the its file indicates signing is enabled - as requested by UBOOT_SIGN_ENABLE (using keys generated - via FIT_GENERATE_KEYS) + as requested by UBOOT_SIGN_ENABLE (using 2 keys + generated via FIT_GENERATE_KEYS) 3) Dumping the FIT image indicates signature values are present (including for images as enabled via FIT_SIGN_INDIVIDUAL) - 4) Examination of the do_assemble_fitimage runfile/logfile - indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and - UBOOT_MKIMAGE_SIGN_ARGS are working as expected. + 4) Verify the FIT image contains the comments passed via + UBOOT_MKIMAGE_SIGN_ARGS once per image and per + configuration node. + Note: This test is mostly for backward compatibility. + The recommended approach is to sign the configuration nodes + which include also the hashes of all the images. Signing + all the images individually is therefore redundant. Product: oe-core Author: Paul Eggleton based upon work by Usama Arif """ - a_comment = "a smart comment" + # Generate a configuration section which gets included into the local.conf file config = """ # Enable creation of fitImage MACHINE = "beaglebone-yocto" @@ -198,120 +666,13 @@ UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" FIT_SIGN_INDIVIDUAL = "1" -UBOOT_MKIMAGE_SIGN_ARGS = "-c '%s'" -""" % a_comment - +UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" +""" config = self._config_add_uboot_env(config) self.write_config(config) + bb_vars = self._fit_get_bb_vars() + self._test_fitimage(bb_vars) - # fitImage is created as part of linux recipe - image = "virtual/kernel" - bitbake(image) - bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'KERNEL_FIT_LINK_NAME'], image) - - fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], - "fitImage-its-%s" % (bb_vars['KERNEL_FIT_LINK_NAME'])) - fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], - "fitImage-%s.bin" % (bb_vars['KERNEL_FIT_LINK_NAME'])) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - req_itspaths = [ - ['/', 'images', 'kernel-1'], - ['/', 'images', 'kernel-1', 'signature-1'], - ['/', 'images', 'fdt-am335x-boneblack.dtb'], - ['/', 'images', 'fdt-am335x-boneblack.dtb', 'signature-1'], - ['/', 'configurations', 'conf-am335x-boneblack.dtb'], - ['/', 'configurations', 'conf-am335x-boneblack.dtb', 'signature-1'], - ] - - itspath = [] - itspaths = [] - linect = 0 - sigs = {} - with open(fitimage_its_path) as its_file: - linect += 1 - for line in its_file: - line = line.strip() - if line.endswith('};'): - itspath.pop() - elif line.endswith('{'): - itspath.append(line[:-1].strip()) - itspaths.append(itspath[:]) - elif itspath and itspath[-1] == 'signature-1': - itsdotpath = '.'.join(itspath) - if not itsdotpath in sigs: - sigs[itsdotpath] = {} - if not '=' in line or not line.endswith(';'): - self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) - key, value = line.split('=', 1) - sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') - - for reqpath in req_itspaths: - if not reqpath in itspaths: - self.fail('Missing section in its file: %s' % reqpath) - - reqsigvalues_image = { - 'algo': '"sha256,rsa2048"', - 'key-name-hint': '"img-oe-selftest"', - } - reqsigvalues_config = { - 'algo': '"sha256,rsa2048"', - 'key-name-hint': '"cfg-oe-selftest"', - 'sign-images': '"kernel", "fdt", "bootscr"', - } - - for itspath, values in sigs.items(): - if 'conf-' in itspath: - reqsigvalues = reqsigvalues_config - else: - reqsigvalues = reqsigvalues_image - for reqkey, reqvalue in reqsigvalues.items(): - value = values.get(reqkey, None) - if value is None: - self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) - self.assertEqual(value, reqvalue) - - # Dump the image to see if it really got signed - uboot_tools_bindir = self._setup_uboot_tools_native() - dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) - in_signed = None - signed_sections = {} - for line in dumpimage_result.output.splitlines(): - if line.startswith((' Configuration', ' Image')): - in_signed = re.search(r'\((.*)\)', line).groups()[0] - elif re.match('^ *', line) in (' ', ''): - in_signed = None - elif in_signed: - if not in_signed in signed_sections: - signed_sections[in_signed] = {} - key, value = line.split(':', 1) - signed_sections[in_signed][key.strip()] = value.strip() - self.assertIn('kernel-1', signed_sections) - self.assertIn('fdt-am335x-boneblack.dtb', signed_sections) - self.assertIn('conf-am335x-boneblack.dtb', signed_sections) - for signed_section, values in signed_sections.items(): - value = values.get('Sign algo', None) - if signed_section.startswith("conf"): - self.assertEqual(value, 'sha256,rsa2048:cfg-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) - else: - self.assertEqual(value, 'sha256,rsa2048:img-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) - value = values.get('Sign value', None) - self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) - - # Check if the u-boot boot.scr script is in the fitImage - self._verify_fitimage_uboot_env(dumpimage_result) - - # Search for the string passed to mkimage: 1 kernel + 3 DTBs + config per DTB = 7 sections - # Looks like mkimage supports to add a comment but does not support to read it back. - found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment) - self.assertEqual(found_comments, 8, "Expected 8 signed and commented section in the fitImage.") - - # Verify the signature for all configurations = DTBs - for dtb in ['am335x-bone.dtb', 'am335x-boneblack.dtb', 'am335x-bonegreen.dtb']: - self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, - os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], dtb), 'conf-' + dtb) def test_initramfs_bundle(self): """ @@ -355,92 +716,224 @@ FIT_HASH_ALG = "sha256" """ config = self._config_add_uboot_env(config) self.write_config(config) + bb_vars = self._fit_get_bb_vars() + self._test_fitimage(bb_vars) - # fitImage is created as part of linux recipe - bitbake("virtual/kernel") - bb_vars = get_bb_vars([ +class UBootFitImageTests(FitImageTestCase): + """Test cases for the uboot-sign bbclass""" + + def _fit_get_bb_vars(self, additional_vars=[]): + """Get bb_vars as needed by _test_sign_fit_image + + Call the get_bb_vars function once and get all variables needed by the test case. + """ + internal_used = { 'DEPLOY_DIR_IMAGE', - 'FIT_HASH_ALG', - 'FIT_KERNEL_COMP_ALG', - 'INITRAMFS_IMAGE', 'MACHINE', + 'SPL_MKIMAGE_SIGN_ARGS', + 'SPL_SIGN_ENABLE', + 'SPL_SIGN_KEYNAME', 'UBOOT_ARCH', - 'UBOOT_ENTRYPOINT', - 'UBOOT_LOADADDRESS', - 'UBOOT_MKIMAGE_KERNEL_TYPE' - ], - 'virtual/kernel') - fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], - "fitImage-its-%s-%s-%s" % (bb_vars['INITRAMFS_IMAGE'], bb_vars['MACHINE'], bb_vars['MACHINE'])) - fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],"fitImage") + 'UBOOT_DTB_BINARY', + 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT', + 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS', + 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE', + 'UBOOT_FIT_CONF_USER_LOADABLES', + 'UBOOT_FIT_DESC', + 'UBOOT_FIT_HASH_ALG', + 'UBOOT_FIT_SIGN_ALG', + 'UBOOT_FIT_TEE_ENTRYPOINT', + 'UBOOT_FIT_TEE_LOADADDRESS', + 'UBOOT_FIT_TEE', + 'UBOOT_FIT_UBOOT_ENTRYPOINT', + 'UBOOT_FIT_UBOOT_LOADADDRESS', + 'UBOOT_FIT_USER_SETTINGS', + 'UBOOT_FITIMAGE_ENABLE', + 'UBOOT_NODTB_BINARY', + 'UBOOT_SIGN_IMG_KEYNAME', + } + bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/bootloader") + return bb_vars - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) + def _bitbake_fit_image(self, bb_vars): + """Bitbake the bootloader and return the paths to the its file and the FIT image""" + bitbake("virtual/bootloader") - its_file = open(fitimage_its_path) + deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] + machine = bb_vars['MACHINE'] + fitimage_its_path = os.path.join(deploy_dir_image, "u-boot-its-%s" % machine) + fitimage_path = os.path.join(deploy_dir_image, "u-boot-fitImage-%s" % machine) + return (fitimage_its_path, fitimage_path) - its_lines = [line.strip() for line in its_file.readlines()] + def _get_req_its_paths(self, bb_vars): + # image nodes + images = [ 'uboot', 'fdt', ] + if bb_vars['UBOOT_FIT_TEE'] == "1": + images.append('tee') + if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": + images.append('atf') + # if bb_vars['UBOOT_FIT_USER_SETTINGS']: - exp_node_lines = [ - 'kernel-1 {', - 'description = "Linux kernel";', - 'data = /incbin/("linux.bin");', - 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";', - 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";', - 'os = "linux";', - 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', - 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;', - 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;', - 'hash-1 {', - 'algo = "' + str(bb_vars['FIT_HASH_ALG']) +'";', - '};', - '};' + # configuration nodes + configurations = [ 'conf'] + + # Create a list of paths for all image and configuration nodes + req_its_paths = [] + for image in images: + req_its_paths.append(['/', 'images', image]) + if bb_vars['SPL_SIGN_ENABLE'] == "1": + req_its_paths.append(['/', 'images', image, 'signature']) + for configuration in configurations: + req_its_paths.append(['/', 'configurations', configuration]) + return req_its_paths + + def _get_req_its_fields(self, bb_vars): + loadables = ["uboot"] + its_field_check = [ + 'description = "%s";' % bb_vars['UBOOT_FIT_DESC'], + 'description = "U-Boot image";', + 'data = /incbin/("%s");' % bb_vars['UBOOT_NODTB_BINARY'], + 'type = "standalone";', + 'os = "u-boot";', + 'arch = "%s";' % bb_vars['UBOOT_ARCH'], + 'compression = "none";', + 'load = <%s>;' % bb_vars['UBOOT_FIT_UBOOT_LOADADDRESS'], + 'entry = <%s>;' % bb_vars['UBOOT_FIT_UBOOT_ENTRYPOINT'], + 'description = "U-Boot FDT";', + 'data = /incbin/("%s");' % bb_vars['UBOOT_DTB_BINARY'], + 'type = "flat_dt";', + 'arch = "%s";' % bb_vars['UBOOT_ARCH'], + 'compression = "none";', ] + if bb_vars['UBOOT_FIT_TEE'] == "1": + its_field_check += [ + 'description = "Trusted Execution Environment";', + 'data = /incbin/("%s");' % bb_vars['UBOOT_FIT_TEE_IMAGE'], + 'type = "tee";', + 'arch = "%s";' % bb_vars['UBOOT_ARCH'], + 'os = "tee";', + 'load = <%s>;' % bb_vars['UBOOT_FIT_TEE_LOADADDRESS'], + 'entry = <%s>;' % bb_vars['UBOOT_FIT_TEE_ENTRYPOINT'], + 'compression = "none";', + ] + loadables.insert(0, "tee") + if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": + its_field_check += [ + 'description = "ARM Trusted Firmware";', + 'data = /incbin/("%s");' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE'], + 'type = "firmware";', + 'arch = "%s";' % bb_vars['UBOOT_ARCH'], + 'os = "arm-trusted-firmware";', + 'load = <%s>;' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS'], + 'entry = <%s>;' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT'], + 'compression = "none";', + ] + loadables.insert(0, "atf") + its_field_check += [ + 'default = "conf";', + 'description = "Boot with signed U-Boot FIT";', + 'loadables = "%s";' % '", "'.join(loadables), + 'fdt = "fdt";', + ] + return its_field_check - node_str = exp_node_lines[0] + def _get_req_sigvalues_config(self, bb_vars): + # COnfigurations are not signed by uboot-sign + return {} - print ("checking kernel node\n") - self.assertIn(node_str, its_lines) + def _get_req_sigvalues_image(self, bb_vars): + if bb_vars['SPL_SIGN_ENABLE'] != "1": + return {} + req_sigvalues_image = { + 'algo': '"%s,%s"' % (bb_vars['UBOOT_FIT_HASH_ALG'], bb_vars['UBOOT_FIT_SIGN_ALG']), + 'key-name-hint': '"%s"' % bb_vars['SPL_SIGN_KEYNAME'], + } + return req_sigvalues_image - node_start_idx = its_lines.index(node_str) - node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))] + def _get_req_sections(self, bb_vars): + """Generate the expected output of dumpimage for beaglebone targets - # Remove the absolute path. This refers to WORKDIR which is not always predictable. - re_data = re.compile(r'^data = /incbin/\(.*/linux\.bin"\);$') - node = [re.sub(re_data, 'data = /incbin/("linux.bin");', cfg_str) for cfg_str in node] + The dict generated by this function is supposed to be compared against + the dict which is generated by the _dump_fitimage function. + """ + loadables = ['uboot'] + req_sections = { + "uboot": { + "Type": "Standalone Program", + "Load Address": bb_vars['UBOOT_FIT_UBOOT_LOADADDRESS'], + "Entry Point": bb_vars['UBOOT_FIT_UBOOT_ENTRYPOINT'], + }, + "fdt": { + "Type": "Flat Device Tree", + } + } + if bb_vars['UBOOT_FIT_TEE'] == "1": + loadables.insert(0, "tee") + req_sections['tee'] = { + "Type": "Trusted Execution Environment Image", + # "Load Address": bb_vars['UBOOT_FIT_TEE_LOADADDRESS'], not printed by mkimage? + # "Entry Point": bb_vars['UBOOT_FIT_TEE_ENTRYPOINT'], not printed by mkimage? + } + if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": + loadables.insert(0, "atf") + req_sections['atf'] = { + "Type": "Firmware", + "Load Address": bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS'], + # "Entry Point": bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT'], not printed by mkimage? + } + req_sections["conf"] = { + "Kernel": "unavailable", + "FDT": "fdt", + "Loadables": ','.join(loadables), + } - self.assertEqual(node, exp_node_lines, "kernel node does not match expectation") + # Add signing related properties if needed + uboot_fit_hash_alg = bb_vars['UBOOT_FIT_HASH_ALG'] + uboot_fit_sign_alg = bb_vars['UBOOT_FIT_SIGN_ALG'] + spl_sign_enable = bb_vars['SPL_SIGN_ENABLE'] + spl_sign_keyname = bb_vars['SPL_SIGN_KEYNAME'] + num_signatures = 0 + if spl_sign_enable == "1": + for section in req_sections: + if not section.startswith('conf'): + req_sections[section]['Sign algo'] = "%s,%s:%s" % \ + (uboot_fit_hash_alg, uboot_fit_sign_alg, spl_sign_keyname) + num_signatures += 1 + return (req_sections, num_signatures) - rx_configs = re.compile("^conf-.*") - its_configs = list(filter(rx_configs.match, its_lines)) + def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): + if bb_vars['UBOOT_FITIMAGE_ENABLE'] == '1' and bb_vars['SPL_SIGN_ENABLE'] == "1": + self.logger.debug("Verifying signatures in the FIT image") + else: + self.logger.debug("FIT image is not signed. Signature verification is not needed.") + return - for cfg_str in its_configs: - cfg_start_idx = its_lines.index(cfg_str) - line_idx = cfg_start_idx + 2 - node_end = False - while node_end == False: - if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" : - node_end = True - line_idx = line_idx + 1 + uboot_fit_hash_alg = bb_vars['UBOOT_FIT_HASH_ALG'] + uboot_fit_sign_alg = bb_vars['UBOOT_FIT_SIGN_ALG'] + spl_sign_keyname = bb_vars['SPL_SIGN_KEYNAME'] + fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[uboot_fit_sign_alg] + for section, values in sections.items(): + # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1") + if section.startswith("conf"): + # uboot-sign does not sign configuration nodes + pass + else: + # uboot-sign does not add hash nodes, only image signatures + sign_algo = values.get('Sign algo', None) + req_sign_algo = "%s,%s:%s" % (uboot_fit_hash_alg, uboot_fit_sign_alg, spl_sign_keyname) + self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) + sign_value = values.get('Sign value', None) + self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) - node = its_lines[cfg_start_idx:line_idx] - print("checking configuration " + cfg_str.rstrip(" {")) - rx_desc_line = re.compile(r'^description = ".*Linux kernel.*') - self.assertEqual(len(list(filter(rx_desc_line.match, node))), 1, "kernel keyword not found in the description line") - - self.assertIn('kernel = "kernel-1";', node) - - rx_sign_line = re.compile(r'^sign-images = .*kernel.*') - self.assertEqual(len(list(filter(rx_sign_line.match, node))), 1, "kernel hash not signed") - - # Verify the signature - uboot_tools_bindir = self._setup_uboot_tools_native() - self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 'am335x-bone.dtb')) - - # Check if the u-boot boot.scr script is in the fitImage - dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) - self._verify_fitimage_uboot_env(dumpimage_result) + # Search for the string passed to mkimage in each signed section of the FIT image. + # Looks like mkimage supports to add a comment but does not support to read it back. + a_comment = FitImageTestCase._get_uboot_mkimage_sign_args(bb_vars['SPL_MKIMAGE_SIGN_ARGS']) + self.logger.debug("a_comment: %s" % a_comment) + if a_comment: + found_comments = FitImageTestCase._find_string_in_bin_file(fitimage_path, a_comment) + self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % + (num_signatures, a_comment)) def test_uboot_fit_image(self): """ @@ -472,47 +965,9 @@ UBOOT_ENTRYPOINT = "0x80080000" UBOOT_FIT_DESC = "A model description" """ self.write_config(config) + bb_vars = self._fit_get_bb_vars() + self._test_fitimage(bb_vars) - # The U-Boot fitImage is created as part of the U-Boot recipe - bitbake("virtual/bootloader") - - deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') - machine = get_bb_var('MACHINE') - fitimage_its_path = os.path.join(deploy_dir_image, - "u-boot-its-%s" % (machine,)) - fitimage_path = os.path.join(deploy_dir_image, - "u-boot-fitImage-%s" % (machine,)) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - # Check that the type, load address, entrypoint address and default - # values for kernel and ramdisk in Image Tree Source are as expected. - # The order of fields in the below array is important. Not all the - # fields are tested, only the key fields that wont vary between - # different architectures. - its_field_check = [ - 'description = "A model description";', - 'type = "standalone";', - 'load = <0x80080000>;', - 'entry = <0x80080000>;', - 'default = "conf";', - 'loadables = "uboot";', - 'fdt = "fdt";' - ] - - with open(fitimage_its_path) as its_file: - field_index = 0 - for line in its_file: - if field_index == len(its_field_check): - break - if its_field_check[field_index] in line: - field_index +=1 - - if field_index != len(its_field_check): # if its equal, the test passed - self.assertTrue(field_index == len(its_field_check), - "Fields in Image Tree Source File %s did not match, error in finding %s" - % (fitimage_its_path, its_field_check[field_index])) def test_sign_standalone_uboot_fit_image(self): """ @@ -533,9 +988,8 @@ UBOOT_FIT_DESC = "A model description" work by Paul Eggleton and Usama Arif """ - a_comment = "a smart U-Boot comment" config = """ -# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at +# There's no U-boot defconfig with CONFIG_FIT_SIGNATURE yet, so we need at # least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set MACHINE = "qemuarm" UBOOT_MACHINE = "am57xx_evm_defconfig" @@ -551,104 +1005,15 @@ UBOOT_LOADADDRESS = "0x80000000" UBOOT_DTB_LOADADDRESS = "0x82000000" UBOOT_ARCH = "arm" SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" -SPL_MKIMAGE_SIGN_ARGS = "-c '%s'" +SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" UBOOT_EXTLINUX = "0" UBOOT_FIT_GENERATE_KEYS = "1" UBOOT_FIT_HASH_ALG = "sha256" -""" % a_comment - +""" self.write_config(config) + bb_vars = self._fit_get_bb_vars() + self._test_fitimage(bb_vars) - # The U-Boot fitImage is created as part of the U-Boot recipe - bitbake("virtual/bootloader") - - deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') - machine = get_bb_var('MACHINE') - fitimage_its_path = os.path.join(deploy_dir_image, - "u-boot-its-%s" % (machine,)) - fitimage_path = os.path.join(deploy_dir_image, - "u-boot-fitImage-%s" % (machine,)) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - req_itspaths = [ - ['/', 'images', 'uboot'], - ['/', 'images', 'uboot', 'signature'], - ['/', 'images', 'fdt'], - ['/', 'images', 'fdt', 'signature'], - ] - - itspath = [] - itspaths = [] - linect = 0 - sigs = {} - with open(fitimage_its_path) as its_file: - linect += 1 - for line in its_file: - line = line.strip() - if line.endswith('};'): - itspath.pop() - elif line.endswith('{'): - itspath.append(line[:-1].strip()) - itspaths.append(itspath[:]) - elif itspath and itspath[-1] == 'signature': - itsdotpath = '.'.join(itspath) - if not itsdotpath in sigs: - sigs[itsdotpath] = {} - if not '=' in line or not line.endswith(';'): - self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) - key, value = line.split('=', 1) - sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') - - for reqpath in req_itspaths: - if not reqpath in itspaths: - self.fail('Missing section in its file: %s' % reqpath) - - reqsigvalues_image = { - 'algo': '"sha256,rsa2048"', - 'key-name-hint': '"spl-oe-selftest"', - } - - for itspath, values in sigs.items(): - reqsigvalues = reqsigvalues_image - for reqkey, reqvalue in reqsigvalues.items(): - value = values.get(reqkey, None) - if value is None: - self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) - self.assertEqual(value, reqvalue) - - # Dump the image to see if it really got signed - uboot_tools_bindir = self._setup_uboot_tools_native() - dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) - in_signed = None - signed_sections = {} - for line in dumpimage_result.output.splitlines(): - if line.startswith((' Image')): - in_signed = re.search(r'\((.*)\)', line).groups()[0] - elif re.match(' \w', line): - in_signed = None - elif in_signed: - if not in_signed in signed_sections: - signed_sections[in_signed] = {} - key, value = line.split(':', 1) - signed_sections[in_signed][key.strip()] = value.strip() - self.assertIn('uboot', signed_sections) - self.assertIn('fdt', signed_sections) - for signed_section, values in signed_sections.items(): - value = values.get('Sign algo', None) - self.assertEqual(value, 'sha256,rsa2048:spl-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) - value = values.get('Sign value', None) - self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) - - # Check for SPL_MKIMAGE_SIGN_ARGS - # Looks like mkimage supports to add a comment but does not support to read it back. - found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment) - self.assertEqual(found_comments, 2, "Expected 2 signed and commented section in the fitImage.") - - # Verify the signature - self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, - os.path.join(deploy_dir_image, 'u-boot-spl.dtb')) def test_sign_cascaded_uboot_fit_image(self): """ @@ -670,7 +1035,6 @@ UBOOT_FIT_HASH_ALG = "sha256" work by Paul Eggleton and Usama Arif """ - a_comment = "a smart cascaded U-Boot comment" config = """ # There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at # least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set @@ -686,7 +1050,7 @@ UBOOT_DTB_BINARY = "u-boot.dtb" UBOOT_ENTRYPOINT = "0x80000000" UBOOT_LOADADDRESS = "0x80000000" UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" -UBOOT_MKIMAGE_SIGN_ARGS = "-c '%s'" +UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'" UBOOT_DTB_LOADADDRESS = "0x82000000" UBOOT_ARCH = "arm" SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" @@ -700,99 +1064,10 @@ UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" FIT_SIGN_INDIVIDUAL = "1" -""" % a_comment +""" self.write_config(config) - - # The U-Boot fitImage is created as part of the U-Boot recipe - bitbake("virtual/bootloader") - - deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') - machine = get_bb_var('MACHINE') - fitimage_its_path = os.path.join(deploy_dir_image, - "u-boot-its-%s" % (machine,)) - fitimage_path = os.path.join(deploy_dir_image, - "u-boot-fitImage-%s" % (machine,)) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - req_itspaths = [ - ['/', 'images', 'uboot'], - ['/', 'images', 'uboot', 'signature'], - ['/', 'images', 'fdt'], - ['/', 'images', 'fdt', 'signature'], - ] - - itspath = [] - itspaths = [] - linect = 0 - sigs = {} - with open(fitimage_its_path) as its_file: - linect += 1 - for line in its_file: - line = line.strip() - if line.endswith('};'): - itspath.pop() - elif line.endswith('{'): - itspath.append(line[:-1].strip()) - itspaths.append(itspath[:]) - elif itspath and itspath[-1] == 'signature': - itsdotpath = '.'.join(itspath) - if not itsdotpath in sigs: - sigs[itsdotpath] = {} - if not '=' in line or not line.endswith(';'): - self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) - key, value = line.split('=', 1) - sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') - - for reqpath in req_itspaths: - if not reqpath in itspaths: - self.fail('Missing section in its file: %s' % reqpath) - - reqsigvalues_image = { - 'algo': '"sha256,rsa2048"', - 'key-name-hint': '"spl-cascaded-oe-selftest"', - } - - for itspath, values in sigs.items(): - reqsigvalues = reqsigvalues_image - for reqkey, reqvalue in reqsigvalues.items(): - value = values.get(reqkey, None) - if value is None: - self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) - self.assertEqual(value, reqvalue) - - # Dump the image to see if it really got signed - uboot_tools_bindir = self._setup_uboot_tools_native() - dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) - in_signed = None - signed_sections = {} - for line in dumpimage_result.output.splitlines(): - if line.startswith((' Image')): - in_signed = re.search(r'\((.*)\)', line).groups()[0] - elif re.match(' \w', line): - in_signed = None - elif in_signed: - if not in_signed in signed_sections: - signed_sections[in_signed] = {} - key, value = line.split(':', 1) - signed_sections[in_signed][key.strip()] = value.strip() - self.assertIn('uboot', signed_sections) - self.assertIn('fdt', signed_sections) - for signed_section, values in signed_sections.items(): - value = values.get('Sign algo', None) - self.assertEqual(value, 'sha256,rsa2048:spl-cascaded-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) - value = values.get('Sign value', None) - self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) - - # Check for SPL_MKIMAGE_SIGN_ARGS - # Looks like mkimage supports to add a comment but does not support to read it back. - found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment) - self.assertEqual(found_comments, 2, "Expected 2 signed and commented section in the fitImage.") - - # Verify the signature - self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, - os.path.join(deploy_dir_image, 'u-boot-spl.dtb')) + bb_vars = self._fit_get_bb_vars() + self._test_fitimage(bb_vars) def test_uboot_atf_tee_fit_image(self): """ @@ -841,67 +1116,20 @@ UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" """ self.write_config(config) + bb_vars = self._fit_get_bb_vars([ + 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE', + 'UBOOT_FIT_TEE_IMAGE', + ]) + # Create an ATF dummy image - atf_dummy_image = os.path.join(self.builddir, "atf-dummy.bin") - cmd = 'dd if=/dev/random of=%s bs=1k count=64' % (atf_dummy_image) - result = runCmd(cmd) - self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) + dummy_atf = os.path.join(self.builddir, bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE']) + FitImageTestCase._gen_random_file(dummy_atf) # Create a TEE dummy image - tee_dummy_image = os.path.join(self.builddir, "tee-dummy.bin") - cmd = 'dd if=/dev/random of=%s bs=1k count=64' % (tee_dummy_image) - result = runCmd(cmd) - self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) - - # The U-Boot fitImage is created as part of the U-Boot recipe - bitbake("virtual/bootloader") - - deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') - machine = get_bb_var('MACHINE') - fitimage_its_path = os.path.join(deploy_dir_image, - "u-boot-its-%s" % (machine,)) - fitimage_path = os.path.join(deploy_dir_image, - "u-boot-fitImage-%s" % (machine,)) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - # Check that the type, load address, entrypoint address and default - # values for u-boot, ATF and TEE in Image Tree Source are as expected. - # The order of fields in the below array is important. Not all the - # fields are tested, only the key fields that wont vary between - # different architectures. - its_field_check = [ - 'description = "A model description";', - 'type = "standalone";', - 'load = <0x80080000>;', - 'entry = <0x80080000>;', - 'description = "Trusted Execution Environment";', - 'os = "tee";', - 'load = <0x80180000>;', - 'entry = <0x80180000>;', - 'description = "ARM Trusted Firmware";', - 'os = "arm-trusted-firmware";', - 'load = <0x80280000>;', - 'entry = <0x80280000>;', - 'default = "conf";', - 'loadables = "atf", "tee", "uboot";', - 'fdt = "fdt";' - ] - - with open(fitimage_its_path) as its_file: - field_index = 0 - for line in its_file: - if field_index == len(its_field_check): - break - if its_field_check[field_index] in line: - field_index +=1 - - if field_index != len(its_field_check): # if its equal, the test passed - self.assertTrue(field_index == len(its_field_check), - "Fields in Image Tree Source File %s did not match, error in finding %s" - % (fitimage_its_path, its_field_check[field_index])) + dummy_tee = os.path.join(self.builddir, bb_vars['UBOOT_FIT_TEE_IMAGE']) + FitImageTestCase._gen_random_file(dummy_tee) + self._test_fitimage(bb_vars) def test_sign_standalone_uboot_atf_tee_fit_image(self): """ @@ -921,7 +1149,6 @@ UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" Product: oe-core Author: Jamin Lin """ - a_comment = "a smart U-Boot ATF TEE comment" config = """ # There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at # least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set @@ -938,7 +1165,7 @@ UBOOT_ENTRYPOINT = "0x80000000" UBOOT_LOADADDRESS = "0x80000000" UBOOT_ARCH = "arm" SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" -SPL_MKIMAGE_SIGN_ARGS = "-c '%s'" +SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot ATF TEE comment'" UBOOT_EXTLINUX = "0" UBOOT_FIT_GENERATE_KEYS = "1" UBOOT_FIT_HASH_ALG = "sha256" @@ -958,115 +1185,20 @@ UBOOT_FIT_ARM_TRUSTED_FIRMWARE = "1" UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE = "${TOPDIR}/atf-dummy.bin" UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS = "0x80280000" UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" -""" % a_comment - +""" self.write_config(config) + bb_vars = self._fit_get_bb_vars([ + 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE', + 'UBOOT_FIT_TEE_IMAGE', + ]) + # Create an ATF dummy image - atf_dummy_image = os.path.join(self.builddir, "atf-dummy.bin") - cmd = 'dd if=/dev/random of=%s bs=1k count=64' % (atf_dummy_image) - result = runCmd(cmd) - self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) + dummy_atf = os.path.join(self.builddir, bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE']) + FitImageTestCase._gen_random_file(dummy_atf) # Create a TEE dummy image - tee_dummy_image = os.path.join(self.builddir, "tee-dummy.bin") - cmd = 'dd if=/dev/random of=%s bs=1k count=64' % (tee_dummy_image) - result = runCmd(cmd) - self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) + dummy_tee = os.path.join(self.builddir, bb_vars['UBOOT_FIT_TEE_IMAGE']) + FitImageTestCase._gen_random_file(dummy_tee) - # The U-Boot fitImage is created as part of the U-Boot recipe - bitbake("virtual/bootloader") - - deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') - machine = get_bb_var('MACHINE') - fitimage_its_path = os.path.join(deploy_dir_image, - "u-boot-its-%s" % (machine,)) - fitimage_path = os.path.join(deploy_dir_image, - "u-boot-fitImage-%s" % (machine,)) - - self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) - self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) - - req_itspaths = [ - ['/', 'images', 'uboot'], - ['/', 'images', 'uboot', 'signature'], - ['/', 'images', 'fdt'], - ['/', 'images', 'fdt', 'signature'], - ['/', 'images', 'tee'], - ['/', 'images', 'tee', 'signature'], - ['/', 'images', 'atf'], - ['/', 'images', 'atf', 'signature'], - ] - - itspath = [] - itspaths = [] - linect = 0 - sigs = {} - with open(fitimage_its_path) as its_file: - linect += 1 - for line in its_file: - line = line.strip() - if line.endswith('};'): - itspath.pop() - elif line.endswith('{'): - itspath.append(line[:-1].strip()) - itspaths.append(itspath[:]) - elif itspath and itspath[-1] == 'signature': - itsdotpath = '.'.join(itspath) - if not itsdotpath in sigs: - sigs[itsdotpath] = {} - if not '=' in line or not line.endswith(';'): - self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) - key, value = line.split('=', 1) - sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') - - for reqpath in req_itspaths: - if not reqpath in itspaths: - self.fail('Missing section in its file: %s' % reqpath) - - reqsigvalues_image = { - 'algo': '"sha256,rsa2048"', - 'key-name-hint': '"spl-oe-selftest"', - } - - for itspath, values in sigs.items(): - reqsigvalues = reqsigvalues_image - for reqkey, reqvalue in reqsigvalues.items(): - value = values.get(reqkey, None) - if value is None: - self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) - self.assertEqual(value, reqvalue) - - # Dump the image to see if it really got signed - uboot_tools_bindir = self._setup_uboot_tools_native() - dumpimage_result = self._run_dumpimage(fitimage_path, uboot_tools_bindir) - in_signed = None - signed_sections = {} - for line in dumpimage_result.output.splitlines(): - if line.startswith((' Image')): - in_signed = re.search(r'\((.*)\)', line).groups()[0] - elif re.match(' \w', line): - in_signed = None - elif in_signed: - if not in_signed in signed_sections: - signed_sections[in_signed] = {} - key, value = line.split(':', 1) - signed_sections[in_signed][key.strip()] = value.strip() - self.assertIn('uboot', signed_sections) - self.assertIn('fdt', signed_sections) - self.assertIn('tee', signed_sections) - self.assertIn('atf', signed_sections) - for signed_section, values in signed_sections.items(): - value = values.get('Sign algo', None) - self.assertEqual(value, 'sha256,rsa2048:spl-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) - value = values.get('Sign value', None) - self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) - - # Check for SPL_MKIMAGE_SIGN_ARGS - # Looks like mkimage supports to add a comment but does not support to read it back. - found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment) - self.assertEqual(found_comments, 4, "Expected 4 signed and commented section in the fitImage.") - - # Verify the signature - self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, - os.path.join(deploy_dir_image, 'u-boot-spl.dtb')) + self._test_fitimage(bb_vars) From patchwork Mon Mar 10 09:35:49 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adrian Freihofer X-Patchwork-Id: 58543 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1952EC282DE for ; Mon, 10 Mar 2025 09:37:16 +0000 (UTC) Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) by mx.groups.io with SMTP id smtpd.web10.33762.1741599425843135804 for ; Mon, 10 Mar 2025 02:37:06 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20230601 header.b=Jqok57Ld; spf=pass (domain: gmail.com, ip: 209.85.221.43, mailfrom: adrian.freihofer@gmail.com) Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-3913d129c1aso987487f8f.0 for ; Mon, 10 Mar 2025 02:37:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741599424; x=1742204224; darn=lists.openembedded.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=Ag88PIB5bnXRAaSn0yh8boc2KFmajBDjctdhyev27H4=; b=Jqok57LdFnenTvUPKG/ONJmYJQkbnPMphMW7cmLMKTWmx5izutxQVmSfXrV6YqEIbW orRglyqeQuO+9lApOYZDaa4KjVQNbKZK0gwNbNrkQHHvpxRR9xUJjM9yu8PTkbCHwbGv 2lZJYkwIbcyAThwq1F5uTlwew8gpOMGSDnzvR26Y9smLO8l2hklEl1WBXROEDtcvfpNw Gl9iFD+d9wNDaiMtoFo0P+FkMU7UuiDOtr/yf8/SIqdBgoW9ACcMichctoIR/Z+FLOTD J0dC4EbOYoCH7gTPvDQZXSeyXwdCV0zc+g55Uv4uUOlgsQpzMECWF0aGLVSKeDY+pwXP pd5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741599424; x=1742204224; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Ag88PIB5bnXRAaSn0yh8boc2KFmajBDjctdhyev27H4=; b=RqoOLbmKbvl9sn903HgMdzl7YDOR0r7p0moZEAJwwFwpmZAeGkBc/0n6hthgsIADMD K+QVpVaTn61QDyy600H6aM9RY3JjLwzfsdZvVfzDOcLmjIIoL1D9R22LHYksfralpaTD F1YQKXzUBdzGUDt7Q0AoEGr4kxi1xBuXcti7C/X377azWI1wAcjiGtA4iFBbdBIUinO0 mzri1/0jc/EBv5GjFgoNhVrxjr0etLPWdKAzwel86c87RQq1crV7J5ET2phYhOvxW2iV 4X8Z5EJIZTwdJR6hfr1I/+90P/e9t1XW1eZwzE6Q2dhqT+JbK+x/E/qwTqTKP9F28Ubj IcuQ== X-Gm-Message-State: AOJu0Yy7IIM74v32Ggb03Kb+Lw9n7Uwat+nIK8hUvL2nQe6qBuiDDB7f D69fONQmkszByQybOgvfRyr1kvBmew/OgP00q0Wg9HIvXt9J1Ps/6hSeQg== X-Gm-Gg: ASbGncv4Z9r/YGZuaMWs5UNVTIkCyC/sj0jTanAAPSDzw0am5FBa+nGJWV/Hi1iQEQz kxQ1uFJIPsUIbGvk6NoObPYWevVTYbr9X7KmXlzhSb+bkY2QWr+q4zqib2pT5Y/zZSzgyFgwRIN 995z/yAkxUiEfn6yCo6+tQ/kaxJHrpO06LvKTPUy6eCuqo2nKAHThKhH/5QJe8ehpO/FZPdE9Px fv3Ib81QN2g92mEYSJienlbWzmVo+jCSsE1bwK61su8V8WDiMUdUMtBFe53EbEG0iZj7Bzpnojq cJd98WYBiXRZmEmREhw/KWB3NiIDlVhWu/iJFur0Px1d9i+31Gjmbh2JubCWNsdlvLY= X-Google-Smtp-Source: AGHT+IFOleMv8DrxwhYdITWB+04vfCdoKxd65BxA1E1JUQd2zZtmJ6wVfW/OO9E1buAO6FSoMGEbSQ== X-Received: by 2002:a05:6000:184b:b0:38f:23c4:208c with SMTP id ffacd0b85a97d-3913af2e472mr4961017f8f.18.1741599423958; Mon, 10 Mar 2025 02:37:03 -0700 (PDT) Received: from wsadrian16.fritz.box ([2a02:169:59a6:0:55c4:f628:91f3:4287]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3912bfb79fbsm14554454f8f.13.2025.03.10.02.37.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 02:37:03 -0700 (PDT) From: Adrian Freihofer X-Google-Original-From: Adrian Freihofer To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH 7/7] oe-selftest: fitimage add more kernel tests Date: Mon, 10 Mar 2025 10:35:49 +0100 Message-ID: <20250310093641.1983560-8-adrian.freihofer@siemens.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250310093641.1983560-1-adrian.freihofer@siemens.com> References: <20250310093641.1983560-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 10 Mar 2025 09:37:16 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/212502 * Test with only one externally provided ssh key not only with two keys generated by the kernel-fitimage.bbclass itself. * Add a test which signs only the configuration but not the image nodes. There was no test case which covered the probably much more important use case of setting FIT_SIGN_INDIVIDUAL = "0". * Cover also the unbundled initramfs use case. Also this use case is probably much more relevant than the bundled initramnfs use case. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/fitimage.py | 151 ++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 6f3bf296d56..721628d8e73 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -39,6 +39,52 @@ class FitImageTestCase(OESelftestTestCase): MKIMAGE_HASH_LENGTHS = { 'sha256': 64, 'sha384': 96, 'sha512': 128 } MKIMAGE_SIGNATURE_LENGTHS = { 'rsa2048': 512 } + def _gen_signing_key(self, bb_vars): + """Generate a key pair and a singing certificate + + Generate a UBOOT_SIGN_KEYNAME in the UBOOT_SIGN_KEYDIR similar to what + the FIT_GENERATE_KEYS feature does. However, having a static key is + probably a more realistic use case than generating a random key with + each clean build. So this needs to be tested as well. + The FIT_GENERATE_KEYS generates 2 keys: The UBOOT_SIGN_KEYNAME and the + UBOOT_SIGN_IMG_KEYNAME. The UBOOT_SIGN_IMG_KEYNAME is used by the + FIT_SIGN_INDIVIDUAL feature only. Testing if everything is working if + there is only one key available is important as well. Therefore this + function generates only the keys which are really needed, not just two. + """ + + # Define some variables which are usually defined by the kernel-fitimage.bbclass. + # But for testing purpose check if the uboot-sign.bbclass is independent from + # the kernel-fitimage.bbclass + fit_sign_numbits = bb_vars['FIT_SIGN_NUMBITS'] or "2048" + fit_key_genrsa_args = bb_vars['FIT_KEY_GENRSA_ARGS'] or "-F4" + fit_key_req_args = bb_vars['FIT_KEY_REQ_ARGS'] or "-batch -new" + fit_key_sign_pkcs = bb_vars['FIT_KEY_SIGN_PKCS'] or "-x509" + + uboot_sign_keydir = bb_vars['UBOOT_SIGN_KEYDIR'] + sign_keys = [bb_vars['UBOOT_SIGN_KEYNAME']] + if bb_vars['FIT_SIGN_INDIVIDUAL'] == "1": + sign_keys.append(bb_vars['UBOOT_SIGN_IMG_KEYNAME']) + for sign_key in sign_keys: + sing_key_path = os.path.join(uboot_sign_keydir, sign_key) + if not os.path.isdir(uboot_sign_keydir): + os.makedirs(uboot_sign_keydir) + openssl_bindir = FitImageTestCase._setup_native('openssl-native') + openssl_path = os.path.join(openssl_bindir, 'openssl') + runCmd("%s genrsa %s -out %s.key %s" % ( + openssl_path, + fit_key_genrsa_args, + sing_key_path, + fit_sign_numbits + )) + runCmd("%s req %s %s -key %s.key -out %s.crt" % ( + openssl_path, + fit_key_req_args, + fit_key_sign_pkcs, + sing_key_path, + sing_key_path + )) + @staticmethod def _gen_random_file(file_path, num_bytes=65536): with open(file_path, 'wb') as file_out: @@ -632,7 +678,50 @@ FIT_DESC = "A model description" self._test_fitimage(bb_vars) - def test_sign_fit_image(self): + def test_sign_fit_image_configurations(self): + """ + Summary: Check if FIT image and Image Tree Source (its) are created + and the configuration nodes are signed correctly. + Expected: 1) its and FIT image are built successfully + 2) Scanning the its file indicates signing is enabled + as requested by UBOOT_SIGN_ENABLE (using 1 key + generated by the test not via FIT_GENERATE_KEYS) + 3) Dumping the FIT image indicates signature values + are present (only for the configuration nodes as + FIT_SIGN_INDIVIDUAL is disabled) + 4) Verify the FIT image contains the comments passed via + UBOOT_MKIMAGE_SIGN_ARGS once per configuration node. + """ + # Generate a configuration section which gets included into the local.conf file + config = """ +# Enable creation of fitImage +MACHINE = "beaglebone-yocto" +KERNEL_IMAGETYPES += " fitImage " +KERNEL_CLASSES = " kernel-fitimage " +UBOOT_SIGN_ENABLE = "1" +UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" +UBOOT_SIGN_KEYNAME = "dev" +UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" +""" + config = self._config_add_uboot_env(config) + self.write_config(config) + + # Retrieve some variables from bitbake + bb_vars = self._fit_get_bb_vars([ + 'FIT_KEY_GENRSA_ARGS', + 'FIT_KEY_REQ_ARGS', + 'FIT_KEY_SIGN_PKCS', + 'FIT_SIGN_NUMBITS', + 'UBOOT_SIGN_KEYDIR', + ]) + + # Do not use the random keys generated by FIT_GENERATE_KEYS. + # Using a static key is probably a more realistic scenario. + self._gen_signing_key(bb_vars) + + self._test_fitimage(bb_vars) + + def test_sign_fit_image_individual(self): """ Summary: Check if FIT image and Image Tree Source (its) are created and all nodes are signed correctly. @@ -673,8 +762,66 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" bb_vars = self._fit_get_bb_vars() self._test_fitimage(bb_vars) + def test_fit_image_sign_initramfs(self): + """ + Summary: Verifies the content of the initramfs node in the FIT Image Tree Source (its) + The FIT settings are set by the test case. + The machine used is beaglebone-yocto. + Expected: 1. The ITS is generated with initramfs support + 2. All the fields in the kernel node are as expected (matching the + conf settings) + 3. The kernel is included in all the available configurations and + its hash is included in the configuration signature - def test_initramfs_bundle(self): + Product: oe-core + Author: Abdellatif El Khlifi + """ + + config = """ +DISTRO="poky" +MACHINE = "beaglebone-yocto" +INITRAMFS_IMAGE = "core-image-minimal-initramfs" +INITRAMFS_SCRIPTS = "" +UBOOT_MACHINE = "am335x_evm_defconfig" +KERNEL_CLASSES = " kernel-fitimage " +KERNEL_IMAGETYPES = "fitImage" +UBOOT_SIGN_ENABLE = "1" +UBOOT_SIGN_KEYNAME = "beaglebonekey" +UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" +UBOOT_DTB_BINARY = "u-boot.dtb" +UBOOT_ENTRYPOINT = "0x80000000" +UBOOT_LOADADDRESS = "0x80000000" +UBOOT_RD_LOADADDRESS = "0x88000000" +UBOOT_RD_ENTRYPOINT = "0x88000000" +UBOOT_DTB_LOADADDRESS = "0x82000000" +UBOOT_ARCH = "arm" +UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" +UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" +UBOOT_EXTLINUX = "0" +FIT_GENERATE_KEYS = "1" +KERNEL_IMAGETYPE_REPLACEMENT = "zImage" +FIT_KERNEL_COMP_ALG = "none" +FIT_HASH_ALG = "sha256" +""" + config = self._config_add_uboot_env(config) + self.write_config(config) + + # Retrieve some variables from bitbake + bb_vars = self._fit_get_bb_vars([ + 'FIT_KEY_GENRSA_ARGS', + 'FIT_KEY_REQ_ARGS', + 'FIT_KEY_SIGN_PKCS', + 'FIT_SIGN_NUMBITS', + 'UBOOT_SIGN_KEYDIR', + ]) + + # Do not use the random keys generated by FIT_GENERATE_KEYS. + # Using a static key is probably a more realistic scenario. + self._gen_signing_key(bb_vars) + + self._test_fitimage(bb_vars) + + def test_fit_image_sign_initramfs_bundle(self): """ Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its) The FIT settings are set by the test case.