From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: hpe.com, ip: 148.163.147.86, mailfrom: prvs=0142e2c9bd=gilbert.chen@hpe.com) Received: from mx0a-002e3701.pphosted.com (mx0a-002e3701.pphosted.com [148.163.147.86]) by groups.io with SMTP; Mon, 26 Aug 2019 23:26:40 -0700 Received: from pps.filterd (m0148663.ppops.net [127.0.0.1]) by mx0a-002e3701.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id x7R6QZK1005570 for ; Tue, 27 Aug 2019 06:26:39 GMT Received: from g9t5008.houston.hpe.com (g9t5008.houston.hpe.com [15.241.48.72]) by mx0a-002e3701.pphosted.com with ESMTP id 2umstv1y29-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Tue, 27 Aug 2019 06:26:39 +0000 Received: from G2W6311.americas.hpqcorp.net (g2w6311.austin.hp.com [16.197.64.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by g9t5008.houston.hpe.com (Postfix) with ESMTPS id E816983 for ; Tue, 27 Aug 2019 06:26:15 +0000 (UTC) Received: from G9W8669.americas.hpqcorp.net (16.220.49.28) by G2W6311.americas.hpqcorp.net (16.197.64.53) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Tue, 27 Aug 2019 06:26:15 +0000 Received: from G4W10205.americas.hpqcorp.net (2002:10cf:520f::10cf:520f) by G9W8669.americas.hpqcorp.net (2002:10dc:311c::10dc:311c) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Tue, 27 Aug 2019 06:26:15 +0000 Received: from NAM02-CY1-obe.outbound.protection.outlook.com (15.241.52.13) by G4W10205.americas.hpqcorp.net (16.207.82.15) with Microsoft SMTP Server (TLS) id 15.0.1367.3 via Frontend Transport; Tue, 27 Aug 2019 06:26:14 +0000 Received: from TU4PR8401MB1056.NAMPRD84.PROD.OUTLOOK.COM (10.169.47.148) by TU4PR8401MB0624.NAMPRD84.PROD.OUTLOOK.COM (10.169.44.20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2178.16; Tue, 27 Aug 2019 06:26:13 +0000 Received: from TU4PR8401MB1056.NAMPRD84.PROD.OUTLOOK.COM ([fe80::cc02:a574:b9e3:cf1e]) by TU4PR8401MB1056.NAMPRD84.PROD.OUTLOOK.COM ([fe80::cc02:a574:b9e3:cf1e%8]) with mapi id 15.20.2199.021; Tue, 27 Aug 2019 06:26:13 +0000 From: "Chen, Gilbert" To: "devel@edk2.groups.io" CC: "Chen, Gilbert" Subject: [PATCH 09/15] [platforms/devel-riscv]: U500Pkg/Library: Initial version of PlatformBootManagerLib Thread-Topic: [PATCH 09/15] [platforms/devel-riscv]: U500Pkg/Library: Initial version of PlatformBootManagerLib Thread-Index: AdVcoFCm1spfR0YrR6OeAKPCVF33Aw== Date: Tue, 27 Aug 2019 06:26:13 +0000 Message-ID: Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [16.242.247.133] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: b83d4a07-6ead-472f-8242-08d72ab7755a x-ms-office365-filtering-ht: Tenant x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(5600166)(711020)(4605104)(1401327)(4618075)(2017052603328)(7193020);SRVR:TU4PR8401MB0624; x-ms-traffictypediagnostic: TU4PR8401MB0624: x-ms-exchange-purlcount: 1 x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:1060; x-forefront-prvs: 0142F22657 x-forefront-antispam-report: SFV:NSPM;SFS:(10019020)(39860400002)(366004)(136003)(376002)(396003)(346002)(199004)(189003)(86362001)(316002)(6436002)(2351001)(2501003)(7736002)(6306002)(478600001)(30864003)(55016002)(102836004)(3846002)(9686003)(6116002)(476003)(53936002)(26005)(256004)(53946003)(76116006)(486006)(14444005)(2906002)(66946007)(64756008)(5640700003)(66476007)(5660300002)(66446008)(66556008)(186003)(71200400001)(71190400001)(966005)(16799955002)(8936002)(66066001)(14454004)(6916009)(19627235002)(53376002)(52536014)(74316002)(8676002)(1730700003)(81166006)(81156014)(25786009)(6506007)(305945005)(15188155005)(99286004)(33656002)(4326008)(7696005)(579004)(569006);DIR:OUT;SFP:1102;SCL:1;SRVR:TU4PR8401MB0624;H:TU4PR8401MB1056.NAMPRD84.PROD.OUTLOOK.COM;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: hpe.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 9e/fFYJw+DeN8BjbaghgqPhpcPWn/AK4mESIps5WsD7uQO4M3+Hb2VOqcu7F6Pzw0iy06tXg9AqWX01ZqONGzIAE1dtCw7yvxuPhl9/ikXKnURdwwcevtz3HI6rrq6i8yg38WULNFvcDhOCUnf/OPY1Kox4qSp8g/z8jJXtzHy+5xiqlYidi0GaxszKv+Ww+LPCJSnCw6qQ5j15okH2rs+nNQOCnHPvwCXjBaprvnH73+ukkC/VbldcQVWEuupjH3f64s7kXHBK++tV1jiDz6HSfL5jlN2LDIfExunnzB2MmLNrlBQo0liQ7Os3xQPEIAqWzLS0TbChDT9Hg5CQGIRAk6vSd1a79DnROaVL/5AlB9yroCtuN+Mdt7m/oQpy9zzhUF6yAWv7qRp81zGioSm269lee4cOy5pF0nLT2KE0= arc-seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=HqOCOplSTi1wJxPC1qKxot29lEjvDqRiEPIg1Z+sPP3vM3QXZV6hGTSpKa3Uf1qWjrJq38T4m0tuQOiF20853ewBBwN+34kw2E4e/nYgzkufmznwGY1Tr9E4XhBqr0fjSKi8nOfQUu2f2DNgXkskYyr+u2RQ5lTUJLwzEZjv9Rh9m3zqByd4lhHwN7MSqdWU+E7gdwL2kphXqSPPckrLILadzA+3ZGuYnBrvPCP7Zsp917mce1TlZ1urKzsJr/PDfSuMAnMhVAN0hc6KfqpfmsgfwjAhJElidkdBPEt76zbTSpwn5d4ELN/eSrHfIr+7NBR0CTNLSJB1+4JdBa0s7A== 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-SenderADCheck; bh=WziZbzWXwadXUA0E+Zw0BBUuqgRIOdFwamq/L8/0iWk=; b=nMUyBapBdeJZYmWWI8fJzaYEnD4RdHj6a5RcufIMSGBgypIMkMoF+NXMLmec8zrZbm8X5tmrUyTLdX0M+Y7b344oueXmzUYEEROCuR8x0bNUcrliqMv6ioyDu6JjshFt/euQurROOKUF1nJCZxVlTeW1EY8uh9OPSj5wRQNhN1dGH1RyNgb3cZt4dNrLkiwTxK5NX4rVuK7OwZ7tLseFdvuUM+aqxMSO9KJJ8gU0dq6EFUzZLrKtcIYXAtU8Q84Rd1LjkMREu/PgE3xko9FZApX4CP5tpXalf8XjjLifG+xzMtshIm//i428Iia3fCTzGlbLzVkLnk78bKB+VLEMJQ== arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=hpe.com; dmarc=pass action=none header.from=hpe.com; dkim=pass header.d=hpe.com; arc=none x-ms-exchange-crosstenant-network-message-id: b83d4a07-6ead-472f-8242-08d72ab7755a x-ms-exchange-crosstenant-originalarrivaltime: 27 Aug 2019 06:26:13.0942 (UTC) x-ms-exchange-crosstenant-fromentityheader: Hosted x-ms-exchange-crosstenant-id: 105b2061-b669-4b31-92ac-24d304d195dc x-ms-exchange-crosstenant-mailboxtype: HOSTED x-ms-exchange-crosstenant-userprincipalname: 5lcoyP8hqu6t93BLxLUFfi3oH1hKZHnIJaSHTXUqPbkw9H8rWDdYXLsiKejAt2OTguZBA3zUa/dskDTD7/nECw== x-ms-exchange-transport-crosstenantheadersstamped: TU4PR8401MB0624 X-OriginatorOrg: hpe.com X-Proofpoint-UnRewURL: 0 URL was un-rewritten MIME-Version: 1.0 X-HPE-SCL: -1 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:5.22.84,1.0.8 definitions=2019-08-26_08:2019-08-26,2019-08-26 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 phishscore=0 clxscore=1015 priorityscore=1501 bulkscore=0 mlxlogscore=999 lowpriorityscore=0 spamscore=0 impostorscore=0 adultscore=0 mlxscore=0 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-1906280000 definitions=main-1908270071 Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable SiFive RISC-V U500 Platform Boot Manager library. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Gilbert Chen --- .../Library/PlatformBootManagerLib/MemoryTest.c | 1110 ++++++++++++++++= ++++ .../PlatformBootManagerLib/PlatformBootManager.c | 269 +++++ .../PlatformBootManagerLib/PlatformBootManager.h | 141 +++ .../PlatformBootManagerLib.inf | 72 ++ .../Library/PlatformBootManagerLib/PlatformData.c | 54 + .../Library/PlatformBootManagerLib/Strings.uni | Bin 0 -> 3922 bytes 6 files changed, 1646 insertions(+) create mode 100644 Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManag= erLib/MemoryTest.c create mode 100644 Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManag= erLib/PlatformBootManager.c create mode 100644 Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManag= erLib/PlatformBootManager.h create mode 100644 Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManag= erLib/PlatformBootManagerLib.inf create mode 100644 Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManag= erLib/PlatformData.c create mode 100644 Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManag= erLib/Strings.uni diff --git a/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/M= emoryTest.c b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/= MemoryTest.c new file mode 100644 index 0000000..841e3d9 --- /dev/null +++ b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/MemoryTe= st.c @@ -0,0 +1,1110 @@ +/** @file + Perform the RISC-V platform memory test + +Copyright (c) 2019, Hewlett Packard Enterprise Development LP. All rights = reserved.
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD = License +which accompanies this distribution. The full text of the license may be = found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLI= ED. + +**/ + +#include "PlatformBootManager.h" + +EFI_HII_HANDLE gStringPackHandle =3D NULL; +EFI_GUID mPlatformBootManagerStringPackGuid =3D { + 0x154dd51, 0x9079, 0x4a10, { 0x89, 0x5c, 0x9c, 0x7, 0x72, 0x81, 0x57, 0x= 88 } + }; +// extern UINT8 BdsDxeStrings[]; + +// +// BDS Platform Functions +// +/** + + Show progress bar with title above it. It only works in Graphics mode. + + @param TitleForeground Foreground color for Title. + @param TitleBackground Background color for Title. + @param Title Title above progress bar. + @param ProgressColor Progress bar color. + @param Progress Progress (0-100) + @param PreviousValue The previous value of the progress. + + @retval EFI_STATUS Success update the progress bar + +**/ +EFI_STATUS +PlatformBootManagerShowProgress ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleForeground, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleBackground, + IN CHAR16 *Title, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL ProgressColor, + IN UINTN Progress, + IN UINTN PreviousValue + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + UINT32 SizeOfX; + UINT32 SizeOfY; + UINT32 ColorDepth; + UINT32 RefreshRate; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color; + UINTN BlockHeight; + UINTN BlockWidth; + UINTN BlockNum; + UINTN PosX; + UINTN PosY; + UINTN Index; + + if (Progress > 100) { + return EFI_INVALID_PARAMETER; + } + + UgaDraw =3D NULL; + Status =3D gBS->HandleProtocol ( + gST->ConsoleOutHandle, + &gEfiGraphicsOutputProtocolGuid, + (VOID **) &GraphicsOutput + ); + if (EFI_ERROR (Status) && FeaturePcdGet (PcdUgaConsumeSupport)) { + GraphicsOutput =3D NULL; + + Status =3D gBS->HandleProtocol ( + gST->ConsoleOutHandle, + &gEfiUgaDrawProtocolGuid, + (VOID **) &UgaDraw + ); + } + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + SizeOfX =3D 0; + SizeOfY =3D 0; + if (GraphicsOutput !=3D NULL) { + SizeOfX =3D GraphicsOutput->Mode->Info->HorizontalResolution; + SizeOfY =3D GraphicsOutput->Mode->Info->VerticalResolution; + } else if (UgaDraw !=3D NULL) { + Status =3D UgaDraw->GetMode ( + UgaDraw, + &SizeOfX, + &SizeOfY, + &ColorDepth, + &RefreshRate + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + } else { + return EFI_UNSUPPORTED; + } + + BlockWidth =3D SizeOfX / 100; + BlockHeight =3D SizeOfY / 50; + + BlockNum =3D Progress; + + PosX =3D 0; + PosY =3D SizeOfY * 48 / 50; + + if (BlockNum =3D=3D 0) { + // + // Clear progress area + // + SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0); + + if (GraphicsOutput !=3D NULL) { + Status =3D GraphicsOutput->Blt ( + GraphicsOutput, + &Color, + EfiBltVideoFill, + 0, + 0, + 0, + PosY - EFI_GLYPH_HEIGHT - 1, + SizeOfX, + SizeOfY - (PosY - EFI_GLYPH_HEIGHT - 1), + SizeOfX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + Status =3D UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) &Color, + EfiUgaVideoFill, + 0, + 0, + 0, + PosY - EFI_GLYPH_HEIGHT - 1, + SizeOfX, + SizeOfY - (PosY - EFI_GLYPH_HEIGHT - 1), + SizeOfX * sizeof (EFI_UGA_PIXEL) + ); + } else { + return EFI_UNSUPPORTED; + } + } + // + // Show progress by drawing blocks + // + for (Index =3D PreviousValue; Index < BlockNum; Index++) { + PosX =3D Index * BlockWidth; + if (GraphicsOutput !=3D NULL) { + Status =3D GraphicsOutput->Blt ( + GraphicsOutput, + &ProgressColor, + EfiBltVideoFill, + 0, + 0, + PosX, + PosY, + BlockWidth - 1, + BlockHeight, + (BlockWidth) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_P= IXEL) + ); + } else if (FeaturePcdGet (PcdUgaConsumeSupport)) { + Status =3D UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) &ProgressColor, + EfiUgaVideoFill, + 0, + 0, + PosX, + PosY, + BlockWidth - 1, + BlockHeight, + (BlockWidth) * sizeof (EFI_UGA_PIXEL) + ); + } else { + return EFI_UNSUPPORTED; + } + } + + PrintXY ( + (SizeOfX - StrLen (Title) * EFI_GLYPH_WIDTH) / 2, + PosY - EFI_GLYPH_HEIGHT - 1, + &TitleForeground, + &TitleBackground, + Title + ); + + return EFI_SUCCESS; +} + +/** + Perform the memory test base on the memory test intensive level, + and update the memory resource. + + @param Level The memory test intensive level. + + @retval EFI_STATUS Success test all the system memory and update + the memory resource + +**/ +EFI_STATUS +PlatformBootManagerMemoryTest ( + IN EXTENDMEM_COVERAGE_LEVEL Level + ) +{ + EFI_STATUS Status; + EFI_STATUS KeyStatus; + EFI_STATUS InitStatus; + EFI_STATUS ReturnStatus; + BOOLEAN RequireSoftECCInit; + EFI_GENERIC_MEMORY_TEST_PROTOCOL *GenMemoryTest; + UINT64 TestedMemorySize; + UINT64 TotalMemorySize; + UINTN TestPercent; + UINT64 PreviousValue; + BOOLEAN ErrorOut; + BOOLEAN TestAbort; + EFI_INPUT_KEY Key; + CHAR16 StrPercent[80]; + CHAR16 *StrTotalMemory; + CHAR16 *Pos; + CHAR16 *TmpStr; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Foreground; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color; + UINT32 TempData; + UINTN StrTotalMemorySize; + + ReturnStatus =3D EFI_SUCCESS; + ZeroMem (&Key, sizeof (EFI_INPUT_KEY)); + + StrTotalMemorySize =3D 128; + Pos =3D AllocateZeroPool (StrTotalMemorySize); + ASSERT (Pos !=3D NULL); + + if (gStringPackHandle =3D=3D NULL) { + gStringPackHandle =3D HiiAddPackages ( + &mPlatformBootManagerStringPackGuid, + gImageHandle, + PlatformBootManagerLibStrings, + NULL + ); + ASSERT (gStringPackHandle !=3D NULL); + } + + StrTotalMemory =3D Pos; + + TestedMemorySize =3D 0; + TotalMemorySize =3D 0; + PreviousValue =3D 0; + ErrorOut =3D FALSE; + TestAbort =3D FALSE; + + SetMem (&Foreground, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff); + SetMem (&Background, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0); + SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff); + + RequireSoftECCInit =3D FALSE; + + Status =3D gBS->LocateProtocol ( + &gEfiGenericMemTestProtocolGuid, + NULL, + (VOID **) &GenMemoryTest + ); + if (EFI_ERROR (Status)) { + FreePool (Pos); + return EFI_SUCCESS; + } + + InitStatus =3D GenMemoryTest->MemoryTestInit ( + GenMemoryTest, + Level, + &RequireSoftECCInit + ); + if (InitStatus =3D=3D EFI_NO_MEDIA) { + // + // The PEI codes also have the relevant memory test code to check the = memory, + // it can select to test some range of the memory or all of them. If P= EI code + // checks all the memory, this BDS memory test will has no not-test me= mory to + // do the test, and then the status of EFI_NO_MEDIA will be returned by + // "MemoryTestInit". So it does not need to test memory again, just re= turn. + // + FreePool (Pos); + return EFI_SUCCESS; + } + + if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) { + TmpStr =3D HiiGetString (gStringPackHandle, STRING_TOKEN (STR_ESC_TO_S= KIP_MEM_TEST), NULL); + + if (TmpStr !=3D NULL) { + PrintXY (10, 10, NULL, NULL, TmpStr); + FreePool (TmpStr); + } + } else { + DEBUG ((EFI_D_INFO, "Enter memory test.\n")); + } + do { + Status =3D GenMemoryTest->PerformMemoryTest ( + GenMemoryTest, + &TestedMemorySize, + &TotalMemorySize, + &ErrorOut, + TestAbort + ); + if (ErrorOut && (Status =3D=3D EFI_DEVICE_ERROR)) { + TmpStr =3D HiiGetString (gStringPackHandle, STRING_TOKEN (STR_SYSTEM= _MEM_ERROR), NULL); + if (TmpStr !=3D NULL) { + PrintXY (10, 10, NULL, NULL, TmpStr); + FreePool (TmpStr); + } + + ASSERT (0); + } + + if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) { + TempData =3D (UINT32) DivU64x32 (TotalMemorySize, 16); + TestPercent =3D (UINTN) DivU64x32 ( + DivU64x32 (MultU64x32 (TestedMemorySize, 100= ), 16), + TempData + ); + if (TestPercent !=3D PreviousValue) { + UnicodeValueToString (StrPercent, 0, TestPercent, 0); + TmpStr =3D HiiGetString (gStringPackHandle, STRING_TOKEN (STR_MEMO= RY_TEST_PERCENT), NULL); + if (TmpStr !=3D NULL) { + // + // TmpStr size is 64, StrPercent is reserved to 16. + // + StrnCatS ( + StrPercent, + sizeof (StrPercent) / sizeof (CHAR16), + TmpStr, + sizeof (StrPercent) / sizeof (CHAR16) - StrLen (StrPercent) - 1 + ); + PrintXY (10, 10, NULL, NULL, StrPercent); + FreePool (TmpStr); + } + + TmpStr =3D HiiGetString (gStringPackHandle, STRING_TOKEN (STR_PERF= ORM_MEM_TEST), NULL); + if (TmpStr !=3D NULL) { + PlatformBootManagerShowProgress ( + Foreground, + Background, + TmpStr, + Color, + TestPercent, + (UINTN) PreviousValue + ); + FreePool (TmpStr); + } + } + + PreviousValue =3D TestPercent; + } else { + DEBUG ((EFI_D_INFO, "Perform memory test (ESC to skip).\n")); + } + + if (!PcdGetBool (PcdConInConnectOnDemand)) { + KeyStatus =3D gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (!EFI_ERROR (KeyStatus) && (Key.ScanCode =3D=3D SCAN_ESC)) { + if (!RequireSoftECCInit) { + if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) { + TmpStr =3D HiiGetString (gStringPackHandle, STRING_TOKEN (STR_= PERFORM_MEM_TEST), NULL); + if (TmpStr !=3D NULL) { + PlatformBootManagerShowProgress ( + Foreground, + Background, + TmpStr, + Color, + 100, + (UINTN) PreviousValue + ); + FreePool (TmpStr); + } + + PrintXY (10, 10, NULL, NULL, L"100"); + } + Status =3D GenMemoryTest->Finished (GenMemoryTest); + goto Done; + } + + TestAbort =3D TRUE; + } + } + } while (Status !=3D EFI_NOT_FOUND); + + Status =3D GenMemoryTest->Finished (GenMemoryTest); + +Done: + if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) { + UnicodeValueToString (StrTotalMemory, COMMA_TYPE, TotalMemorySize, 0); + if (StrTotalMemory[0] =3D=3D L',') { + StrTotalMemory++; + StrTotalMemorySize -=3D sizeof (CHAR16); + } + + TmpStr =3D HiiGetString (gStringPackHandle, STRING_TOKEN (STR_MEM_TEST= _COMPLETED), NULL); + if (TmpStr !=3D NULL) { + StrnCatS ( + StrTotalMemory, + StrTotalMemorySize / sizeof (CHAR16), + TmpStr, + StrTotalMemorySize / sizeof (CHAR16) - StrLen (StrTotalMemory) - 1 + ); + FreePool (TmpStr); + } + + PrintXY (10, 10, NULL, NULL, StrTotalMemory); + PlatformBootManagerShowProgress ( + Foreground, + Background, + StrTotalMemory, + Color, + 100, + (UINTN) PreviousValue + ); + + } else { + DEBUG ((EFI_D_INFO, "%d bytes of system memory tested OK\r\n", TotalMe= morySize)); + } + + FreePool (Pos); + return ReturnStatus; +} + +/** + Convert a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer + is passed in a GopBlt buffer will be allocated by this routine. If a Gop= Blt + buffer is passed in it will be used if it is big enough. + + @param BmpImage Pointer to BMP file + @param BmpImageSize Number of bytes in BmpImage + @param GopBlt Buffer containing GOP version of BmpImage. + @param GopBltSize Size of GopBlt in bytes. + @param PixelHeight Height of GopBlt/BmpImage in pixels + @param PixelWidth Width of GopBlt/BmpImage in pixels + + @retval EFI_SUCCESS GopBlt and GopBltSize are returned. + @retval EFI_UNSUPPORTED BmpImage is not a valid *.BMP image + @retval EFI_BUFFER_TOO_SMALL The passed in GopBlt buffer is not big eno= ugh. + GopBltSize will contain the required size. + @retval EFI_OUT_OF_RESOURCES No enough buffer to allocate. + +**/ +EFI_STATUS +PlatformBootManagerConvertBmpToGopBlt ( + IN VOID *BmpImage, + IN UINTN BmpImageSize, + IN OUT VOID **GopBlt, + IN OUT UINTN *GopBltSize, + OUT UINTN *PixelHeight, + OUT UINTN *PixelWidth + ) +{ + UINT8 *Image; + UINT8 *ImageHeader; + BMP_IMAGE_HEADER *BmpHeader; + BMP_COLOR_MAP *BmpColorMap; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + UINT64 BltBufferSize; + UINTN Index; + UINTN Height; + UINTN Width; + UINTN ImageIndex; + UINT32 DataSizePerLine; + BOOLEAN IsAllocated; + UINT32 ColorMapNum; + + if (sizeof (BMP_IMAGE_HEADER) > BmpImageSize) { + DEBUG ((DEBUG_INFO, "BMP_IMAGE_HEADER) > BmpImageSize.\n")); + return EFI_INVALID_PARAMETER; + } + + BmpHeader =3D (BMP_IMAGE_HEADER *) BmpImage; + + if (BmpHeader->CharB !=3D 'B' || BmpHeader->CharM !=3D 'M') { + DEBUG ((DEBUG_INFO, "(BmpHeader->CharB !=3D 'B' || BmpHeader->CharM != =3D 'M').\n")); + return EFI_UNSUPPORTED; + } + + // + // Doesn't support compress. + // + if (BmpHeader->CompressionType !=3D 0) { + DEBUG ((DEBUG_INFO, "It's compressed! We dont support.\n")); + return EFI_UNSUPPORTED; + } + + // + // Only support BITMAPINFOHEADER format. + // BITMAPFILEHEADER + BITMAPINFOHEADER =3D BMP_IMAGE_HEADER + // + if (BmpHeader->HeaderSize !=3D sizeof (BMP_IMAGE_HEADER) - OFFSET_OF(BMP= _IMAGE_HEADER, HeaderSize)) { + DEBUG ((DEBUG_INFO, "Only support BITMAPINFOHEADER.\n")); + return EFI_UNSUPPORTED; + } + + // + // The data size in each line must be 4 byte alignment. + // + DataSizePerLine =3D ((BmpHeader->PixelWidth * BmpHeader->BitPerPixel + 3= 1) >> 3) & (~0x3); + BltBufferSize =3D MultU64x32 (DataSizePerLine, BmpHeader->PixelHeight); + if (BltBufferSize > (UINT32) ~0) { + DEBUG ((DEBUG_INFO, "The data size in each line must be 4 byte alignme= nt.\n")); + return EFI_INVALID_PARAMETER; + } + + if ((BmpHeader->Size !=3D BmpImageSize) || + (BmpHeader->Size < BmpHeader->ImageOffset) || + (BmpHeader->Size - BmpHeader->ImageOffset !=3D BmpHeader->PixelHeig= ht * DataSizePerLine)) { + DEBUG ((DEBUG_INFO, "BmpHeader->Size problem.\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Calculate Color Map offset in the image. + // + Image =3D BmpImage; + BmpColorMap =3D (BMP_COLOR_MAP *) (Image + sizeof (BMP_IMAGE_HEADER)); + if (BmpHeader->ImageOffset < sizeof (BMP_IMAGE_HEADER)) { + DEBUG ((DEBUG_INFO, "BmpHeader->ImageOffset < sizeof (BMP_IMAGE_HEADER= )\n")); + return EFI_INVALID_PARAMETER; + } + + if (BmpHeader->ImageOffset > sizeof (BMP_IMAGE_HEADER)) { + switch (BmpHeader->BitPerPixel) { + case 1: + ColorMapNum =3D 2; + break; + case 4: + ColorMapNum =3D 16; + break; + case 8: + ColorMapNum =3D 256; + break; + default: + ColorMapNum =3D 0; + break; + } + // + // BMP file may has padding data between the bmp header section and th= e bmp data section. + // + if (BmpHeader->ImageOffset - sizeof (BMP_IMAGE_HEADER) < sizeof (BMP_C= OLOR_MAP) * ColorMapNum) { + DEBUG ((DEBUG_INFO, "(BmpHeader->ImageOffset - sizeof (BMP_IMAGE_HEA= DER) < sizeof (BMP_COLOR_MAP) * ColorMapNum)\n")); + return EFI_INVALID_PARAMETER; + } + } + + // + // Calculate graphics image data address in the image + // + Image =3D ((UINT8 *) BmpImage) + BmpHeader->ImageOffset; + ImageHeader =3D Image; + + // + // Calculate the BltBuffer needed size. + // + BltBufferSize =3D MultU64x32 ((UINT64) BmpHeader->PixelWidth, BmpHeader-= >PixelHeight); + // + // Ensure the BltBufferSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doe= sn't overflow + // + if (BltBufferSize > DivU64x32 ((UINTN) ~0, sizeof (EFI_GRAPHICS_OUTPUT_B= LT_PIXEL))) { + return EFI_UNSUPPORTED; + } + BltBufferSize =3D MultU64x32 (BltBufferSize, sizeof (EFI_GRAPHICS_OUTPUT= _BLT_PIXEL)); + + IsAllocated =3D FALSE; + if (*GopBlt =3D=3D NULL) { + // + // GopBlt is not allocated by caller. + // + *GopBltSize =3D (UINTN) BltBufferSize; + *GopBlt =3D AllocatePool (*GopBltSize); + IsAllocated =3D TRUE; + if (*GopBlt =3D=3D NULL) { + DEBUG ((DEBUG_INFO, "EFI_OUT_OF_RESOURCES\n")); + return EFI_OUT_OF_RESOURCES; + } + } else { + // + // GopBlt has been allocated by caller. + // + if (*GopBltSize < (UINTN) BltBufferSize) { + *GopBltSize =3D (UINTN) BltBufferSize; + DEBUG ((DEBUG_INFO, "EEFI_BUFFER_TOO_SMALL\n")); + return EFI_BUFFER_TOO_SMALL; + } + } + + *PixelWidth =3D BmpHeader->PixelWidth; + *PixelHeight =3D BmpHeader->PixelHeight; + + // + // Convert image from BMP to Blt buffer format + // + BltBuffer =3D *GopBlt; + for (Height =3D 0; Height < BmpHeader->PixelHeight; Height++) { + Blt =3D &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->= PixelWidth]; + for (Width =3D 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt= ++) { + switch (BmpHeader->BitPerPixel) { + case 1: + // + // Convert 1-bit (2 colors) BMP to 24-bit color + // + for (Index =3D 0; Index < 8 && Width < BmpHeader->PixelWidth; Inde= x++) { + Blt->Red =3D BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Red; + Blt->Green =3D BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Gre= en; + Blt->Blue =3D BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Blu= e; + Blt++; + Width++; + } + + Blt--; + Width--; + break; + + case 4: + // + // Convert 4-bit (16 colors) BMP Palette to 24-bit color + // + Index =3D (*Image) >> 4; + Blt->Red =3D BmpColorMap[Index].Red; + Blt->Green =3D BmpColorMap[Index].Green; + Blt->Blue =3D BmpColorMap[Index].Blue; + if (Width < (BmpHeader->PixelWidth - 1)) { + Blt++; + Width++; + Index =3D (*Image) & 0x0f; + Blt->Red =3D BmpColorMap[Index].Red; + Blt->Green =3D BmpColorMap[Index].Green; + Blt->Blue =3D BmpColorMap[Index].Blue; + } + break; + + case 8: + // + // Convert 8-bit (256 colors) BMP Palette to 24-bit color + // + Blt->Red =3D BmpColorMap[*Image].Red; + Blt->Green =3D BmpColorMap[*Image].Green; + Blt->Blue =3D BmpColorMap[*Image].Blue; + break; + + case 24: + // + // It is 24-bit BMP. + // + Blt->Blue =3D *Image++; + Blt->Green =3D *Image++; + Blt->Red =3D *Image; + break; + + default: + // + // Other bit format BMP is not supported. + // + if (IsAllocated) { + FreePool (*GopBlt); + *GopBlt =3D NULL; + } + DEBUG ((DEBUG_INFO, "Other bit format BMP is not supported.\n")); + return EFI_UNSUPPORTED; + break; + }; + + } + + ImageIndex =3D (UINTN) (Image - ImageHeader); + if ((ImageIndex % 4) !=3D 0) { + // + // Bmp Image starts each row on a 32-bit boundary! + // + Image =3D Image + (4 - (ImageIndex % 4)); + } + } + + return EFI_SUCCESS; +} + +/** + Use SystemTable Conout to stop video based Simple Text Out consoles from= going + to the video device. Put up LogoFile on every video device that is a con= sole. + + @param[in] LogoFile File name of logo to display on the center of the= screen. + + @retval EFI_SUCCESS ConsoleControl has been flipped to graphics and = logo displayed. + @retval EFI_UNSUPPORTED Logo not found + +**/ +EFI_STATUS +PlatformBootManagerEnableQuietBoot ( + IN EFI_GUID *LogoFile + ) +{ + EFI_STATUS Status; + EFI_OEM_BADGING_PROTOCOL *Badging; + UINT32 SizeOfX; + UINT32 SizeOfY; + INTN DestX; + INTN DestY; + UINT8 *ImageData; + UINTN ImageSize; + UINTN BltSize; + UINT32 Instance; + EFI_BADGING_FORMAT Format; + EFI_BADGING_DISPLAY_ATTRIBUTE Attribute; + UINTN CoordinateX; + UINTN CoordinateY; + UINTN Height; + UINTN Width; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + EFI_UGA_DRAW_PROTOCOL *UgaDraw; + UINT32 ColorDepth; + UINT32 RefreshRate; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_BOOT_LOGO_PROTOCOL *BootLogo; + UINTN NumberOfLogos; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *LogoBlt; + UINTN LogoDestX; + UINTN LogoDestY; + UINTN LogoHeight; + UINTN LogoWidth; + UINTN NewDestX; + UINTN NewDestY; + UINTN NewHeight; + UINTN NewWidth; + UINT64 BufferSize; + + UgaDraw =3D NULL; + // + // Try to open GOP first + // + Status =3D gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutp= utProtocolGuid, (VOID **) &GraphicsOutput); + if (EFI_ERROR (Status) && FeaturePcdGet (PcdUgaConsumeSupport)) { + DEBUG ((DEBUG_INFO, "No GOP.\n")); + GraphicsOutput =3D NULL; + // + // Open GOP failed, try to open UGA + // + Status =3D gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiUgaDrawPro= tocolGuid, (VOID **) &UgaDraw); + } + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "No GOP or UGA.\n")); + return EFI_UNSUPPORTED; + } + + // + // Try to open Boot Logo Protocol. + // + BootLogo =3D NULL; + gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLog= o); + + // + // Erase Cursor from screen + // + gST->ConOut->EnableCursor (gST->ConOut, FALSE); + + Badging =3D NULL; + Status =3D gBS->LocateProtocol (&gEfiOEMBadgingProtocolGuid, NULL, (VOI= D **) &Badging); + if (Badging !=3D NULL) { + DEBUG ((DEBUG_INFO, "OEM Badging existing!\n")); + } + if (GraphicsOutput !=3D NULL) { + SizeOfX =3D GraphicsOutput->Mode->Info->HorizontalResolution; + SizeOfY =3D GraphicsOutput->Mode->Info->VerticalResolution; + + } else if (UgaDraw !=3D NULL && FeaturePcdGet (PcdUgaConsumeSupport)) { + Status =3D UgaDraw->GetMode (UgaDraw, &SizeOfX, &SizeOfY, &ColorDepth,= &RefreshRate); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + } else { + return EFI_UNSUPPORTED; + } + + Blt =3D NULL; + NumberOfLogos =3D 0; + LogoDestX =3D 0; + LogoDestY =3D 0; + LogoHeight =3D 0; + LogoWidth =3D 0; + NewDestX =3D 0; + NewDestY =3D 0; + NewHeight =3D 0; + NewWidth =3D 0; + Instance =3D 0; + while (1) { + ImageData =3D NULL; + ImageSize =3D 0; + + if (Badging !=3D NULL) { + // + // Get image from OEMBadging protocol. + // + Status =3D Badging->GetImage ( + Badging, + &Instance, + &Format, + &ImageData, + &ImageSize, + &Attribute, + &CoordinateX, + &CoordinateY + ); + if (EFI_ERROR (Status)) { + goto Done; + } + DEBUG ((DEBUG_INFO, "Badging->GetImage: Image size %x Attribute %x\n= ", ImageSize, Attribute)); + + // + // Currently only support BMP format. + // + if (Format !=3D EfiBadgingFormatBMP) { + if (ImageData !=3D NULL) { + FreePool (ImageData); + } + continue; + } + } else { + // + // Get the specified image from FV. + // + DEBUG ((DEBUG_INFO, "Load logo file.\n")); + Status =3D GetSectionFromAnyFv (LogoFile, EFI_SECTION_RAW, 0, (VOID = **) &ImageData, &ImageSize); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + CoordinateX =3D 0; + CoordinateY =3D 0; + if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) { + DEBUG ((DEBUG_INFO, "Logo enable only =3D FALSE.\n")); + Attribute =3D EfiBadgingDisplayAttributeCenter; + } else { + DEBUG ((DEBUG_INFO, "Logo enable only =3D TRUE.\n")); + Attribute =3D EfiBadgingDisplayAttributeCustomized; + } + } + + if (Blt !=3D NULL) { + FreePool (Blt); + } + Blt =3D NULL; + Status =3D PlatformBootManagerConvertBmpToGopBlt ( + ImageData, + ImageSize, + (VOID **) &Blt, + &BltSize, + &Height, + &Width + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "PlatformBootManagerConvertBmpToGopBlt FALSE.\n"= )); + FreePool (ImageData); + + if (Badging =3D=3D NULL) { + return Status; + } else { + continue; + } + } + DEBUG ((DEBUG_INFO, "Badging->GetImage: Image H: %d, Width:%d\n", Heig= ht, Width)); + + // + // Calculate the display position according to Attribute. + // + switch (Attribute) { + case EfiBadgingDisplayAttributeLeftTop: + DestX =3D CoordinateX; + DestY =3D CoordinateY; + break; + + case EfiBadgingDisplayAttributeCenterTop: + DestX =3D (SizeOfX - Width) / 2; + DestY =3D CoordinateY; + break; + + case EfiBadgingDisplayAttributeRightTop: + DestX =3D (SizeOfX - Width - CoordinateX); + DestY =3D CoordinateY;; + break; + + case EfiBadgingDisplayAttributeCenterRight: + DestX =3D (SizeOfX - Width - CoordinateX); + DestY =3D (SizeOfY - Height) / 2; + break; + + case EfiBadgingDisplayAttributeRightBottom: + DestX =3D (SizeOfX - Width - CoordinateX); + DestY =3D (SizeOfY - Height - CoordinateY); + break; + + case EfiBadgingDisplayAttributeCenterBottom: + DestX =3D (SizeOfX - Width) / 2; + DestY =3D (SizeOfY - Height - CoordinateY); + break; + + case EfiBadgingDisplayAttributeLeftBottom: + DestX =3D CoordinateX; + DestY =3D (SizeOfY - Height - CoordinateY); + break; + + case EfiBadgingDisplayAttributeCenterLeft: + DestX =3D CoordinateX; + DestY =3D (SizeOfY - Height) / 2; + break; + + case EfiBadgingDisplayAttributeCenter: + DestX =3D (SizeOfX - Width) / 2; + DestY =3D (SizeOfY - Height) / 2; + break; + + case EfiBadgingDisplayAttributeCustomized: + DestX =3D CoordinateX; + DestY =3D CoordinateY; + break; + + default: + DestX =3D CoordinateX; + DestY =3D CoordinateY; + break; + } + + if ((DestX >=3D 0) && (DestY >=3D 0)) { + if (GraphicsOutput !=3D NULL) { + Status =3D GraphicsOutput->Blt ( + GraphicsOutput, + Blt, + EfiBltBufferToVideo, + 0, + 0, + (UINTN) DestX, + (UINTN) DestY, + Width, + Height, + Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + ); + } else if (UgaDraw !=3D NULL && FeaturePcdGet (PcdUgaConsumeSupport)= ) { + Status =3D UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) Blt, + EfiUgaBltBufferToVideo, + 0, + 0, + (UINTN) DestX, + (UINTN) DestY, + Width, + Height, + Width * sizeof (EFI_UGA_PIXEL) + ); + } else { + Status =3D EFI_UNSUPPORTED; + } + + // + // Report displayed Logo information. + // + if (!EFI_ERROR (Status)) { + NumberOfLogos++; + + if (LogoWidth =3D=3D 0) { + // + // The first Logo. + // + LogoDestX =3D (UINTN) DestX; + LogoDestY =3D (UINTN) DestY; + LogoWidth =3D Width; + LogoHeight =3D Height; + } else { + // + // Merge new logo with old one. + // + NewDestX =3D MIN ((UINTN) DestX, LogoDestX); + NewDestY =3D MIN ((UINTN) DestY, LogoDestY); + NewWidth =3D MAX ((UINTN) DestX + Width, LogoDestX + LogoWidth) = - NewDestX; + NewHeight =3D MAX ((UINTN) DestY + Height, LogoDestY + LogoHeigh= t) - NewDestY; + + LogoDestX =3D NewDestX; + LogoDestY =3D NewDestY; + LogoWidth =3D NewWidth; + LogoHeight =3D NewHeight; + } + } + } + + FreePool (ImageData); + + if (Badging =3D=3D NULL) { + break; + } + } + +Done: + if (BootLogo =3D=3D NULL || NumberOfLogos =3D=3D 0) { + // + // No logo displayed. + // + if (Blt !=3D NULL) { + FreePool (Blt); + } + + return Status; + } + + // + // Advertise displayed Logo information. + // + if (NumberOfLogos =3D=3D 1) { + // + // Only one logo displayed, use its Blt buffer directly for BootLogo p= rotocol. + // + LogoBlt =3D Blt; + Status =3D EFI_SUCCESS; + } else { + // + // More than one Logo displayed, get merged BltBuffer using VideoToBuf= fer operation. + // + if (Blt !=3D NULL) { + FreePool (Blt); + } + + // + // Ensure the LogoHeight * LogoWidth doesn't overflow + // + if (LogoHeight > DivU64x64Remainder ((UINTN) ~0, LogoWidth, NULL)) { + return EFI_UNSUPPORTED; + } + BufferSize =3D MultU64x64 (LogoWidth, LogoHeight); + + // + // Ensure the BufferSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) does= n't overflow + // + if (BufferSize > DivU64x32 ((UINTN) ~0, sizeof (EFI_GRAPHICS_OUTPUT_BL= T_PIXEL))) { + return EFI_UNSUPPORTED; + } + + LogoBlt =3D AllocateZeroPool ((UINTN)BufferSize * sizeof (EFI_GRAPHICS= _OUTPUT_BLT_PIXEL)); + if (LogoBlt =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (GraphicsOutput !=3D NULL) { + DEBUG ((DEBUG_INFO, "GraphicsOutput->Blt.\n")); + Status =3D GraphicsOutput->Blt ( + GraphicsOutput, + LogoBlt, + EfiBltVideoToBltBuffer, + LogoDestX, + LogoDestY, + 0, + 0, + LogoWidth, + LogoHeight, + LogoWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXE= L) + ); + } else if (UgaDraw !=3D NULL && FeaturePcdGet (PcdUgaConsumeSupport)) { + DEBUG ((DEBUG_INFO, " UgaDraw->Blt.\n")); + Status =3D UgaDraw->Blt ( + UgaDraw, + (EFI_UGA_PIXEL *) LogoBlt, + EfiUgaVideoToBltBuffer, + LogoDestX, + LogoDestY, + 0, + 0, + LogoWidth, + LogoHeight, + LogoWidth * sizeof (EFI_UGA_PIXEL) + ); + } else { + Status =3D EFI_UNSUPPORTED; + } + } + + if (!EFI_ERROR (Status)) { + BootLogo->SetBootLogo (BootLogo, LogoBlt, LogoDestX, LogoDestY, LogoWi= dth, LogoHeight); + } + FreePool (LogoBlt); + + return Status; +} + +/** + Use SystemTable Conout to turn on video based Simple Text Out consoles. = The + Simple Text Out screens will now be synced up with all non video output = devices + + @retval EFI_SUCCESS UGA devices are back in text mode and synced up. + +**/ +EFI_STATUS +PlatformBootManagerDisableQuietBoot ( + VOID + ) +{ + // + // Enable Cursor on Screen + // + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + return EFI_SUCCESS; +} diff --git a/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/P= latformBootManager.c b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootMa= nagerLib/PlatformBootManager.c new file mode 100644 index 0000000..51535be --- /dev/null +++ b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/Platform= BootManager.c @@ -0,0 +1,269 @@ +/** @file + This file include all platform action which can be customized + by IBV/OEM. + +Copyright (c) 2019, Hewlett Packard Enterprise Development LP. All rights = reserved.
+Copyright (c) 2015, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD = License +which accompanies this distribution. The full text of the license may be = found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLI= ED. + +**/ + +#include "PlatformBootManager.h" + + +EFI_GUID mUefiShellFileGuid =3D { 0x7C04A583, 0x9E3E, 0x4f1c, {0xAD, 0x65,= 0xE0, 0x52, 0x68, 0xD0, 0xB4, 0xD1}}; + +/** + Perform the platform diagnostic, such like test memory. OEM/IBV also + can customize this function to support specific platform diagnostic. + + @param MemoryTestLevel The memory test intensive level + @param QuietBoot Indicate if need to enable the quiet boot + +**/ +VOID +PlatformBootManagerDiagnostics ( + IN EXTENDMEM_COVERAGE_LEVEL MemoryTestLevel, + IN BOOLEAN QuietBoot + ) +{ + EFI_STATUS Status; + + // + // Here we can decide if we need to show + // the diagnostics screen + // Notes: this quiet boot code should be remove + // from the graphic lib + // + if (QuietBoot) { + PlatformBootManagerEnableQuietBoot (PcdGetPtr(PcdLogoFile)); + + // + // Perform system diagnostic + // + Status =3D PlatformBootManagerMemoryTest (MemoryTestLevel); + if (EFI_ERROR (Status)) { + PlatformBootManagerDisableQuietBoot (); + } + + return; + } + + // + // Perform system diagnostic + // + Status =3D PlatformBootManagerMemoryTest (MemoryTestLevel); +} + +/** + Return the index of the load option in the load option array. + + The function consider two load options are equal when the + OptionType, Attributes, Description, FilePath and OptionalData are equal. + + @param Key Pointer to the load option to be found. + @param Array Pointer to the array of load options to be found. + @param Count Number of entries in the Array. + + @retval -1 Key wasn't found in the Array. + @retval 0 ~ Count-1 The index of the Key in the Array. +**/ +INTN +PlatformFindLoadOption ( + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key, + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array, + IN UINTN Count + ) +{ + UINTN Index; + + for (Index =3D 0; Index < Count; Index++) { + if ((Key->OptionType =3D=3D Array[Index].OptionType) && + (Key->Attributes =3D=3D Array[Index].Attributes) && + (StrCmp (Key->Description, Array[Index].Description) =3D=3D 0) && + (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSi= ze (Key->FilePath)) =3D=3D 0) && + (Key->OptionalDataSize =3D=3D Array[Index].OptionalDataSize) && + (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->Op= tionalDataSize) =3D=3D 0)) { + return (INTN) Index; + } + } + + return -1; +} + +VOID +PlatformRegisterFvBootOption ( + EFI_GUID *FileGuid, + CHAR16 *Description, + UINT32 Attributes + ) +{ + EFI_STATUS Status; + UINTN OptionIndex; + EFI_BOOT_MANAGER_LOAD_OPTION NewOption; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + Status =3D gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageProtocolGu= id, (VOID **) &LoadedImage); + ASSERT_EFI_ERROR (Status); + + EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid); + DevicePath =3D AppendDevicePathNode ( + DevicePathFromHandle (LoadedImage->DeviceHandle), + (EFI_DEVICE_PATH_PROTOCOL *) &FileNode + ); + + Status =3D EfiBootManagerInitializeLoadOption ( + &NewOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + Attributes, + Description, + DevicePath, + NULL, + 0 + ); + if (!EFI_ERROR (Status)) { + BootOptions =3D EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOp= tionTypeBoot); + + OptionIndex =3D PlatformFindLoadOption (&NewOption, BootOptions, BootO= ptionCount); + + if (OptionIndex =3D=3D -1) { + Status =3D EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) = -1); + ASSERT_EFI_ERROR (Status); + } + EfiBootManagerFreeLoadOption (&NewOption); + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + } +} + +/** + Do the platform specific action before the console is connected. + + Such as: + Update console variable; + Register new Driver#### or Boot####; + Signal ReadyToLock event. +**/ +VOID +EFIAPI +PlatformBootManagerBeforeConsole ( + VOID + ) +{ + UINTN Index; + EFI_STATUS Status; + EFI_INPUT_KEY Enter; + EFI_INPUT_KEY F2; + EFI_BOOT_MANAGER_LOAD_OPTION BootOption; + + // + // Update the console variables. + // + for (Index =3D 0; gPlatformConsole[Index].DevicePath !=3D NULL; Index++)= { + DEBUG ((DEBUG_INFO, "Check gPlatformConsole %d\n", Index)); + if ((gPlatformConsole[Index].ConnectType & CONSOLE_IN) =3D=3D CONSOLE_= IN) { + Status =3D EfiBootManagerUpdateConsoleVariable (ConIn, gPlatformCons= ole[Index].DevicePath, NULL); + DEBUG ((DEBUG_INFO, "CONSOLE_IN variable set %s : %r\n", ConvertDevi= cePathToText (gPlatformConsole[Index].DevicePath, FALSE, FALSE), Status)); + } + + if ((gPlatformConsole[Index].ConnectType & CONSOLE_OUT) =3D=3D CONSOLE= _OUT) { + Status =3D EfiBootManagerUpdateConsoleVariable (ConOut, gPlatformCon= sole[Index].DevicePath, NULL); + DEBUG ((DEBUG_INFO, "CONSOLE_OUT variable set %s : %r\n", ConvertDev= icePathToText (gPlatformConsole[Index].DevicePath, FALSE, FALSE), Status)); + } + + if ((gPlatformConsole[Index].ConnectType & STD_ERROR) =3D=3D STD_ERROR= ) { + Status =3D EfiBootManagerUpdateConsoleVariable (ErrOut, gPlatformCon= sole[Index].DevicePath, NULL); + DEBUG ((DEBUG_INFO, "STD_ERROR variable set %r", Status)); + } + } + + // + // Register ENTER as CONTINUE key + // + Enter.ScanCode =3D SCAN_NULL; + Enter.UnicodeChar =3D CHAR_CARRIAGE_RETURN; + EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL); + // + // Map F2 to Boot Manager Menu + // + F2.ScanCode =3D SCAN_F2; + F2.UnicodeChar =3D CHAR_NULL; + EfiBootManagerGetBootManagerMenu (&BootOption); + EfiBootManagerAddKeyOptionVariable (NULL, (UINT16) BootOption.OptionNumb= er, 0, &F2, NULL); + // + // Register UEFI Shell + // + PlatformRegisterFvBootOption (&mUefiShellFileGuid, L"UEFI Shell", LOAD_O= PTION_ACTIVE); +} + +/** + Do the platform specific action after the console is connected. + + Such as: + Dynamically switch output mode; + Signal console ready platform customized event; + Run diagnostics like memory testing; + Connect certain devices; + Dispatch aditional option roms. +**/ +VOID +EFIAPI +PlatformBootManagerAfterConsole ( + VOID + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL White; + + Black.Blue =3D Black.Green =3D Black.Red =3D Black.Reserved =3D 0; + White.Blue =3D White.Green =3D White.Red =3D White.Reserved =3D 0xFF; + + EfiBootManagerConnectAll (); + EfiBootManagerRefreshAllBootOption (); + + PlatformBootManagerDiagnostics (QUICK, TRUE); + + PrintXY (10, 10, &White, &Black, L"F2 to enter Boot Manager Menu. = "); + PrintXY (10, 30, &White, &Black, L"Enter to boot directly."); +} + +/** + This function is called each second during the boot manager waits the ti= meout. + + @param TimeoutRemain The remaining timeout. +**/ +VOID +EFIAPI +PlatformBootManagerWaitCallback ( + UINT16 TimeoutRemain + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL White; + UINT16 Timeout; + + Timeout =3D PcdGet16 (PcdPlatformBootTimeOut); + + Black.Blue =3D Black.Green =3D Black.Red =3D Black.Reserved =3D 0; + White.Blue =3D White.Green =3D White.Red =3D White.Reserved =3D 0xFF; + + PlatformBootManagerShowProgress ( + White, + Black, + L"Start boot option", + White, + (Timeout - TimeoutRemain) * 100 / Timeout, + 0 + ); +} diff --git a/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/P= latformBootManager.h b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootMa= nagerLib/PlatformBootManager.h new file mode 100644 index 0000000..cf26bd7 --- /dev/null +++ b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/Platform= BootManager.h @@ -0,0 +1,141 @@ +/**@file + Head file for BDS Platform specific code + +Copyright (c) 2016, Hewlett Packard Enterprise Development LP. All rights = reserved.
+Copyright (c) 2015, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD = License +which accompanies this distribution. The full text of the license may be = found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLI= ED. +**/ + +#ifndef _PLATFORM_BOOT_MANAGER_H +#define _PLATFORM_BOOT_MANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN ConnectType; +} PLATFORM_CONSOLE_CONNECT_ENTRY; + +extern PLATFORM_CONSOLE_CONNECT_ENTRY gPlatformConsole[]; + +#define gEndEntire \ + { \ + END_DEVICE_PATH_TYPE,\ + END_ENTIRE_DEVICE_PATH_SUBTYPE,\ + END_DEVICE_PATH_LENGTH,\ + 0\ + } + +#define CONSOLE_OUT BIT0 +#define CONSOLE_IN BIT1 +#define STD_ERROR BIT2 + +//D3987D4B-971A-435F-8CAF-4967EB627241 +#define EFI_SERIAL_DXE_GUID \ + { 0xD3987D4B, 0x971A, 0x435F, { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72= , 0x41 } } + +typedef struct { + VENDOR_DEVICE_PATH Guid; + UART_DEVICE_PATH Uart; + VENDOR_DEVICE_PATH TerminalType; + EFI_DEVICE_PATH_PROTOCOL End; +} SERIAL_CONSOLE_DEVICE_PATH; + +/** + Use SystemTable Conout to stop video based Simple Text Out consoles from= going + to the video device. Put up LogoFile on every video device that is a con= sole. + + @param[in] LogoFile File name of logo to display on the center of the= screen. + + @retval EFI_SUCCESS ConsoleControl has been flipped to graphics and = logo displayed. + @retval EFI_UNSUPPORTED Logo not found + +**/ +EFI_STATUS +PlatformBootManagerEnableQuietBoot ( + IN EFI_GUID *LogoFile + ); + +/** + Use SystemTable Conout to turn on video based Simple Text Out consoles. = The + Simple Text Out screens will now be synced up with all non video output = devices + + @retval EFI_SUCCESS UGA devices are back in text mode and synced up. + +**/ +EFI_STATUS +PlatformBootManagerDisableQuietBoot ( + VOID + ); + +/** + Perform the memory test base on the memory test intensive level, + and update the memory resource. + + @param Level The memory test intensive level. + + @retval EFI_STATUS Success test all the system memory and update + the memory resource + +**/ +EFI_STATUS +PlatformBootManagerMemoryTest ( + IN EXTENDMEM_COVERAGE_LEVEL Level + ); + +/** + + Show progress bar with title above it. It only works in Graphics mode. + + + @param TitleForeground Foreground color for Title. + @param TitleBackground Background color for Title. + @param Title Title above progress bar. + @param ProgressColor Progress bar color. + @param Progress Progress (0-100) + @param PreviousValue The previous value of the progress. + + @retval EFI_STATUS Success update the progress bar + +**/ +EFI_STATUS +PlatformBootManagerShowProgress ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleForeground, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleBackground, + IN CHAR16 *Title, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL ProgressColor, + IN UINTN Progress, + IN UINTN PreviousValue + ); + +#endif // _PLATFORM_BOOT_MANAGER_H diff --git a/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/P= latformBootManagerLib.inf b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformB= ootManagerLib/PlatformBootManagerLib.inf new file mode 100644 index 0000000..a8cf986 --- /dev/null +++ b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/Platform= BootManagerLib.inf @@ -0,0 +1,72 @@ +## @file +# Include all platform action which can be customized by IBV/OEM. +# +# Copyright (c) 2019, Hewlett Packard Enterprise Development LP. All righ= ts reserved.
+# Copyright (c) 2012 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the B= SD License +# which accompanies this distribution. The full text of the license may = be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IM= PLIED. +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D PlatformBootManagerLib + FILE_GUID =3D 7DDA7916-6139-4D46-A415-30E854AF3BC7 + MODULE_TYPE =3D DXE_DRIVER + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D PlatformBootManagerLib|DXE_DRIVER + +# +# The following information is for reference only and not required by the = build tools. +# +# VALID_ARCHITECTURES =3D RISCV +# + +[Sources] + PlatformData.c + PlatformBootManager.c + PlatformBootManager.h + MemoryTest.c + Strings.uni + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + +[LibraryClasses] + BaseLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiLib + UefiBootManagerLib + PcdLib + DxeServicesLib + MemoryAllocationLib + DevicePathLib + HiiLib + PrintLib + +[Guids] + +[Protocols] + gEfiGenericMemTestProtocolGuid ## CONSUMES + gEfiGraphicsOutputProtocolGuid ## CONSUMES + gEfiUgaDrawProtocolGuid ## CONSUMES + gEfiOEMBadgingProtocolGuid ## CONSUMES + gEfiBootLogoProtocolGuid ## CONSUMES + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutRow + gEfiMdeModulePkgTokenSpaceGuid.PcdConOutColumn + gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdLogoFile + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdBootlogoOnlyEnable + gEfiMdeModulePkgTokenSpaceGuid.PcdConInConnectOnDemand diff --git a/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/P= latformData.c b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLi= b/PlatformData.c new file mode 100644 index 0000000..e5e682f --- /dev/null +++ b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/Platform= Data.c @@ -0,0 +1,54 @@ +/**@file + Defined the platform specific device path which will be filled to + ConIn/ConOut variables. + +Copyright (c) 2019, Hewlett Packard Enterprise Development LP. All rights = reserved.
+Copyright (c) 2015, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD = License +which accompanies this distribution. The full text of the license may be = found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLI= ED. +**/ + +#include "PlatformBootManager.h" + +// +// Platform specific serial device path +// +SERIAL_CONSOLE_DEVICE_PATH gSerialConsoleDevicePath0 =3D { + { + { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0= } }, + EFI_SERIAL_DXE_GUID // Use the driver's GUID + }, + { + { MESSAGING_DEVICE_PATH, MSG_UART_DP, { sizeof (UART_DEVICE_PATH), 0} = }, + 0, // Reserved + 115200, // BaudRate + 8, // DataBits + 1, // Parity + 1 // StopBits + }, + { + { MESSAGING_DEVICE_PATH, MSG_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH),= 0} }, + DEVICE_PATH_MESSAGING_PC_ANSI + }, + { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DE= VICE_PATH_PROTOCOL), 0 } } +}; + +// +// Predefined platform default console device path +// +PLATFORM_CONSOLE_CONNECT_ENTRY gPlatformConsole[] =3D { + { + (EFI_DEVICE_PATH_PROTOCOL *) &gSerialConsoleDevicePath0, + CONSOLE_OUT | CONSOLE_IN + }, + { + NULL, + 0 + } +}; diff --git a/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/S= trings.uni b/Platform/RiscV/SiFive/U500Pkg/Library/PlatformBootManagerLib/S= trings.uni new file mode 100644 index 0000000000000000000000000000000000000000..83bdc22e5b8e5289463a63af1b0= e17103f0e50d1 GIT binary patch literal 3922 zcmc(iYi|=3D*5QgV-rT&MNqY9`<0*HQS)mBa74Om=3Dk#YPkfDN39R7Iqx17f60F@F#%h zo$-2i9lMDjRb=3Dg+%bqjy&O4WL{PoMa>GvzY#Qw1(8`#he?PvSR=3DM!eDeT}=3D8TgJK* zJ7#piYVA7V$$7@~Cd?nv%V;~u+v7`&N4ClQnDH}aPk4^mpV?D(?%Uc-S5@a0>rd_6 zbspOr_9k}Q`gVu*f!(wF_PgDs{}@Rh9DVL3w5kSiul+;T_vtA%4qbAgLHiX$((#^0@wZ1TC<+sM=3DQzZ3i6yj+9Csl_*o3$q zgJYtu+_y>OHW@3{N-R~hADoXq?~4`>z$4F#k>E_)CX5vs`yj1)Coyp=3D3y$~_5hUDa ztV+OI#Z~7ff#W^+O^_M;$5R%BDX+1sT*&drAD*kql3-XnGPiLJT^W8|pYyYTr@=3Dd>yFVkRV% z{%8^BDJ!}zxlK=3Dbr+0%^awWAvyXj+%?$!{#WQF9mU1wr1S-0cZo-^M_ZiEB&u%pYTQ4RbkSpmarqWe`j(^=3D`Gmk5M$EWtW>mWB~`Uucl^QZ zU$!zEt>6zu^|5D#;zU3BbrsvBr-5XBm)|D%s^NPV?>oZ#tG4ZSH)xgLC-#xI_Qp?}Gc1kMBhExw z^w}*MV3XFBmZ+X9LuJk0R8BUtXqhj9d&TgpiU$fGMFw5;$RdlX(Ikt00ZF|kP4I&F zmrs`YChB;b42d|p4*p9Si*V0-Lb&7XP|jRrJiZmSd~*LN5+~04IsTDfn)q*n2;1@~ zZ%(aQ=3D7oqxWk4G*biF=3DwF3pTZ*35gM<8dNvsx%SvZ$TLOP>ua%9=3DRz$Y>#r|cPQq;5JY^_(M`3kh_ev6u$dR@{PKmWT5|2lEcr&%O~b<6UjOLfx85a&eUEuA8|FX;SV^t+0Gy0x05?9r`H zH$>T-xy|)cM6f25EB<`^V_M!&qLuv9~IdhA9&iDHur_yF%R<^`HIpxYJGx6W#=3D6JqU!%^@il)5FWG6j=3DO>VDkU I{|~I+0EuNbcK`qY literal 0 HcmV?d00001 -- 2.7.4