3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
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
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.
15 #include "AndroidFastbootApp.h"
17 #include <Protocol/AndroidFastbootTransport.h>
18 #include <Protocol/AndroidFastbootPlatform.h>
19 #include <Protocol/SimpleTextOut.h>
20 #include <Protocol/SimpleTextIn.h>
22 #include <Library/PcdLib.h>
23 #include <Library/UefiRuntimeServicesTableLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/UefiBootServicesTableLib.h>
26 #include <Library/UefiApplicationEntryPoint.h>
27 #include <Library/PrintLib.h>
30 * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and
31 * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol.
34 STATIC FASTBOOT_TRANSPORT_PROTOCOL
*mTransport
;
35 STATIC FASTBOOT_PLATFORM_PROTOCOL
*mPlatform
;
37 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*mTextOut
;
43 } ANDROID_FASTBOOT_STATE
;
45 STATIC ANDROID_FASTBOOT_STATE mState
= ExpectCmdState
;
47 // When in ExpectDataState, the number of bytes of data to expect:
48 STATIC UINT64 mNumDataBytes
;
49 // .. and the number of bytes so far received this data phase
50 STATIC UINT64 mBytesReceivedSoFar
;
51 // .. and the buffer to save data into
52 STATIC UINT8
*mDataBuffer
= NULL
;
54 // Event notify functions, from which gBS->Exit shouldn't be called, can signal
55 // this event when the application should exit
56 STATIC EFI_EVENT mFinishedEvent
;
58 STATIC EFI_EVENT mFatalSendErrorEvent
;
60 // This macro uses sizeof - only use it on arrays (i.e. string literals)
61 #define SEND_LITERAL(Str) mTransport->Send ( \
64 &mFatalSendErrorEvent \
66 #define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)
68 #define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z')
70 #define FASTBOOT_STRING_MAX_LENGTH 256
71 #define FASTBOOT_COMMAND_MAX_LENGTH 64
79 CHAR8 Response
[FASTBOOT_COMMAND_MAX_LENGTH
+ 1] = "OKAY";
82 // Respond to getvar:version with 0.4 (version of Fastboot protocol)
83 if (!AsciiStrnCmp ("version", CmdArg
, sizeof ("version") - 1 )) {
84 SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION
);
86 // All other variables are assumed to be platform specific
87 Status
= mPlatform
->GetVar (CmdArg
, Response
+ 4);
88 if (EFI_ERROR (Status
)) {
89 SEND_LITERAL ("FAILSomething went wrong when looking up the variable");
91 mTransport
->Send (AsciiStrLen (Response
), Response
, &mFatalSendErrorEvent
);
99 IN CHAR8
*NumBytesString
102 CHAR8 Response
[12] = "DATA";
103 CHAR16 OutputString
[FASTBOOT_STRING_MAX_LENGTH
];
105 // Argument is 8-character ASCII string hex representation of number of bytes
106 // that will be sent in the data phase.
107 // Response is "DATA" + that same 8-character string.
109 // Replace any previously downloaded data
110 if (mDataBuffer
!= NULL
) {
111 FreePool (mDataBuffer
);
115 // Parse out number of data bytes to expect
116 mNumDataBytes
= AsciiStrHexToUint64 (NumBytesString
);
117 if (mNumDataBytes
== 0) {
118 mTextOut
->OutputString (mTextOut
, L
"ERROR: Fail to get the number of bytes to download.\r\n");
119 SEND_LITERAL ("FAILFailed to get the number of bytes to download");
123 UnicodeSPrint (OutputString
, sizeof (OutputString
), L
"Downloading %d bytes\r\n", mNumDataBytes
);
124 mTextOut
->OutputString (mTextOut
, OutputString
);
126 mDataBuffer
= AllocatePool (mNumDataBytes
);
127 if (mDataBuffer
== NULL
) {
128 SEND_LITERAL ("FAILNot enough memory");
130 AsciiStrnCpy (Response
+ 4, NumBytesString
, 8);
131 mTransport
->Send (sizeof(Response
), Response
, &mFatalSendErrorEvent
);
133 mState
= ExpectDataState
;
134 mBytesReceivedSoFar
= 0;
141 IN CHAR8
*PartitionName
145 CHAR16 OutputString
[FASTBOOT_STRING_MAX_LENGTH
];
147 // Build output string
148 UnicodeSPrint (OutputString
, sizeof (OutputString
), L
"Flashing partition %a\r\n", PartitionName
);
149 mTextOut
->OutputString (mTextOut
, OutputString
);
151 if (mDataBuffer
== NULL
) {
152 // Doesn't look like we were sent any data
153 SEND_LITERAL ("FAILNo data to flash");
157 Status
= mPlatform
->FlashPartition (
162 if (Status
== EFI_NOT_FOUND
) {
163 SEND_LITERAL ("FAILNo such partition.");
164 mTextOut
->OutputString (mTextOut
, L
"No such partition.\r\n");
165 } else if (EFI_ERROR (Status
)) {
166 SEND_LITERAL ("FAILError flashing partition.");
167 mTextOut
->OutputString (mTextOut
, L
"Error flashing partition.\r\n");
168 DEBUG ((EFI_D_ERROR
, "Couldn't flash image: %r\n", Status
));
170 mTextOut
->OutputString (mTextOut
, L
"Done.\r\n");
171 SEND_LITERAL ("OKAY");
178 IN CHAR8
*PartitionName
182 CHAR16 OutputString
[FASTBOOT_STRING_MAX_LENGTH
];
184 // Build output string
185 UnicodeSPrint (OutputString
, sizeof (OutputString
), L
"Erasing partition %a\r\n", PartitionName
);
186 mTextOut
->OutputString (mTextOut
, OutputString
);
188 Status
= mPlatform
->ErasePartition (PartitionName
);
189 if (EFI_ERROR (Status
)) {
190 SEND_LITERAL ("FAILCheck device console.");
191 DEBUG ((EFI_D_ERROR
, "Couldn't erase image: %r\n", Status
));
193 SEND_LITERAL ("OKAY");
205 mTextOut
->OutputString (mTextOut
, L
"Booting downloaded image\r\n");
207 if (mDataBuffer
== NULL
) {
208 // Doesn't look like we were sent any data
209 SEND_LITERAL ("FAILNo image in memory");
213 // We don't really have any choice but to report success, because once we
214 // boot we lose control of the system.
215 SEND_LITERAL ("OKAY");
217 Status
= BootAndroidBootImg (mNumDataBytes
, mDataBuffer
);
218 if (EFI_ERROR (Status
)) {
219 DEBUG ((EFI_D_ERROR
, "Failed to boot downloaded image: %r\n", Status
));
221 // We shouldn't get here
232 Status
= mPlatform
->DoOemCommand (Command
);
233 if (Status
== EFI_NOT_FOUND
) {
234 SEND_LITERAL ("FAILOEM Command not recognised.");
235 } else if (Status
== EFI_DEVICE_ERROR
) {
236 SEND_LITERAL ("FAILError while executing command");
237 } else if (EFI_ERROR (Status
)) {
238 SEND_LITERAL ("FAIL");
240 SEND_LITERAL ("OKAY");
251 CHAR8 Command
[FASTBOOT_COMMAND_MAX_LENGTH
+ 1];
253 // Max command size is 64 bytes
254 if (Size
> FASTBOOT_COMMAND_MAX_LENGTH
) {
255 SEND_LITERAL ("FAILCommand too large");
259 // Commands aren't null-terminated. Let's get a null-terminated version.
260 AsciiStrnCpy (Command
, Data
, Size
);
261 Command
[Size
] = '\0';
264 if (MATCH_CMD_LITERAL ("getvar", Command
)) {
265 HandleGetVar (Command
+ sizeof ("getvar"));
266 } else if (MATCH_CMD_LITERAL ("download", Command
)) {
267 HandleDownload (Command
+ sizeof ("download"));
268 } else if (MATCH_CMD_LITERAL ("verify", Command
)) {
269 SEND_LITERAL ("FAILNot supported");
270 } else if (MATCH_CMD_LITERAL ("flash", Command
)) {
271 HandleFlash (Command
+ sizeof ("flash"));
272 } else if (MATCH_CMD_LITERAL ("erase", Command
)) {
273 HandleErase (Command
+ sizeof ("erase"));
274 } else if (MATCH_CMD_LITERAL ("boot", Command
)) {
276 } else if (MATCH_CMD_LITERAL ("continue", Command
)) {
277 SEND_LITERAL ("OKAY");
278 mTextOut
->OutputString (mTextOut
, L
"Received 'continue' command. Exiting Fastboot mode\r\n");
280 gBS
->SignalEvent (mFinishedEvent
);
281 } else if (MATCH_CMD_LITERAL ("reboot", Command
)) {
282 if (MATCH_CMD_LITERAL ("reboot-booloader", Command
)) {
283 // fastboot_protocol.txt:
284 // "reboot-bootloader Reboot back into the bootloader."
285 // I guess this means reboot back into fastboot mode to save the user
286 // having to do whatever they did to get here again.
287 // Here we just reboot normally.
288 SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally.");
290 SEND_LITERAL ("OKAY");
291 gRT
->ResetSystem (EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
293 // Shouldn't get here
294 DEBUG ((EFI_D_ERROR
, "Fastboot: gRT->ResetSystem didn't work\n"));
295 } else if (MATCH_CMD_LITERAL ("powerdown", Command
)) {
296 SEND_LITERAL ("OKAY");
297 gRT
->ResetSystem (EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
299 // Shouldn't get here
300 DEBUG ((EFI_D_ERROR
, "Fastboot: gRT->ResetSystem didn't work\n"));
301 } else if (MATCH_CMD_LITERAL ("oem", Command
)) {
302 // The "oem" command isn't in the specification, but it was observed in the
303 // wild, followed by a space, followed by the actual command.
304 HandleOemCommand (Command
+ sizeof ("oem"));
305 } else if (IS_LOWERCASE_ASCII (Command
[0])) {
306 // Commands starting with lowercase ASCII characters are reserved for the
307 // Fastboot protocol. If we don't recognise it, it's probably the future
308 // and there are new commmands in the protocol.
309 // (By the way, the "oem" command mentioned above makes this reservation
310 // redundant, but we handle it here to be spec-compliant)
311 SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");
313 HandleOemCommand (Command
);
324 UINT32 RemainingBytes
= mNumDataBytes
- mBytesReceivedSoFar
;
325 CHAR16 OutputString
[FASTBOOT_STRING_MAX_LENGTH
];
326 STATIC UINTN Count
= 0;
328 // Protocol doesn't say anything about sending extra data so just ignore it.
329 if (Size
> RemainingBytes
) {
330 Size
= RemainingBytes
;
333 CopyMem (&mDataBuffer
[mBytesReceivedSoFar
], Data
, Size
);
335 mBytesReceivedSoFar
+= Size
;
337 // Show download progress. Don't do it for every packet as outputting text
338 // might be time consuming - do it on the last packet and on every 32nd packet
339 if ((Count
++ % 32) == 0 || Size
== RemainingBytes
) {
340 // (Note no newline in format string - it will overwrite the line each time)
343 sizeof (OutputString
),
344 L
"\r%8d / %8d bytes downloaded (%d%%)",
347 (mBytesReceivedSoFar
* 100) / mNumDataBytes
// percentage
349 mTextOut
->OutputString (mTextOut
, OutputString
);
352 if (mBytesReceivedSoFar
== mNumDataBytes
) {
353 // Download finished.
355 mTextOut
->OutputString (mTextOut
, L
"\r\n");
356 SEND_LITERAL ("OKAY");
357 mState
= ExpectCmdState
;
362 This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint
363 It will be called by the UEFI event framework when the transport protocol
364 implementation signals that data has been received from the Fastboot host.
365 The parameters are ignored.
379 Status
= mTransport
->Receive (&Size
, &Data
);
380 if (!EFI_ERROR (Status
)) {
381 if (mState
== ExpectCmdState
) {
382 AcceptCmd (Size
, (CHAR8
*) Data
);
383 } else if (mState
== ExpectDataState
) {
384 AcceptData (Size
, Data
);
390 } while (!EFI_ERROR (Status
));
392 // Quit if there was a fatal error
393 if (Status
!= EFI_NOT_READY
) {
394 ASSERT (Status
== EFI_DEVICE_ERROR
);
395 // (Put a newline at the beginning as we are probably in the data phase,
396 // so the download progress line, with no '\n' is probably on the console)
397 mTextOut
->OutputString (mTextOut
, L
"\r\nFatal error receiving data. Exiting.\r\n");
398 gBS
->SignalEvent (mFinishedEvent
);
403 Event notify for a fatal error in transmission.
412 mTextOut
->OutputString (mTextOut
, L
"Fatal error sending command response. Exiting.\r\n");
413 gBS
->SignalEvent (mFinishedEvent
);
418 FastbootAppEntryPoint (
419 IN EFI_HANDLE ImageHandle
,
420 IN EFI_SYSTEM_TABLE
*SystemTable
424 EFI_EVENT ReceiveEvent
;
425 EFI_EVENT WaitEventArray
[2];
427 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*TextIn
;
431 Status
= gBS
->LocateProtocol (
432 &gAndroidFastbootTransportProtocolGuid
,
434 (VOID
**) &mTransport
436 if (EFI_ERROR (Status
)) {
437 DEBUG ((EFI_D_ERROR
, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status
));
441 Status
= gBS
->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid
, NULL
, (VOID
**) &mPlatform
);
442 if (EFI_ERROR (Status
)) {
443 DEBUG ((EFI_D_ERROR
, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status
));
447 Status
= mPlatform
->Init ();
448 if (EFI_ERROR (Status
)) {
449 DEBUG ((EFI_D_ERROR
, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status
));
453 Status
= gBS
->LocateProtocol (&gEfiSimpleTextOutProtocolGuid
, NULL
, (VOID
**) &mTextOut
);
454 if (EFI_ERROR (Status
)) {
456 "Fastboot: Couldn't open Text Output Protocol: %r\n", Status
461 Status
= gBS
->LocateProtocol (&gEfiSimpleTextInProtocolGuid
, NULL
, (VOID
**) &TextIn
);
462 if (EFI_ERROR (Status
)) {
463 DEBUG ((EFI_D_ERROR
, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status
));
468 Status
= gBS
->SetWatchdogTimer (0, 0x10000, 0, NULL
);
469 if (EFI_ERROR (Status
)) {
470 DEBUG ((EFI_D_ERROR
, "Fastboot: Couldn't disable watchdog timer: %r\n", Status
));
473 // Create event for receipt of data from the host
474 Status
= gBS
->CreateEvent (
481 ASSERT_EFI_ERROR (Status
);
483 // Create event for exiting application when "continue" command is received
484 Status
= gBS
->CreateEvent (0, TPL_CALLBACK
, NULL
, NULL
, &mFinishedEvent
);
485 ASSERT_EFI_ERROR (Status
);
487 // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a
489 Status
= gBS
->CreateEvent (
494 &mFatalSendErrorEvent
496 ASSERT_EFI_ERROR (Status
);
499 // Start listening for data
500 Status
= mTransport
->Start (
503 if (EFI_ERROR (Status
)) {
504 DEBUG ((EFI_D_ERROR
, "Fastboot: Couldn't start transport: %r\n", Status
));
509 mTextOut
->OutputString (mTextOut
,
510 L
"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION
". Press any key to quit.\r\n");
512 // Quit when the user presses any key, or mFinishedEvent is signalled
513 WaitEventArray
[0] = mFinishedEvent
;
514 WaitEventArray
[1] = TextIn
->WaitForKey
;
515 gBS
->WaitForEvent (2, WaitEventArray
, &EventIndex
);
518 if (EFI_ERROR (Status
)) {
519 DEBUG ((EFI_D_ERROR
, "Warning: Fastboot Transport Stop: %r\n", Status
));
521 mPlatform
->UnInit ();