EmbeddedPkg/AndroidFastboot: Introduce Android FastBoot Application
This application enables Android FastBoot on UEFI. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15312 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
committed by
oliviermartin
parent
ddd1436622
commit
f6755908de
524
EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
Normal file
524
EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
Normal file
@ -0,0 +1,524 @@
|
||||
/** @file
|
||||
|
||||
Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
|
||||
|
||||
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 IMPLIED.
|
||||
|
||||
**/
|
||||
|
||||
#include "AndroidFastbootApp.h"
|
||||
|
||||
#include <Protocol/AndroidFastbootTransport.h>
|
||||
#include <Protocol/AndroidFastbootPlatform.h>
|
||||
#include <Protocol/SimpleTextOut.h>
|
||||
#include <Protocol/SimpleTextIn.h>
|
||||
|
||||
#include <Library/PcdLib.h>
|
||||
#include <Library/UefiRuntimeServicesTableLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
#include <Library/UefiApplicationEntryPoint.h>
|
||||
#include <Library/PrintLib.h>
|
||||
|
||||
/*
|
||||
* UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and
|
||||
* FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol.
|
||||
*/
|
||||
|
||||
STATIC FASTBOOT_TRANSPORT_PROTOCOL *mTransport;
|
||||
STATIC FASTBOOT_PLATFORM_PROTOCOL *mPlatform;
|
||||
|
||||
STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut;
|
||||
|
||||
typedef enum {
|
||||
ExpectCmdState,
|
||||
ExpectDataState,
|
||||
FastbootStateMax
|
||||
} ANDROID_FASTBOOT_STATE;
|
||||
|
||||
STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState;
|
||||
|
||||
// When in ExpectDataState, the number of bytes of data to expect:
|
||||
STATIC UINT32 mNumDataBytes;
|
||||
// .. and the number of bytes so far received this data phase
|
||||
STATIC UINT32 mBytesReceivedSoFar;
|
||||
// .. and the buffer to save data into
|
||||
STATIC UINT8 *mDataBuffer = NULL;
|
||||
|
||||
// Event notify functions, from which gBS->Exit shouldn't be called, can signal
|
||||
// this event when the application should exit
|
||||
STATIC EFI_EVENT mFinishedEvent;
|
||||
|
||||
STATIC EFI_EVENT mFatalSendErrorEvent;
|
||||
|
||||
// This macro uses sizeof - only use it on arrays (i.e. string literals)
|
||||
#define SEND_LITERAL(Str) mTransport->Send ( \
|
||||
sizeof (Str) - 1, \
|
||||
Str, \
|
||||
&mFatalSendErrorEvent \
|
||||
)
|
||||
#define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)
|
||||
|
||||
#define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z')
|
||||
|
||||
#define FASTBOOT_STRING_MAX_LENGTH 256
|
||||
#define FASTBOOT_COMMAND_MAX_LENGTH 64
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
HandleGetVar (
|
||||
IN CHAR8 *CmdArg
|
||||
)
|
||||
{
|
||||
CHAR8 Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY";
|
||||
EFI_STATUS Status;
|
||||
|
||||
// Respond to getvar:version with 0.4 (version of Fastboot protocol)
|
||||
if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1 )) {
|
||||
SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION);
|
||||
} else {
|
||||
// All other variables are assumed to be platform specific
|
||||
Status = mPlatform->GetVar (CmdArg, Response + 4);
|
||||
if (EFI_ERROR (Status)) {
|
||||
SEND_LITERAL ("FAILSomething went wrong when looking up the variable");
|
||||
} else {
|
||||
mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
HandleDownload (
|
||||
IN CHAR8 *NumBytesString
|
||||
)
|
||||
{
|
||||
CHAR8 Response[12] = "DATA";
|
||||
CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
|
||||
|
||||
// Argument is 8-character ASCII string hex representation of number of bytes
|
||||
// that will be sent in the data phase.
|
||||
// Response is "DATA" + that same 8-character string.
|
||||
|
||||
// Replace any previously downloaded data
|
||||
if (mDataBuffer != NULL) {
|
||||
FreePool (mDataBuffer);
|
||||
mDataBuffer = NULL;
|
||||
}
|
||||
|
||||
// Parse out number of data bytes to expect
|
||||
mNumDataBytes = AsciiStrHexToUint64 (NumBytesString);
|
||||
if (mNumDataBytes == 0) {
|
||||
mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n");
|
||||
SEND_LITERAL ("FAILFailed to get the number of bytes to download");
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes);
|
||||
mTextOut->OutputString (mTextOut, OutputString);
|
||||
|
||||
mDataBuffer = AllocatePool (mNumDataBytes);
|
||||
if (mDataBuffer == NULL) {
|
||||
SEND_LITERAL ("FAILNot enough memory");
|
||||
} else {
|
||||
AsciiStrnCpy (Response + 4, NumBytesString, 8);
|
||||
mTransport->Send (sizeof(Response), Response, &mFatalSendErrorEvent);
|
||||
|
||||
mState = ExpectDataState;
|
||||
mBytesReceivedSoFar = 0;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
HandleFlash (
|
||||
IN CHAR8 *PartitionName
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
|
||||
|
||||
// Build output string
|
||||
UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);
|
||||
mTextOut->OutputString (mTextOut, OutputString);
|
||||
|
||||
if (mDataBuffer == NULL) {
|
||||
// Doesn't look like we were sent any data
|
||||
SEND_LITERAL ("FAILNo data to flash");
|
||||
return;
|
||||
}
|
||||
|
||||
Status = mPlatform->FlashPartition (
|
||||
PartitionName,
|
||||
mNumDataBytes,
|
||||
mDataBuffer
|
||||
);
|
||||
if (Status == EFI_NOT_FOUND) {
|
||||
SEND_LITERAL ("FAILNo such partition.");
|
||||
mTextOut->OutputString (mTextOut, L"No such partition.\r\n");
|
||||
} else if (EFI_ERROR (Status)) {
|
||||
SEND_LITERAL ("FAILError flashing partition.");
|
||||
mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n");
|
||||
DEBUG ((EFI_D_ERROR, "Couldn't flash image: %r\n", Status));
|
||||
} else {
|
||||
mTextOut->OutputString (mTextOut, L"Done.\r\n");
|
||||
SEND_LITERAL ("OKAY");
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
HandleErase (
|
||||
IN CHAR8 *PartitionName
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
|
||||
|
||||
// Build output string
|
||||
UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);
|
||||
mTextOut->OutputString (mTextOut, OutputString);
|
||||
|
||||
Status = mPlatform->ErasePartition (PartitionName);
|
||||
if (EFI_ERROR (Status)) {
|
||||
SEND_LITERAL ("FAILCheck device console.");
|
||||
DEBUG ((EFI_D_ERROR, "Couldn't erase image: %r\n", Status));
|
||||
} else {
|
||||
SEND_LITERAL ("OKAY");
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
HandleBoot (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");
|
||||
|
||||
if (mDataBuffer == NULL) {
|
||||
// Doesn't look like we were sent any data
|
||||
SEND_LITERAL ("FAILNo image in memory");
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't really have any choice but to report success, because once we
|
||||
// boot we lose control of the system.
|
||||
SEND_LITERAL ("OKAY");
|
||||
|
||||
Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status));
|
||||
}
|
||||
// We shouldn't get here
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
HandleOemCommand (
|
||||
IN CHAR8 *Command
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
Status = mPlatform->DoOemCommand (Command);
|
||||
if (Status == EFI_NOT_FOUND) {
|
||||
SEND_LITERAL ("FAILOEM Command not recognised.");
|
||||
} else if (Status == EFI_DEVICE_ERROR) {
|
||||
SEND_LITERAL ("FAILError while executing command");
|
||||
} else if (EFI_ERROR (Status)) {
|
||||
SEND_LITERAL ("FAIL");
|
||||
} else {
|
||||
SEND_LITERAL ("OKAY");
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
AcceptCmd (
|
||||
IN UINTN Size,
|
||||
IN CONST CHAR8 *Data
|
||||
)
|
||||
{
|
||||
CHAR8 Command[FASTBOOT_COMMAND_MAX_LENGTH + 1];
|
||||
|
||||
// Max command size is 64 bytes
|
||||
if (Size > FASTBOOT_COMMAND_MAX_LENGTH) {
|
||||
SEND_LITERAL ("FAILCommand too large");
|
||||
return;
|
||||
}
|
||||
|
||||
// Commands aren't null-terminated. Let's get a null-terminated version.
|
||||
AsciiStrnCpy (Command, Data, Size);
|
||||
Command[Size] = '\0';
|
||||
|
||||
// Parse command
|
||||
if (MATCH_CMD_LITERAL ("getvar", Command)) {
|
||||
HandleGetVar (Command + sizeof ("getvar"));
|
||||
} else if (MATCH_CMD_LITERAL ("download", Command)) {
|
||||
HandleDownload (Command + sizeof ("download"));
|
||||
} else if (MATCH_CMD_LITERAL ("verify", Command)) {
|
||||
SEND_LITERAL ("FAILNot supported");
|
||||
} else if (MATCH_CMD_LITERAL ("flash", Command)) {
|
||||
HandleFlash (Command + sizeof ("flash"));
|
||||
} else if (MATCH_CMD_LITERAL ("erase", Command)) {
|
||||
HandleErase (Command + sizeof ("erase"));
|
||||
} else if (MATCH_CMD_LITERAL ("boot", Command)) {
|
||||
HandleBoot ();
|
||||
} else if (MATCH_CMD_LITERAL ("continue", Command)) {
|
||||
SEND_LITERAL ("OKAY");
|
||||
mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n");
|
||||
|
||||
gBS->SignalEvent (mFinishedEvent);
|
||||
} else if (MATCH_CMD_LITERAL ("reboot", Command)) {
|
||||
if (MATCH_CMD_LITERAL ("reboot-booloader", Command)) {
|
||||
// fastboot_protocol.txt:
|
||||
// "reboot-bootloader Reboot back into the bootloader."
|
||||
// I guess this means reboot back into fastboot mode to save the user
|
||||
// having to do whatever they did to get here again.
|
||||
// Here we just reboot normally.
|
||||
SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally.");
|
||||
}
|
||||
SEND_LITERAL ("OKAY");
|
||||
gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
|
||||
|
||||
// Shouldn't get here
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
|
||||
} else if (MATCH_CMD_LITERAL ("powerdown", Command)) {
|
||||
SEND_LITERAL ("OKAY");
|
||||
gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
|
||||
|
||||
// Shouldn't get here
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
|
||||
} else if (MATCH_CMD_LITERAL ("oem", Command)) {
|
||||
// The "oem" command isn't in the specification, but it was observed in the
|
||||
// wild, followed by a space, followed by the actual command.
|
||||
HandleOemCommand (Command + sizeof ("oem"));
|
||||
} else if (IS_LOWERCASE_ASCII (Command[0])) {
|
||||
// Commands starting with lowercase ASCII characters are reserved for the
|
||||
// Fastboot protocol. If we don't recognise it, it's probably the future
|
||||
// and there are new commmands in the protocol.
|
||||
// (By the way, the "oem" command mentioned above makes this reservation
|
||||
// redundant, but we handle it here to be spec-compliant)
|
||||
SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");
|
||||
} else {
|
||||
HandleOemCommand (Command);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
AcceptData (
|
||||
IN UINTN Size,
|
||||
IN VOID *Data
|
||||
)
|
||||
{
|
||||
UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar;
|
||||
CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
|
||||
STATIC UINTN Count = 0;
|
||||
|
||||
// Protocol doesn't say anything about sending extra data so just ignore it.
|
||||
if (Size > RemainingBytes) {
|
||||
Size = RemainingBytes;
|
||||
}
|
||||
|
||||
CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size);
|
||||
|
||||
mBytesReceivedSoFar += Size;
|
||||
|
||||
// Show download progress. Don't do it for every packet as outputting text
|
||||
// might be time consuming - do it on the last packet and on every 32nd packet
|
||||
if ((Count++ % 32) == 0 || Size == RemainingBytes) {
|
||||
// (Note no newline in format string - it will overwrite the line each time)
|
||||
UnicodeSPrint (
|
||||
OutputString,
|
||||
sizeof (OutputString),
|
||||
L"\r%8d / %8d bytes downloaded (%d%%)",
|
||||
mBytesReceivedSoFar,
|
||||
mNumDataBytes,
|
||||
(mBytesReceivedSoFar * 100) / mNumDataBytes // percentage
|
||||
);
|
||||
mTextOut->OutputString (mTextOut, OutputString);
|
||||
}
|
||||
|
||||
if (mBytesReceivedSoFar == mNumDataBytes) {
|
||||
// Download finished.
|
||||
|
||||
mTextOut->OutputString (mTextOut, L"\r\n");
|
||||
SEND_LITERAL ("OKAY");
|
||||
mState = ExpectCmdState;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint
|
||||
It will be called by the UEFI event framework when the transport protocol
|
||||
implementation signals that data has been received from the Fastboot host.
|
||||
The parameters are ignored.
|
||||
*/
|
||||
STATIC
|
||||
VOID
|
||||
DataReady (
|
||||
IN EFI_EVENT Event,
|
||||
IN VOID *Context
|
||||
)
|
||||
{
|
||||
UINTN Size;
|
||||
VOID *Data;
|
||||
EFI_STATUS Status;
|
||||
|
||||
do {
|
||||
Status = mTransport->Receive (&Size, &Data);
|
||||
if (!EFI_ERROR (Status)) {
|
||||
if (mState == ExpectCmdState) {
|
||||
AcceptCmd (Size, (CHAR8 *) Data);
|
||||
} else if (mState == ExpectDataState) {
|
||||
AcceptData (Size, Data);
|
||||
} else {
|
||||
ASSERT (FALSE);
|
||||
}
|
||||
FreePool (Data);
|
||||
}
|
||||
} while (!EFI_ERROR (Status));
|
||||
|
||||
// Quit if there was a fatal error
|
||||
if (Status != EFI_NOT_READY) {
|
||||
ASSERT (Status == EFI_DEVICE_ERROR);
|
||||
// (Put a newline at the beginning as we are probably in the data phase,
|
||||
// so the download progress line, with no '\n' is probably on the console)
|
||||
mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");
|
||||
gBS->SignalEvent (mFinishedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Event notify for a fatal error in transmission.
|
||||
*/
|
||||
STATIC
|
||||
VOID
|
||||
FatalErrorNotify (
|
||||
IN EFI_EVENT Event,
|
||||
IN VOID *Context
|
||||
)
|
||||
{
|
||||
mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");
|
||||
gBS->SignalEvent (mFinishedEvent);
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FastbootAppEntryPoint (
|
||||
IN EFI_HANDLE ImageHandle,
|
||||
IN EFI_SYSTEM_TABLE *SystemTable
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_EVENT ReceiveEvent;
|
||||
EFI_EVENT WaitEventArray[2];
|
||||
UINTN EventIndex;
|
||||
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;
|
||||
|
||||
mDataBuffer = NULL;
|
||||
|
||||
Status = gBS->LocateProtocol (
|
||||
&gAndroidFastbootTransportProtocolGuid,
|
||||
NULL,
|
||||
(VOID **) &mTransport
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = mPlatform->Init ();
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR,
|
||||
"Fastboot: Couldn't open Text Output Protocol: %r\n", Status
|
||||
));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
// Disable watchdog
|
||||
Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status));
|
||||
}
|
||||
|
||||
// Create event for receipt of data from the host
|
||||
Status = gBS->CreateEvent (
|
||||
EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK,
|
||||
DataReady,
|
||||
NULL,
|
||||
&ReceiveEvent
|
||||
);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
|
||||
// Create event for exiting application when "continue" command is received
|
||||
Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
|
||||
// Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a
|
||||
// fatal error
|
||||
Status = gBS->CreateEvent (
|
||||
EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK,
|
||||
FatalErrorNotify,
|
||||
NULL,
|
||||
&mFatalSendErrorEvent
|
||||
);
|
||||
ASSERT_EFI_ERROR (Status);
|
||||
|
||||
|
||||
// Start listening for data
|
||||
Status = mTransport->Start (
|
||||
ReceiveEvent
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
// Talk to the user
|
||||
mTextOut->OutputString (mTextOut,
|
||||
L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press any key to quit.\r\n");
|
||||
|
||||
// Quit when the user presses any key, or mFinishedEvent is signalled
|
||||
WaitEventArray[0] = mFinishedEvent;
|
||||
WaitEventArray[1] = TextIn->WaitForKey;
|
||||
gBS->WaitForEvent (2, WaitEventArray, &EventIndex);
|
||||
|
||||
mTransport->Stop ();
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));
|
||||
}
|
||||
mPlatform->UnInit ();
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user