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