]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp.c
EmbeddedPkg: Fix various typos
[mirror_edk2.git] / EmbeddedPkg / Application / AndroidFastboot / AndroidFastbootApp.c
CommitLineData
f6755908
OM
1/** @file\r
2\r
3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
4\r
878b807a 5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
f6755908
OM
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
cb1d0668 42STATIC UINT64 mNumDataBytes;\r
f6755908 43// .. and the number of bytes so far received this data phase\r
cb1d0668 44STATIC UINT64 mBytesReceivedSoFar;\r
f6755908
OM
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
31090876 96 CHAR8 Response[13];\r
f6755908
OM
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
31090876
AB
124 ZeroMem (Response, sizeof Response);\r
125 AsciiSPrint (Response, sizeof Response, "DATA%x",\r
126 (UINT32)mNumDataBytes);\r
127 mTransport->Send (sizeof Response - 1, Response, &mFatalSendErrorEvent);\r
f6755908
OM
128\r
129 mState = ExpectDataState;\r
130 mBytesReceivedSoFar = 0;\r
131 }\r
132}\r
133\r
134STATIC\r
135VOID\r
136HandleFlash (\r
137 IN CHAR8 *PartitionName\r
138 )\r
139{\r
140 EFI_STATUS Status;\r
141 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
142\r
143 // Build output string\r
144 UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);\r
145 mTextOut->OutputString (mTextOut, OutputString);\r
146\r
147 if (mDataBuffer == NULL) {\r
148 // Doesn't look like we were sent any data\r
149 SEND_LITERAL ("FAILNo data to flash");\r
150 return;\r
151 }\r
152\r
153 Status = mPlatform->FlashPartition (\r
154 PartitionName,\r
155 mNumDataBytes,\r
156 mDataBuffer\r
157 );\r
158 if (Status == EFI_NOT_FOUND) {\r
159 SEND_LITERAL ("FAILNo such partition.");\r
160 mTextOut->OutputString (mTextOut, L"No such partition.\r\n");\r
161 } else if (EFI_ERROR (Status)) {\r
162 SEND_LITERAL ("FAILError flashing partition.");\r
163 mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n");\r
164 DEBUG ((EFI_D_ERROR, "Couldn't flash image: %r\n", Status));\r
165 } else {\r
166 mTextOut->OutputString (mTextOut, L"Done.\r\n");\r
167 SEND_LITERAL ("OKAY");\r
168 }\r
169}\r
170\r
171STATIC\r
172VOID\r
173HandleErase (\r
174 IN CHAR8 *PartitionName\r
175 )\r
176{\r
177 EFI_STATUS Status;\r
178 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
179\r
180 // Build output string\r
181 UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);\r
182 mTextOut->OutputString (mTextOut, OutputString);\r
183\r
184 Status = mPlatform->ErasePartition (PartitionName);\r
185 if (EFI_ERROR (Status)) {\r
186 SEND_LITERAL ("FAILCheck device console.");\r
187 DEBUG ((EFI_D_ERROR, "Couldn't erase image: %r\n", Status));\r
188 } else {\r
189 SEND_LITERAL ("OKAY");\r
190 }\r
191}\r
192\r
193STATIC\r
194VOID\r
195HandleBoot (\r
196 VOID\r
197 )\r
198{\r
199 EFI_STATUS Status;\r
200\r
201 mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");\r
202\r
203 if (mDataBuffer == NULL) {\r
204 // Doesn't look like we were sent any data\r
205 SEND_LITERAL ("FAILNo image in memory");\r
206 return;\r
207 }\r
208\r
209 // We don't really have any choice but to report success, because once we\r
210 // boot we lose control of the system.\r
211 SEND_LITERAL ("OKAY");\r
212\r
213 Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer);\r
214 if (EFI_ERROR (Status)) {\r
215 DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status));\r
216 }\r
217 // We shouldn't get here\r
218}\r
219\r
220STATIC\r
221VOID\r
222HandleOemCommand (\r
223 IN CHAR8 *Command\r
224 )\r
225{\r
226 EFI_STATUS Status;\r
227\r
228 Status = mPlatform->DoOemCommand (Command);\r
229 if (Status == EFI_NOT_FOUND) {\r
230 SEND_LITERAL ("FAILOEM Command not recognised.");\r
231 } else if (Status == EFI_DEVICE_ERROR) {\r
232 SEND_LITERAL ("FAILError while executing command");\r
233 } else if (EFI_ERROR (Status)) {\r
234 SEND_LITERAL ("FAIL");\r
235 } else {\r
236 SEND_LITERAL ("OKAY");\r
237 }\r
238}\r
239\r
240STATIC\r
241VOID\r
242AcceptCmd (\r
243 IN UINTN Size,\r
244 IN CONST CHAR8 *Data\r
245 )\r
246{\r
247 CHAR8 Command[FASTBOOT_COMMAND_MAX_LENGTH + 1];\r
248\r
249 // Max command size is 64 bytes\r
250 if (Size > FASTBOOT_COMMAND_MAX_LENGTH) {\r
251 SEND_LITERAL ("FAILCommand too large");\r
252 return;\r
253 }\r
254\r
255 // Commands aren't null-terminated. Let's get a null-terminated version.\r
31090876 256 AsciiStrnCpyS (Command, sizeof Command, Data, Size);\r
f6755908
OM
257\r
258 // Parse command\r
259 if (MATCH_CMD_LITERAL ("getvar", Command)) {\r
260 HandleGetVar (Command + sizeof ("getvar"));\r
261 } else if (MATCH_CMD_LITERAL ("download", Command)) {\r
262 HandleDownload (Command + sizeof ("download"));\r
263 } else if (MATCH_CMD_LITERAL ("verify", Command)) {\r
264 SEND_LITERAL ("FAILNot supported");\r
265 } else if (MATCH_CMD_LITERAL ("flash", Command)) {\r
266 HandleFlash (Command + sizeof ("flash"));\r
267 } else if (MATCH_CMD_LITERAL ("erase", Command)) {\r
268 HandleErase (Command + sizeof ("erase"));\r
269 } else if (MATCH_CMD_LITERAL ("boot", Command)) {\r
270 HandleBoot ();\r
271 } else if (MATCH_CMD_LITERAL ("continue", Command)) {\r
272 SEND_LITERAL ("OKAY");\r
273 mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n");\r
274\r
275 gBS->SignalEvent (mFinishedEvent);\r
276 } else if (MATCH_CMD_LITERAL ("reboot", Command)) {\r
277 if (MATCH_CMD_LITERAL ("reboot-booloader", Command)) {\r
278 // fastboot_protocol.txt:\r
279 // "reboot-bootloader Reboot back into the bootloader."\r
280 // I guess this means reboot back into fastboot mode to save the user\r
281 // having to do whatever they did to get here again.\r
282 // Here we just reboot normally.\r
283 SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally.");\r
284 }\r
285 SEND_LITERAL ("OKAY");\r
286 gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);\r
287\r
288 // Shouldn't get here\r
289 DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));\r
290 } else if (MATCH_CMD_LITERAL ("powerdown", Command)) {\r
291 SEND_LITERAL ("OKAY");\r
292 gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL);\r
293\r
294 // Shouldn't get here\r
295 DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n"));\r
296 } else if (MATCH_CMD_LITERAL ("oem", Command)) {\r
297 // The "oem" command isn't in the specification, but it was observed in the\r
298 // wild, followed by a space, followed by the actual command.\r
299 HandleOemCommand (Command + sizeof ("oem"));\r
300 } else if (IS_LOWERCASE_ASCII (Command[0])) {\r
301 // Commands starting with lowercase ASCII characters are reserved for the\r
302 // Fastboot protocol. If we don't recognise it, it's probably the future\r
c6a72cd7 303 // and there are new commands in the protocol.\r
f6755908
OM
304 // (By the way, the "oem" command mentioned above makes this reservation\r
305 // redundant, but we handle it here to be spec-compliant)\r
306 SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version.");\r
307 } else {\r
308 HandleOemCommand (Command);\r
309 }\r
310}\r
311\r
312STATIC\r
313VOID\r
314AcceptData (\r
315 IN UINTN Size,\r
316 IN VOID *Data\r
317 )\r
318{\r
319 UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar;\r
320 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
321 STATIC UINTN Count = 0;\r
322\r
323 // Protocol doesn't say anything about sending extra data so just ignore it.\r
324 if (Size > RemainingBytes) {\r
325 Size = RemainingBytes;\r
326 }\r
327\r
328 CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size);\r
329\r
330 mBytesReceivedSoFar += Size;\r
331\r
332 // Show download progress. Don't do it for every packet as outputting text\r
333 // might be time consuming - do it on the last packet and on every 32nd packet\r
334 if ((Count++ % 32) == 0 || Size == RemainingBytes) {\r
335 // (Note no newline in format string - it will overwrite the line each time)\r
336 UnicodeSPrint (\r
337 OutputString,\r
338 sizeof (OutputString),\r
339 L"\r%8d / %8d bytes downloaded (%d%%)",\r
340 mBytesReceivedSoFar,\r
341 mNumDataBytes,\r
342 (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage\r
343 );\r
344 mTextOut->OutputString (mTextOut, OutputString);\r
345 }\r
346\r
347 if (mBytesReceivedSoFar == mNumDataBytes) {\r
348 // Download finished.\r
349\r
350 mTextOut->OutputString (mTextOut, L"\r\n");\r
351 SEND_LITERAL ("OKAY");\r
352 mState = ExpectCmdState;\r
353 }\r
354}\r
355\r
356/*\r
357 This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint\r
358 It will be called by the UEFI event framework when the transport protocol\r
359 implementation signals that data has been received from the Fastboot host.\r
360 The parameters are ignored.\r
361*/\r
362STATIC\r
363VOID\r
364DataReady (\r
365 IN EFI_EVENT Event,\r
366 IN VOID *Context\r
367 )\r
368{\r
369 UINTN Size;\r
370 VOID *Data;\r
371 EFI_STATUS Status;\r
372\r
373 do {\r
374 Status = mTransport->Receive (&Size, &Data);\r
375 if (!EFI_ERROR (Status)) {\r
376 if (mState == ExpectCmdState) {\r
377 AcceptCmd (Size, (CHAR8 *) Data);\r
378 } else if (mState == ExpectDataState) {\r
379 AcceptData (Size, Data);\r
380 } else {\r
381 ASSERT (FALSE);\r
382 }\r
383 FreePool (Data);\r
384 }\r
385 } while (!EFI_ERROR (Status));\r
386\r
387 // Quit if there was a fatal error\r
388 if (Status != EFI_NOT_READY) {\r
389 ASSERT (Status == EFI_DEVICE_ERROR);\r
390 // (Put a newline at the beginning as we are probably in the data phase,\r
391 // so the download progress line, with no '\n' is probably on the console)\r
392 mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");\r
393 gBS->SignalEvent (mFinishedEvent);\r
394 }\r
395}\r
396\r
397/*\r
398 Event notify for a fatal error in transmission.\r
399*/\r
400STATIC\r
401VOID\r
402FatalErrorNotify (\r
403 IN EFI_EVENT Event,\r
404 IN VOID *Context\r
405 )\r
406{\r
407 mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");\r
408 gBS->SignalEvent (mFinishedEvent);\r
409}\r
410\r
411EFI_STATUS\r
412EFIAPI\r
413FastbootAppEntryPoint (\r
414 IN EFI_HANDLE ImageHandle,\r
415 IN EFI_SYSTEM_TABLE *SystemTable\r
416 )\r
417{\r
418 EFI_STATUS Status;\r
419 EFI_EVENT ReceiveEvent;\r
420 EFI_EVENT WaitEventArray[2];\r
421 UINTN EventIndex;\r
422 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;\r
68617659 423 EFI_INPUT_KEY Key;\r
f6755908
OM
424\r
425 mDataBuffer = NULL;\r
426\r
427 Status = gBS->LocateProtocol (\r
428 &gAndroidFastbootTransportProtocolGuid,\r
429 NULL,\r
430 (VOID **) &mTransport\r
431 );\r
432 if (EFI_ERROR (Status)) {\r
433 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));\r
434 return Status;\r
435 }\r
436\r
437 Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform);\r
438 if (EFI_ERROR (Status)) {\r
439 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));\r
440 return Status;\r
441 }\r
442\r
443 Status = mPlatform->Init ();\r
444 if (EFI_ERROR (Status)) {\r
445 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));\r
446 return Status;\r
447 }\r
448\r
449 Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);\r
450 if (EFI_ERROR (Status)) {\r
451 DEBUG ((EFI_D_ERROR,\r
452 "Fastboot: Couldn't open Text Output Protocol: %r\n", Status\r
453 ));\r
454 return Status;\r
455 }\r
456\r
457 Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn);\r
458 if (EFI_ERROR (Status)) {\r
459 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status));\r
460 return Status;\r
461 }\r
462\r
463 // Disable watchdog\r
464 Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);\r
465 if (EFI_ERROR (Status)) {\r
466 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status));\r
467 }\r
468\r
469 // Create event for receipt of data from the host\r
470 Status = gBS->CreateEvent (\r
471 EVT_NOTIFY_SIGNAL,\r
472 TPL_CALLBACK,\r
473 DataReady,\r
474 NULL,\r
475 &ReceiveEvent\r
476 );\r
477 ASSERT_EFI_ERROR (Status);\r
478\r
479 // Create event for exiting application when "continue" command is received\r
480 Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent);\r
481 ASSERT_EFI_ERROR (Status);\r
482\r
483 // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a\r
484 // fatal error\r
485 Status = gBS->CreateEvent (\r
486 EVT_NOTIFY_SIGNAL,\r
487 TPL_CALLBACK,\r
488 FatalErrorNotify,\r
489 NULL,\r
490 &mFatalSendErrorEvent\r
491 );\r
492 ASSERT_EFI_ERROR (Status);\r
493\r
494\r
495 // Start listening for data\r
496 Status = mTransport->Start (\r
497 ReceiveEvent\r
498 );\r
499 if (EFI_ERROR (Status)) {\r
500 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));\r
501 return Status;\r
502 }\r
503\r
504 // Talk to the user\r
505 mTextOut->OutputString (mTextOut,\r
68617659 506 L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press RETURN or SPACE key to quit.\r\n");\r
f6755908
OM
507\r
508 // Quit when the user presses any key, or mFinishedEvent is signalled\r
509 WaitEventArray[0] = mFinishedEvent;\r
510 WaitEventArray[1] = TextIn->WaitForKey;\r
68617659
HZ
511 while (1) {\r
512 gBS->WaitForEvent (2, WaitEventArray, &EventIndex);\r
513 Status = TextIn->ReadKeyStroke (gST->ConIn, &Key);\r
514 if (Key.ScanCode == SCAN_NULL) {\r
515 if ((Key.UnicodeChar == CHAR_CARRIAGE_RETURN) ||\r
516 (Key.UnicodeChar == L' ')) {\r
517 break;\r
518 }\r
519 }\r
520 }\r
f6755908
OM
521\r
522 mTransport->Stop ();\r
523 if (EFI_ERROR (Status)) {\r
524 DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));\r
525 }\r
526 mPlatform->UnInit ();\r
527\r
528 return EFI_SUCCESS;\r
529}\r