From mboxrd@z Thu Jan 1 00:00:00 1970 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 phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 89994C433F5 for ; Mon, 3 Oct 2022 12:01:01 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 2322684D55; Mon, 3 Oct 2022 14:00:59 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=sancloud.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=sancloud.onmicrosoft.com header.i=@sancloud.onmicrosoft.com header.b="jqYbItyB"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 934D984DE5; Mon, 3 Oct 2022 14:00:57 +0200 (CEST) Received: from GBR01-CWL-obe.outbound.protection.outlook.com (mail-cwlgbr01on2108.outbound.protection.outlook.com [40.107.11.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id F0FA584915 for ; Mon, 3 Oct 2022 14:00:51 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=sancloud.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=paul.barker@sancloud.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=HBtvFB1ORFbNEEkwFrWuqcqPf4HKpuP6PBO9oDOuTuTPFZOrxO57b3aDHXcRYuKaqvl8qZreMLZJSfkspog4bwcqCZTTUXBs3Tl2A2VMWiTt98VX7uU3/3gNse7GT9aykj94IoKXZK6Q/042TbiGNIW0MKtJw/eziGb1Gxt+Yl6FPbGWYeAiNQ9rEeiszehgom2WdfEg9HI914QtGrkD83XOxJJv7BsKUwTYs9mgTtZOsoe11f/GFmtijsDoqDQiLBZ9axVXGySP71F1/ChKuBMmtx76W/ljJeAykQux8agxBV9f7CXKV1JPQXnulcPYBa+GslTgYql7riULtd03rQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Rm72pro2B3oJETblTEOWJn62T8W0DlgxqF4FKqF2yF4=; b=a5i3lnHetICYOcY5ZvvaXyDqV3fTyt99Ru5aAmCvKcJeRGkO/CxVBw9F5KnN+2rvfV6nryQwCLbm3kk6He5ipeXxe7GqIqSJCtK2OxUjIfMmqVtYmbxfl+ke4/jO25x4/I3KcWA08mwKYiN6VPUVEDVh/7n2BQPMh9ZKqmvkWZsd7tQvyh/8Eo3wwM6G5pL69xh8sG8ZeORGF26IWTKoEjclq2CdDy49EBVy2IBE5ef4N7J/SCo/pyUkSHVFEhqfaPnx9P8pvv/A1X8DbSozoSZaq2Xl/1OPQUAe6O0hKVplN1Jv8c3owayiJiZZXZcEKrmDxf3c8+vYZGNMlH6DZQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=sancloud.com; dmarc=pass action=none header.from=sancloud.com; dkim=pass header.d=sancloud.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sancloud.onmicrosoft.com; s=selector2-sancloud-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Rm72pro2B3oJETblTEOWJn62T8W0DlgxqF4FKqF2yF4=; b=jqYbItyBN6fODtwL1jHwF3gSwGdIwpyEjYocN0EjFotMWL48cmk2EXklit+Oz62WxsfpNrKeWMdiyB48Sgaq0ERLdgZ1M9H9R2T5NG9uGADYqE9cCS9zjQT4ANP7Tv94wid+jQ+eC8MhKjwKnbHLmN18JWVa1SLRrcAkL54JwKQ= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=sancloud.com; Received: from CWLP123MB2241.GBRP123.PROD.OUTLOOK.COM (2603:10a6:401:61::19) by CWLP123MB2707.GBRP123.PROD.OUTLOOK.COM (2603:10a6:400:51::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5676.24; Mon, 3 Oct 2022 12:00:50 +0000 Received: from CWLP123MB2241.GBRP123.PROD.OUTLOOK.COM ([fe80::11d5:40af:9060:6b08]) by CWLP123MB2241.GBRP123.PROD.OUTLOOK.COM ([fe80::11d5:40af:9060:6b08%6]) with mapi id 15.20.5676.028; Mon, 3 Oct 2022 12:00:50 +0000 Message-ID: <5c31d901-ff55-e25d-8e10-64fe1acbfb80@sancloud.com> Date: Mon, 3 Oct 2022 13:00:48 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Thunderbird/102.3.1 To: Ilias Apalodimas Cc: u-boot@lists.denx.de, Heinrich Schuchardt , Tom Rini References: <20220921160628.4166966-1-paul.barker@sancloud.com> <20220921160628.4166966-2-paul.barker@sancloud.com> From: Paul Barker Subject: Re: [PATCH v3 1/3] efi_loader: Add SPI I/O protocol support In-Reply-To: Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="------------zjXNwzL0eJwQF8Gq1uSfCImS" X-ClientProxiedBy: LO4P123CA0446.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:1a9::19) To CWLP123MB2241.GBRP123.PROD.OUTLOOK.COM (2603:10a6:401:61::19) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CWLP123MB2241:EE_|CWLP123MB2707:EE_ X-MS-Office365-Filtering-Correlation-Id: 6636843f-7bd0-4a33-c5ab-08daa536e9f3 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 04m6vz6YEbiVtvGMHqKJlAcUuet2h7ULoaqsSfBbAcSteN5KocXeay7UZJsEmbvJGiDK5M6VDouh0H87qefcV9uZ8O60N3/Ze1pJubWZGGcMZJSFDu3GuHbRrxBLwIU5CbWSU6YDR26CKj9tX9wwYTfal2VntWlPK2j4bVZByWHJrWeNMKrU6BMGma6BR62ZFow0zMQ1s31K/7g/0MEMsg6yF78OYkXXm+Adct2gy5PoOYP7l4fzvUrpGxwQ0kKz/jaUSKH4DUXf3Da28nRZBtKr0V6kW68zrcc2luUSh9pQ5+G0C/nF2WX6kETBfc5+aKzkpcLYyW0Mvc8kLT3AsldErdwVp1w3l6QQT6AVwrvNLPF/SQlTqycnMbcWQxfNAdR2zjjLueDu7s0KWNIc6S3phqtUXZFzx8yIEE0LAVAOrekX16f5b008ufNYotpTeCKM07jI5EnGAhC6cLCn9BPLNiJveg68EZFrbsGwPqPhImN6z90LePO0vQIo6gDMd3ErgIfvRJw8fliJYhDTiPEi0INVcEAYEzun+XXpI7aOn2vzTnetscUjR+tQU/J4npW2QwyB1IQC72MOLvy5TJPvalMbGDOHN+neLuoKGeO/5FudJijupnw0HGP0YeDLYLt9cHLFbe9RbOznAwsuc/lTL5JHcYdV2ZTQEFt0kmj7Xhf0SOv2kPbxSC+M5lA6eWQZ7XXRRd9GiAVQ80UB/1CYjGOczknF+fTkQPqHKqNC7FWdvj2JsXZWrzvHmrc3uf5vPGgSJtbUF+VP21szq3Rsrp+dt/awoMXHXDezpK4ZPrcUvHiyDxKIuxOgRiruGJJXe2Gv0o53FFx5xPqVzG5oqn9wptg64BVfQi/eF5o= X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:CWLP123MB2241.GBRP123.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230022)(396003)(366004)(39830400003)(346002)(136003)(376002)(451199015)(316002)(31686004)(235185007)(30864003)(36756003)(8936002)(5660300002)(4326008)(66946007)(66556008)(66476007)(8676002)(478600001)(6916009)(2906002)(86362001)(966005)(54906003)(6486002)(41300700001)(44832011)(31696002)(2616005)(52116002)(53546011)(6506007)(6512007)(33964004)(26005)(83380400001)(21480400003)(186003)(38100700002)(38350700002)(43740500002)(45980500001); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?RW1jL0kvUXdBN0hRV2Jjd3djb2lIczVXcXVvODBzNUFZU2xUSENROUV5cUMr?= =?utf-8?B?eHIrZXh2bHQ4a291U1JlWStQV1VMWDlneHF6NFNDK0JUbjhNQjJUdm9KcmN4?= =?utf-8?B?alBFeXppQXdMMDZjKzJxTXA3a1lpWUZ6STBQMlYycXdwRUdRQ1c4YVgzZDJl?= =?utf-8?B?UnBOWUFXR2pBa0Y3Z3ZKQVlDdGNjaHVQcFdnSHJDZDJQVUlYZXUycWJZUXpN?= =?utf-8?B?emFWSjJmTkJhdjNIQk54RGFaN3ZlZDhjSitKTjJnZFRnNEhmSGc3L0M3WGl4?= =?utf-8?B?ZVhkZW5sU2IrcWFaNTMwelVUakdZKzV3ZW9LUEE5cmJhTHpuRFRjMFRZYUpG?= =?utf-8?B?UzNvb1ZwRGxLNndoVFFpdml5clFtVHhkWTBPN0p0Mk9RZHkwcjhjZ3IrczFM?= =?utf-8?B?MVJydzhhN2dPNlVrSTRsckwyYktSeDE1SC9DbCtFNU0xNVB1WXF4bS9iSzE4?= =?utf-8?B?dGFoYlQzSDlhM1hZbWRUL3J1cVNOT2pqVGJDbFkzb3AxMzNicm9vbFc2MEw2?= =?utf-8?B?dTNORG5mNHB6c0RqYTZ2WEhGOVE1N1RNK2V2S3dFb3hJVFBBQklPTHlxSUNM?= =?utf-8?B?TmUyRFVpeTQ1S1VIVEtIVGZVZG9Bck5OUFB4amtBbVFoaGxPcDJEZ3FUZzZq?= =?utf-8?B?dmh3RHZJakRqSkhpL0NKVHZZR1Foc1Njbis1WTRWTmZ2TTc4bEQwU1RMVWpJ?= =?utf-8?B?YWR0Z2dOc3Z4cHMyZ1ovQVhPdlNEalJua1UvOHhzSmUrSVlkdDNCMXMvVXJk?= =?utf-8?B?T0RQMHBtaUdLKzZXUHlrdWExdEE3bWt4SkhHcFh3RzJwQnZZejBUWlNGbnN1?= =?utf-8?B?U21USXJJVEVaenJ4N3JXVThVNkJLYU9GVWFPL1l6bDZWMVhFZjExWUk3TjYw?= =?utf-8?B?R1pSenBmQm9jUUMrRUt6L3FCdmJ6ZkRxVDlPMUdRUjhEZFZYZjFnMnUrckJT?= =?utf-8?B?TTFkSFZCM0FnYmQ1dVlJRmpDNEhIMjI0cWwxVXhDbkVuTllOUDZBS2VOWkh5?= =?utf-8?B?c2dYTi9LSzg3aStVcko4SjFLcGx6em1xVEMyczRYN3pxN0duWEpsc0JoRTB1?= =?utf-8?B?TGkvVThVL2lFYWQxM01qak5hbjVBTHhLTE1QdjFXc3gxMzN6aEhHVTYyOFNS?= =?utf-8?B?NU0rUjI4dVA3Y29hNkRvVlcyQ0t1TU1UeVVFS2M0cmhWdTZKRGV4L1lLdDM0?= =?utf-8?B?SkVQbjRpR1FWNmtXdHFicERoSFJLa2l1L09CYlZlME1jZUprMTlCeUx4bXF5?= =?utf-8?B?TDN2WU5pZzZweFl0VEYwZTROVTlFejJQeVJGY3ZveUM4WU9tUjVaQS9Ocmd0?= =?utf-8?B?MW1wUHhtR3VocG1hMEYzVXpKbjZIZTVYZXk1S1hQSGJZYlU1RzBhVXdaQ1Zp?= =?utf-8?B?TDl1ekk3OEUvRGk0RThRWlhpMFBpOFpMWGtoUHU0R1BTeXBUTzZJM09zSFRw?= =?utf-8?B?akg1cDZpQVZBdWdVOWFscWpaVW9MelNhREZPRVBIYXdWcXhZa0Q4dVNYeHRB?= =?utf-8?B?YjAxK0pQS0RQcTdBb2pjallJN1NFS1VQck1WR3p1L3Y4UGI2ZlljamFsS3dh?= =?utf-8?B?c3NZMUo0bWxZejZQWEJvUlNyVjFraHorVjYrRTNTTmJ6Tm5zSEZTeWRDVGpj?= =?utf-8?B?dHh0dnVMcU5NWG9RSHQyd1ZMMkQ2dStxMWVrUU9yeTRRVUltSGpDVFhhV2ZP?= =?utf-8?B?Wm5JSXlRNEkvYXREbUJJUXFLY09aejluejhpUlBoZ3cwVmF0Wk5Ua2I1Tmxl?= =?utf-8?B?NmkyWUJ1YXQ3UURUOG5hYnhrK2xvQzZycE1hUG1UTFJVSzh5L2VSeTQ4V2hl?= =?utf-8?B?bDhPRUlSMUdTSVMvNVRNeUhmdzhNS3RnU3VqbG1Wcm1BK3FvbGFBRHNSZzR5?= =?utf-8?B?cmJnMmJjQi9VS3hSUlU0d0ptYUYvdk9tVTNBOEdRdGRUL3hocFVoVHRCdDUv?= =?utf-8?B?M0x0a3JEbHNCSnFNaStZNGIxN1dsdjhxN1NUSnhUaHVrZG5zNFRxcndmNDlG?= =?utf-8?B?TXJRS1l6V2Jhcm5KbzcxbkZrNlJIZmFFZnlDbjJNa0lvb1hSV1R3bEZ0R0tP?= =?utf-8?B?bGttRTQxYmVnOXEwYlEwbzZKSElieFVQaThQY1cwb3VjM2lvNG9EdThoa3VK?= =?utf-8?B?U2tydFg3eEhMTE5PWXBHSU1peUo4S2g0dXBVUFBDZG4wck9BTWRiRDBGTzl6?= =?utf-8?B?Ymc9PQ==?= X-OriginatorOrg: sancloud.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6636843f-7bd0-4a33-c5ab-08daa536e9f3 X-MS-Exchange-CrossTenant-AuthSource: CWLP123MB2241.GBRP123.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Oct 2022 12:00:49.9480 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 840be37c-244a-450e-9bcc-2064862de1f4 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: TEW1TqRS4fTEYCkbCPBO9Y5iQiut/SRXg9plJOsejW9DlLa/LOvIDfTTsyvd/3kI0+UIGBd0ac/wtIuXtZL0crObVKyAxGfxRATm8DL2Fzc= X-MS-Exchange-Transport-CrossTenantHeadersStamped: CWLP123MB2707 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.6 at phobos.denx.de X-Virus-Status: Clean --------------zjXNwzL0eJwQF8Gq1uSfCImS Content-Type: multipart/mixed; boundary="------------zzlLgyMa12lr6mzUSioPrv69"; protected-headers="v1" From: Paul Barker To: Ilias Apalodimas Cc: u-boot@lists.denx.de, Heinrich Schuchardt , Tom Rini Message-ID: <5c31d901-ff55-e25d-8e10-64fe1acbfb80@sancloud.com> Subject: Re: [PATCH v3 1/3] efi_loader: Add SPI I/O protocol support References: <20220921160628.4166966-1-paul.barker@sancloud.com> <20220921160628.4166966-2-paul.barker@sancloud.com> In-Reply-To: --------------zzlLgyMa12lr6mzUSioPrv69 Content-Type: multipart/mixed; boundary="------------jn6roVf3nxuh7Xntd8lVV0Ci" --------------jn6roVf3nxuh7Xntd8lVV0Ci Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable On 26/09/2022 13:43, Ilias Apalodimas wrote: > Hi Paul,=20 >=20 > On Wed, Sep 21, 2022 at 05:06:26PM +0100, Paul Barker wrote: >> This addition allows UEFI applications running under u-boot to access >> peripherals on SPI busses. It is based on the UEFI Platform >> Initialization (PI) Specification, Version 1.7 Errata A (April 2020). >> Only the core functionality required to discover SPI peripherals and >> communicate with them is currently implemented. Other functionality su= ch >> as the legacy SPI controller interface and the ability to update the S= PI >> peripheral object associated with a particular SPI I/O protocol object= >> is currently unimplemented. >> >> The following protocols are defined: >> * EFI_SPI_CONFIGURATION_PROTOCOL >> * EFI_SPI_IO_PROTOCOL >> * EFI_LEGACY_SPI_CONTROLLER_PROTOCOL >> >> Since there are no open source implementations of these protocols to u= se >> as an example, educated guesses/hacks have been made in cases where th= e >> UEFI PI specification is unclear and these are documented in comments.= >> >> This implementation has been tested on the SanCloud BBE Lite and allow= ed >> a UEFI test application to successfully communicate with a Micron >> Authenta flash device connected via the SPI bus. It has also been test= ed >> with the sandbox target using the included efi_selftest case. >> >> Signed-off-by: Paul Barker >> --- >> MAINTAINERS | 7 + >> arch/sandbox/dts/test.dts | 13 + >> include/efi_api.h | 4 + >> include/efi_loader.h | 4 + >> include/efi_spi_protocol.h | 166 +++++ >> lib/efi_loader/Kconfig | 8 + >> lib/efi_loader/Makefile | 1 + >> lib/efi_loader/efi_setup.c | 6 + >> lib/efi_loader/efi_spi_protocol.c | 614 ++++++++++++++++++= + >> lib/efi_selftest/Makefile | 1 + >> lib/efi_selftest/efi_selftest_spi_protocol.c | 284 +++++++++ >> lib/uuid.c | 4 + >> 12 files changed, 1112 insertions(+) >> create mode 100644 include/efi_spi_protocol.h >> create mode 100644 lib/efi_loader/efi_spi_protocol.c >> create mode 100644 lib/efi_selftest/efi_selftest_spi_protocol.c >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> index 83346183ee4b..a58b2083a218 100644 >> --- a/MAINTAINERS >> +++ b/MAINTAINERS >> @@ -857,6 +857,13 @@ F: tools/efivar.py >> F: tools/file2include.c >> F: tools/mkeficapsule.c >> =20 >> +EFI SPI SUPPORT >> +M: Paul Barker >> +S: Maintained >> +F: include/efi_spi_protocol.h >> +F: lib/efi_loader/efi_spi_protocol.c >> +F: lib/efi_selftest/efi_selftest_spi_protocol.c >> + >> EFI VARIABLES VIA OP-TEE >> M: Ilias Apalodimas >> S: Maintained >> diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts >> index 2761588f0dad..05c3e0377ac4 100644 >> --- a/arch/sandbox/dts/test.dts >> +++ b/arch/sandbox/dts/test.dts >> @@ -1185,6 +1185,13 @@ >> compatible =3D "spansion,m25p16", "jedec,spi-nor"; >> spi-max-frequency =3D <40000000>; >> sandbox,filename =3D "spi.bin"; >> + >> + uefi-spi-vendor =3D "spansion"; >> + uefi-spi-part-number =3D "mt25p16"; >> + >> + /* GUID in UEFI format: b881eb5d-ad92-4a48-8fdd-fa75a8e4c6b8 */ >> + uefi-spi-io-guid =3D [5d eb 81 b8 92 ad 48 4a >> + 8f dd fa 75 a8 e4 c6 b8]; >> }; >> spi.bin@1 { >> reg =3D <1>; >> @@ -1193,6 +1200,12 @@ >> sandbox,filename =3D "spi.bin"; >> spi-cpol; >> spi-cpha; >> + >> + uefi-spi-vendor =3D "spansion"; >> + uefi-spi-part-number =3D "mt25p16"; >=20 > This is needed to identify the flash we want to access through the prot= ocol > right? We keep dumping info on the DT that I am not sure it belongs the= re.=20 In export_spi_peripheral(), we need to be able to get these values given a struct udevice pointer. I can't think of anywhere else to put them other than the device tree, we don't want to go back to hardcoding things in board.c files. If there's somewhere better to store this info then let me know. >=20 >> + /* GUID in UEFI format: b6b39ecd-2b1f-a643-b8d7-3192d7cf7270 */ >> + uefi-spi-io-guid =3D [cd 9e b3 b6 1f 2b 43 a6 >> + b8 d7 31 92 d7 cf 72 70]; >> }; >> }; >> =20 >=20 > [...] >=20 >> + */ >> + >> +#define LOG_CATEGORY LOGC_EFI >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +static efi_string_t convert_efi_string(const char *str) >> +{ >> + efi_string_t str_16, tmp; >> + size_t sz_8, sz_16; >> + >> + sz_8 =3D strlen(str); >> + sz_16 =3D utf8_utf16_strlen(str); >> + str_16 =3D calloc(sz_16 + 1, 2); >> + if (!str_16) >> + return NULL; >> + >> + tmp =3D str_16; >> + utf8_utf16_strcpy(&tmp, str); >> + >> + return str_16; >> +} >=20 > This seems useful overall, mind moving it to lib/efi_loader/efi_string.= c > and add sphinx style comments with the description? And while at it re= place > '2' with sizeof(u16)? This is definitely possilble. I'll make the change in v4. >=20 >> + >=20 > [...] >=20 >> +static efi_status_t EFIAPI >> +efi_spi_io_transaction(const struct efi_spi_io_protocol *this, >> + enum efi_spi_transaction_type transaction_type, >> + bool debug_transaction, >> + u32 clock_hz, >> + u32 bus_width, >> + u32 frame_size, >> + u32 write_bytes, >> + u8 *write_buffer, >> + u32 read_bytes, >> + u8 *read_buffer) >> +{ >> + struct spi_slave *target; >> + efi_status_t status =3D EFI_SUCCESS; >> + int r; >> + >> + /* We ignore the bus_width and frame_size arguments to this function= as the >> + * appropriate bus configuration for the connected device will be pe= rformed >> + * during spi_claim_bus(). >> + */ >> + >> + /* TODO: Print transaction details if debug_transaction is true. */ >> + >> + EFI_ENTRY("%p, %u, %u, %u, %u, %u, %u, %p, %u, %p", >> + this, transaction_type, debug_transaction, >> + clock_hz, bus_width, frame_size, >> + write_bytes, write_buffer, read_bytes, read_buffer); >> + >> + if (!this) >> + return EFI_EXIT(EFI_INVALID_PARAMETER); >> + >> + target =3D container_of(this, struct efi_spi_peripheral_priv, io_pro= tocol)->target; >> + >> + if (clock_hz > this->spi_peripheral->max_clock_hz) >> + return EFI_EXIT(EFI_UNSUPPORTED); >> + >> + r =3D spi_claim_bus(target); >> + if (r !=3D 0) >> + return EFI_EXIT(EFI_DEVICE_ERROR); >> + EFI_PRINT("SPI IO: Bus claimed\n"); >> + >> + if (clock_hz) { >> + EFI_PRINT("SPI IO: Setting clock rate to %u Hz\n", clock_hz); >> + spi_get_ops(target->dev->parent)->set_speed(target->dev->parent, cl= ock_hz); >=20 > Is set_speed always implemented in the driver model or will we crash? > I think we should be using spi_set_speed_mode()? I'll modify this in v4. >=20 >> + } else { >> + EFI_PRINT("SPI IO: Using default clock rate\n"); >> + } >> + >> + switch (transaction_type) { >> + case SPI_TRANSACTION_FULL_DUPLEX: >> + EFI_PRINT("SPI IO: Full-duplex\n"); >> + if (write_bytes !=3D read_bytes || !write_bytes || !write_buffer ||= !read_buffer) { >> + status =3D EFI_INVALID_PARAMETER; >> + break; >> + } >> + if (debug_transaction) >> + dump_buffer("SPI IO: write", write_bytes, write_buffer); >> + r =3D spi_xfer(target, 8 * write_bytes, >> + write_buffer, read_buffer, SPI_XFER_ONCE); >> + EFI_PRINT("SPI IO: xfer returned %d\n", r); >> + if (debug_transaction) >> + dump_buffer("SPI IO: read", read_bytes, read_buffer); >> + status =3D (r =3D=3D 0) ? EFI_SUCCESS : EFI_DEVICE_ERROR; >> + break; >> + case SPI_TRANSACTION_READ_ONLY: >> + EFI_PRINT("SPI IO: Read-only\n"); >> + if (!read_bytes || !read_buffer) { >> + status =3D EFI_INVALID_PARAMETER; >> + break; >> + } >> + r =3D spi_xfer(target, 8 * read_bytes, >> + NULL, read_buffer, SPI_XFER_ONCE); >> + EFI_PRINT("SPI IO: xfer returned %d\n", r); >> + if (debug_transaction) >> + dump_buffer("SPI IO: read", read_bytes, read_buffer); >> + status =3D (r =3D=3D 0) ? EFI_SUCCESS : EFI_DEVICE_ERROR; >> + break; >> + case SPI_TRANSACTION_WRITE_ONLY: >> + EFI_PRINT("SPI IO: Write-only\n"); >> + if (!write_bytes || !write_buffer) { >> + status =3D EFI_INVALID_PARAMETER; >> + break; >> + } >> + if (debug_transaction) >> + dump_buffer("SPI IO: write", write_bytes, write_buffer); >> + r =3D spi_xfer(target, 8 * write_bytes, >> + write_buffer, NULL, SPI_XFER_ONCE); >> + EFI_PRINT("SPI IO: xfer returned %d\n", r); >> + status =3D (r =3D=3D 0) ? EFI_SUCCESS : EFI_DEVICE_ERROR; >> + break; >> + case SPI_TRANSACTION_WRITE_THEN_READ: >> + EFI_PRINT("SPI IO: Write-then-read\n"); >> + if (!write_bytes || !write_buffer || !read_bytes || !read_buffer) {= >> + status =3D EFI_INVALID_PARAMETER; >> + break; >> + } >> + if (debug_transaction) >> + dump_buffer("SPI IO: write", write_bytes, write_buffer); >> + r =3D spi_xfer(target, 8 * write_bytes, >> + write_buffer, NULL, SPI_XFER_BEGIN); >> + EFI_PRINT("SPI IO: xfer [1/2] returned %d\n", r); >> + if (r !=3D 0) { >> + status =3D EFI_DEVICE_ERROR; >> + break; >> + } >> + r =3D spi_xfer(target, 8 * read_bytes, >> + NULL, read_buffer, SPI_XFER_END); >> + EFI_PRINT("SPI IO: xfer [2/2] returned %d\n", r); >> + if (debug_transaction) >> + dump_buffer("SPI IO: read", read_bytes, read_buffer); >> + status =3D (r =3D=3D 0) ? EFI_SUCCESS : EFI_DEVICE_ERROR; >> + break; >> + default: >> + status =3D EFI_INVALID_PARAMETER; >> + break; >> + } >> + >> + spi_release_bus(target); >> + EFI_PRINT("SPI IO: Released bus\n"); >> + return EFI_EXIT(status); >> +} >> + >> +static struct efi_device_path null_device_path =3D { >> + .type =3D DEVICE_PATH_TYPE_END, >> + .sub_type =3D DEVICE_PATH_SUB_TYPE_END, >> + .length =3D 4 >> +}; >> + >> +static struct efi_legacy_spi_controller_protocol >> +dummy_legacy_spi_controller_protocol =3D { >> + .maximum_offset =3D 0, >> + .maximum_range_bytes =3D 0, >> + .range_register_count =3D 0, >> + .erase_block_opcode =3D legacy_erase_block_opcode, >> + .write_status_prefix =3D legacy_write_status_prefix, >> + .bios_base_address =3D legacy_bios_base_address, >> + .clear_spi_protect =3D legacy_clear_spi_protect, >> + .is_range_protected =3D legacy_is_range_protected, >> + .protect_next_range =3D legacy_protect_next_range, >> + .lock_controller =3D legacy_lock_controller >> +}; >=20 > Keeping in mind all these return EFI_UNSUPPORTED can we get rid of them= and > set the legacy_spi_protocol to NULL? Or defining them is mandatory fro= m the PI spec? > Do you plan to implement it in the future? The spec does not say anything about allowing NULLs here so I don't want to assume that an arbitrary UEFI app will check the function pointers are non-NULL before calling them. Hence the functions which return EFI_UNSUPPORTED. >=20 >> + >> +static efi_guid_t efi_spi_configuration_guid =3D EFI_SPI_CONFIGURATIO= N_GUID; >> + >> +static void destroy_efi_spi_peripheral(struct efi_spi_peripheral *per= ipheral) >> +{ >> + struct efi_spi_peripheral_priv *priv =3D >> + container_of(peripheral, >> + struct efi_spi_peripheral_priv, >> + peripheral); >> + free(priv->peripheral.friendly_name); >> + free(priv->part.vendor); >> + free(priv->part.part_number); >> + efi_delete_handle(priv->handle); >=20 > Does this handle has any more protocols? In theory we shouldn't be call= ing > this. Uninstalling protocols from a handler should take care of this f= or > us. IOW if the protocol you uninstalled was the last one on the handle= r, > we automatically delete it. I can drop this call in v4. >=20 >> + free(priv); >> +} >> + >> +static void destroy_efi_spi_bus(struct efi_spi_bus *bus) >> +{ >> + struct efi_spi_peripheral *peripheral =3D bus->peripheral_list; >> + >> + while (peripheral) { >> + struct efi_spi_peripheral *next =3D >> + peripheral->next_spi_peripheral; >> + destroy_efi_spi_peripheral(peripheral); >> + peripheral =3D next; >> + } >> + free(bus->friendly_name); >> + free(bus); >> +} >> + >> +static efi_status_t efi_spi_new_handle(const efi_guid_t *guid, void *= proto) >> +{ >> + efi_status_t status; >> + efi_handle_t handle; >> + >> + status =3D efi_create_handle(&handle); >> + if (status !=3D EFI_SUCCESS) { >> + printf("Failed to create EFI handle\n"); >> + goto fail_1; >> + } >> + >> + status =3D efi_add_protocol(handle, guid, proto); >> + if (status !=3D EFI_SUCCESS) { >> + printf("Failed to add protocol\n"); >> + goto fail_2; >> + } >> + >> + return EFI_SUCCESS; >> + >> +fail_2: >> + efi_delete_handle(handle); >> +fail_1: >> + return status; >> +} >> + >> +static void >> +efi_spi_init_part(struct efi_spi_part *part, >> + struct spi_slave *target, >> + efi_string_t vendor_utf16, >> + efi_string_t part_number_utf16 >> +) >> +{ >> + part->vendor =3D vendor_utf16; >> + part->part_number =3D part_number_utf16; >> + part->min_clock_hz =3D 0; >> + part->max_clock_hz =3D target->max_hz; >> + part->chip_select_polarity =3D >> + (target->mode & SPI_CS_HIGH) ? true : false; >> +} >> + >> +static void >> +efi_spi_init_peripheral(struct efi_spi_peripheral *peripheral, >> + struct efi_spi_part *part, >> + struct efi_spi_bus *bus, >> + struct spi_slave *target, >> + efi_guid_t *guid, >> + efi_string_t name_utf16 >> +) >> +{ >> + peripheral->friendly_name =3D name_utf16; >> + peripheral->spi_part =3D part; >> + peripheral->next_spi_peripheral =3D NULL; >> + peripheral->spi_peripheral_driver_guid =3D guid; >> + peripheral->max_clock_hz =3D target->max_hz; >> + peripheral->clock_polarity =3D (target->mode & SPI_CPOL) ? true : fa= lse; >> + peripheral->clock_phase =3D (target->mode & SPI_CPHA) ? true : false= ; >> + peripheral->attributes =3D 0; >> + peripheral->configuration_data =3D NULL; >> + peripheral->spi_bus =3D bus; >> + peripheral->chip_select =3D efi_spi_peripheral_chip_select; >> + peripheral->chip_select_parameter =3D NULL; >> +} >> + >> +static void >> +efi_spi_append_peripheral(struct efi_spi_peripheral *peripheral, >> + struct efi_spi_bus *bus >> +) >> +{ >> + if (bus->peripheral_list) { >> + struct efi_spi_peripheral *tmp =3D bus->peripheral_list; >> + >> + while (tmp->next_spi_peripheral) >> + tmp =3D tmp->next_spi_peripheral; >> + >> + tmp->next_spi_peripheral =3D peripheral; >> + } else { >> + bus->peripheral_list =3D peripheral; >> + } >> +} >> + >> +static void >> +efi_spi_init_io_protocol(struct efi_spi_io_protocol *io_protocol, >> + struct efi_spi_peripheral *peripheral, >> + struct spi_slave *target >> +) >> +{ >> + u32 max_read, max_write; >> + >> + io_protocol->spi_peripheral =3D peripheral; >> + io_protocol->original_spi_peripheral =3D peripheral; >> + io_protocol->legacy_spi_protocol =3D &dummy_legacy_spi_controller_pr= otocol; >> + io_protocol->transaction =3D efi_spi_io_transaction; >> + io_protocol->update_spi_peripheral =3D efi_spi_io_update_spi_periphe= ral; >> + >> + /* This is a bit of a hack. The EFI data structures do not allow us = to >> + * represent a frame size greater than 32 bits. >> + */ >> + if (target->wordlen <=3D 32) >> + io_protocol->frame_size_support_mask =3D >> + 1 << (target->wordlen - 1); >> + else >> + io_protocol->frame_size_support_mask =3D 0; >> + >> + /* Again, this is a bit of a hack. The EFI data structures only allo= w >> + * for a single maximum transfer size whereas the u-boot spi_slave >> + * structure records maximum read transfer size and maximum write >> + * transfer size separately. So we need to take the minimum of these= two >> + * values. >> + * >> + * In u-boot, a value of zero for these fields means there is no lim= it >> + * on the transfer size. However in the UEFI PI spec a value of zero= is >> + * invalid so we'll use 0xFFFFFFFF as a substitute unlimited value. >> + */ >> + max_write =3D target->max_write_size ? target->max_write_size : 0xFF= FFFFFF; >> + max_read =3D target->max_read_size ? target->max_read_size : 0xFFFFF= FFF; >> + io_protocol->maximum_transfer_bytes =3D (max_read > max_write) ? max= _write : max_read; >> + >> + /* Hack++. Leave attributes set to zero since the flags listed in th= e >> + * UEFI PI spec have no defined numerical values and so cannot be us= ed. >> + */ >> + io_protocol->attributes =3D 0; >> +} >> + >> +static efi_status_t >> +export_spi_peripheral(struct efi_spi_bus *bus, struct udevice *dev) >> +{ >> + efi_string_t name_utf16, vendor_utf16, part_number_utf16; >> + struct efi_spi_peripheral_priv *priv; >> + efi_status_t status; >> + struct udevice *dev_bus =3D dev->parent; >> + struct spi_slave *target; >> + const char *name =3D dev_read_name(dev); >> + const char *vendor =3D dev_read_string(dev, "uefi-spi-vendor"); >> + const char *part_number =3D dev_read_string(dev, "uefi-spi-part-numb= er"); >> + efi_guid_t *guid =3D >> + (efi_guid_t *)dev_read_u8_array_ptr(dev, "uefi-spi-io-guid", 16); >> + >> + if (device_get_uclass_id(dev) =3D=3D UCLASS_SPI_EMUL) { >> + debug("Skipping emulated SPI peripheral %s\n", name); >> + goto fail_1; >> + } >> + >> + if (!vendor || !part_number || !guid) { >> + debug("Skipping SPI peripheral %s\n", name); >> + status =3D EFI_UNSUPPORTED; >> + goto fail_1; >> + } >> + >> + if (!device_active(dev)) { >> + int ret =3D device_probe(dev); >> + if (ret) { >> + debug("Skipping SPI peripheral %s, probe failed\n", name); >> + goto fail_1; >> + } >> + } >> + >> + target =3D dev_get_parent_priv(dev); >> + if (!target) { >> + debug("Skipping uninitialized SPI peripheral %s\n", name); >> + status =3D EFI_UNSUPPORTED; >> + goto fail_1; >> + } >> + >> + debug("Registering SPI dev %d:%d, name %s\n", >> + dev_bus->seq_, spi_chip_select(dev), name); >> + >> + priv =3D calloc(1, sizeof(*priv)); >> + if (!priv) { >> + status =3D EFI_OUT_OF_RESOURCES; >> + goto fail_1; >> + } >> + >> + vendor_utf16 =3D convert_efi_string(vendor); >> + if (!vendor_utf16) { >> + status =3D EFI_OUT_OF_RESOURCES; >> + goto fail_2; >> + } >> + >> + part_number_utf16 =3D convert_efi_string(part_number); >> + if (!part_number_utf16) { >> + status =3D EFI_OUT_OF_RESOURCES; >> + goto fail_3; >> + } >> + >> + name_utf16 =3D convert_efi_string(name); >> + if (!name_utf16) { >> + status =3D EFI_OUT_OF_RESOURCES; >> + goto fail_4; >> + } >> + >> + priv->target =3D target; >> + >> + efi_spi_init_part(&priv->part, target, vendor_utf16, part_number_utf= 16); >> + >> + efi_spi_init_peripheral(&priv->peripheral, &priv->part, >> + bus, target, guid, name_utf16); >> + >> + efi_spi_append_peripheral(&priv->peripheral, bus); >> + >> + efi_spi_init_io_protocol(&priv->io_protocol, &priv->peripheral, targ= et); >> + >> + status =3D efi_spi_new_handle(guid, &priv->io_protocol); >> + if (status !=3D EFI_SUCCESS) >> + goto fail_5; >> + >> + log_debug("Added EFI_SPI_IO_PROTOCOL for %s with guid " >> + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x= %02x\n", >> + name, >> + guid->b[3], guid->b[2], guid->b[1], guid->b[0], >> + guid->b[5], guid->b[4], guid->b[7], guid->b[6], >> + guid->b[8], guid->b[9], guid->b[10], guid->b[11], >> + guid->b[12], guid->b[13], guid->b[14], guid->b[15]); >> + return EFI_SUCCESS; >> + >> +fail_5: >> + free(name_utf16); >> +fail_4: >> + free(part_number_utf16); >> +fail_3: >> + free(vendor_utf16); >> +fail_2: >> + free(priv); >> +fail_1: >> + return status; >> +} >> + >> +static struct efi_spi_bus *export_spi_bus(int i) >> +{ >> + struct efi_spi_bus *bus; >> + struct udevice *dev, *child; >> + const char *name; >> + int r; >> + >> + r =3D uclass_get_device(UCLASS_SPI, i, &dev); >> + if (r < 0) { >> + printf("Failed to get SPI bus %d\n", i); >> + goto fail_1; >> + } >> + >> + name =3D dev_read_name(dev); >> + debug("Registering SPI bus %d, name %s\n", i, name); >> + >> + bus =3D calloc(1, sizeof(*bus)); >> + if (!bus) >> + goto fail_1; >> + >> + bus->friendly_name =3D convert_efi_string(name); >> + if (!bus->friendly_name) >> + goto fail_2; >> + >> + bus->peripheral_list =3D NULL; >> + bus->clock =3D efi_spi_bus_clock; >> + bus->clock_parameter =3D NULL; >> + >> + /* For the purposes of the current implementation, we do not need to= expose >> + * the hardware device path to users of the SPI I/O protocol. >> + */ >> + bus->controller_path =3D &null_device_path; >> + >> + device_foreach_child(child, dev) { >> + efi_status_t status =3D export_spi_peripheral(bus, child); >> + >> + if (status =3D=3D EFI_OUT_OF_RESOURCES) >> + goto fail_3; >> + } >> + >> + return bus; >> + >> +fail_3: >> + destroy_efi_spi_bus(bus); >> +fail_2: >> + free(bus); >> +fail_1: >> + return NULL; >> +} >> + >> +efi_status_t efi_spi_protocol_register(void) >> +{ >> + efi_status_t status; >> + struct efi_spi_configuration_protocol *proto; >> + uint i; >> + >> + printf("Registering EFI_SPI_CONFIGURATION_PROTOCOL\n"); >> + >> + proto =3D calloc(1, sizeof(*proto)); >> + if (!proto) { >> + status =3D EFI_OUT_OF_RESOURCES; >> + goto fail_1; >> + } >> + >> + proto->bus_count =3D uclass_id_count(UCLASS_SPI); >> + proto->bus_list =3D calloc(proto->bus_count, sizeof(*proto->bus_list= )); >> + if (!proto->bus_list) { >> + status =3D EFI_OUT_OF_RESOURCES; >> + goto fail_2; >> + } >> + >> + for (i =3D 0; i < proto->bus_count; i++) { >> + proto->bus_list[i] =3D export_spi_bus(i); >> + if (!proto->bus_list[i]) >> + goto fail_3; >> + } >> + >> + status =3D efi_spi_new_handle(&efi_spi_configuration_guid, proto); >> + if (status !=3D EFI_SUCCESS) >> + goto fail_3; >> + >> + return EFI_SUCCESS; >> + >> +fail_3: >> + for (i =3D 0; i < proto->bus_count; i++) { >> + if (proto->bus_list[i]) >> + destroy_efi_spi_bus(proto->bus_list[i]); >> + } >> + free(proto->bus_list); >> +fail_2: >> + free(proto); >> +fail_1: >> + return status; >> +} >> diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile >=20 >=20 > Would you mind splitting of the selftest on a different patchset? =20 I'll put it in a separate patch in v4. >=20 >> index daac6c396820..2790fcd784e0 100644 >> --- a/lib/efi_selftest/Makefile >> +++ b/lib/efi_selftest/Makefile >> @@ -63,6 +63,7 @@ obj-$(CONFIG_EFI_LOADER_HII) +=3D efi_selftest_hii.o= >> obj-$(CONFIG_EFI_RNG_PROTOCOL) +=3D efi_selftest_rng.o >> obj-$(CONFIG_EFI_GET_TIME) +=3D efi_selftest_rtc.o >> obj-$(CONFIG_EFI_TCG2_PROTOCOL) +=3D efi_selftest_tcg2.o >> +obj-$(CONFIG_EFI_SPI_PROTOCOL) +=3D efi_selftest_spi_protocol.o >> =20 >> ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) >> obj-y +=3D efi_selftest_fdt.o >> diff --git a/lib/efi_selftest/efi_selftest_spi_protocol.c b/lib/efi_se= lftest/efi_selftest_spi_protocol.c >> new file mode 100644 >> index 000000000000..946d04dbb557 >> --- /dev/null >> +++ b/lib/efi_selftest/efi_selftest_spi_protocol.c >> @@ -0,0 +1,284 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> +/* >> + * Copyright (c) 2022 Micron Technology, Inc. >> + */ >> + >> +#include >> +#include >> + >> +static struct efi_boot_services *boottime; >> +static efi_guid_t efi_spi_configuration_guid =3D EFI_SPI_CONFIGURATIO= N_GUID; >> + >> +static int setup(const efi_handle_t img_handle, >> + const struct efi_system_table *systable) >> +{ >> + boottime =3D systable->boottime; >> + return EFI_ST_SUCCESS; >> +} >> + >=20 > [...] >=20 > Thanks! > /Ilias Thanks for the review. --=20 Paul Barker Principal Software Engineer SanCloud Ltd e: paul.barker@sancloud.com w: https://sancloud.com/ --------------jn6roVf3nxuh7Xntd8lVV0Ci Content-Type: application/pgp-keys; name="OpenPGP_0xA67255DFCCE62ECD.asc" Content-Disposition: attachment; filename="OpenPGP_0xA67255DFCCE62ECD.asc" Content-Description: OpenPGP public key Content-Transfer-Encoding: quoted-printable -----BEGIN PGP PUBLIC KEY BLOCK----- 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 =3DsH2a -----END PGP PUBLIC KEY BLOCK----- --------------jn6roVf3nxuh7Xntd8lVV0Ci-- --------------zzlLgyMa12lr6mzUSioPrv69-- --------------zjXNwzL0eJwQF8Gq1uSfCImS Content-Type: application/pgp-signature; name="OpenPGP_signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="OpenPGP_signature" -----BEGIN PGP SIGNATURE----- wnsEABYIACMWIQS7m7rT1+CktY3YG1PYN3IfEvob6gUCYzrO8AUDAAAAAAAKCRDYN3IfEvob6m3r AQDUWYk+ht9dP5JC8lETeG5G8Jh730+BVnHYxM1Zv6iTvQD9EZ0XJvWPcLNH/Guuf0ujUaJ8sJFi YoGfoi0MOYxVfQw= =NJAc -----END PGP SIGNATURE----- --------------zjXNwzL0eJwQF8Gq1uSfCImS--