From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from NAM10-MW2-obe.outbound.protection.outlook.com (NAM10-MW2-obe.outbound.protection.outlook.com [40.107.94.127]) by mx.groups.io with SMTP id smtpd.web10.14895.1684218557971360534 for ; Mon, 15 May 2023 23:29:18 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="body hash did not verify" header.i=@os.amperecomputing.com header.s=selector2 header.b=fqlxKnqK; spf=pass (domain: os.amperecomputing.com, ip: 40.107.94.127, mailfrom: tinhnguyen@os.amperecomputing.com) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ILBfdC9/pEhdTuhpVL3j1YSzrM0QYCd63T+eENosCYgyMkCkCt5ut89teSv//GJznIRbwgm3gBdTvDo7cCFn47OmLdjUJFvTWRygF80sq/doVc0u46D+0s8YNNhqLluattEVEKn/k3lLbdjqpo0gFHjKJmEIHGKQkf+DWa5Ex/vMW7xdRx9KMhxZP2YvdCBUWZj/A/Dam4Gu/6Dl3kMVHrBlTtRZnPqDHtNuVUPs2HKtnFnDxYFNJCxM4PK9NnXu+JtXeRj/yiSoI9gWOG1E6OEvszcNFQ4/hBTlOIE6R2UdcBLmcckln+yH/rUU7VXLw6Jlvqt9juHod/t6f6XTbg== 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=y1ugLtH0ciFQ9Jmq+e08JjEra6SdRkrS+7qrznhCBxY=; b=GN6WgJ4/bp2SRdDujYUamT5ENZPvR85bJgdbtzRgl06rEQhkVlZRrIVRHIlSBsCsxufToegj81+PzrIrnqM3iZBQnEEVK7VjUrbTrVLHl7NRprnJ5uQ/82urr/a/Flnw8DluYdbK1iAT6uZlmCChljfYRKT4L9ihXVbPb4+UG3hxSuoQrRm61KEOJKtai0jfNEcuSAIO3aAKk/QMQ4gc6Ab2XgI5yFHVs3owahvGM6GiuvAduGxfQPmUQC6rx+NwRnHQcos8dTdxSVanHTs3QlS8OKAra/44SMlXDnMbOOxZ9NCtDsJs2sTrvc/jyYg/niztT1f/CJjHwi5E5TjTtw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=os.amperecomputing.com; dmarc=pass action=none header.from=os.amperecomputing.com; dkim=pass header.d=os.amperecomputing.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=os.amperecomputing.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=y1ugLtH0ciFQ9Jmq+e08JjEra6SdRkrS+7qrznhCBxY=; b=fqlxKnqKzyl6zRZcYVOKIe8Muid4s+mZ7qO8epAulVslrcg45LCleTWyZz9PWUdaiy8sk7nUgyt1xTPP9448nzS2bXS8Ebrx5s7BAaNjWLTpgsPZtEU35qnIF8twiNbmrvzUcDpwlMF6FUC38r+zjz9AmbHKpKTSJxjh7MxVfxw= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=os.amperecomputing.com; Received: from DM5PR0102MB3336.prod.exchangelabs.com (2603:10b6:4:9f::11) by SN6PR0102MB3568.prod.exchangelabs.com (2603:10b6:805:11::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6387.30; Tue, 16 May 2023 06:29:14 +0000 Received: from DM5PR0102MB3336.prod.exchangelabs.com ([fe80::fefc:e2a9:e823:fe42]) by DM5PR0102MB3336.prod.exchangelabs.com ([fe80::fefc:e2a9:e823:fe42%6]) with mapi id 15.20.6387.020; Tue, 16 May 2023 06:29:14 +0000 From: "Tinh Nguyen" To: devel@edk2.groups.io CC: patches@amperecomputing.com, quic_llindhol@quicinc.com, ardb+tianocore@kernel.org, nhi@os.amperecomputing.com, thang@os.amperecomputing.com, minhnguyen1@os.amperecomputing.com, khanh.pham@os.amperecomputing.com, Tinh Nguyen Subject: [edk2-platforms][PATCH 1/1] AmpereAltraPkg: Add PlatformInitDxe module Date: Tue, 16 May 2023 13:28:38 +0700 Message-ID: <20230516062838.830272-1-tinhnguyen@os.amperecomputing.com> X-Mailer: git-send-email 2.40.0 X-ClientProxiedBy: SG3P274CA0011.SGPP274.PROD.OUTLOOK.COM (2603:1096:4:be::23) To DM5PR0102MB3336.prod.exchangelabs.com (2603:10b6:4:9f::11) Return-Path: tinhnguyen@os.amperecomputing.com MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DM5PR0102MB3336:EE_|SN6PR0102MB3568:EE_ X-MS-Office365-Filtering-Correlation-Id: 1c8e6c85-16d3-48d2-85d2-08db55d6de1a X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 2toJU/TIiHxJFvf2zh1iNhLeWbxpfuILxWywdSxw4Wndf8cF93lj4Q/cUy88F2vzoxorJLvOk+x0wv/kNwwhQmpTDRdO9vxL0w9TIluZcnCbxVXvThsL/DA1dvAvDr5WdMJ3DAKhED4JZFfEel4BHQudQgHqCkaVxYXootMG0LDdbC49EmRObpdl8fRAnAveSGiV5RJWkRYeUmSB+FwZZY7+xAYEoWiAZjzz2L/1Dvfsxt+ymI6BajfnOC72HiW8MtnHbGsewN0um2kvhEfAeIoA6/tNRe7ftMvxxYfVBNvT0Wbbse5S+zw1peVr8GgcWeoMKvL6z5gzu/Jlfx8uST+KY6w7HG/rlBXig1dJlGDchpWHOwR7SnrIK9cPTR9LYCaHuwH4H3WO7sqZ6yzeHhJCQyJzJF40HMesl+IH2KvBLT6T75qSrHiFuKpRRxyLbIDSY5CuxPLqEYT/+F1H6e7hNTLacHl9dyLay8DdsZiRgnTc/rcRfOMd+38pGB4hUwlP0HFLCFj2OohXepKy1qAEE/cPjS62cunKTN+/ynENSsUDDhoh3yobMHZJmDEtYfGN6ofwFMApZxvwfIObeAIA0ms7oQqi3kcwJl2WWm4Wzg0mpxDjQARTR+Zae9mN X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DM5PR0102MB3336.prod.exchangelabs.com;PTR:;CAT:NONE;SFS:(13230028)(4636009)(396003)(376002)(136003)(346002)(39850400004)(366004)(451199021)(2616005)(41300700001)(66476007)(2906002)(6666004)(83380400001)(6486002)(66946007)(66556008)(52116002)(107886003)(478600001)(6512007)(1076003)(8676002)(186003)(8936002)(26005)(6506007)(316002)(6916009)(5660300002)(4326008)(38350700002)(38100700002)(86362001);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?Cf6isdpzByGrMXD1p1xfIgXxT3CCws/SQ7uLBqRjqiFwdmNt8T9scioZNlbw?= =?us-ascii?Q?hZHnzCQm0xmRbSLTdrqpgUoLvcp6r3WXsO7P27Xmla8qUopKr1bOEJlX4wUT?= =?us-ascii?Q?hUABaZEk4/KDf413oET/9ix8pQQSo4pgVcvyrMojU/l5TvtMTgY8MDoz8Qip?= =?us-ascii?Q?NreMN0JxOv3YBuFwfvwhpQ1k67p4Safqup27Bg2cPS9HayWA+wewACnf9av6?= =?us-ascii?Q?QccQJWgjiFRSm0BJCdir9Duqpo61+UyMpeINobranSF6PETjws4th6DRa6MN?= =?us-ascii?Q?eUIXaY1UP+AX1TvfXP/V9EvrKhg9cXgIEhC4z3m8CQ/4WCQX9nAJCkFpKTZ3?= =?us-ascii?Q?XKgZdhCw/bZIfNImewWQin68g2tLob7wz6NZhdlvJ6Kf1hxfRpx1i33BTdiJ?= =?us-ascii?Q?oJahdTxNEG/ISw3X6Z/8dUelvDKw7/CjBNbDqSfrCeE4AwNDdmjsC5abggcK?= =?us-ascii?Q?8jwWin5tsrdwR0c9VsKh6PmDY+aRFKp9pnQlhFpYkkU7+jV0ZbW1WKbxfKfg?= =?us-ascii?Q?Sq4+k2g7+Xe2L+47yKSS4rVhmhqtGrprSuFfyj8zgRzjkkGkUms6+5AWH1cf?= =?us-ascii?Q?WPycKIiEvdWSK4GZBGlmsxG6JlsmAnSL8N0E0pkLtTEPop1JsHqtdzl1KvDG?= =?us-ascii?Q?OPimZLuMlvaB4AFvByXAocxaw9yiFnBHcEOg+RVaMwU30vuQs5qrPDa2XLhM?= =?us-ascii?Q?2Nps6Yi+asLTcaYeMqrkWzAYE+ZTvWs715uKOf+FGD/VOujHO0Wpm79c6gDK?= =?us-ascii?Q?B3fyPhjctq40ti7z37xc8qOQ6cuQfPAn2eOyy48nQoWeRAMJRnaQXmMPn+za?= =?us-ascii?Q?2U+/fqSIzvGiOtCNSn4G6bLnrWxOUdtqlWaoXVvMlYSTx8qYTVPeiOAI4p2f?= =?us-ascii?Q?IQZDifpg37s7O5dsBrW8Ujkj9iiqSybHDotWKpA4l3dQeHvSzPEtORsO6Khe?= =?us-ascii?Q?rgddoSJChkwQjBObnEpYR/Ufwl6tiY3qgzZwJmL+scWfeyVDCHem4OLWlvNa?= =?us-ascii?Q?pAQm/gO8pJvKVe9enCK2OYHm0QyTULw/95ldSesLMIc900bmE8eJsZ4gtgxy?= =?us-ascii?Q?IaJ2LyAMjFZW8MPanqWqGOVAJ428uAse9yplr0XCTQdSbGTtm0655ml26Ajo?= =?us-ascii?Q?wB2uS3nkecTk+crsTcXBMs4shtuDnnJ1Guh1dgXVIZ5W0UXZ3wrH8r0mNlmR?= =?us-ascii?Q?OLLRdXE41/bTVDpRrEzMJxibZPkA5KoqIube7+W1NwheQJ0n2rNhR/OXDIhr?= =?us-ascii?Q?3EySf3lsn6WJiX8yFMKRlZdHVaMIkm3lVYRtqHeYJbqhvd5A4pc1ypqaBn15?= =?us-ascii?Q?naag/YA9xaEa2BTOhW/0GgTKFdpjVOeCho4fSEJdD1T5CF7tCIcRpMDmOc1e?= =?us-ascii?Q?kqhImg5cfuPXinJFfM54RbBOqgwng9nEmVOVvo17//QnOR+mjjPm6Q2dVsi+?= =?us-ascii?Q?GeQzw7o3/vAX9Fx5GBJxFpuN2bHo7db3GzNALw5YUHmrgYe7fogl8sLPU6kR?= =?us-ascii?Q?TIEFeFLrCoJdNkVTqJpKA0Y9tHRsF+ZIz36/6q8mdApkaWyf0w0FecEZ+kCL?= =?us-ascii?Q?XjTaheWeFzMpKJo9DyjbrXON17EQLy2+MnxEmyVoQOvmOfj7HTHsyMgJ/zc8?= =?us-ascii?Q?yHOLj/VJlHH0WTWwd5lzDwk=3D?= X-OriginatorOrg: os.amperecomputing.com X-MS-Exchange-CrossTenant-Network-Message-Id: 1c8e6c85-16d3-48d2-85d2-08db55d6de1a X-MS-Exchange-CrossTenant-AuthSource: DM5PR0102MB3336.prod.exchangelabs.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 May 2023 06:29:14.3308 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 3bc2b170-fd94-476d-b0ce-4229bdc904a7 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 1LcUUzOuZWWkpKe3IgHGE0IbgIw13IyD9i6sUstynXS9mZY/bDTVNmsR73+k8GjHyPWMIiWCFc22JP3LCjORf+smWeQlJWNNhkpUuvqWLRooYdrLPXD4Gm2s249A2mP7 X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN6PR0102MB3568 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain From: Minh Nguyen In FailSafe context, there's one field to indicate which setting is using to boot (BOOT_LAST_KNOWN_SETTINGS, BOOT_DEFAULT_SETTINGS, BOOT_NORMAL). At SCP and ATF side, they will check their NVPARAM for Failsafe (NV_SI_PMPRO_FAILURE_FAILSAFE - NV_SI_ATF_FAILURE_FAILSAFE) to decide which setting they will use. If Failsafe occurs at SCP or ATF, a mission of UEFI at DXE phase is to clear Failsafe context for normal behavior. Signed-off-by: Tinh Nguyen --- Platform/Ampere/JadePkg/Jade.fdf = | 5 + Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc = | 5 + Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDxe.c = | 133 ++++++++++++++++++++ Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDxe.h = | 38 ++++++ Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDxe.inf = | 33 +++++ 5 files changed, 214 insertions(+) diff --git a/Platform/Ampere/JadePkg/Jade.fdf b/Platform/Ampere/JadePkg/Jad= e.fdf index a578d05330..9d26847b36 100644 --- a/Platform/Ampere/JadePkg/Jade.fdf +++ b/Platform/Ampere/JadePkg/Jade.fdf @@ -228,6 +228,11 @@ APRIORI DXE { INF MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf INF ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf =20 + # + # Initialize works at Dxe phase + # + INF Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDx= e.inf + # # Environment Variables Protocol # diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc b/Silicon= /Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc index 9275e0053a..8191da5820 100644 --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc @@ -609,6 +609,11 @@ NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf } =20 + # + # Initialize works at Dxe phase + # + Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDxe.in= f + # # Timer # diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/Platform= InitDxe.c b/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformI= nitDxe.c new file mode 100644 index 0000000000..6e92a59f4b --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDxe= .c @@ -0,0 +1,133 @@ +/** @file + + Copyright (c) 2023, Ampere Computing LLC. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include + +#include "PlatformInitDxe.h" + +UINT16 +CheckCrc16 ( + IN UINT8 *Pointer, + IN INTN Count + ) +{ + INTN Crc =3D 0; + INTN Index; + + while (--Count >=3D 0) { + Crc =3D Crc ^ (INTN)*Pointer++ << 8; + for (Index =3D 0; Index < 8; ++Index) { + if ((Crc & 0x8000) !=3D 0) { + Crc =3D Crc << 1 ^ 0x1021; + } else { + Crc =3D Crc << 1; + } + } + } + + return Crc & 0xFFFF; +} + +BOOLEAN +FailSafeValidCRC ( + IN FAIL_SAFE_CONTEXT *FailSafeBuf + ) +{ + BOOLEAN Valid; + UINT16 Crc; + UINT32 Len; + + Len =3D sizeof (FAIL_SAFE_CONTEXT); + Crc =3D FailSafeBuf->CRC16; + FailSafeBuf->CRC16 =3D 0; + + Valid =3D (Crc =3D=3D CheckCrc16 ((UINT8 *)FailSafeBuf, Len= )); + FailSafeBuf->CRC16 =3D Crc; + + return Valid; +} + +BOOLEAN +FailSafeFailureStatus ( + IN UINT8 Status + ) +{ + if ((Status =3D=3D FAILSAFE_BOOT_LAST_KNOWN_SETTINGS) || + (Status =3D=3D FAILSAFE_BOOT_DEFAULT_SETTINGS) || + (Status =3D=3D FAILSAFE_BOOT_DDR_DOWNGRADE)) + { + return TRUE; + } + + return FALSE; +} + +EFI_STATUS +FailSafeClearContext ( + VOID + ) +{ + EFI_STATUS Status; + FAIL_SAFE_CONTEXT FailSafeBuf; + UINT32 FailSafeSize; + UINT64 FailSafeStartOffset; + + Status =3D FlashGetFailSafeInfo (&FailSafeStartOffset, &FailSafeSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to get context region information\n",= __func__)); + return EFI_DEVICE_ERROR; + } + + Status =3D FlashReadCommand (FailSafeStartOffset, (UINT8 *)&FailSafeBuf,= sizeof (FAIL_SAFE_CONTEXT)); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If failsafe context is valid, and: + // - The status indicate non-failure, then don't clear it + // - The status indicate a failure, then go and clear it + // + if ( FailSafeValidCRC (&FailSafeBuf) + && !FailSafeFailureStatus (FailSafeBuf.Status)) + { + return EFI_SUCCESS; + } + + return FlashEraseCommand (FailSafeStartOffset, FailSafeSize); +} + +EFI_STATUS +EFIAPI +PlatformInitDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // In FailSafe context, there's one field to indicate which setting is u= sing + // to boot (BOOT_LAST_KNOWN_SETTINGS, BOOT_DEFAULT_SETTINGS, BOOT_NORMAL= ). + // + // At SCP and ATF side, they will check their NVPARAM for Failsafe + // (NV_SI_PMPRO_FAILURE_FAILSAFE - NV_SI_ATF_FAILURE_FAILSAFE) to decide + // which setting they will use. If Failsafe occurs at SCP or ATF, a miss= ion + // of UEFI at DXE phase is to clear Failsafe context for normal behavior= . + // + + Status =3D FailSafeClearContext (); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/Platform= InitDxe.h b/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformI= nitDxe.h new file mode 100644 index 0000000000..3e6da742d0 --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDxe= .h @@ -0,0 +1,38 @@ +/** @file + + Copyright (c) 2023, Ampere Computing LLC. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef PLATFORM_INIT_DXE_H_ +#define PLATFORM_INIT_DXE_H_ + +#define FAILSAFE_BOOT_NORMAL 0x00 +#define FAILSAFE_BOOT_LAST_KNOWN_SETTINGS 0x01 +#define FAILSAFE_BOOT_DEFAULT_SETTINGS 0x02 +#define FAILSAFE_BOOT_DDR_DOWNGRADE 0x03 +#define FAILSAFE_BOOT_SUCCESSFUL 0x04 + +#pragma pack(1) +typedef struct { + UINT8 ImgMajorVer; + UINT8 ImgMinorVer; + UINT32 NumRetry1; + UINT32 NumRetry2; + UINT32 MaxRetry; + UINT8 Status; + // + // Byte[3]: Reserved + // Byte[2]: Slave MCU Failure Mask + // Byte[1]: Reserved + // Byte[0]: Master MCU Failure Mask + // + UINT32 MCUFailsMask; + UINT16 CRC16; + UINT8 Reserved[3]; +} FAIL_SAFE_CONTEXT; +#pragma pack() + +#endif /* PLATFORM_INIT_DXE_H_ */ diff --git a/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/Platform= InitDxe.inf b/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/Platfor= mInitDxe.inf new file mode 100644 index 0000000000..9074e56ded --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Drivers/PlatformInitDxe/PlatformInitDxe= .inf @@ -0,0 +1,33 @@ +## @file +# +# Copyright (c) 2023, Ampere Computing LLC. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x0001001B + BASE_NAME =3D PlatformInitDxe + FILE_GUID =3D 59E2571E-9A57-4247-A586-BF151F900876 + MODULE_TYPE =3D DXE_DRIVER + VERSION_STRING =3D 1.0 + ENTRY_POINT =3D PlatformInitDxeEntryPoint + +[Sources] + PlatformInitDxe.h + PlatformInitDxe.c + +[Packages] + MdePkg/MdePkg.dec + Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec + +[LibraryClasses] + DebugLib + FlashLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Depex] + TRUE --=20 2.40.0