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