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