1 /** @file 2 3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR> 4 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include "AndroidFastbootApp.h" 16 17 #include <Protocol/AndroidFastbootTransport.h> 18 #include <Protocol/AndroidFastbootPlatform.h> 19 #include <Protocol/SimpleTextOut.h> 20 #include <Protocol/SimpleTextIn.h> 21 22 #include <Library/ArmLib.h> 23 #include <Library/PcdLib.h> 24 #include <Library/UefiRuntimeServicesTableLib.h> 25 #include <Library/BaseMemoryLib.h> 26 #include <Library/UefiBootServicesTableLib.h> 27 #include <Library/UefiApplicationEntryPoint.h> 28 #include <Library/PrintLib.h> 29 30 /* 31 * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and 32 * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol. 33 */ 34 35 STATIC FASTBOOT_TRANSPORT_PROTOCOL *mTransport; 36 STATIC FASTBOOT_PLATFORM_PROTOCOL *mPlatform; 37 38 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut; 39 40 typedef enum { 41 ExpectCmdState, 42 ExpectDataState, 43 FastbootStateMax 44 } ANDROID_FASTBOOT_STATE; 45 46 STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState; 47 48 // When in ExpectDataState, the number of bytes of data to expect: 49 STATIC UINTN mNumDataBytes; 50 // .. and the number of bytes so far received this data phase 51 STATIC UINTN mBytesReceivedSoFar; 52 // .. and the buffer to save data into 53 STATIC UINT8 *mDataBuffer = NULL; 54 55 // Event notify functions, from which gBS->Exit shouldn't be called, can signal 56 // this event when the application should exit 57 STATIC EFI_EVENT mFinishedEvent; 58 59 STATIC EFI_EVENT mFatalSendErrorEvent; 60 61 // This macro uses sizeof - only use it on arrays (i.e. string literals) 62 #define SEND_LITERAL(Str) mTransport->Send ( \ 63 sizeof (Str) - 1, \ 64 Str, \ 65 &mFatalSendErrorEvent \ 66 ) 67 #define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1) 68 69 #define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z') 70 71 #define FASTBOOT_STRING_MAX_LENGTH 256 72 #define FASTBOOT_COMMAND_MAX_LENGTH 64 73 74 STATIC 75 VOID 76 HandleGetVar ( 77 IN CHAR8 *CmdArg 78 ) 79 { 80 CHAR8 Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY"; 81 EFI_STATUS Status; 82 83 // Respond to getvar:version with 0.4 (version of Fastboot protocol) 84 if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1 )) { 85 SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION); 86 } else { 87 // All other variables are assumed to be platform specific 88 Status = mPlatform->GetVar (CmdArg, Response + 4); 89 if (EFI_ERROR (Status)) { 90 SEND_LITERAL ("FAILSomething went wrong when looking up the variable"); 91 } else { 92 mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent); 93 } 94 } 95 } 96 97 STATIC 98 VOID 99 HandleDownload ( 100 IN CHAR8 *NumBytesString 101 ) 102 { 103 CHAR8 Response[12] = "DATA"; 104 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 105 106 // Argument is 8-character ASCII string hex representation of number of bytes 107 // that will be sent in the data phase. 108 // Response is "DATA" + that same 8-character string. 109 110 // Replace any previously downloaded data 111 if (mDataBuffer != NULL) { 112 FreePool (mDataBuffer); 113 mDataBuffer = NULL; 114 } 115 116 // Parse out number of data bytes to expect 117 mNumDataBytes = AsciiStrHexToUint64 (NumBytesString); 118 if (mNumDataBytes == 0) { 119 mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n"); 120 SEND_LITERAL ("FAILFailed to get the number of bytes to download"); 121 return; 122 } 123 124 UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes); 125 mTextOut->OutputString (mTextOut, OutputString); 126 127 mDataBuffer = AllocatePool (mNumDataBytes); 128 if (mDataBuffer == NULL) { 129 SEND_LITERAL ("FAILNot enough memory"); 130 } else { 131 AsciiStrnCpy (Response + 4, NumBytesString, 8); 132 mTransport->Send (sizeof(Response), Response, &mFatalSendErrorEvent); 133 134 mState = ExpectDataState; 135 mBytesReceivedSoFar = 0; 136 } 137 } 138 139 STATIC 140 VOID 141 HandleFlash ( 142 IN CHAR8 *PartitionName 143 ) 144 { 145 EFI_STATUS Status; 146 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 147 148 // Build output string 149 UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName); 150 mTextOut->OutputString (mTextOut, OutputString); 151 152 if (mDataBuffer == NULL) { 153 // Doesn't look like we were sent any data 154 SEND_LITERAL ("FAILNo data to flash"); 155 return; 156 } 157 158 Status = mPlatform->FlashPartition ( 159 PartitionName, 160 mNumDataBytes, 161 mDataBuffer 162 ); 163 if (Status == EFI_NOT_FOUND) { 164 SEND_LITERAL ("FAILNo such partition."); 165 mTextOut->OutputString (mTextOut, L"No such partition.\r\n"); 166 } else if (EFI_ERROR (Status)) { 167 SEND_LITERAL ("FAILError flashing partition."); 168 mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n"); 169 DEBUG ((EFI_D_ERROR, "Couldn't flash image: %r\n", Status)); 170 } else { 171 mTextOut->OutputString (mTextOut, L"Done.\r\n"); 172 SEND_LITERAL ("OKAY"); 173 } 174 } 175 176 STATIC 177 VOID 178 HandleErase ( 179 IN CHAR8 *PartitionName 180 ) 181 { 182 EFI_STATUS Status; 183 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 184 185 // Build output string 186 UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName); 187 mTextOut->OutputString (mTextOut, OutputString); 188 189 Status = mPlatform->ErasePartition (PartitionName); 190 if (EFI_ERROR (Status)) { 191 SEND_LITERAL ("FAILCheck device console."); 192 DEBUG ((EFI_D_ERROR, "Couldn't erase image: %r\n", Status)); 193 } else { 194 SEND_LITERAL ("OKAY"); 195 } 196 } 197 198 STATIC 199 VOID 200 HandleBoot ( 201 VOID 202 ) 203 { 204 EFI_STATUS Status; 205 206 mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n"); 207 208 if (mDataBuffer == NULL) { 209 // Doesn't look like we were sent any data 210 SEND_LITERAL ("FAILNo image in memory"); 211 return; 212 } 213 214 // We don't really have any choice but to report success, because once we 215 // boot we lose control of the system. 216 SEND_LITERAL ("OKAY"); 217 218 Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer); 219 if (EFI_ERROR (Status)) { 220 DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status)); 221 } 222 // We shouldn't get here 223 } 224 225 STATIC 226 VOID 227 HandleOemCommand ( 228 IN CHAR8 *Command 229 ) 230 { 231 EFI_STATUS Status; 232 233 Status = mPlatform->DoOemCommand (Command); 234 if (Status == EFI_NOT_FOUND) { 235 SEND_LITERAL ("FAILOEM Command not recognised."); 236 } else if (Status == EFI_DEVICE_ERROR) { 237 SEND_LITERAL ("FAILError while executing command"); 238 } else if (EFI_ERROR (Status)) { 239 SEND_LITERAL ("FAIL"); 240 } else { 241 SEND_LITERAL ("OKAY"); 242 } 243 } 244 245 STATIC 246 VOID 247 AcceptCmd ( 248 IN UINTN Size, 249 IN CONST CHAR8 *Data 250 ) 251 { 252 CHAR8 Command[FASTBOOT_COMMAND_MAX_LENGTH + 1]; 253 254 // Max command size is 64 bytes 255 if (Size > FASTBOOT_COMMAND_MAX_LENGTH) { 256 SEND_LITERAL ("FAILCommand too large"); 257 return; 258 } 259 260 // Commands aren't null-terminated. Let's get a null-terminated version. 261 AsciiStrnCpy (Command, Data, Size); 262 Command[Size] = '\0'; 263 264 // Parse command 265 if (MATCH_CMD_LITERAL ("getvar", Command)) { 266 HandleGetVar (Command + sizeof ("getvar")); 267 } else if (MATCH_CMD_LITERAL ("download", Command)) { 268 HandleDownload (Command + sizeof ("download")); 269 } else if (MATCH_CMD_LITERAL ("verify", Command)) { 270 SEND_LITERAL ("FAILNot supported"); 271 } else if (MATCH_CMD_LITERAL ("flash", Command)) { 272 HandleFlash (Command + sizeof ("flash")); 273 } else if (MATCH_CMD_LITERAL ("erase", Command)) { 274 HandleErase (Command + sizeof ("erase")); 275 } else if (MATCH_CMD_LITERAL ("boot", Command)) { 276 HandleBoot (); 277 } else if (MATCH_CMD_LITERAL ("continue", Command)) { 278 SEND_LITERAL ("OKAY"); 279 mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n"); 280 281 gBS->SignalEvent (mFinishedEvent); 282 } else if (MATCH_CMD_LITERAL ("reboot", Command)) { 283 EFI_RESET_TYPE rtype = EfiResetCold; 284 if (MATCH_CMD_LITERAL ("reboot-bootloader", Command)) { 285 // fastboot_protocol.txt: 286 // "reboot-bootloader Reboot back into the bootloader." 287 #define REBOOT_REASON_ADDR 0x05F01000 288 #define REBOOT_REASON_BOOTLOADER 0x77665500 289 UINT32 *addr = (UINT32*)REBOOT_REASON_ADDR; 290 /* Write REBOOT_BOOTLOADER to the reason address */ 291 *addr = REBOOT_REASON_BOOTLOADER; 292 rtype = EfiResetWarm; 293 ArmCleanDataCache(); 294 } 295 SEND_LITERAL ("OKAY"); 296 gRT->ResetSystem (rtype, EFI_SUCCESS, 0, NULL); 297 298 // Shouldn't get here 299 DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n")); 300 } else if (MATCH_CMD_LITERAL ("powerdown", Command)) { 301 SEND_LITERAL ("OKAY"); 302 gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL); 303 304 // Shouldn't get here 305 DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n")); 306 } else if (MATCH_CMD_LITERAL ("oem", Command)) { 307 // The "oem" command isn't in the specification, but it was observed in the 308 // wild, followed by a space, followed by the actual command. 309 HandleOemCommand (Command + sizeof ("oem")); 310 } else if (IS_LOWERCASE_ASCII (Command[0])) { 311 // Commands starting with lowercase ASCII characters are reserved for the 312 // Fastboot protocol. If we don't recognise it, it's probably the future 313 // and there are new commmands in the protocol. 314 // (By the way, the "oem" command mentioned above makes this reservation 315 // redundant, but we handle it here to be spec-compliant) 316 SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version."); 317 } else { 318 HandleOemCommand (Command); 319 } 320 } 321 322 STATIC 323 VOID 324 AcceptData ( 325 IN UINTN Size, 326 IN VOID *Data 327 ) 328 { 329 UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar; 330 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 331 STATIC UINTN Count = 0; 332 333 // Protocol doesn't say anything about sending extra data so just ignore it. 334 if (Size > RemainingBytes) { 335 Size = RemainingBytes; 336 } 337 338 CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size); 339 340 mBytesReceivedSoFar += Size; 341 342 // Show download progress. Don't do it for every packet as outputting text 343 // might be time consuming - do it on the last packet and on every 32nd packet 344 if ((Count++ % 32) == 0 || Size == RemainingBytes) { 345 // (Note no newline in format string - it will overwrite the line each time) 346 UnicodeSPrint ( 347 OutputString, 348 sizeof (OutputString), 349 L"\r%8d / %8d bytes downloaded (%d%%)", 350 mBytesReceivedSoFar, 351 mNumDataBytes, 352 (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage 353 ); 354 mTextOut->OutputString (mTextOut, OutputString); 355 } 356 357 if (mBytesReceivedSoFar == mNumDataBytes) { 358 // Download finished. 359 360 mTextOut->OutputString (mTextOut, L"\r\n"); 361 SEND_LITERAL ("OKAY"); 362 mState = ExpectCmdState; 363 } 364 } 365 366 /* 367 This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint 368 It will be called by the UEFI event framework when the transport protocol 369 implementation signals that data has been received from the Fastboot host. 370 The parameters are ignored. 371 */ 372 STATIC 373 VOID 374 DataReady ( 375 IN EFI_EVENT Event, 376 IN VOID *Context 377 ) 378 { 379 UINTN Size; 380 VOID *Data; 381 EFI_STATUS Status; 382 383 do { 384 Status = mTransport->Receive (&Size, &Data); 385 if (!EFI_ERROR (Status)) { 386 if (mState == ExpectCmdState) { 387 AcceptCmd (Size, (CHAR8 *) Data); 388 } else if (mState == ExpectDataState) { 389 AcceptData (Size, Data); 390 } else { 391 ASSERT (FALSE); 392 } 393 FreePool (Data); 394 } 395 } while (!EFI_ERROR (Status)); 396 397 // Quit if there was a fatal error 398 if (Status != EFI_NOT_READY) { 399 ASSERT (Status == EFI_DEVICE_ERROR); 400 // (Put a newline at the beginning as we are probably in the data phase, 401 // so the download progress line, with no '\n' is probably on the console) 402 mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n"); 403 gBS->SignalEvent (mFinishedEvent); 404 } 405 } 406 407 /* 408 Event notify for a fatal error in transmission. 409 */ 410 STATIC 411 VOID 412 FatalErrorNotify ( 413 IN EFI_EVENT Event, 414 IN VOID *Context 415 ) 416 { 417 mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n"); 418 gBS->SignalEvent (mFinishedEvent); 419 } 420 421 EFI_STATUS 422 EFIAPI 423 FastbootAppEntryPoint ( 424 IN EFI_HANDLE ImageHandle, 425 IN EFI_SYSTEM_TABLE *SystemTable 426 ) 427 { 428 EFI_STATUS Status; 429 EFI_EVENT ReceiveEvent; 430 EFI_EVENT WaitEventArray[2]; 431 UINTN EventIndex; 432 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn; 433 434 mDataBuffer = NULL; 435 436 Status = gBS->LocateProtocol ( 437 &gAndroidFastbootTransportProtocolGuid, 438 NULL, 439 (VOID **) &mTransport 440 ); 441 if (EFI_ERROR (Status)) { 442 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status)); 443 return Status; 444 } 445 446 Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform); 447 if (EFI_ERROR (Status)) { 448 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status)); 449 return Status; 450 } 451 452 Status = mPlatform->Init (); 453 if (EFI_ERROR (Status)) { 454 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status)); 455 return Status; 456 } 457 458 Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut); 459 if (EFI_ERROR (Status)) { 460 DEBUG ((EFI_D_ERROR, 461 "Fastboot: Couldn't open Text Output Protocol: %r\n", Status 462 )); 463 return Status; 464 } 465 466 Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn); 467 if (EFI_ERROR (Status)) { 468 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status)); 469 return Status; 470 } 471 472 // Disable watchdog 473 Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL); 474 if (EFI_ERROR (Status)) { 475 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status)); 476 } 477 478 // Create event for receipt of data from the host 479 Status = gBS->CreateEvent ( 480 EVT_NOTIFY_SIGNAL, 481 TPL_CALLBACK, 482 DataReady, 483 NULL, 484 &ReceiveEvent 485 ); 486 ASSERT_EFI_ERROR (Status); 487 488 // Create event for exiting application when "continue" command is received 489 Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent); 490 ASSERT_EFI_ERROR (Status); 491 492 // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a 493 // fatal error 494 Status = gBS->CreateEvent ( 495 EVT_NOTIFY_SIGNAL, 496 TPL_CALLBACK, 497 FatalErrorNotify, 498 NULL, 499 &mFatalSendErrorEvent 500 ); 501 ASSERT_EFI_ERROR (Status); 502 503 504 // Start listening for data 505 Status = mTransport->Start ( 506 ReceiveEvent 507 ); 508 if (EFI_ERROR (Status)) { 509 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status)); 510 return Status; 511 } 512 513 // Talk to the user 514 mTextOut->OutputString (mTextOut, 515 L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press any key to quit.\r\n"); 516 517 // Quit when the user presses any key, or mFinishedEvent is signalled 518 WaitEventArray[0] = mFinishedEvent; 519 WaitEventArray[1] = TextIn->WaitForKey; 520 gBS->WaitForEvent (2, WaitEventArray, &EventIndex); 521 522 mTransport->Stop (); 523 if (EFI_ERROR (Status)) { 524 DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status)); 525 } 526 mPlatform->UnInit (); 527 528 return EFI_SUCCESS; 529 } 530