TianoCore EDK2 master
Loading...
Searching...
No Matches
AndroidFastbootApp.c
Go to the documentation of this file.
1
10
15
16#include <Library/PcdLib.h>
21#include <Library/PrintLib.h>
22
23/*
24 * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and
25 * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol.
26 */
27
30
32
33typedef enum {
34 ExpectCmdState,
35 ExpectDataState,
36 FastbootStateMax
37} ANDROID_FASTBOOT_STATE;
38
39STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState;
40
41// When in ExpectDataState, the number of bytes of data to expect:
42STATIC UINT64 mNumDataBytes;
43// .. and the number of bytes so far received this data phase
44STATIC UINT64 mBytesReceivedSoFar;
45// .. and the buffer to save data into
46STATIC UINT8 *mDataBuffer = NULL;
47
48// Event notify functions, from which gBS->Exit shouldn't be called, can signal
49// this event when the application should exit
50STATIC EFI_EVENT mFinishedEvent;
51
52STATIC EFI_EVENT mFatalSendErrorEvent;
53
54// This macro uses sizeof - only use it on arrays (i.e. string literals)
55#define SEND_LITERAL(Str) mTransport->Send ( \
56 sizeof (Str) - 1, \
57 Str, \
58 &mFatalSendErrorEvent \
59 )
60#define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1)
61
62#define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z')
63
64#define FASTBOOT_STRING_MAX_LENGTH 256
65#define FASTBOOT_COMMAND_MAX_LENGTH 64
66
68VOID
69HandleGetVar (
70 IN CHAR8 *CmdArg
71 )
72{
73 CHAR8 Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY";
74 EFI_STATUS Status;
75
76 // Respond to getvar:version with 0.4 (version of Fastboot protocol)
77 if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1)) {
78 SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION);
79 } else {
80 // All other variables are assumed to be platform specific
81 Status = mPlatform->GetVar (CmdArg, Response + 4);
82 if (EFI_ERROR (Status)) {
83 SEND_LITERAL ("FAILSomething went wrong when looking up the variable");
84 } else {
85 mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent);
86 }
87 }
88}
89
91VOID
92HandleDownload (
93 IN CHAR8 *NumBytesString
94 )
95{
96 CHAR8 Response[13];
97 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
98
99 // Argument is 8-character ASCII string hex representation of number of bytes
100 // that will be sent in the data phase.
101 // Response is "DATA" + that same 8-character string.
102
103 // Replace any previously downloaded data
104 if (mDataBuffer != NULL) {
105 FreePool (mDataBuffer);
106 mDataBuffer = NULL;
107 }
108
109 // Parse out number of data bytes to expect
110 mNumDataBytes = AsciiStrHexToUint64 (NumBytesString);
111 if (mNumDataBytes == 0) {
112 mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n");
113 SEND_LITERAL ("FAILFailed to get the number of bytes to download");
114 return;
115 }
116
117 UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes);
118 mTextOut->OutputString (mTextOut, OutputString);
119
120 mDataBuffer = AllocatePool (mNumDataBytes);
121 if (mDataBuffer == NULL) {
122 SEND_LITERAL ("FAILNot enough memory");
123 } else {
124 ZeroMem (Response, sizeof Response);
126 Response,
127 sizeof Response,
128 "DATA%x",
129 (UINT32)mNumDataBytes
130 );
131 mTransport->Send (sizeof Response - 1, Response, &mFatalSendErrorEvent);
132
133 mState = ExpectDataState;
134 mBytesReceivedSoFar = 0;
135 }
136}
137
138STATIC
139VOID
140HandleFlash (
141 IN CHAR8 *PartitionName
142 )
143{
144 EFI_STATUS Status;
145 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
146
147 // Build output string
148 UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);
149 mTextOut->OutputString (mTextOut, OutputString);
150
151 if (mDataBuffer == NULL) {
152 // Doesn't look like we were sent any data
153 SEND_LITERAL ("FAILNo data to flash");
154 return;
155 }
156
157 Status = mPlatform->FlashPartition (
158 PartitionName,
159 mNumDataBytes,
160 mDataBuffer
161 );
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 ((DEBUG_ERROR, "Couldn't flash image: %r\n", Status));
169 } else {
170 mTextOut->OutputString (mTextOut, L"Done.\r\n");
171 SEND_LITERAL ("OKAY");
172 }
173}
174
175STATIC
176VOID
177HandleErase (
178 IN CHAR8 *PartitionName
179 )
180{
181 EFI_STATUS Status;
182 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
183
184 // Build output string
185 UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);
186 mTextOut->OutputString (mTextOut, OutputString);
187
188 Status = mPlatform->ErasePartition (PartitionName);
189 if (EFI_ERROR (Status)) {
190 SEND_LITERAL ("FAILCheck device console.");
191 DEBUG ((DEBUG_ERROR, "Couldn't erase image: %r\n", Status));
192 } else {
193 SEND_LITERAL ("OKAY");
194 }
195}
196
197STATIC
198VOID
199HandleBoot (
200 VOID
201 )
202{
203 EFI_STATUS Status;
204
205 mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");
206
207 if (mDataBuffer == NULL) {
208 // Doesn't look like we were sent any data
209 SEND_LITERAL ("FAILNo image in memory");
210 return;
211 }
212
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");
216
217 Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer);
218 if (EFI_ERROR (Status)) {
219 DEBUG ((DEBUG_ERROR, "Failed to boot downloaded image: %r\n", Status));
220 }
221
222 // We shouldn't get here
223}
224
225STATIC
226VOID
227HandleOemCommand (
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
245STATIC
246VOID
247AcceptCmd (
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 AsciiStrnCpyS (Command, sizeof Command, Data, Size);
262
263 // Parse command
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)) {
275 HandleBoot ();
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");
279
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.");
289 }
290
291 SEND_LITERAL ("OKAY");
292 gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
293
294 // Shouldn't get here
295 DEBUG ((DEBUG_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
296 } else if (MATCH_CMD_LITERAL ("powerdown", Command)) {
297 SEND_LITERAL ("OKAY");
298 gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);
299
300 // Shouldn't get here
301 DEBUG ((DEBUG_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));
302 } else if (MATCH_CMD_LITERAL ("oem", Command)) {
303 // The "oem" command isn't in the specification, but it was observed in the
304 // wild, followed by a space, followed by the actual command.
305 HandleOemCommand (Command + sizeof ("oem"));
306 } else if (IS_LOWERCASE_ASCII (Command[0])) {
307 // Commands starting with lowercase ASCII characters are reserved for the
308 // Fastboot protocol. If we don't recognise it, it's probably the future
309 // and there are new commands in the protocol.
310 // (By the way, the "oem" command mentioned above makes this reservation
311 // redundant, but we handle it here to be spec-compliant)
312 SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");
313 } else {
314 HandleOemCommand (Command);
315 }
316}
317
318STATIC
319VOID
320AcceptData (
321 IN UINTN Size,
322 IN VOID *Data
323 )
324{
325 UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar;
326 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];
327 STATIC UINTN Count = 0;
328
329 // Protocol doesn't say anything about sending extra data so just ignore it.
330 if (Size > RemainingBytes) {
331 Size = RemainingBytes;
332 }
333
334 CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size);
335
336 mBytesReceivedSoFar += Size;
337
338 // Show download progress. Don't do it for every packet as outputting text
339 // might be time consuming - do it on the last packet and on every 32nd packet
340 if (((Count++ % 32) == 0) || (Size == RemainingBytes)) {
341 // (Note no newline in format string - it will overwrite the line each time)
343 OutputString,
344 sizeof (OutputString),
345 L"\r%8d / %8d bytes downloaded (%d%%)",
346 mBytesReceivedSoFar,
347 mNumDataBytes,
348 (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage
349 );
350 mTextOut->OutputString (mTextOut, OutputString);
351 }
352
353 if (mBytesReceivedSoFar == mNumDataBytes) {
354 // Download finished.
355
356 mTextOut->OutputString (mTextOut, L"\r\n");
357 SEND_LITERAL ("OKAY");
358 mState = ExpectCmdState;
359 }
360}
361
362/*
363 This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint
364 It will be called by the UEFI event framework when the transport protocol
365 implementation signals that data has been received from the Fastboot host.
366 The parameters are ignored.
367*/
368STATIC
369VOID
370DataReady (
371 IN EFI_EVENT Event,
372 IN VOID *Context
373 )
374{
375 UINTN Size;
376 VOID *Data;
377 EFI_STATUS Status;
378
379 do {
380 Status = mTransport->Receive (&Size, &Data);
381 if (!EFI_ERROR (Status)) {
382 if (mState == ExpectCmdState) {
383 AcceptCmd (Size, (CHAR8 *)Data);
384 } else if (mState == ExpectDataState) {
385 AcceptData (Size, Data);
386 } else {
387 ASSERT (FALSE);
388 }
389
390 FreePool (Data);
391 }
392 } while (!EFI_ERROR (Status));
393
394 // Quit if there was a fatal error
395 if (Status != EFI_NOT_READY) {
396 ASSERT (Status == EFI_DEVICE_ERROR);
397 // (Put a newline at the beginning as we are probably in the data phase,
398 // so the download progress line, with no '\n' is probably on the console)
399 mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");
400 gBS->SignalEvent (mFinishedEvent);
401 }
402}
403
404/*
405 Event notify for a fatal error in transmission.
406*/
407STATIC
408VOID
409FatalErrorNotify (
410 IN EFI_EVENT Event,
411 IN VOID *Context
412 )
413{
414 mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");
415 gBS->SignalEvent (mFinishedEvent);
416}
417
419EFIAPI
420FastbootAppEntryPoint (
421 IN EFI_HANDLE ImageHandle,
422 IN EFI_SYSTEM_TABLE *SystemTable
423 )
424{
425 EFI_STATUS Status;
426 EFI_EVENT ReceiveEvent;
427 EFI_EVENT WaitEventArray[2];
428 UINTN EventIndex;
430 EFI_INPUT_KEY Key;
431
432 mDataBuffer = NULL;
433
434 Status = gBS->LocateProtocol (
435 &gAndroidFastbootTransportProtocolGuid,
436 NULL,
437 (VOID **)&mTransport
438 );
439 if (EFI_ERROR (Status)) {
440 DEBUG ((DEBUG_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));
441 return Status;
442 }
443
444 Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **)&mPlatform);
445 if (EFI_ERROR (Status)) {
446 DEBUG ((DEBUG_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));
447 return Status;
448 }
449
450 Status = mPlatform->Init ();
451 if (EFI_ERROR (Status)) {
452 DEBUG ((DEBUG_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));
453 return Status;
454 }
455
456 Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **)&mTextOut);
457 if (EFI_ERROR (Status)) {
458 DEBUG ((
459 DEBUG_ERROR,
460 "Fastboot: Couldn't open Text Output Protocol: %r\n",
461 Status
462 ));
463 return Status;
464 }
465
466 Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **)&TextIn);
467 if (EFI_ERROR (Status)) {
468 DEBUG ((DEBUG_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 ((DEBUG_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 // Start listening for data
504 Status = mTransport->Start (
505 ReceiveEvent
506 );
507 if (EFI_ERROR (Status)) {
508 DEBUG ((DEBUG_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));
509 return Status;
510 }
511
512 // Talk to the user
513 mTextOut->OutputString (
514 mTextOut,
515 L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press RETURN or SPACE key to quit.\r\n"
516 );
517
518 // Quit when the user presses any key, or mFinishedEvent is signalled
519 WaitEventArray[0] = mFinishedEvent;
520 WaitEventArray[1] = TextIn->WaitForKey;
521 while (1) {
522 gBS->WaitForEvent (2, WaitEventArray, &EventIndex);
523 Status = TextIn->ReadKeyStroke (gST->ConIn, &Key);
524 if (Key.ScanCode == SCAN_NULL) {
525 if ((Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||
526 (Key.UnicodeChar == L' '))
527 {
528 break;
529 }
530 }
531 }
532
533 mTransport->Stop ();
534 if (EFI_ERROR (Status)) {
535 DEBUG ((DEBUG_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));
536 }
537
538 mPlatform->UnInit ();
539
540 return EFI_SUCCESS;
541}
UINT64 UINTN
RETURN_STATUS EFIAPI AsciiStrnCpyS(OUT CHAR8 *Destination, IN UINTN DestMax, IN CONST CHAR8 *Source, IN UINTN Length)
Definition: SafeString.c:1875
UINTN EFIAPI AsciiStrLen(IN CONST CHAR8 *String)
Definition: String.c:641
INTN EFIAPI AsciiStrnCmp(IN CONST CHAR8 *FirstString, IN CONST CHAR8 *SecondString, IN UINTN Length)
Definition: String.c:872
UINT64 EFIAPI AsciiStrHexToUint64(IN CONST CHAR8 *String)
Definition: String.c:1155
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
VOID EFIAPI FreePool(IN VOID *Buffer)
UINTN EFIAPI UnicodeSPrint(OUT CHAR16 *StartOfBuffer, IN UINTN BufferSize, IN CONST CHAR16 *FormatString,...)
Definition: PrintLib.c:408
UINTN EFIAPI AsciiSPrint(OUT CHAR8 *StartOfBuffer, IN UINTN BufferSize, IN CONST CHAR8 *FormatString,...)
Definition: PrintLib.c:813
EFI_RUNTIME_SERVICES * gRT
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define STATIC
Definition: Base.h:264
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_SYSTEM_TABLE * gST
EFI_BOOT_SERVICES * gBS
@ EfiResetCold
@ EfiResetShutdown
EFI_SIMPLE_TEXT_INPUT_PROTOCOL * ConIn
Definition: UefiSpec.h:2053