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