| /** @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. |
| |
| **/ |
| |
| /* |
| * Copyright (c) 2009, Google Inc. |
| * All rights reserved. |
| * |
| * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include <Uefi.h> |
| #include <Library/UefiLib.h> |
| #include <Library/PcdLib.h> |
| #include <Library/BaseMemoryLib.h> |
| #include <Library/PrintLib.h> |
| #include <Library/BaseLib.h> |
| #include <Library/BaseMemoryLib.h> |
| #include <Library/DebugLib.h> |
| #include <Library/DevicePathLib.h> |
| #include <Library/MemoryAllocationLib.h> |
| #include <Library/UefiApplicationEntryPoint.h> |
| #include <Library/UefiBootServicesTableLib.h> |
| #include <Library/UefiRuntimeServicesTableLib.h> |
| #include <Library/MenuKeysDetection.h> |
| #include <Library/PartitionTableUpdate.h> |
| #include <Library/BoardCustom.h> |
| #include <Library/DeviceInfo.h> |
| |
| #include <Protocol/BlockIo.h> |
| |
| #include <Guid/EventGroup.h> |
| |
| #include <Protocol/BlockIo.h> |
| #include <Protocol/DiskIo.h> |
| #include <Protocol/SimpleTextOut.h> |
| #include <Protocol/SimpleTextIn.h> |
| #include <Protocol/EFIUsbDevice.h> |
| |
| #include "FastbootMain.h" |
| #include "FastbootCmds.h" |
| #include "SparseFormat.h" |
| #include "MetaFormat.h" |
| #include "BootImage.h" |
| #include "BootLinux.h" |
| #include "LinuxLoaderLib.h" |
| #include "BootStats.h" |
| |
| struct GetVarPartitionInfo part_info[] = |
| { |
| { "system" , "partition-size:", "partition-type:", "", "ext4" }, |
| { "userdata", "partition-size:", "partition-type:", "", "ext4" }, |
| { "cache" , "partition-size:", "partition-type:", "", "ext4" }, |
| }; |
| |
| #ifdef ENABLE_UPDATE_PARTITIONS_CMDS |
| STATIC CONST CHAR16 *CriticalPartitions[] = { |
| L"abl", |
| L"rpm", |
| L"tz", |
| L"sdi", |
| L"xbl", |
| L"hyp", |
| L"pmic", |
| L"bootloader", |
| L"devinfo", |
| L"partition", |
| L"devcfg", |
| L"ddr", |
| L"frp", |
| L"cdt", |
| L"cmnlib", |
| L"cmnlib64", |
| L"keymaster", |
| L"mdtp" |
| }; |
| #endif |
| |
| STATIC FASTBOOT_VAR *Varlist; |
| BOOLEAN Finished = FALSE; |
| CHAR8 StrSerialNum[MAX_RSP_SIZE]; |
| CHAR8 FullProduct[MAX_RSP_SIZE]; |
| CHAR8 StrVariant[MAX_RSP_SIZE]; |
| CHAR8 StrBatteryVoltage[MAX_RSP_SIZE]; |
| CHAR8 StrBatterySocOk[MAX_RSP_SIZE]; |
| CHAR8 ChargeScreenEnable[MAX_RSP_SIZE]; |
| CHAR8 OffModeCharge[MAX_RSP_SIZE]; |
| |
| struct GetVarSlotInfo { |
| CHAR8 SlotSuffix[MAX_SLOT_SUFFIX_SZ]; |
| CHAR8 SlotSuccessfulVar[SLOT_ATTR_SIZE]; |
| CHAR8 SlotUnbootableVar[SLOT_ATTR_SIZE]; |
| CHAR8 SlotRetryCountVar[SLOT_ATTR_SIZE]; |
| CHAR8 SlotSuccessfulVal[ATTR_RESP_SIZE]; |
| CHAR8 SlotUnbootableVal[ATTR_RESP_SIZE]; |
| CHAR8 SlotRetryCountVal[ATTR_RESP_SIZE]; |
| }; |
| |
| STATIC struct GetVarSlotInfo *BootSlotInfo = NULL; |
| STATIC CHAR8 SlotSuffixArray[SLOT_SUFFIX_ARRAY_SIZE]; |
| STATIC CHAR8 CurrentSlotFB[MAX_SLOT_SUFFIX_SZ]; |
| |
| /*Note: This needs to be used only when Slot already has prefix "_" */ |
| #define SKIP_FIRSTCHAR_IN_SLOT_SUFFIX(Slot) \ |
| do { \ |
| int i = 0; \ |
| do { \ |
| Slot[i] = Slot[i+1]; \ |
| i++; \ |
| } while(i < MAX_SLOT_SUFFIX_SZ-1); \ |
| } while(0); |
| |
| /*This variable is used to skip populating the FastbootVar |
| * When PopulateMultiSlotInfo called while flashing each Lun |
| */ |
| STATIC BOOLEAN InitialPopulate = FALSE; |
| extern struct PartitionEntry PtnEntries[MAX_NUM_PARTITIONS]; |
| |
| STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState; |
| /* When in ExpectDataState, the number of bytes of data to expect: */ |
| STATIC UINT64 mNumDataBytes; |
| /* .. and the number of bytes so far received this data phase */ |
| STATIC UINT64 mBytesReceivedSoFar; |
| /* and the buffer to save data into */ |
| STATIC UINT8 *mDataBuffer = NULL; |
| |
| STATIC INT32 Lun = NO_LUN; |
| STATIC BOOLEAN LunSet; |
| |
| STATIC FASTBOOT_CMD *cmdlist; |
| STATIC UINT32 IsAllowUnlock; |
| |
| STATIC EFI_STATUS FastbootCommandSetup(VOID *base, UINT32 size); |
| STATIC VOID AcceptCmd (IN UINT64 Size,IN CHAR8 *Data); |
| |
| /* Clean up memory for the getvar variables during exit */ |
| EFI_STATUS |
| FastbootUnInit() |
| { |
| FASTBOOT_VAR *Var; |
| FASTBOOT_VAR *VarPrev = NULL; |
| |
| for (Var = Varlist; Var->next; Var = Var->next) |
| { |
| if(VarPrev) |
| FreePool(VarPrev); |
| VarPrev = Var; |
| } |
| if(Var) |
| { |
| FreePool(Var); |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| /* Publish a variable readable by the built-in getvar command |
| * These Variables must not be temporary, shallow copies are used. |
| */ |
| EFI_STATUS |
| FastbootPublishVar ( |
| IN CONST CHAR8 *Name, |
| IN CONST CHAR8 *Value |
| ) |
| { |
| FASTBOOT_VAR *Var; |
| Var = AllocatePool(sizeof(*Var)); |
| if (Var) |
| { |
| Var->next = Varlist; |
| Varlist = Var; |
| Var->name = Name; |
| Var->value = Value; |
| } |
| return EFI_SUCCESS; |
| } |
| |
| /* Returns the Remaining amount of bytes expected |
| * This lets us bypass ZLT issues |
| */ |
| UINTN GetXfrSize(VOID) |
| { |
| UINTN BytesLeft = mNumDataBytes - mBytesReceivedSoFar; |
| if (mState == ExpectDataState) |
| { |
| if (BytesLeft > USB_BUFFER_SIZE) |
| return USB_BUFFER_SIZE; |
| else |
| return BytesLeft; |
| } |
| else |
| { |
| return USB_BUFFER_SIZE; |
| } |
| } |
| |
| /* Acknowlege to host, INFO, OKAY and FAILURE */ |
| STATIC VOID FastbootAck ( |
| IN CONST CHAR8 *code, |
| CONST CHAR8 *Reason |
| ) |
| { |
| if (Reason == 0) |
| Reason = ""; |
| |
| AsciiSPrint(GetFastbootDeviceData().gTxBuffer, MAX_RSP_SIZE, "%a%a", code,Reason); |
| GetFastbootDeviceData().UsbDeviceProtocol->Send(ENDPOINT_OUT, AsciiStrLen(GetFastbootDeviceData().gTxBuffer), GetFastbootDeviceData().gTxBuffer); |
| DEBUG((EFI_D_VERBOSE, "Sending %d:%a\n", AsciiStrLen(GetFastbootDeviceData().gTxBuffer), GetFastbootDeviceData().gTxBuffer)); |
| } |
| |
| VOID FastbootFail(IN CONST CHAR8 *Reason) |
| { |
| FastbootAck("FAIL", Reason); |
| } |
| |
| VOID FastbootInfo(IN CONST CHAR8 *Info) |
| { |
| FastbootAck("INFO", Info); |
| } |
| |
| VOID FastbootOkay(IN CONST CHAR8 *info) |
| { |
| FastbootAck("OKAY", info); |
| } |
| |
| VOID PartitionDump () |
| { |
| EFI_STATUS Status; |
| EFI_PARTITION_ENTRY *PartEntry; |
| UINT16 i; |
| UINT32 j; |
| /* By default the LunStart and LunEnd would point to '0' and max value */ |
| UINT32 LunStart = 0; |
| UINT32 LunEnd = GetMaxLuns(); |
| |
| /* If Lun is set in the Handle flash command then find the block io for that lun */ |
| if (LunSet) |
| { |
| LunStart = Lun; |
| LunEnd = Lun + 1; |
| } |
| for (i = LunStart; i < LunEnd; i++) |
| { |
| for (j = 0; j < Ptable[i].MaxHandles; j++) |
| { |
| Status = gBS->HandleProtocol(Ptable[i].HandleInfoList[j].Handle, &gEfiPartitionRecordGuid, (VOID **)&PartEntry); |
| if (EFI_ERROR (Status)) |
| { |
| DEBUG((EFI_D_VERBOSE, "Error getting the partition record for Lun %d and Handle: %d : %r\n", i, j,Status)); |
| continue; |
| } |
| DEBUG((EFI_D_INFO, "Name:[%s] StartLba: %u EndLba:%u\n", PartEntry->PartitionName, PartEntry->StartingLBA, PartEntry->EndingLBA)); |
| } |
| } |
| } |
| |
| EFI_STATUS |
| PartitionGetInfo ( |
| IN CHAR16 *PartitionName, |
| OUT EFI_BLOCK_IO_PROTOCOL **BlockIo, |
| OUT EFI_HANDLE **Handle |
| ) |
| { |
| EFI_STATUS Status; |
| BOOLEAN PartitionFound = FALSE; |
| EFI_PARTITION_ENTRY *PartEntry; |
| UINT16 i; |
| UINT32 j; |
| /* By default the LunStart and LunEnd would point to '0' and max value */ |
| UINT32 LunStart = 0; |
| UINT32 LunEnd = GetMaxLuns(); |
| |
| /* If Lun is set in the Handle flash command then find the block io for that lun */ |
| if (LunSet) |
| { |
| LunStart = Lun; |
| LunEnd = Lun + 1; |
| } |
| for (i = LunStart; i < LunEnd; i++) |
| { |
| for (j = 0; j < Ptable[i].MaxHandles; j++) |
| { |
| Status = gBS->HandleProtocol(Ptable[i].HandleInfoList[j].Handle, &gEfiPartitionRecordGuid, (VOID **)&PartEntry); |
| if (EFI_ERROR (Status)) |
| { |
| continue; |
| } |
| if (!(StrCmp(PartitionName, PartEntry->PartitionName))) |
| { |
| PartitionFound = TRUE; |
| *BlockIo = Ptable[i].HandleInfoList[j].BlkIo; |
| *Handle = Ptable[i].HandleInfoList[j].Handle; |
| goto out; |
| } |
| } |
| } |
| |
| if (!PartitionFound) |
| { |
| DEBUG((EFI_D_ERROR, "Partition not found : %s\n", PartitionName)); |
| return EFI_NOT_FOUND; |
| } |
| out: |
| return Status; |
| } |
| |
| STATIC VOID FastbootPublishSlotVars() { |
| UINT32 i; |
| UINT32 j; |
| CHAR8 *Suffix = NULL; |
| UINT32 PartitionCount =0; |
| CHAR8 PartitionNameAscii[MAX_GPT_NAME_SIZE]; |
| UINT32 RetryCount = 0; |
| BOOLEAN Set = FALSE; |
| |
| GetPartitionCount(&PartitionCount); |
| /*Scan through partition entries, populate the attributes*/ |
| for (i = 0,j = 0;i < PartitionCount; i++) { |
| UnicodeStrToAsciiStr(PtnEntries[i].PartEntry.PartitionName, PartitionNameAscii); |
| |
| if(!(AsciiStrnCmp(PartitionNameAscii,"boot",AsciiStrLen("boot")))) { |
| Suffix = PartitionNameAscii + AsciiStrLen("boot_"); |
| |
| AsciiStrnCpyS(BootSlotInfo[j].SlotSuffix, MAX_SLOT_SUFFIX_SZ, Suffix, AsciiStrLen(Suffix)); |
| AsciiStrnCpyS(BootSlotInfo[j].SlotSuccessfulVar, SLOT_ATTR_SIZE, "slot-successful:", AsciiStrLen("slot-successful:")); |
| Set = PtnEntries[i].PartEntry.Attributes & PART_ATT_SUCCESSFUL_VAL ? TRUE : FALSE; |
| AsciiStrnCpyS(BootSlotInfo[j].SlotSuccessfulVal, ATTR_RESP_SIZE, Set ? "yes": "no", Set? AsciiStrLen("yes"): AsciiStrLen("no")); |
| AsciiStrnCatS(BootSlotInfo[j].SlotSuccessfulVar, SLOT_ATTR_SIZE, Suffix, AsciiStrLen(Suffix)); |
| FastbootPublishVar(BootSlotInfo[j].SlotSuccessfulVar, BootSlotInfo[j].SlotSuccessfulVal); |
| |
| AsciiStrnCpyS(BootSlotInfo[j].SlotUnbootableVar, SLOT_ATTR_SIZE, "slot-unbootable:", AsciiStrLen("slot-unbootable:")); |
| Set = PtnEntries[i].PartEntry.Attributes & PART_ATT_UNBOOTABLE_VAL ? TRUE : FALSE; |
| AsciiStrnCpyS(BootSlotInfo[j].SlotUnbootableVal, ATTR_RESP_SIZE, Set? "yes": "no", Set? AsciiStrLen("yes"): AsciiStrLen("no")); |
| AsciiStrnCatS(BootSlotInfo[j].SlotUnbootableVar, SLOT_ATTR_SIZE, Suffix, AsciiStrLen(Suffix)); |
| FastbootPublishVar(BootSlotInfo[j].SlotUnbootableVar, BootSlotInfo[j].SlotUnbootableVal); |
| |
| AsciiStrnCpyS(BootSlotInfo[j].SlotRetryCountVar, SLOT_ATTR_SIZE, "slot-retry-count:", AsciiStrLen("slot-retry-count:")); |
| RetryCount = (PtnEntries[i].PartEntry.Attributes & PART_ATT_MAX_RETRY_COUNT_VAL) >> PART_ATT_MAX_RETRY_CNT_BIT; |
| AsciiSPrint(BootSlotInfo[j].SlotRetryCountVal, ATTR_RESP_SIZE, "%llu", RetryCount); |
| AsciiStrnCatS(BootSlotInfo[j].SlotRetryCountVar, SLOT_ATTR_SIZE, Suffix, AsciiStrLen(Suffix)); |
| FastbootPublishVar(BootSlotInfo[j].SlotRetryCountVar, BootSlotInfo[j].SlotRetryCountVal); |
| j++; |
| } |
| } |
| FastbootPublishVar("has-slot:boot","yes"); |
| UnicodeStrToAsciiStr(GetCurrentSlotSuffix(),CurrentSlotFB); |
| if (AsciiStrStr(CurrentSlotFB, "_")) { |
| SKIP_FIRSTCHAR_IN_SLOT_SUFFIX(CurrentSlotFB); |
| } |
| FastbootPublishVar("current-slot", CurrentSlotFB); |
| FastbootPublishVar("has-slot:system",PartitionHasMultiSlot(L"system") ? "yes" : "no"); |
| FastbootPublishVar("has-slot:modem",PartitionHasMultiSlot(L"modem") ? "yes" : "no"); |
| return; |
| } |
| |
| /*Function to populate attribute fields |
| *Note: It traverses through the partition entries structure, |
| *populates has-slot, slot-successful,slot-unbootable and |
| *slot-retry-count attributes of the boot slots. |
| */ |
| void PopulateMultislotMetadata() |
| { |
| UINT32 i; |
| UINT32 j; |
| UINT32 SlotCount =0; |
| UINT32 PartitionCount =0; |
| CHAR8 *Suffix = NULL; |
| CHAR8 PartitionNameAscii[MAX_GPT_NAME_SIZE]; |
| |
| GetPartitionCount(&PartitionCount); |
| if (!InitialPopulate) { |
| /*Traverse through partition entries,count matching slots with boot */ |
| for (i = 0; i < PartitionCount; i++) { |
| UnicodeStrToAsciiStr(PtnEntries[i].PartEntry.PartitionName, PartitionNameAscii); |
| if(!(AsciiStrnCmp(PartitionNameAscii,"boot",AsciiStrLen("boot")))) { |
| SlotCount++; |
| Suffix = PartitionNameAscii + AsciiStrLen("boot"); |
| if (!AsciiStrStr(SlotSuffixArray, Suffix)) { |
| AsciiStrnCatS(SlotSuffixArray, sizeof(SlotSuffixArray), Suffix, AsciiStrLen(Suffix)); |
| AsciiStrnCatS(SlotSuffixArray, sizeof(SlotSuffixArray), ",", AsciiStrLen(",")); |
| } |
| } |
| } |
| i = AsciiStrLen(SlotSuffixArray); |
| SlotSuffixArray[i] = '\0'; |
| FastbootPublishVar("slot-suffixes",SlotSuffixArray); |
| |
| /*Allocate memory for available number of slots*/ |
| BootSlotInfo = AllocatePool(SlotCount * sizeof(struct GetVarSlotInfo)); |
| if (BootSlotInfo == NULL) |
| { |
| DEBUG((EFI_D_ERROR,"Unable to allocate memory for BootSlotInfo\n")); |
| return; |
| } |
| SetMem((VOID *) BootSlotInfo, SlotCount * sizeof(struct GetVarSlotInfo), 0); |
| FastbootPublishSlotVars(); |
| InitialPopulate = TRUE; |
| } else { |
| /*While updating gpt from fastboot dont need to populate all the variables as above*/ |
| for (i = 0; i < MAX_SLOTS; i++) { |
| AsciiStrnCpyS(BootSlotInfo[i].SlotSuccessfulVal, sizeof(BootSlotInfo[i].SlotSuccessfulVal), "no", AsciiStrLen("no")); |
| AsciiStrnCpyS(BootSlotInfo[i].SlotUnbootableVal, sizeof(BootSlotInfo[i].SlotUnbootableVal), "no", AsciiStrLen("no")); |
| AsciiSPrint(BootSlotInfo[i].SlotRetryCountVal,sizeof(BootSlotInfo[j].SlotRetryCountVal),"%d",MAX_RETRY_COUNT); |
| } |
| } |
| return; |
| } |
| |
| /* Helper function to write data to disk */ |
| STATIC EFI_STATUS |
| WriteToDisk ( |
| IN EFI_BLOCK_IO_PROTOCOL *BlockIo, |
| IN EFI_HANDLE *Handle, |
| IN VOID *Image, |
| IN UINT64 Size, |
| IN UINT64 offset |
| ) |
| { |
| return BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, offset, ROUND_TO_PAGE(Size, BlockIo->Media->BlockSize - 1), Image); |
| } |
| |
| STATIC BOOLEAN GetPartitionHasSlot(CHAR16* PartitionName, UINT32 PnameMaxSize, CHAR16* SlotSuffix, UINT32 SlotSuffixMaxSize) { |
| INT32 Index = INVALID_PTN; |
| BOOLEAN HasSlot = FALSE; |
| CHAR16* CurrentSlot; |
| |
| Index = GetPartitionIndex(PartitionName); |
| if (Index == INVALID_PTN) { |
| CurrentSlot = GetCurrentSlotSuffix(); |
| StrnCpyS(SlotSuffix, SlotSuffixMaxSize, CurrentSlot, StrLen(CurrentSlot)); |
| StrnCatS(PartitionName, PnameMaxSize, CurrentSlot, StrLen(CurrentSlot)); |
| HasSlot = TRUE; |
| } |
| else { |
| /*Check for _a or _b slots, if available then copy to SlotSuffix Array*/ |
| if (StrStr(PartitionName, L"_a") || StrStr(PartitionName, L"_b")) { |
| StrnCpyS(SlotSuffix, SlotSuffixMaxSize, (PartitionName + (StrLen(PartitionName) - 2)), MAX_SLOT_SUFFIX_SZ); |
| HasSlot = TRUE; |
| } |
| } |
| return HasSlot; |
| } |
| |
| /* Handle Sparse Image Flashing */ |
| EFI_STATUS |
| HandleSparseImgFlash( |
| IN CHAR16 *PartitionName, |
| IN UINT32 PartitionMaxSize, |
| IN VOID *Image, |
| IN UINT64 sz |
| ) |
| { |
| UINT32 chunk; |
| UINT64 chunk_data_sz; |
| UINT32 *fill_buf = NULL; |
| UINT32 fill_val; |
| sparse_header_t *sparse_header; |
| chunk_header_t *chunk_header; |
| UINT32 total_blocks = 0; |
| UINT64 block_count_factor = 0; |
| UINT64 written_block_count = 0; |
| UINT64 PartitionSize = 0; |
| UINT32 i; |
| UINT64 ImageEnd; |
| EFI_STATUS Status; |
| EFI_BLOCK_IO_PROTOCOL *BlockIo = NULL; |
| EFI_HANDLE *Handle = NULL; |
| CHAR16 SlotSuffix[MAX_SLOT_SUFFIX_SZ]; |
| BOOLEAN MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| BOOLEAN HasSlot = FALSE; |
| |
| if (CHECK_ADD64((UINT64)Image, sz)) { |
| DEBUG((EFI_D_ERROR, "Integer overflow while adding Image and sz\n")); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| ImageEnd = (UINT64) Image + sz; |
| |
| /* For multislot boot the partition may not support a/b slots. |
| * Look for default partition, if it does not exist then try for a/b |
| */ |
| if (MultiSlotBoot) |
| HasSlot = GetPartitionHasSlot(PartitionName, PartitionMaxSize, SlotSuffix, MAX_SLOT_SUFFIX_SZ); |
| |
| Status = PartitionGetInfo(PartitionName, &BlockIo, &Handle); |
| if (Status != EFI_SUCCESS) |
| return Status; |
| if (!BlockIo) { |
| DEBUG((EFI_D_ERROR, "BlockIo for %a is corrupted\n",PartitionName)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| if (!Handle) { |
| DEBUG((EFI_D_ERROR, "EFI handle for %a is corrupted\n",PartitionName)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| // Check image will fit on device |
| PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize; |
| |
| if (sz < sizeof(sparse_header_t)) |
| { |
| FastbootFail("Input image is invalid\n"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| sparse_header = (sparse_header_t *) Image; |
| if (((UINT64) sparse_header->total_blks * (UINT64) sparse_header->blk_sz) > PartitionSize) |
| { |
| FastbootFail("Image is too large for the partition"); |
| return EFI_VOLUME_FULL; |
| } |
| |
| Image += sizeof(sparse_header_t); |
| |
| if (ImageEnd < (UINT64) Image) |
| { |
| FastbootFail("buffer overreads occured due to invalid sparse header"); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| if (sparse_header->file_hdr_sz != sizeof(sparse_header_t)) |
| { |
| FastbootFail("Sparse header size mismatch"); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| if ((sparse_header->blk_sz) % (BlockIo->Media->BlockSize)) { |
| DEBUG((EFI_D_ERROR, "Unsupported sparse block size %x\n", sparse_header->blk_sz)); |
| FastbootFail("Unsupported sparse block size"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| block_count_factor = (sparse_header->blk_sz) / (BlockIo->Media->BlockSize); |
| |
| DEBUG((EFI_D_VERBOSE, "=== Sparse Image Header ===\n")); |
| DEBUG((EFI_D_VERBOSE, "magic: 0x%x\n", sparse_header->magic)); |
| DEBUG((EFI_D_VERBOSE, "major_version: 0x%x\n", sparse_header->major_version)); |
| DEBUG((EFI_D_VERBOSE, "minor_version: 0x%x\n", sparse_header->minor_version)); |
| DEBUG((EFI_D_VERBOSE, "file_hdr_sz: %d\n", sparse_header->file_hdr_sz)); |
| DEBUG((EFI_D_VERBOSE, "chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz)); |
| DEBUG((EFI_D_VERBOSE, "blk_sz: %d\n", sparse_header->blk_sz)); |
| DEBUG((EFI_D_VERBOSE, "total_blks: %d\n", sparse_header->total_blks)); |
| DEBUG((EFI_D_VERBOSE, "total_chunks: %d\n", sparse_header->total_chunks)); |
| |
| /* Start processing the chunks */ |
| for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) |
| { |
| if (((UINT64) total_blocks * (UINT64) sparse_header->blk_sz) >= PartitionSize) |
| { |
| FastbootFail("Size of image is too large for the partition"); |
| return EFI_VOLUME_FULL; |
| } |
| |
| /* Read and skip over chunk header */ |
| chunk_header = (chunk_header_t *) Image; |
| Image += sizeof(chunk_header_t); |
| |
| if (ImageEnd < (UINT64) Image) |
| { |
| FastbootFail("buffer overreads occured due to invalid sparse header"); |
| return EFI_BAD_BUFFER_SIZE; |
| } |
| |
| DEBUG((EFI_D_VERBOSE, "=== Chunk Header ===\n")); |
| DEBUG((EFI_D_VERBOSE, "chunk_type: 0x%x\n", chunk_header->chunk_type)); |
| DEBUG((EFI_D_VERBOSE, "chunk_data_sz: 0x%x\n", chunk_header->chunk_sz)); |
| DEBUG((EFI_D_VERBOSE, "total_size: 0x%x\n", chunk_header->total_sz)); |
| |
| if (sparse_header->chunk_hdr_sz != sizeof(chunk_header_t)) |
| { |
| FastbootFail("chunk header size mismatch"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| if (!sparse_header->blk_sz) |
| { |
| FastbootFail("Invalid block size in the sparse header\n"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| chunk_data_sz = (UINT64)sparse_header->blk_sz * chunk_header->chunk_sz; |
| /* Make sure that chunk size calculate from sparse image does not exceed the |
| * partition size |
| */ |
| if ((UINT64) total_blocks * (UINT64) sparse_header->blk_sz + chunk_data_sz > PartitionSize) |
| { |
| FastbootFail("Chunk data size exceeds partition size"); |
| return EFI_VOLUME_FULL; |
| } |
| |
| switch (chunk_header->chunk_type) |
| { |
| case CHUNK_TYPE_RAW: |
| if ((UINT64)chunk_header->total_sz != ((UINT64)sparse_header->chunk_hdr_sz + chunk_data_sz)) |
| { |
| FastbootFail("Bogus chunk size for chunk type Raw"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| if (ImageEnd < (UINT64)Image + chunk_data_sz) |
| { |
| FastbootFail("buffer overreads occured due to invalid sparse header"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| /* Data is validated, now write to the disk */ |
| written_block_count = total_blocks * block_count_factor; |
| Status = WriteToDisk(BlockIo, Handle, Image, chunk_data_sz, written_block_count); |
| if (EFI_ERROR(Status)) |
| { |
| FastbootFail("Flash Write Failure"); |
| return Status; |
| } |
| |
| if (total_blocks > (MAX_UINT32 - chunk_header->chunk_sz)) |
| { |
| FastbootFail("Bogus size for RAW chunk Type"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| total_blocks += chunk_header->chunk_sz; |
| Image += chunk_data_sz; |
| break; |
| |
| case CHUNK_TYPE_FILL: |
| if (chunk_header->total_sz != (sparse_header->chunk_hdr_sz + sizeof(UINT32))) |
| { |
| FastbootFail("Bogus chunk size for chunk type FILL"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| fill_buf = AllocatePool(sparse_header->blk_sz); |
| if (!fill_buf) |
| { |
| FastbootFail("Malloc failed for: CHUNK_TYPE_FILL"); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| |
| if (ImageEnd < (UINT64)Image + sizeof(UINT32)) |
| { |
| FastbootFail("Buffer overread occured due to invalid sparse header"); |
| FreePool(fill_buf); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| fill_val = *(UINT32 *)Image; |
| Image = (CHAR8 *) Image + sizeof(UINT32); |
| for (i = 0 ; i < (sparse_header->blk_sz / sizeof(fill_val)); i++) |
| fill_buf[i] = fill_val; |
| |
| for (i = 0 ; i < chunk_header->chunk_sz; i++) |
| { |
| /* Make sure the data does not exceed the partition size */ |
| if ((UINT64)total_blocks * (UINT64)sparse_header->blk_sz + sparse_header->blk_sz > PartitionSize) |
| { |
| FastbootFail("Chunk data size for fill type exceeds partition size"); |
| FreePool(fill_buf); |
| return EFI_VOLUME_FULL; |
| } |
| |
| written_block_count = total_blocks * block_count_factor; |
| Status = WriteToDisk(BlockIo, Handle, (VOID *) fill_buf, sparse_header->blk_sz, written_block_count); |
| if (EFI_ERROR(Status)) |
| { |
| FastbootFail("Flash write failure for FILL Chunk"); |
| FreePool(fill_buf); |
| return Status; |
| } |
| |
| total_blocks++; |
| } |
| |
| FreePool(fill_buf); |
| break; |
| |
| case CHUNK_TYPE_DONT_CARE: |
| if (total_blocks > (MAX_UINT32 - chunk_header->chunk_sz)) |
| { |
| FastbootFail("bogus size for chunk DONT CARE type"); |
| return EFI_INVALID_PARAMETER; |
| } |
| total_blocks += chunk_header->chunk_sz; |
| break; |
| |
| case CHUNK_TYPE_CRC: |
| if (chunk_header->total_sz != sparse_header->chunk_hdr_sz) |
| { |
| FastbootFail("Bogus chunk size for chunk type CRC"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| if (total_blocks > (MAX_UINT32 - chunk_header->chunk_sz)) |
| { |
| FastbootFail("Bogus size for chunk type CRC"); |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| total_blocks += chunk_header->chunk_sz; |
| if ((UINT64) Image > MAX_UINT32 - chunk_data_sz) |
| { |
| FastbootFail("Buffer overflow occured"); |
| return EFI_INVALID_PARAMETER; |
| } |
| Image += (UINT32) chunk_data_sz; |
| if (ImageEnd < (UINT64)Image) |
| { |
| FastbootFail("buffer overreads occured due to invalid sparse header"); |
| return EFI_INVALID_PARAMETER; |
| } |
| break; |
| |
| default: |
| DEBUG((EFI_D_ERROR, "Unknown chunk type: %x\n", chunk_header->chunk_type)); |
| FastbootFail("Unknown chunk type"); |
| return EFI_INVALID_PARAMETER; |
| } |
| } |
| |
| DEBUG((EFI_D_INFO, "Wrote %d blocks, expected to write %d blocks\n", total_blocks, sparse_header->total_blks)); |
| |
| if (total_blocks != sparse_header->total_blks) { |
| FastbootFail("Sparse Image Write Failure"); |
| Status = EFI_VOLUME_CORRUPTED; |
| } |
| |
| return Status; |
| } |
| |
| STATIC VOID FastbootUpdateAttr(CONST CHAR16 *SlotSuffix) |
| { |
| struct PartitionEntry *Ptn_Entries_Ptr = NULL; |
| UINT32 j; |
| INT32 Index; |
| CHAR16 PartName[MAX_GPT_NAME_SIZE]; |
| CHAR8 SlotSuffixAscii[MAX_SLOT_SUFFIX_SZ]; |
| UnicodeStrToAsciiStr(SlotSuffix, SlotSuffixAscii); |
| |
| StrnCpyS(PartName, StrLen(L"boot") + 1, L"boot", StrLen(L"boot")); |
| StrnCatS(PartName, MAX_GPT_NAME_SIZE - 1, SlotSuffix, StrLen(SlotSuffix)); |
| |
| Index = GetPartitionIndex(PartName); |
| if (Index == INVALID_PTN) |
| { |
| DEBUG((EFI_D_ERROR, "Error boot partition for slot: %s not found\n", SlotSuffix)); |
| return; |
| } |
| |
| Ptn_Entries_Ptr = &PtnEntries[Index]; |
| Ptn_Entries_Ptr->PartEntry.Attributes = (Ptn_Entries_Ptr->PartEntry.Attributes | PART_ATT_MAX_RETRY_COUNT_VAL) |
| & (~PART_ATT_SUCCESSFUL_VAL); |
| UpdatePartitionAttributes(); |
| for (j = 0; j < MAX_SLOTS; j++) |
| { |
| if(!AsciiStrnCmp(BootSlotInfo[j].SlotSuffix, SlotSuffixAscii, AsciiStrLen(SlotSuffixAscii))) |
| { |
| AsciiStrnCpyS(BootSlotInfo[j].SlotSuccessfulVal, sizeof(BootSlotInfo[j].SlotSuccessfulVal), "no", AsciiStrLen("no")); |
| AsciiStrnCpyS(BootSlotInfo[j].SlotUnbootableVal, sizeof(BootSlotInfo[j].SlotUnbootableVal), "no", AsciiStrLen("no")); |
| AsciiSPrint(BootSlotInfo[j].SlotRetryCountVal,sizeof(BootSlotInfo[j].SlotRetryCountVal),"%d",MAX_RETRY_COUNT); |
| } |
| } |
| } |
| |
| /* Raw Image flashing */ |
| EFI_STATUS |
| HandleRawImgFlash( |
| IN CHAR16 *PartitionName, |
| IN UINT32 PartitionMaxSize, |
| IN VOID *Image, |
| IN UINT64 Size |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_BLOCK_IO_PROTOCOL *BlockIo = NULL; |
| UINT64 PartitionSize; |
| EFI_HANDLE *Handle = NULL; |
| CHAR16 SlotSuffix[MAX_SLOT_SUFFIX_SZ]; |
| BOOLEAN MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| BOOLEAN HasSlot = FALSE; |
| |
| /* For multislot boot the partition may not support a/b slots. |
| * Look for default partition, if it does not exist then try for a/b |
| */ |
| if (MultiSlotBoot) |
| HasSlot = GetPartitionHasSlot(PartitionName, PartitionMaxSize, SlotSuffix, MAX_SLOT_SUFFIX_SZ); |
| |
| Status = PartitionGetInfo(PartitionName, &BlockIo, &Handle); |
| if (Status != EFI_SUCCESS) |
| return Status; |
| if (!BlockIo) { |
| DEBUG((EFI_D_ERROR, "BlockIo for %a is corrupted\n",PartitionName)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| if (!Handle) { |
| DEBUG((EFI_D_ERROR, "EFI handle for %a is corrupted\n",PartitionName)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| // Check image will fit on device |
| PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize; |
| if (PartitionSize < Size) |
| { |
| DEBUG ((EFI_D_ERROR, "Partition not big enough.\n")); |
| DEBUG ((EFI_D_ERROR, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize, Size)); |
| |
| return EFI_VOLUME_FULL; |
| } |
| Status = BlockIo->WriteBlocks(BlockIo, BlockIo->Media->MediaId, 0, ROUND_TO_PAGE(Size, BlockIo->Media->BlockSize - 1), Image); |
| if (MultiSlotBoot && HasSlot && !(StrnCmp(PartitionName, L"boot", StrLen(L"boot")))) |
| FastbootUpdateAttr(SlotSuffix); |
| return Status; |
| } |
| |
| /* Meta Image flashing */ |
| EFI_STATUS |
| HandleMetaImgFlash( |
| IN CHAR16 *PartitionName, |
| IN UINT32 PartitionMaxSize, |
| IN VOID *Image, |
| IN UINT64 Size |
| ) |
| { |
| UINT32 i; |
| UINT32 images; |
| EFI_STATUS Status = EFI_DEVICE_ERROR; |
| img_header_entry_t *img_header_entry; |
| meta_header_t *meta_header; |
| CHAR16 PartitionNameFromMeta[MAX_GPT_NAME_SIZE]; |
| |
| meta_header = (meta_header_t *) Image; |
| img_header_entry = (img_header_entry_t *) (Image + sizeof(meta_header_t)); |
| images = meta_header->img_hdr_sz / sizeof(img_header_entry_t); |
| |
| for (i = 0; i < images; i++) |
| { |
| if (img_header_entry[i].ptn_name == NULL || img_header_entry[i].start_offset == 0 || img_header_entry[i].size == 0) |
| break; |
| AsciiStrToUnicodeStr(img_header_entry[i].ptn_name, PartitionNameFromMeta); |
| Status = HandleRawImgFlash(PartitionNameFromMeta, sizeof(PartitionNameFromMeta), |
| (void *) Image + img_header_entry[i].start_offset, img_header_entry[i].size); |
| } |
| |
| /* ToDo: Add Bootloader version support */ |
| return Status; |
| } |
| |
| #ifdef ENABLE_UPDATE_PARTITIONS_CMDS |
| /* Erase partition */ |
| STATIC EFI_STATUS |
| FastbootErasePartition( |
| IN CHAR16 *PartitionName |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_BLOCK_IO_PROTOCOL *BlockIo = NULL; |
| EFI_HANDLE *Handle = NULL; |
| |
| Status = PartitionGetInfo(PartitionName, &BlockIo, &Handle); |
| if (Status != EFI_SUCCESS) |
| return Status; |
| if (!BlockIo) { |
| DEBUG((EFI_D_ERROR, "BlockIo for %s is corrupted\n", PartitionName)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| if (!Handle) { |
| DEBUG((EFI_D_ERROR, "EFI handle for %s is corrupted\n", PartitionName)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| |
| Status = ErasePartition(BlockIo, Handle); |
| if (Status != EFI_SUCCESS) { |
| DEBUG((EFI_D_ERROR, "Partition Erase failed: %r\n", Status)); |
| return Status; |
| } |
| |
| if (!(StrCmp(L"userdata", PartitionName))) { |
| Status = ResetDeviceState(); |
| if (Status != EFI_SUCCESS) |
| return Status; |
| } |
| |
| return Status; |
| } |
| #endif |
| |
| /* Handle Download Command */ |
| STATIC VOID CmdDownload( |
| IN CONST CHAR8 *arg, |
| IN VOID *data, |
| IN UINT32 sz |
| ) |
| { |
| CHAR8 Response[12] = "DATA"; |
| CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; |
| CHAR8 *NumBytesString = (CHAR8 *)arg; |
| |
| /* 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. |
| */ |
| |
| // Parse out number of data bytes to expect |
| mNumDataBytes = AsciiStrHexToUint64(NumBytesString); |
| if (mNumDataBytes == 0) |
| { |
| DEBUG((EFI_D_ERROR, "ERROR: Fail to get the number of bytes to download.\n")); |
| FastbootFail("Failed to get the number of bytes to download"); |
| return; |
| } |
| |
| if (mNumDataBytes > MAX_DOWNLOAD_SIZE) |
| { |
| DEBUG((EFI_D_ERROR, "ERROR: Data size (%d) is more than max download size (%d)", mNumDataBytes, MAX_DOWNLOAD_SIZE)); |
| FastbootFail("Requested download size is more than max allowed\n"); |
| return; |
| } |
| |
| UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes); |
| AsciiStrnCpyS(Response + 4, sizeof(Response), NumBytesString, sizeof(Response)-4); |
| CopyMem(GetFastbootDeviceData().gTxBuffer, Response, 12); |
| mState = ExpectDataState; |
| mBytesReceivedSoFar = 0; |
| GetFastbootDeviceData().UsbDeviceProtocol->Send(ENDPOINT_OUT, 12 , GetFastbootDeviceData().gTxBuffer); |
| DEBUG((EFI_D_VERBOSE, "CmdDownload: Send 12 %a\n", GetFastbootDeviceData().gTxBuffer)); |
| } |
| |
| /* Function needed for event notification callback */ |
| VOID BlockIoCallback(IN EFI_EVENT Event,IN VOID *Context) |
| { |
| } |
| |
| #ifdef ENABLE_UPDATE_PARTITIONS_CMDS |
| BOOLEAN NamePropertyMatches(CHAR8* Name) { |
| |
| return (BOOLEAN)(!AsciiStrnCmp(Name, "has-slot", AsciiStrLen("has-slot")) || |
| !AsciiStrnCmp(Name, "current-slot", AsciiStrLen("current-slot")) || |
| !AsciiStrnCmp(Name, "slot-retry-count", AsciiStrLen("slot-retry-count")) || |
| !AsciiStrnCmp(Name, "slot-unbootable", AsciiStrLen("slot-unbootable")) || |
| !AsciiStrnCmp(Name, "slot-successful", AsciiStrLen("slot-successful")) || |
| !AsciiStrnCmp(Name, "slot-suffixes", AsciiStrLen("slot-suffixes")) || |
| !AsciiStrnCmp(Name, "partition-type:system", AsciiStrLen("partition-type:system")) || |
| !AsciiStrnCmp(Name, "partition-size:system", AsciiStrLen("partition-size:system"))); |
| } |
| |
| STATIC VOID ClearFastbootVarsofAB() { |
| FASTBOOT_VAR *CurrentList = NULL; |
| FASTBOOT_VAR *PrevList = NULL; |
| FASTBOOT_VAR *NextList = NULL; |
| |
| if (!Varlist) { |
| DEBUG((EFI_D_VERBOSE, "Varlist is Empty\n")); |
| return; |
| } |
| |
| for (CurrentList = Varlist; CurrentList != NULL; CurrentList = NextList) { |
| NextList = CurrentList->next; |
| if (!NamePropertyMatches((CHAR8*)CurrentList->name)) { |
| PrevList = CurrentList; |
| continue; |
| } |
| |
| if (!PrevList) |
| Varlist = CurrentList->next; |
| else |
| PrevList->next = CurrentList->next; |
| |
| FreePool(CurrentList); |
| } |
| } |
| |
| VOID IsBootPtnUpdated(INT32 Lun, BOOLEAN *BootPtnUpdated) { |
| EFI_STATUS Status; |
| EFI_PARTITION_ENTRY *PartEntry; |
| UINT32 j; |
| |
| if (Lun == NO_LUN) |
| Lun = 0; |
| |
| for (j = 0; j < Ptable[Lun].MaxHandles; j++) { |
| Status = gBS->HandleProtocol(Ptable[Lun].HandleInfoList[j].Handle, &gEfiPartitionRecordGuid, (VOID **)&PartEntry); |
| |
| if (EFI_ERROR (Status)) { |
| DEBUG((EFI_D_VERBOSE, "Error getting the partition record for Lun %d and Handle: %d : %r\n", Lun, j,Status)); |
| continue; |
| } |
| |
| if (!StrnCmp(PartEntry->PartitionName, L"boot", StrLen(L"boot"))) { |
| DEBUG((EFI_D_VERBOSE, "Boot Partition is updated\n")); |
| *BootPtnUpdated = TRUE; |
| return; |
| } |
| } |
| } |
| |
| STATIC BOOLEAN IsCriticalPartition(CHAR16 *PartitionName) |
| { |
| UINT32 i =0; |
| |
| if (PartitionName == NULL) |
| return FALSE; |
| |
| for (i = 0; i < ARRAY_SIZE(CriticalPartitions); i++) { |
| if (!StrnCmp(PartitionName, CriticalPartitions[i], StrLen(CriticalPartitions[i]))) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| /* Handle Flash Command */ |
| STATIC VOID CmdFlash( |
| IN CONST CHAR8 *arg, |
| IN VOID *data, |
| IN UINT32 sz |
| ) |
| { |
| EFI_STATUS Status = 0; |
| sparse_header_t *sparse_header; |
| meta_header_t *meta_header; |
| CHAR16 PartitionName[MAX_GPT_NAME_SIZE]; |
| CHAR16 *Token = NULL; |
| LunSet = FALSE; |
| EFI_EVENT gBlockIoRefreshEvt; |
| CHAR16 NullSlot[MAX_SLOT_SUFFIX_SZ] = {'\0'}; |
| BOOLEAN MultiSlotBoot = FALSE; |
| EFI_GUID gBlockIoRefreshGuid = { 0xb1eb3d10, 0x9d67, 0x40ca, |
| { 0x95, 0x59, 0xf1, 0x48, 0x8b, 0x1b, 0x2d, 0xdb } }; |
| BOOLEAN BootPtnUpdated = FALSE; |
| UINT32 UfsBootLun = 0; |
| CHAR8 BootDeviceType[BOOT_DEV_NAME_SIZE_MAX]; |
| |
| if (mDataBuffer == NULL) |
| { |
| // Doesn't look like we were sent any data |
| FastbootFail("No data to flash"); |
| return; |
| } |
| AsciiStrToUnicodeStr(arg, PartitionName); |
| |
| if (TargetBuildVariantUser()) { |
| if (!IsUnlocked()) { |
| FastbootFail("Flashing is not allowed in Lock State"); |
| return; |
| } |
| |
| if (!IsUnlockCritical() && IsCriticalPartition(PartitionName)) { |
| FastbootFail("Flashing is not allowed for Critical Partitions\n"); |
| return; |
| } |
| } |
| |
| /* Handle virtual partition avb_custom_key */ |
| if (!StrnCmp(PartitionName, L"avb_custom_key", StrLen(L"avb_custom_key"))) { |
| DEBUG((EFI_D_INFO, "flashing avb_custom_key\n")); |
| Status = StoreUserKey(data, sz); |
| if (Status != EFI_SUCCESS) { |
| FastbootFail("Flashing avb_custom_key failed"); |
| } else { |
| FastbootOkay(""); |
| } |
| return; |
| } |
| |
| /* Find the lun number from input string */ |
| Token = StrStr(PartitionName, L":"); |
| |
| if (Token) |
| { |
| /* Skip past ":" to the lun number */ |
| Token++; |
| Lun = StrDecimalToUintn(Token); |
| |
| if (Lun >= MAX_LUNS) |
| { |
| FastbootFail("Invalid Lun number passed\n"); |
| goto out; |
| } |
| |
| LunSet = TRUE; |
| } |
| |
| if (!StrnCmp(PartitionName, L"partition", StrLen(L"partition"))) { |
| GetRootDeviceType(BootDeviceType, BOOT_DEV_NAME_SIZE_MAX); |
| if (!AsciiStrnCmp(BootDeviceType, "UFS", AsciiStrLen("UFS"))) { |
| UfsGetSetBootLun(&UfsBootLun, TRUE); /* True = Get */ |
| if (UfsBootLun != 0x1) { |
| UfsBootLun = 0x1; |
| UfsGetSetBootLun(&UfsBootLun, FALSE); /* False = Set */ |
| } |
| } |
| DEBUG((EFI_D_INFO, "Attemping to update partition table\n")); |
| DEBUG((EFI_D_INFO, "*************** Current partition Table Dump Start *******************\n")); |
| PartitionDump(); |
| DEBUG((EFI_D_INFO, "*************** Current partition Table Dump End *******************\n")); |
| Status = UpdatePartitionTable(data, sz, Lun, Ptable); |
| /* Signal the Block IO to updae and reenumerate the parition table */ |
| if (Status == EFI_SUCCESS) |
| { |
| Status = gBS->CreateEventEx(EVT_NOTIFY_SIGNAL, |
| TPL_CALLBACK, BlockIoCallback, NULL, |
| &gBlockIoRefreshGuid, &gBlockIoRefreshEvt); |
| if (Status != EFI_SUCCESS) |
| { |
| DEBUG((EFI_D_ERROR, "Error Creating event for Block Io refresh:%x\n", Status)); |
| FastbootFail("Failed to update partition Table\n"); |
| goto out; |
| } |
| Status = gBS->SignalEvent(gBlockIoRefreshEvt); |
| if (Status != EFI_SUCCESS) |
| { |
| DEBUG((EFI_D_ERROR, "Error Signalling event for Block Io refresh:%x\n", Status)); |
| FastbootFail("Failed to update partition Table\n"); |
| goto out; |
| } |
| Status = EnumeratePartitions(); |
| if (EFI_ERROR(Status)) |
| { |
| FastbootFail("Partition table update failed\n"); |
| goto out; |
| } |
| UpdatePartitionEntries(); |
| |
| IsBootPtnUpdated(Lun, &BootPtnUpdated); |
| if (BootPtnUpdated) { |
| /*Check for multislot boot support*/ |
| MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| if (MultiSlotBoot) { |
| FindPtnActiveSlot(); |
| PopulateMultislotMetadata(); |
| DEBUG((EFI_D_VERBOSE, "Multi Slot boot is supported\n")); |
| } else { |
| DEBUG((EFI_D_VERBOSE, "Multi Slot boot is not supported\n")); |
| if (BootSlotInfo == NULL) |
| DEBUG((EFI_D_VERBOSE, "No change in Ptable\n")); |
| else { |
| DEBUG((EFI_D_VERBOSE, "Nullifying A/B info\n")); |
| SetCurrentSlotSuffix(NullSlot); |
| ClearFastbootVarsofAB(); |
| FreePool(BootSlotInfo); |
| SetMem((VOID*)SlotSuffixArray, SLOT_SUFFIX_ARRAY_SIZE, 0); |
| InitialPopulate = FALSE; |
| } |
| } |
| BootPtnUpdated = FALSE; |
| } |
| |
| DEBUG((EFI_D_INFO, "*************** New partition Table Dump Start *******************\n")); |
| PartitionDump(); |
| DEBUG((EFI_D_INFO, "*************** New partition Table Dump End *******************\n")); |
| FastbootOkay(""); |
| goto out; |
| } |
| else |
| { |
| FastbootFail("Error Updating partition Table\n"); |
| goto out; |
| } |
| } |
| sparse_header = (sparse_header_t *) mDataBuffer; |
| meta_header = (meta_header_t *) mDataBuffer; |
| |
| if (sparse_header->magic == SPARSE_HEADER_MAGIC) |
| Status = HandleSparseImgFlash(PartitionName, sizeof(PartitionName), mDataBuffer, mNumDataBytes); |
| else if (meta_header->magic == META_HEADER_MAGIC) |
| Status = HandleMetaImgFlash(PartitionName, sizeof(PartitionName), mDataBuffer, mNumDataBytes); |
| else |
| Status = HandleRawImgFlash(PartitionName, sizeof(PartitionName), mDataBuffer, mNumDataBytes); |
| |
| if (Status == EFI_NOT_FOUND) |
| { |
| FastbootFail("No such partition."); |
| DEBUG((EFI_D_ERROR, " (%s) No such partition\n", PartitionName)); |
| goto out; |
| } |
| else if (EFI_ERROR (Status)) |
| { |
| FastbootFail("Error flashing partition."); |
| DEBUG ((EFI_D_ERROR, "Couldn't flash image: %r\n", Status)); |
| goto out; |
| } |
| else |
| { |
| DEBUG ((EFI_D_ERROR, "flash image status: %r\n", Status)); |
| FastbootOkay(""); |
| goto out; |
| } |
| out: |
| LunSet = FALSE; |
| } |
| |
| STATIC VOID CmdErase( |
| IN CONST CHAR8 *arg, |
| IN VOID *data, |
| IN UINT32 sz |
| ) |
| { |
| EFI_STATUS Status; |
| CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; |
| BOOLEAN HasSlot = FALSE; |
| CHAR16 SlotSuffix[MAX_SLOT_SUFFIX_SZ]; |
| BOOLEAN MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| CHAR16 PartitionName[MAX_GPT_NAME_SIZE]; |
| AsciiStrToUnicodeStr(arg, PartitionName); |
| |
| if (TargetBuildVariantUser()) { |
| if (!IsUnlocked()) { |
| FastbootFail("Erase is not allowed in Lock State"); |
| return; |
| } |
| |
| if (!IsUnlockCritical() && IsCriticalPartition(PartitionName)) { |
| FastbootFail("Erase is not allowed for Critical Partitions\n"); |
| return; |
| } |
| } |
| |
| /* Handle virtual partition avb_custom_key */ |
| if (!StrnCmp(PartitionName, L"avb_custom_key", StrLen(L"avb_custom_key"))) { |
| DEBUG((EFI_D_INFO, "erasing avb_custom_key\n")); |
| Status = EraseUserKey(); |
| if (Status != EFI_SUCCESS) { |
| FastbootFail("Erasing avb_custom_key failed"); |
| } else { |
| FastbootOkay(""); |
| } |
| return; |
| } |
| |
| /* In A/B to have backward compatibility user can still give fastboot flash boot/system/modem etc |
| * based on current slot Suffix try to look for "partition"_a/b if not found fall back to look for |
| * just the "partition" in case some of the partitions are no included for A/B implementation |
| */ |
| if(MultiSlotBoot) |
| HasSlot = GetPartitionHasSlot(PartitionName, sizeof(PartitionName), SlotSuffix, MAX_SLOT_SUFFIX_SZ); |
| |
| // Build output string |
| UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %s\r\n", PartitionName); |
| Status = FastbootErasePartition (PartitionName); |
| if (EFI_ERROR (Status)) |
| { |
| FastbootFail("Check device console."); |
| DEBUG ((EFI_D_ERROR, "Couldn't erase image: %r\n", Status)); |
| } |
| else |
| { |
| if (MultiSlotBoot && HasSlot && !(StrnCmp(PartitionName, L"boot", StrLen(L"boot")))) |
| FastbootUpdateAttr(SlotSuffix); |
| FastbootOkay(""); |
| } |
| } |
| |
| /*Function to set given slot as high priority |
| *Arg: slot Suffix |
| *Note: increase the priority of slot to max priority |
| *at the same time decrease the priority of other |
| *slots. |
| */ |
| VOID CmdSetActive(CONST CHAR8 *Arg, VOID *Data, UINT32 Size) |
| { |
| UINT32 i; |
| CHAR16 SetActive[MAX_GPT_NAME_SIZE] = L"boot"; |
| struct PartitionEntry *PartEntriesPtr = NULL; |
| CHAR8 *InputSlot = NULL; |
| CHAR16 InputSlotInUnicode[MAX_SLOT_SUFFIX_SZ]; |
| CHAR16 InputSlotInUnicodetemp[MAX_SLOT_SUFFIX_SZ]; |
| CONST CHAR8 *Delim = ":"; |
| UINT16 j = 0; |
| BOOLEAN SlotVarUpdateComplete = FALSE; |
| CHAR16 SlotSuffixUnicode[MAX_SLOT_SUFFIX_SZ]; |
| UINT32 PartitionCount =0; |
| UINT32 SlotEnd = 0; |
| BOOLEAN SwitchSlot = FALSE; |
| BOOLEAN MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| |
| if (!IsUnlocked()) { |
| FastbootFail("Slot Change is not allowed in Lock State\n"); |
| return; |
| } |
| |
| if(!MultiSlotBoot) |
| { |
| FastbootFail("This Command not supported"); |
| return; |
| } |
| |
| if (!Arg) { |
| FastbootFail("Invalid Input Parameters"); |
| return; |
| } |
| |
| InputSlot = AsciiStrStr(Arg, Delim); |
| if (InputSlot) { |
| InputSlot++; |
| if (!AsciiStrStr(InputSlot, "_")) { |
| AsciiStrToUnicodeStr(InputSlot, InputSlotInUnicodetemp); |
| StrnCpyS(InputSlotInUnicode, MAX_SLOT_SUFFIX_SZ, L"_", StrLen(L"_")); |
| StrnCatS(InputSlotInUnicode, MAX_SLOT_SUFFIX_SZ, InputSlotInUnicodetemp, StrLen(InputSlotInUnicodetemp)); |
| } else { |
| AsciiStrToUnicodeStr(InputSlot, InputSlotInUnicode); |
| } |
| if (StrnCmp(GetCurrentSlotSuffix(), InputSlotInUnicode, StrLen(InputSlotInUnicode))) |
| SwitchSlot = TRUE; |
| |
| if ((AsciiStrLen(InputSlot) == MAX_SLOT_SUFFIX_SZ-2) || (AsciiStrLen(InputSlot) == MAX_SLOT_SUFFIX_SZ-1) ) { |
| SlotEnd = AsciiStrLen(InputSlot); |
| if((InputSlot[SlotEnd] != 0) || !AsciiStrStr(SlotSuffixArray, InputSlot)) { |
| DEBUG((EFI_D_ERROR,"%a Invalid InputSlot Suffix\n",InputSlot)); |
| FastbootFail("Invalid Slot Suffix"); |
| return; |
| } |
| } |
| /*Arg will be either _a or _b, so apppend it to boot*/ |
| StrnCatS(SetActive, MAX_GPT_NAME_SIZE - 1, InputSlotInUnicode, StrLen(InputSlotInUnicode)); |
| } else { |
| FastbootFail("set_active _a or _b should be entered"); |
| return; |
| } |
| |
| GetPartitionCount(&PartitionCount); |
| for (i=0; i < PartitionCount; i++) |
| { |
| if (!StrnCmp(PtnEntries[i].PartEntry.PartitionName, L"boot", StrLen(L"boot"))) |
| { |
| if (!StrnCmp(PtnEntries[i].PartEntry.PartitionName, SetActive, StrLen(SetActive))) |
| { |
| PartEntriesPtr = &PtnEntries[i]; |
| /* |
| * select the slot and increase the priority = 3,retry-count =7,slot_successful = 0 |
| * Mark the slot as active and slot_unbootable = 0 |
| */ |
| PartEntriesPtr->PartEntry.Attributes = |
| (PartEntriesPtr->PartEntry.Attributes | PART_ATT_ACTIVE_VAL | PART_ATT_PRIORITY_VAL |
| | PART_ATT_MAX_RETRY_COUNT_VAL) & (~PART_ATT_SUCCESSFUL_VAL & ~PART_ATT_UNBOOTABLE_VAL); |
| |
| AsciiStrnCpyS(CurrentSlotFB, MAX_SLOT_SUFFIX_SZ, InputSlot, AsciiStrLen(InputSlot)); |
| if (AsciiStrStr(CurrentSlotFB, "_")) { |
| SKIP_FIRSTCHAR_IN_SLOT_SUFFIX(CurrentSlotFB); |
| } |
| StrnCpyS(SlotSuffixUnicode, MAX_SLOT_SUFFIX_SZ, InputSlotInUnicode, StrLen(InputSlotInUnicode)); |
| SetCurrentSlotSuffix(SlotSuffixUnicode); |
| } |
| else |
| { |
| PartEntriesPtr = &PtnEntries[i]; |
| /* Reduce the priority and clear the active flag */ |
| PartEntriesPtr->PartEntry.Attributes = |
| ((PartEntriesPtr->PartEntry.Attributes & (~PART_ATT_PRIORITY_VAL) & ~PART_ATT_ACTIVE_VAL) |
| | (((UINT64)MAX_PRIORITY - 1) << PART_ATT_PRIORITY_BIT)); |
| } |
| } |
| } |
| |
| do { |
| if (!AsciiStrnCmp(BootSlotInfo[j].SlotSuffix, InputSlot, AsciiStrLen(InputSlot))) { |
| AsciiStrnCpyS(BootSlotInfo[j].SlotSuccessfulVal, ATTR_RESP_SIZE, "no", AsciiStrLen("no")); |
| AsciiStrnCpyS(BootSlotInfo[j].SlotUnbootableVal, ATTR_RESP_SIZE, "no", AsciiStrLen("no")); |
| AsciiSPrint(BootSlotInfo[j].SlotRetryCountVal, sizeof(BootSlotInfo[j].SlotRetryCountVal), "%d", MAX_RETRY_COUNT); |
| SlotVarUpdateComplete = TRUE; |
| } |
| j++; |
| } while(!SlotVarUpdateComplete); |
| |
| if (SwitchSlot) |
| SwitchPtnSlots(SlotSuffixUnicode); |
| UpdatePartitionAttributes(); |
| FastbootOkay(""); |
| } |
| #endif |
| |
| STATIC VOID AcceptData (IN UINT64 Size, IN VOID *Data) |
| { |
| UINT64 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar; |
| |
| /* Protocol doesn't say anything about sending extra data so just ignore it.*/ |
| if (Size > RemainingBytes) |
| { |
| Size = RemainingBytes; |
| } |
| |
| mBytesReceivedSoFar += Size; |
| |
| /* Either queue the max transfer size 1 MB or only queue the remaining |
| * amount of data left to avoid zlt issues |
| */ |
| if (mBytesReceivedSoFar == mNumDataBytes) |
| { |
| /* Download Finished */ |
| DEBUG((EFI_D_INFO, "Download Finished\n")); |
| FastbootOkay(""); |
| mState = ExpectCmdState; |
| } |
| else |
| { |
| GetFastbootDeviceData().UsbDeviceProtocol->Send(ENDPOINT_IN, GetXfrSize(), (mDataBuffer + mBytesReceivedSoFar)); |
| DEBUG((EFI_D_VERBOSE, "AcceptData: Send %d: %a\n", GetXfrSize(), (mDataBuffer + mBytesReceivedSoFar))); |
| } |
| } |
| |
| /* Called based on the event received from USB device protocol: |
| */ |
| VOID DataReady( |
| IN UINT64 Size, |
| IN VOID *Data |
| ) |
| { |
| DEBUG((EFI_D_VERBOSE, "DataReady %d\n", Size)); |
| if (mState == ExpectCmdState) |
| AcceptCmd (Size, (CHAR8 *) Data); |
| else if (mState == ExpectDataState) |
| AcceptData (Size, Data); |
| else { |
| DEBUG((EFI_D_ERROR, "DataReady Unknown status received\r\n")); |
| return; |
| } |
| } |
| |
| STATIC VOID FatalErrorNotify( |
| IN EFI_EVENT Event, |
| IN VOID *Context |
| ) |
| { |
| DEBUG((EFI_D_ERROR, "Fatal error sending command response. Exiting.\r\n")); |
| Finished = TRUE; |
| } |
| |
| /* Fatal error during fastboot */ |
| BOOLEAN FastbootFatal() |
| { |
| return Finished; |
| } |
| |
| /* This function must be called to deallocate the USB buffers, as well |
| * as the main Fastboot Buffer. Also Frees Variable data Structure |
| */ |
| EFI_STATUS |
| FastbootCmdsUnInit(VOID) |
| { |
| EFI_STATUS Status; |
| |
| if (mDataBuffer) |
| { |
| Status = GetFastbootDeviceData().UsbDeviceProtocol->FreeTransferBuffer((VOID*)mDataBuffer); |
| if (Status != EFI_SUCCESS) |
| { |
| DEBUG((EFI_D_ERROR, "Failed to free up fastboot buffer\n")); |
| return Status; |
| } |
| } |
| FastbootUnInit(); |
| GetFastbootDeviceData().UsbDeviceProtocol->Stop(); |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS |
| FastbootCmdsInit (VOID) |
| { |
| EFI_STATUS Status; |
| EFI_EVENT mFatalSendErrorEvent; |
| CHAR8 *FastBootBuffer; |
| |
| mDataBuffer = NULL; |
| |
| DEBUG((EFI_D_INFO, "Fastboot: Initializing...\n")); |
| |
| /* 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 to pass to FASTBOOT_PROTOCOL.Send, signalling a fatal error */ |
| Status = gBS->CreateEvent ( |
| EVT_NOTIFY_SIGNAL, |
| TPL_CALLBACK, |
| FatalErrorNotify, |
| NULL, |
| &mFatalSendErrorEvent |
| ); |
| if (EFI_ERROR (Status)) |
| { |
| DEBUG ((EFI_D_ERROR, "Couldn't create Fastboot protocol send event: %r\n", Status)); |
| return Status; |
| } |
| |
| /* Allocate buffer used to store images passed by the download command */ |
| Status = GetFastbootDeviceData().UsbDeviceProtocol->AllocateTransferBuffer(MAX_BUFFER_SIZE, (VOID**) &FastBootBuffer); |
| if (Status != EFI_SUCCESS) |
| { |
| DEBUG((EFI_D_ERROR, "Not enough memory to Allocate Fastboot Buffer")); |
| return Status; |
| } |
| |
| FastbootCommandSetup( (void*) FastBootBuffer, MAX_BUFFER_SIZE); |
| return EFI_SUCCESS; |
| } |
| |
| /* See header for documentation */ |
| VOID FastbootRegister( |
| IN CONST CHAR8 *prefix, |
| IN VOID (*handle)(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| ) |
| { |
| FASTBOOT_CMD *cmd; |
| cmd = AllocatePool(sizeof(*cmd)); |
| if (cmd) |
| { |
| cmd->prefix = prefix; |
| cmd->prefix_len = AsciiStrLen(prefix); |
| cmd->handle = handle; |
| cmd->next = cmdlist; |
| cmdlist = cmd; |
| } |
| } |
| |
| STATIC VOID CmdReboot( |
| IN CONST CHAR8 *arg, |
| IN VOID *data, |
| IN UINT32 sz |
| ) |
| { |
| DEBUG((EFI_D_INFO, "rebooting the device")); |
| FastbootOkay(""); |
| |
| RebootDevice(NORMAL_MODE); |
| |
| // Shouldn't get here |
| FastbootFail("Failed to reboot"); |
| } |
| |
| STATIC VOID CmdContinue( |
| IN CONST CHAR8 *Arg, |
| IN VOID *Data, |
| IN UINT32 Size |
| ) |
| { |
| EFI_STATUS Status = EFI_SUCCESS; |
| CHAR8 Resp[MAX_RSP_SIZE]; |
| BootInfo Info = {0}; |
| |
| Info.MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| Status = LoadImageAndAuth(&Info); |
| if (Status != EFI_SUCCESS) { |
| AsciiSPrint(Resp, sizeof(Resp), "Failed to load image from partition: %r", Status); |
| FastbootFail(Resp); |
| return; |
| } |
| |
| /* Exit keys' detection firstly */ |
| ExitMenuKeysDetection(); |
| |
| FastbootOkay(""); |
| FastbootUsbDeviceStop(); |
| Finished = TRUE; |
| // call start Linux here |
| BootLinux(&Info); |
| } |
| |
| STATIC VOID UpdateGetVarVariable() |
| { |
| BOOLEAN BatterySocOk = FALSE; |
| UINT32 BatteryVoltage = 0; |
| |
| BatterySocOk = TargetBatterySocOk(&BatteryVoltage); |
| AsciiSPrint(StrBatteryVoltage, sizeof(StrBatteryVoltage), "%d", BatteryVoltage); |
| AsciiSPrint(StrBatterySocOk, sizeof(StrBatterySocOk), "%a", BatterySocOk ? "yes" : "no"); |
| AsciiSPrint(ChargeScreenEnable, sizeof(ChargeScreenEnable), "%d", IsChargingScreenEnable()); |
| AsciiSPrint(OffModeCharge, sizeof(OffModeCharge), "%d", IsChargingScreenEnable()); |
| } |
| |
| STATIC VOID WaitForTransferComplete() |
| { |
| USB_DEVICE_EVENT Msg; |
| USB_DEVICE_EVENT_DATA Payload; |
| UINTN PayloadSize; |
| |
| /* Wait for the transfer to complete */ |
| while (1) |
| { |
| GetFastbootDeviceData().UsbDeviceProtocol->HandleEvent(&Msg, &PayloadSize, &Payload); |
| if (UsbDeviceEventTransferNotification == Msg) |
| { |
| if (1 == USB_INDEX_TO_EP(Payload.TransferOutcome.EndpointIndex)) { |
| if (USB_ENDPOINT_DIRECTION_IN == USB_INDEX_TO_EPDIR(Payload.TransferOutcome.EndpointIndex)) |
| break; |
| } |
| } |
| } |
| } |
| |
| STATIC VOID CmdGetVarAll() |
| { |
| FASTBOOT_VAR *Var; |
| CHAR8 GetVarAll[MAX_RSP_SIZE]; |
| |
| for (Var = Varlist; Var; Var = Var->next) |
| { |
| AsciiStrnCpyS(GetVarAll, sizeof(GetVarAll), Var->name, AsciiStrLen(Var->name)); |
| AsciiStrnCatS(GetVarAll, sizeof(GetVarAll), ":", AsciiStrLen(":")); |
| AsciiStrnCatS(GetVarAll, sizeof(GetVarAll), Var->value, AsciiStrLen(Var->value)); |
| FastbootInfo(GetVarAll); |
| /* Wait for the transfer to complete */ |
| WaitForTransferComplete(); |
| ZeroMem(GetVarAll, sizeof(GetVarAll)); |
| } |
| |
| FastbootOkay(GetVarAll); |
| } |
| |
| STATIC VOID CmdGetVar(CONST CHAR8 *Arg, VOID *Data, UINT32 Size) |
| { |
| FASTBOOT_VAR *Var; |
| |
| UpdateGetVarVariable(); |
| if (!(AsciiStrCmp("all", Arg))) |
| { |
| CmdGetVarAll(); |
| return; |
| } |
| for (Var = Varlist; Var; Var = Var->next) |
| { |
| if (!AsciiStrCmp(Var->name, Arg)) |
| { |
| FastbootOkay(Var->value); |
| return; |
| } |
| } |
| |
| FastbootFail("GetVar Variable Not found"); |
| } |
| |
| #ifdef ENABLE_BOOT_CMD |
| STATIC VOID CmdBoot(CONST CHAR8 *Arg, VOID *Data, UINT32 Size) |
| { |
| struct boot_img_hdr *hdr = (struct boot_img_hdr *) Data; |
| EFI_STATUS Status = EFI_SUCCESS; |
| UINT32 ImageSizeActual = 0; |
| UINT32 ImageHdrSize = 0; |
| UINT32 PageSize = 0; |
| UINT32 SigActual = SIGACTUAL; |
| CHAR8 Resp[MAX_RSP_SIZE]; |
| BOOLEAN MdtpActive = FALSE; |
| BootInfo Info = {0}; |
| |
| if (FixedPcdGetBool(EnableMdtpSupport)) { |
| Status = IsMdtpActive(&MdtpActive); |
| |
| if (EFI_ERROR(Status)) { |
| FastbootFail("Failed to get MDTP activation state, blocking fastboot boot"); |
| return; |
| } |
| |
| if (MdtpActive == TRUE) { |
| FastbootFail("Fastboot boot command is not available while MDTP is active"); |
| return; |
| } |
| } |
| |
| if (Size < sizeof(struct boot_img_hdr)) |
| { |
| FastbootFail("Invalid Boot image Header"); |
| return; |
| } |
| |
| hdr->cmdline[BOOT_ARGS_SIZE - 1] = '\0'; |
| |
| // Setup page size information for nv storage |
| GetPageSize(&ImageHdrSize); |
| |
| Status = CheckImageHeader(Data, ImageHdrSize, &ImageSizeActual, &PageSize); |
| if (Status != EFI_SUCCESS) |
| { |
| AsciiSPrint(Resp, sizeof(Resp), "Invalid Boot image Header: %r", Status); |
| FastbootFail(Resp); |
| return; |
| } |
| |
| if (ImageSizeActual > Size) |
| { |
| FastbootFail("BootImage is Incomplete"); |
| return; |
| } |
| if ((MAX_DOWNLOAD_SIZE - (ImageSizeActual - SigActual)) < PageSize) |
| { |
| FastbootFail("BootImage: Size os greater than boot image buffer can hold"); |
| return; |
| } |
| |
| Info.Images[0].ImageBuffer = Data; |
| Info.Images[0].ImageSize = ImageSizeActual; |
| Info.Images[0].Name = "boot"; |
| Info.NumLoadedImages = 1; |
| Info.MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| |
| Status = LoadImageAndAuth(&Info); |
| if (Status != EFI_SUCCESS) { |
| AsciiSPrint(Resp, sizeof(Resp), "Failed to load/authenticate boot image: %r", Status); |
| FastbootFail(Resp); |
| return; |
| } |
| |
| /* Exit keys' detection firstly */ |
| ExitMenuKeysDetection(); |
| |
| FastbootOkay(""); |
| FastbootUsbDeviceStop(); |
| BootLinux(&Info); |
| } |
| #endif |
| |
| STATIC VOID CmdRebootBootloader(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| DEBUG((EFI_D_INFO, "Rebooting the device into bootloader mode\n")); |
| FastbootOkay(""); |
| RebootDevice(FASTBOOT_MODE); |
| |
| // Shouldn't get here |
| FastbootFail("Failed to reboot"); |
| |
| } |
| |
| #if (defined(ENABLE_DEVICE_CRITICAL_LOCK_UNLOCK_CMDS) || defined(ENABLE_UPDATE_PARTITIONS_CMDS)) |
| STATIC VOID SetDeviceUnlock(UINT32 Type, BOOLEAN State) |
| { |
| BOOLEAN is_unlocked = FALSE; |
| char response[MAX_RSP_SIZE] = {0}; |
| EFI_STATUS Status; |
| |
| if (Type == UNLOCK) |
| is_unlocked = IsUnlocked(); |
| else if (Type == UNLOCK_CRITICAL) |
| is_unlocked = IsUnlockCritical(); |
| if (State == is_unlocked) |
| { |
| AsciiSPrint(response, MAX_RSP_SIZE, "\tDevice already : %a", (State ? "unlocked!" : "locked!")); |
| FastbootFail(response); |
| return; |
| } |
| |
| /* If State is TRUE that means set the unlock to true */ |
| if (State) |
| { |
| if(!IsAllowUnlock) |
| { |
| FastbootFail("Flashing Unlock is not allowed\n"); |
| return; |
| } |
| } |
| |
| Status = SetDeviceUnlockValue(Type, State); |
| if (Status != EFI_SUCCESS) { |
| AsciiSPrint(response, MAX_RSP_SIZE, "\tSet device %a failed: %r", (State ? "unlocked!" : "locked!"), Status); |
| FastbootFail(response); |
| return; |
| } |
| |
| FastbootOkay(""); |
| RebootDevice(RECOVERY_MODE); |
| } |
| #endif |
| |
| #ifdef ENABLE_UPDATE_PARTITIONS_CMDS |
| STATIC VOID CmdFlashingUnlock(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| SetDeviceUnlock(UNLOCK, TRUE); |
| } |
| |
| STATIC VOID CmdFlashingLock(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| SetDeviceUnlock(UNLOCK, FALSE); |
| } |
| #endif |
| |
| #ifdef ENABLE_DEVICE_CRITICAL_LOCK_UNLOCK_CMDS |
| STATIC VOID CmdFlashingLockCritical(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| SetDeviceUnlock(UNLOCK_CRITICAL, FALSE); |
| } |
| |
| STATIC VOID CmdFlashingUnLockCritical(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| SetDeviceUnlock(UNLOCK_CRITICAL, TRUE); |
| } |
| #endif |
| |
| STATIC VOID CmdOemEnableChargerScreen(CONST CHAR8 *Arg, VOID *Data, UINT32 Size) |
| { |
| EFI_STATUS Status; |
| DEBUG((EFI_D_INFO, "Enabling Charger Screen\n")); |
| |
| Status = EnableChargingScreen(TRUE); |
| if (Status != EFI_SUCCESS) { |
| FastbootFail("Failed to enable charger screen"); |
| } else { |
| FastbootOkay(""); |
| } |
| } |
| |
| STATIC VOID CmdOemDisableChargerScreen(CONST CHAR8 *Arg, VOID *Data, UINT32 Size) |
| { |
| EFI_STATUS Status; |
| DEBUG((EFI_D_INFO, "Disabling Charger Screen\n")); |
| |
| Status = EnableChargingScreen(FALSE); |
| if (Status != EFI_SUCCESS) { |
| FastbootFail("Failed to disable charger screen"); |
| } else { |
| FastbootOkay(""); |
| } |
| } |
| |
| STATIC VOID CmdOemOffModeCharger(CONST CHAR8 *Arg, VOID *Data, UINT32 Size) |
| { |
| CHAR8 *Ptr = NULL; |
| CONST CHAR8 *Delim = " "; |
| EFI_STATUS Status; |
| BOOLEAN IsEnable = FALSE; |
| CHAR8 Resp[MAX_RSP_SIZE] = "Set off mode charger: "; |
| |
| if (Arg) { |
| Ptr = AsciiStrStr(Arg, Delim); |
| if (Ptr) { |
| Ptr++; |
| if (!AsciiStrCmp(Ptr, "0")) |
| IsEnable = FALSE; |
| else if (!AsciiStrCmp(Ptr, "1")) |
| IsEnable = TRUE; |
| else { |
| FastbootFail("Invalid input entered"); |
| return; |
| } |
| } else { |
| FastbootFail("Enter fastboot oem off-mode-charge 0/1"); |
| return; |
| } |
| } |
| |
| AsciiStrnCatS(Resp, sizeof(Resp), Arg, AsciiStrLen(Arg)); |
| /* update charger_screen_enabled value for getvar command */ |
| Status = EnableChargingScreen(IsEnable); |
| if (Status != EFI_SUCCESS) { |
| AsciiStrnCatS(Resp, sizeof(Resp), ": failed", AsciiStrLen(": failed")); |
| FastbootFail(Resp); |
| } else { |
| AsciiStrnCatS(Resp, sizeof(Resp), ": done", AsciiStrLen(": done")); |
| FastbootOkay(Resp); |
| } |
| } |
| |
| STATIC VOID CmdOemSelectDisplayPanel(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| EFI_STATUS Status; |
| CHAR8 resp[MAX_RSP_SIZE] = "Selecting Panel: "; |
| |
| AsciiStrnCatS(resp, sizeof(resp), arg, AsciiStrLen(arg)); |
| |
| /* Update the environment variable with the selected panel */ |
| Status = gRT->SetVariable( |
| L"DisplayPanelOverride", |
| &gQcomTokenSpaceGuid, |
| EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, |
| AsciiStrLen(arg), |
| (VOID*)arg); |
| if (Status != EFI_SUCCESS) |
| { |
| DEBUG((EFI_D_ERROR, "Failed to set panel name, %r\n", Status)); |
| AsciiStrnCatS(resp, sizeof(resp), ": failed", AsciiStrLen(": failed")); |
| FastbootFail(resp); |
| } |
| else |
| { |
| AsciiStrnCatS(resp, sizeof(resp), ": done", AsciiStrLen(": done")); |
| FastbootOkay(resp); |
| } |
| } |
| |
| #ifdef ENABLE_UPDATE_PARTITIONS_CMDS |
| STATIC VOID CmdFlashingGetUnlockAbility(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| CHAR8 UnlockAbilityInfo[MAX_RSP_SIZE]; |
| |
| AsciiSPrint(UnlockAbilityInfo, sizeof(UnlockAbilityInfo), "get_unlock_ability: %d", IsAllowUnlock); |
| FastbootInfo(UnlockAbilityInfo); |
| WaitForTransferComplete(); |
| FastbootOkay(""); |
| } |
| #endif |
| |
| STATIC VOID CmdOemDevinfo(CONST CHAR8 *arg, VOID *data, UINT32 sz) |
| { |
| CHAR8 DeviceInfo[MAX_RSP_SIZE]; |
| |
| AsciiSPrint(DeviceInfo, sizeof(DeviceInfo), "Verity mode: %a", IsEnforcing() ? "true" : "false"); |
| FastbootInfo(DeviceInfo); |
| WaitForTransferComplete(); |
| AsciiSPrint(DeviceInfo, sizeof(DeviceInfo), "Device unlocked: %a", IsUnlocked() ? "true" : "false"); |
| FastbootInfo(DeviceInfo); |
| WaitForTransferComplete(); |
| AsciiSPrint(DeviceInfo, sizeof(DeviceInfo), "Device critical unlocked: %a", IsUnlockCritical() ? "true" : "false"); |
| FastbootInfo(DeviceInfo); |
| WaitForTransferComplete(); |
| AsciiSPrint(DeviceInfo, sizeof(DeviceInfo), "Charger screen enabled: %a", IsChargingScreenEnable() ? "true" : "false"); |
| FastbootInfo(DeviceInfo); |
| WaitForTransferComplete(); |
| FastbootOkay(""); |
| } |
| |
| STATIC VOID AcceptCmd( |
| IN UINT64 Size, |
| IN CHAR8 *Data |
| ) |
| { |
| FASTBOOT_CMD *cmd; |
| if (!Data) |
| { |
| FastbootFail("Invalid input command"); |
| return; |
| } |
| if (Size > MAX_FASTBOOT_COMMAND_SIZE) |
| Size = MAX_FASTBOOT_COMMAND_SIZE; |
| Data[Size] = '\0'; |
| DEBUG((EFI_D_INFO, "Handling Cmd: %a\n", Data)); |
| |
| for (cmd = cmdlist; cmd; cmd = cmd->next) |
| { |
| if (AsciiStrnCmp(Data, cmd->prefix, cmd->prefix_len)) |
| continue; |
| cmd->handle((CONST CHAR8*) Data + cmd->prefix_len, (VOID *) mDataBuffer, (UINT32)mBytesReceivedSoFar); |
| return; |
| } |
| DEBUG((EFI_D_ERROR, "\nFastboot Send Fail\n")); |
| FastbootFail("unknown command"); |
| } |
| |
| STATIC EFI_STATUS PublishGetVarPartitionInfo( |
| IN struct GetVarPartitionInfo *info, |
| IN UINT32 num_parts |
| ) |
| { |
| UINT32 i; |
| EFI_BLOCK_IO_PROTOCOL *BlockIo = NULL; |
| EFI_HANDLE *Handle = NULL; |
| EFI_STATUS Status = EFI_INVALID_PARAMETER; |
| CHAR16 PartitionNameUniCode[MAX_GPT_NAME_SIZE]; |
| CHAR16* CurrentSlot; |
| CHAR8 CurrSlotAscii[MAX_SLOT_SUFFIX_SZ]; |
| |
| for (i = 0; i < num_parts; i++) |
| { |
| AsciiStrToUnicodeStr(info[i].part_name, PartitionNameUniCode); |
| if (PartitionHasMultiSlot(PartitionNameUniCode)) { |
| CurrentSlot = GetCurrentSlotSuffix(); |
| StrnCatS(PartitionNameUniCode, MAX_GPT_NAME_SIZE, CurrentSlot, StrLen(CurrentSlot)); |
| UnicodeStrToAsciiStr(GetCurrentSlotSuffix(), CurrSlotAscii); |
| AsciiStrnCatS((CHAR8 *)info[i].part_name, MAX_GET_VAR_NAME_SIZE, CurrSlotAscii, MAX_SLOT_SUFFIX_SZ); |
| } |
| Status = PartitionGetInfo(PartitionNameUniCode, &BlockIo, &Handle); |
| if (Status != EFI_SUCCESS) |
| return Status; |
| if (!BlockIo) { |
| DEBUG((EFI_D_ERROR, "BlockIo for %a is corrupted\n", info[i].part_name)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| if (!Handle) { |
| DEBUG((EFI_D_ERROR, "EFI handle for %a is corrupted\n",info[i].part_name)); |
| return EFI_VOLUME_CORRUPTED; |
| } |
| |
| AsciiSPrint(info[i].size_response, MAX_RSP_SIZE, " 0x%llx", (UINT64)(BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize); |
| |
| Status = AsciiStrnCatS(info[i].getvar_size_str, MAX_GET_VAR_NAME_SIZE, info[i].part_name, AsciiStrLen(info[i].part_name)); |
| if (EFI_ERROR(Status)) |
| DEBUG((EFI_D_ERROR, "Error Publishing the partition size info\n")); |
| |
| Status = AsciiStrnCatS(info[i].getvar_type_str, MAX_GET_VAR_NAME_SIZE, info[i].part_name, AsciiStrLen(info[i].part_name)); |
| if (EFI_ERROR(Status)) |
| DEBUG((EFI_D_ERROR, "Error Publishing the partition type info\n")); |
| |
| FastbootPublishVar(info[i].getvar_size_str, info[i].size_response); |
| FastbootPublishVar(info[i].getvar_type_str, info[i].type_response); |
| } |
| return Status; |
| } |
| |
| STATIC EFI_STATUS ReadAllowUnlockValue(UINT32 *IsAllowUnlock) |
| { |
| EFI_STATUS Status; |
| EFI_BLOCK_IO_PROTOCOL *BlockIo = NULL; |
| EFI_HANDLE *Handle = NULL; |
| UINT8 *Buffer; |
| |
| Status = PartitionGetInfo(L"frp", &BlockIo, &Handle); |
| if (Status != EFI_SUCCESS) |
| return Status; |
| |
| if (!BlockIo) |
| return EFI_NOT_FOUND; |
| |
| Buffer = AllocatePool(BlockIo->Media->BlockSize); |
| if (!Buffer) |
| { |
| DEBUG((EFI_D_ERROR, "Failed to allocate memory for unlock value \n")); |
| return EFI_OUT_OF_RESOURCES; |
| } |
| |
| Status = BlockIo->ReadBlocks(BlockIo, |
| BlockIo->Media->MediaId, |
| BlockIo->Media->LastBlock, |
| BlockIo->Media->BlockSize, |
| Buffer); |
| if (Status != EFI_SUCCESS) |
| return Status; |
| |
| /* IsAllowUnlock value stored at the LSB of last byte*/ |
| *IsAllowUnlock = Buffer[BlockIo->Media->BlockSize - 1] & 0x01; |
| FreePool(Buffer); |
| return Status; |
| } |
| |
| /* Registers all Stock commands, Publishes all stock variables |
| * and partitiion sizes. base and size are the respective parameters |
| * to the Fastboot Buffer used to store the downloaded image for flashing |
| */ |
| STATIC EFI_STATUS FastbootCommandSetup( |
| IN VOID *base, |
| IN UINT32 size |
| ) |
| { |
| EFI_STATUS Status; |
| CHAR8 HWPlatformBuf[MAX_RSP_SIZE] = "\0"; |
| CHAR8 DeviceType[MAX_RSP_SIZE] = "\0"; |
| CHAR8 VersionTemp[MAX_VERSION_LEN] = "\0"; |
| BOOLEAN BatterySocOk = FALSE; |
| UINT32 BatteryVoltage = 0; |
| |
| mDataBuffer = base; |
| mNumDataBytes = size; |
| BOOLEAN MultiSlotBoot = PartitionHasMultiSlot(L"boot"); |
| |
| /* Find all Software Partitions in the User Partition */ |
| UINT32 i; |
| |
| struct FastbootCmdDesc cmd_list[] = |
| { |
| /* By Default enable list is empty */ |
| { "", NULL}, |
| /*CAUTION(High): Enabling these commands will allow changing the partitions |
| *like system,userdata,cachec etc... |
| */ |
| #ifdef ENABLE_UPDATE_PARTITIONS_CMDS |
| { "flash:", CmdFlash }, |
| { "erase:", CmdErase }, |
| { "set_active", CmdSetActive }, |
| { "flashing get_unlock_ability", CmdFlashingGetUnlockAbility }, |
| { "flashing unlock", CmdFlashingUnlock }, |
| { "flashing lock", CmdFlashingLock }, |
| #endif |
| /* |
| *CAUTION(CRITICAL): Enabling these commands will allow changes to bootimage. |
| */ |
| #ifdef ENABLE_DEVICE_CRITICAL_LOCK_UNLOCK_CMDS |
| { "flashing unlock_critical", CmdFlashingUnLockCritical }, |
| { "flashing lock_critical", CmdFlashingLockCritical }, |
| #endif |
| /* |
| *CAUTION(CRITICAL): Enabling this command will allow boot with different bootimage. |
| */ |
| #ifdef ENABLE_BOOT_CMD |
| { "boot", CmdBoot }, |
| #endif |
| { "oem enable-charger-screen", CmdOemEnableChargerScreen }, |
| { "oem disable-charger-screen", CmdOemDisableChargerScreen }, |
| { "oem off-mode-charge", CmdOemOffModeCharger }, |
| { "oem select-display-panel", CmdOemSelectDisplayPanel }, |
| { "oem device-info", CmdOemDevinfo}, |
| { "continue", CmdContinue }, |
| { "reboot", CmdReboot }, |
| { "reboot-bootloader", CmdRebootBootloader }, |
| { "getvar:", CmdGetVar }, |
| { "download:", CmdDownload }, |
| }; |
| |
| /* Register the commands only for non-user builds */ |
| Status = BoardSerialNum(StrSerialNum, sizeof(StrSerialNum)); |
| if (Status != EFI_SUCCESS) |
| { |
| DEBUG((EFI_D_ERROR, "Error Finding board serial num: %x\n", Status)); |
| return Status; |
| } |
| /* Publish getvar variables */ |
| FastbootPublishVar("kernel", "uefi"); |
| FastbootPublishVar("max-download-size", MAX_DOWNLOAD_SIZE_STR); |
| AsciiSPrint(FullProduct, sizeof(FullProduct), "%a", PRODUCT_NAME); |
| FastbootPublishVar("product", FullProduct); |
| FastbootPublishVar("serial", StrSerialNum); |
| FastbootPublishVar("secure", IsSecureBootEnabled()? "yes":"no"); |
| if (MultiSlotBoot) |
| { |
| /*Find ActiveSlot, bydefault _a will be the active slot |
| *Populate MultiSlotMeta data will publish fastboot variables |
| *like slot_successful, slot_unbootable,slot_retry_count and |
| *CurrenSlot, these can modified using fastboot set_active command |
| */ |
| FindPtnActiveSlot(); |
| PopulateMultislotMetadata(); |
| DEBUG((EFI_D_VERBOSE, "Multi Slot boot is supported\n")); |
| } |
| |
| Status = PublishGetVarPartitionInfo(part_info, sizeof(part_info)/sizeof(part_info[0])); |
| if (Status != EFI_SUCCESS) |
| DEBUG((EFI_D_ERROR, "Partition Table info is not populated\n")); |
| BoardHwPlatformName(HWPlatformBuf, sizeof(HWPlatformBuf)); |
| GetRootDeviceType(DeviceType, sizeof(DeviceType)); |
| AsciiSPrint(StrVariant, sizeof(StrVariant), "%a %a", HWPlatformBuf, DeviceType); |
| FastbootPublishVar("variant", StrVariant); |
| GetBootloaderVersion(VersionTemp, sizeof(VersionTemp)); |
| FastbootPublishVar("version-bootloader", VersionTemp); |
| ZeroMem(VersionTemp, sizeof(VersionTemp)); |
| GetRadioVersion(VersionTemp, sizeof(VersionTemp)); |
| FastbootPublishVar("version-baseband", VersionTemp); |
| BatterySocOk = TargetBatterySocOk(&BatteryVoltage); |
| AsciiSPrint(StrBatteryVoltage, sizeof(StrBatteryVoltage), "%d", BatteryVoltage); |
| FastbootPublishVar("battery-voltage", StrBatteryVoltage); |
| AsciiSPrint(StrBatterySocOk, sizeof(StrBatterySocOk), "%a", BatterySocOk ? "yes" : "no"); |
| FastbootPublishVar("battery-soc-ok", StrBatterySocOk); |
| AsciiSPrint(ChargeScreenEnable, sizeof(ChargeScreenEnable), "%d", IsChargingScreenEnable()); |
| FastbootPublishVar("charger-screen-enabled", ChargeScreenEnable); |
| AsciiSPrint(OffModeCharge, sizeof(OffModeCharge), "%d", IsChargingScreenEnable()); |
| FastbootPublishVar("off-mode-charge", ChargeScreenEnable); |
| FastbootPublishVar("unlocked", IsUnlocked() ? "yes":"no"); |
| |
| /* Register handlers for the supported commands*/ |
| UINT32 FastbootCmdCnt = sizeof(cmd_list)/sizeof(cmd_list[0]); |
| for (i = 1 ; i < FastbootCmdCnt; i++) |
| FastbootRegister(cmd_list[i].name, cmd_list[i].cb); |
| |
| // Read Allow Ulock Flag |
| Status = ReadAllowUnlockValue(&IsAllowUnlock); |
| DEBUG((EFI_D_VERBOSE, "IsAllowUnlock is %d\n", IsAllowUnlock)); |
| |
| if (Status != EFI_SUCCESS) |
| { |
| DEBUG((EFI_D_ERROR, "Error Reading FRP partition: %r\n", Status)); |
| return Status; |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| VOID *FastbootDloadBuffer() |
| { |
| return (VOID *)mDataBuffer; |
| } |
| |
| ANDROID_FASTBOOT_STATE FastbootCurrentState() |
| { |
| return mState; |
| } |