EmbeddedPkg/AndroidFastboot: eliminate deprecated string function calls
[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
cb1d0668 48STATIC UINT64 mNumDataBytes;\r
f6755908 49// .. and the number of bytes so far received this data phase\r
cb1d0668 50STATIC UINT64 mBytesReceivedSoFar;\r
f6755908
OM
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
31090876 102 CHAR8 Response[13];\r
f6755908
OM
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
31090876
AB
130 ZeroMem (Response, sizeof Response);\r
131 AsciiSPrint (Response, sizeof Response, "DATA%x",\r
132 (UINT32)mNumDataBytes);\r
133 mTransport->Send (sizeof Response - 1, Response, &mFatalSendErrorEvent);\r
f6755908
OM
134\r
135 mState = ExpectDataState;\r
136 mBytesReceivedSoFar = 0;\r
137 }\r
138}\r
139\r
140STATIC\r
141VOID\r
142HandleFlash (\r
143 IN CHAR8 *PartitionName\r
144 )\r
145{\r
146 EFI_STATUS Status;\r
147 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
148\r
149 // Build output string\r
150 UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName);\r
151 mTextOut->OutputString (mTextOut, OutputString);\r
152\r
153 if (mDataBuffer == NULL) {\r
154 // Doesn't look like we were sent any data\r
155 SEND_LITERAL ("FAILNo data to flash");\r
156 return;\r
157 }\r
158\r
159 Status = mPlatform->FlashPartition (\r
160 PartitionName,\r
161 mNumDataBytes,\r
162 mDataBuffer\r
163 );\r
164 if (Status == EFI_NOT_FOUND) {\r
165 SEND_LITERAL ("FAILNo such partition.");\r
166 mTextOut->OutputString (mTextOut, L"No such partition.\r\n");\r
167 } else if (EFI_ERROR (Status)) {\r
168 SEND_LITERAL ("FAILError flashing partition.");\r
169 mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n");\r
170 DEBUG ((EFI_D_ERROR, "Couldn't flash image: %r\n", Status));\r
171 } else {\r
172 mTextOut->OutputString (mTextOut, L"Done.\r\n");\r
173 SEND_LITERAL ("OKAY");\r
174 }\r
175}\r
176\r
177STATIC\r
178VOID\r
179HandleErase (\r
180 IN CHAR8 *PartitionName\r
181 )\r
182{\r
183 EFI_STATUS Status;\r
184 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH];\r
185\r
186 // Build output string\r
187 UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName);\r
188 mTextOut->OutputString (mTextOut, OutputString);\r
189\r
190 Status = mPlatform->ErasePartition (PartitionName);\r
191 if (EFI_ERROR (Status)) {\r
192 SEND_LITERAL ("FAILCheck device console.");\r
193 DEBUG ((EFI_D_ERROR, "Couldn't erase image: %r\n", Status));\r
194 } else {\r
195 SEND_LITERAL ("OKAY");\r
196 }\r
197}\r
198\r
199STATIC\r
200VOID\r
201HandleBoot (\r
202 VOID\r
203 )\r
204{\r
205 EFI_STATUS Status;\r
206\r
207 mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n");\r
208\r
209 if (mDataBuffer == NULL) {\r
210 // Doesn't look like we were sent any data\r
211 SEND_LITERAL ("FAILNo image in memory");\r
212 return;\r
213 }\r
214\r
215 // We don't really have any choice but to report success, because once we\r
216 // boot we lose control of the system.\r
217 SEND_LITERAL ("OKAY");\r
218\r
219 Status = BootAndroidBootImg (mNumDataBytes, mDataBuffer);\r
220 if (EFI_ERROR (Status)) {\r
221 DEBUG ((EFI_D_ERROR, "Failed to boot downloaded image: %r\n", Status));\r
222 }\r
223 // We shouldn't get here\r
224}\r
225\r
226STATIC\r
227VOID\r
228HandleOemCommand (\r
229 IN CHAR8 *Command\r
230 )\r
231{\r
232 EFI_STATUS Status;\r
233\r
234 Status = mPlatform->DoOemCommand (Command);\r
235 if (Status == EFI_NOT_FOUND) {\r
236 SEND_LITERAL ("FAILOEM Command not recognised.");\r
237 } else if (Status == EFI_DEVICE_ERROR) {\r
238 SEND_LITERAL ("FAILError while executing command");\r
239 } else if (EFI_ERROR (Status)) {\r
240 SEND_LITERAL ("FAIL");\r
241 } else {\r
242 SEND_LITERAL ("OKAY");\r
243 }\r
244}\r
245\r
246STATIC\r
247VOID\r
248AcceptCmd (\r
249 IN UINTN Size,\r
250 IN CONST CHAR8 *Data\r
251 )\r
252{\r
253 CHAR8 Command[FASTBOOT_COMMAND_MAX_LENGTH + 1];\r
254\r
255 // Max command size is 64 bytes\r
256 if (Size > FASTBOOT_COMMAND_MAX_LENGTH) {\r
257 SEND_LITERAL ("FAILCommand too large");\r
258 return;\r
259 }\r
260\r
261 // Commands aren't null-terminated. Let's get a null-terminated version.\r
31090876 262 AsciiStrnCpyS (Command, sizeof Command, Data, Size);\r
f6755908
OM
263\r
264 // Parse command\r
265 if (MATCH_CMD_LITERAL ("getvar", Command)) {\r
266 HandleGetVar (Command + sizeof ("getvar"));\r
267 } else if (MATCH_CMD_LITERAL ("download", Command)) {\r
268 HandleDownload (Command + sizeof ("download"));\r
269 } else if (MATCH_CMD_LITERAL ("verify", Command)) {\r
270 SEND_LITERAL ("FAILNot supported");\r
271 } else if (MATCH_CMD_LITERAL ("flash", Command)) {\r
272 HandleFlash (Command + sizeof ("flash"));\r
273 } else if (MATCH_CMD_LITERAL ("erase", Command)) {\r
274 HandleErase (Command + sizeof ("erase"));\r
275 } else if (MATCH_CMD_LITERAL ("boot", Command)) {\r
276 HandleBoot ();\r
277 } else if (MATCH_CMD_LITERAL ("continue", Command)) {\r
278 SEND_LITERAL ("OKAY");\r
279 mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n");\r
280\r
281 gBS->SignalEvent (mFinishedEvent);\r
282 } else if (MATCH_CMD_LITERAL ("reboot", Command)) {\r
283 if (MATCH_CMD_LITERAL ("reboot-booloader", Command)) {\r
284 // fastboot_protocol.txt:\r
285 // "reboot-bootloader Reboot back into the bootloader."\r
286 // I guess this means reboot back into fastboot mode to save the user\r
287 // having to do whatever they did to get here again.\r
288 // Here we just reboot normally.\r
289 SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally.");\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 ((EFI_D_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 ((EFI_D_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 commmands 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 FreePool (Data);\r
390 }\r
391 } while (!EFI_ERROR (Status));\r
392\r
393 // Quit if there was a fatal error\r
394 if (Status != EFI_NOT_READY) {\r
395 ASSERT (Status == EFI_DEVICE_ERROR);\r
396 // (Put a newline at the beginning as we are probably in the data phase,\r
397 // so the download progress line, with no '\n' is probably on the console)\r
398 mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n");\r
399 gBS->SignalEvent (mFinishedEvent);\r
400 }\r
401}\r
402\r
403/*\r
404 Event notify for a fatal error in transmission.\r
405*/\r
406STATIC\r
407VOID\r
408FatalErrorNotify (\r
409 IN EFI_EVENT Event,\r
410 IN VOID *Context\r
411 )\r
412{\r
413 mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n");\r
414 gBS->SignalEvent (mFinishedEvent);\r
415}\r
416\r
417EFI_STATUS\r
418EFIAPI\r
419FastbootAppEntryPoint (\r
420 IN EFI_HANDLE ImageHandle,\r
421 IN EFI_SYSTEM_TABLE *SystemTable\r
422 )\r
423{\r
424 EFI_STATUS Status;\r
425 EFI_EVENT ReceiveEvent;\r
426 EFI_EVENT WaitEventArray[2];\r
427 UINTN EventIndex;\r
428 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn;\r
429\r
430 mDataBuffer = NULL;\r
431\r
432 Status = gBS->LocateProtocol (\r
433 &gAndroidFastbootTransportProtocolGuid,\r
434 NULL,\r
435 (VOID **) &mTransport\r
436 );\r
437 if (EFI_ERROR (Status)) {\r
438 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status));\r
439 return Status;\r
440 }\r
441\r
442 Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform);\r
443 if (EFI_ERROR (Status)) {\r
444 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status));\r
445 return Status;\r
446 }\r
447\r
448 Status = mPlatform->Init ();\r
449 if (EFI_ERROR (Status)) {\r
450 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status));\r
451 return Status;\r
452 }\r
453\r
454 Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut);\r
455 if (EFI_ERROR (Status)) {\r
456 DEBUG ((EFI_D_ERROR,\r
457 "Fastboot: Couldn't open Text Output Protocol: %r\n", Status\r
458 ));\r
459 return Status;\r
460 }\r
461\r
462 Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn);\r
463 if (EFI_ERROR (Status)) {\r
464 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status));\r
465 return Status;\r
466 }\r
467\r
468 // Disable watchdog\r
469 Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);\r
470 if (EFI_ERROR (Status)) {\r
471 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status));\r
472 }\r
473\r
474 // Create event for receipt of data from the host\r
475 Status = gBS->CreateEvent (\r
476 EVT_NOTIFY_SIGNAL,\r
477 TPL_CALLBACK,\r
478 DataReady,\r
479 NULL,\r
480 &ReceiveEvent\r
481 );\r
482 ASSERT_EFI_ERROR (Status);\r
483\r
484 // Create event for exiting application when "continue" command is received\r
485 Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent);\r
486 ASSERT_EFI_ERROR (Status);\r
487\r
488 // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a\r
489 // fatal error\r
490 Status = gBS->CreateEvent (\r
491 EVT_NOTIFY_SIGNAL,\r
492 TPL_CALLBACK,\r
493 FatalErrorNotify,\r
494 NULL,\r
495 &mFatalSendErrorEvent\r
496 );\r
497 ASSERT_EFI_ERROR (Status);\r
498\r
499\r
500 // Start listening for data\r
501 Status = mTransport->Start (\r
502 ReceiveEvent\r
503 );\r
504 if (EFI_ERROR (Status)) {\r
505 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status));\r
506 return Status;\r
507 }\r
508\r
509 // Talk to the user\r
510 mTextOut->OutputString (mTextOut,\r
511 L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ". Press any key to quit.\r\n");\r
512\r
513 // Quit when the user presses any key, or mFinishedEvent is signalled\r
514 WaitEventArray[0] = mFinishedEvent;\r
515 WaitEventArray[1] = TextIn->WaitForKey;\r
516 gBS->WaitForEvent (2, WaitEventArray, &EventIndex);\r
517\r
518 mTransport->Stop ();\r
519 if (EFI_ERROR (Status)) {\r
520 DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status));\r
521 }\r
522 mPlatform->UnInit ();\r
523\r
524 return EFI_SUCCESS;\r
525}\r