1 /** @file
2
3 Copyright (c) 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 /*
16 Implementation of the Android Fastboot Platform protocol, to be used by the
17 Fastboot UEFI application, for ARM Versatile Express platforms.
18 */
19
20 #include <Protocol/AndroidFastbootPlatform.h>
21 #include <Protocol/BlockIo.h>
22 #include <Protocol/DiskIo.h>
23
24 #include <Library/BaseLib.h>
25 #include <Library/BaseMemoryLib.h>
26 #include <Library/DebugLib.h>
27 #include <Library/DevicePathLib.h>
28 #include <Library/MemoryAllocationLib.h>
29 #include <Library/UefiBootServicesTableLib.h>
30
31 #define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \
32 sizeof (EFI_DEVICE_PATH_PROTOCOL))
33
34 #define PARTITION_NAME_MAX_LENGTH 72/2
35
36 #define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \
37 ((Char) <= L'Z' && (Char) >= L'Z'))
38
39 typedef struct _FASTBOOT_PARTITION_LIST {
40 LIST_ENTRY Link;
41 CHAR16 PartitionName[PARTITION_NAME_MAX_LENGTH];
42 EFI_HANDLE PartitionHandle;
43 } FASTBOOT_PARTITION_LIST;
44
45 STATIC LIST_ENTRY mPartitionListHead;
46
47 /*
48 Helper to free the partition list
49 */
50 STATIC
51 VOID
FreePartitionList(VOID)52 FreePartitionList (
53 VOID
54 )
55 {
56 FASTBOOT_PARTITION_LIST *Entry;
57 FASTBOOT_PARTITION_LIST *NextEntry;
58
59 Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&mPartitionListHead);
60 while (!IsNull (&mPartitionListHead, &Entry->Link)) {
61 NextEntry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &Entry->Link);
62
63 RemoveEntryList (&Entry->Link);
64 FreePool (Entry);
65
66 Entry = NextEntry;
67 }
68 }
69 /*
70 Read the PartitionName fields from the GPT partition entries, putting them
71 into an allocated array that should later be freed.
72 */
73 STATIC
74 EFI_STATUS
ReadPartitionEntries(IN EFI_BLOCK_IO_PROTOCOL * BlockIo,OUT EFI_PARTITION_ENTRY ** PartitionEntries)75 ReadPartitionEntries (
76 IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
77 OUT EFI_PARTITION_ENTRY **PartitionEntries
78 )
79 {
80 UINTN EntrySize;
81 UINTN NumEntries;
82 UINTN BufferSize;
83 UINT32 MediaId;
84 EFI_PARTITION_TABLE_HEADER *GptHeader;
85 EFI_STATUS Status;
86
87 MediaId = BlockIo->Media->MediaId;
88
89 //
90 // Read size of Partition entry and number of entries from GPT header
91 //
92
93 GptHeader = AllocatePool (BlockIo->Media->BlockSize);
94 if (GptHeader == NULL) {
95 return EFI_OUT_OF_RESOURCES;
96 }
97
98 Status = BlockIo->ReadBlocks (BlockIo, MediaId, 1, BlockIo->Media->BlockSize, (VOID *) GptHeader);
99 if (EFI_ERROR (Status)) {
100 return Status;
101 }
102
103 // Check there is a GPT on the media
104 if (GptHeader->Header.Signature != EFI_PTAB_HEADER_ID ||
105 GptHeader->MyLBA != 1) {
106 DEBUG ((EFI_D_ERROR,
107 "Fastboot platform: No GPT on flash. "
108 "Fastboot on Versatile Express does not support MBR.\n"
109 ));
110 return EFI_DEVICE_ERROR;
111 }
112
113 EntrySize = GptHeader->SizeOfPartitionEntry;
114 NumEntries = GptHeader->NumberOfPartitionEntries;
115
116 FreePool (GptHeader);
117
118 ASSERT (EntrySize != 0);
119 ASSERT (NumEntries != 0);
120
121 BufferSize = ALIGN_VALUE (EntrySize * NumEntries, BlockIo->Media->BlockSize);
122 *PartitionEntries = AllocatePool (BufferSize);
123 if (PartitionEntries == NULL) {
124 return EFI_OUT_OF_RESOURCES;
125 }
126
127 Status = BlockIo->ReadBlocks (BlockIo, MediaId, 2, BufferSize, (VOID *) *PartitionEntries);
128 if (EFI_ERROR (Status)) {
129 FreePool (PartitionEntries);
130 return Status;
131 }
132
133 return Status;
134 }
135
136
137 /*
138 Initialise: Open the Android NVM device and find the partitions on it. Save them in
139 a list along with the "PartitionName" fields for their GPT entries.
140 We will use these partition names as the key in
141 ArmFastbootPlatformFlashPartition.
142 */
143 EFI_STATUS
ArmFastbootPlatformInit(VOID)144 ArmFastbootPlatformInit (
145 VOID
146 )
147 {
148 EFI_STATUS Status;
149 EFI_DEVICE_PATH_PROTOCOL *FlashDevicePath;
150 EFI_DEVICE_PATH_PROTOCOL *FlashDevicePathDup;
151 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
152 EFI_DEVICE_PATH_PROTOCOL *NextNode;
153 HARDDRIVE_DEVICE_PATH *PartitionNode;
154 UINTN NumHandles;
155 EFI_HANDLE *AllHandles;
156 UINTN LoopIndex;
157 EFI_HANDLE FlashHandle;
158 EFI_BLOCK_IO_PROTOCOL *FlashBlockIo;
159 EFI_PARTITION_ENTRY *PartitionEntries;
160 FASTBOOT_PARTITION_LIST *Entry;
161
162 InitializeListHead (&mPartitionListHead);
163
164 //
165 // Get EFI_HANDLES for all the partitions on the block devices pointed to by
166 // PcdFastbootFlashDevicePath, also saving their GPT partition labels.
167 // There's no way to find all of a device's children, so we get every handle
168 // in the system supporting EFI_BLOCK_IO_PROTOCOL and then filter out ones
169 // that don't represent partitions on the flash device.
170 //
171
172 FlashDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath));
173
174 //
175 // Open the Disk IO protocol on the flash device - this will be used to read
176 // partition names out of the GPT entries
177 //
178 // Create another device path pointer because LocateDevicePath will modify it.
179 FlashDevicePathDup = FlashDevicePath;
180 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &FlashDevicePathDup, &FlashHandle);
181 if (EFI_ERROR (Status)) {
182 DEBUG ((EFI_D_ERROR, "Warning: Couldn't locate Android NVM device (status: %r)\n", Status));
183 // Failing to locate partitions should not prevent to do other Android FastBoot actions
184 return EFI_SUCCESS;
185 }
186
187 Status = gBS->OpenProtocol (
188 FlashHandle,
189 &gEfiBlockIoProtocolGuid,
190 (VOID **) &FlashBlockIo,
191 gImageHandle,
192 NULL,
193 EFI_OPEN_PROTOCOL_GET_PROTOCOL
194 );
195 if (EFI_ERROR (Status)) {
196 DEBUG ((EFI_D_ERROR, "Fastboot platform: Couldn't open Android NVM device (status: %r)\n", Status));
197 return EFI_DEVICE_ERROR;
198 }
199
200 // Read the GPT partition entry array into memory so we can get the partition names
201 Status = ReadPartitionEntries (FlashBlockIo, &PartitionEntries);
202 if (EFI_ERROR (Status)) {
203 DEBUG ((EFI_D_ERROR, "Warning: Failed to read partitions from Android NVM device (status: %r)\n", Status));
204 // Failing to locate partitions should not prevent to do other Android FastBoot actions
205 return EFI_SUCCESS;
206 }
207
208 // Get every Block IO protocol instance installed in the system
209 Status = gBS->LocateHandleBuffer (
210 ByProtocol,
211 &gEfiBlockIoProtocolGuid,
212 NULL,
213 &NumHandles,
214 &AllHandles
215 );
216 ASSERT_EFI_ERROR (Status);
217
218 // Filter out handles that aren't children of the flash device
219 for (LoopIndex = 0; LoopIndex < NumHandles; LoopIndex++) {
220 // Get the device path for the handle
221 Status = gBS->OpenProtocol (
222 AllHandles[LoopIndex],
223 &gEfiDevicePathProtocolGuid,
224 (VOID **) &DevicePath,
225 gImageHandle,
226 NULL,
227 EFI_OPEN_PROTOCOL_GET_PROTOCOL
228 );
229 ASSERT_EFI_ERROR (Status);
230
231 // Check if it is a sub-device of the flash device
232 if (!CompareMem (DevicePath, FlashDevicePath, FLASH_DEVICE_PATH_SIZE (FlashDevicePath))) {
233 // Device path starts with path of flash device. Check it isn't the flash
234 // device itself.
235 NextNode = NextDevicePathNode (DevicePath);
236 if (IsDevicePathEndType (NextNode)) {
237 continue;
238 }
239
240 // Assert that this device path node represents a partition.
241 ASSERT (NextNode->Type == MEDIA_DEVICE_PATH &&
242 NextNode->SubType == MEDIA_HARDDRIVE_DP);
243
244 PartitionNode = (HARDDRIVE_DEVICE_PATH *) NextNode;
245
246 // Assert that the partition type is GPT. ReadPartitionEntries checks for
247 // presence of a GPT, so we should never find MBR partitions.
248 // ("MBRType" is a misnomer - this field is actually called "Partition
249 // Format")
250 ASSERT (PartitionNode->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER);
251
252 // The firmware may install a handle for "partition 0", representing the
253 // whole device. Ignore it.
254 if (PartitionNode->PartitionNumber == 0) {
255 continue;
256 }
257
258 //
259 // Add the partition handle to the list
260 //
261
262 // Create entry
263 Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));
264 if (Entry == NULL) {
265 Status = EFI_OUT_OF_RESOURCES;
266 FreePartitionList ();
267 goto Exit;
268 }
269
270 // Copy handle and partition name
271 Entry->PartitionHandle = AllHandles[LoopIndex];
272 StrnCpy (
273 Entry->PartitionName,
274 PartitionEntries[PartitionNode->PartitionNumber - 1].PartitionName, // Partition numbers start from 1.
275 PARTITION_NAME_MAX_LENGTH
276 );
277 InsertTailList (&mPartitionListHead, &Entry->Link);
278
279 // Print a debug message if the partition label is empty or looks like
280 // garbage.
281 if (!IS_ALPHA (Entry->PartitionName[0])) {
282 DEBUG ((EFI_D_ERROR,
283 "Warning: Partition %d doesn't seem to have a GPT partition label. "
284 "You won't be able to flash it with Fastboot.\n",
285 PartitionNode->PartitionNumber
286 ));
287 }
288 }
289 }
290
291 Exit:
292 FreePool (PartitionEntries);
293 FreePool (FlashDevicePath);
294 FreePool (AllHandles);
295 return Status;
296
297 }
298
299 VOID
ArmFastbootPlatformUnInit(VOID)300 ArmFastbootPlatformUnInit (
301 VOID
302 )
303 {
304 FreePartitionList ();
305 }
306
307 EFI_STATUS
ArmFastbootPlatformFlashPartition(IN CHAR8 * PartitionName,IN UINTN Size,IN VOID * Image)308 ArmFastbootPlatformFlashPartition (
309 IN CHAR8 *PartitionName,
310 IN UINTN Size,
311 IN VOID *Image
312 )
313 {
314 EFI_STATUS Status;
315 EFI_BLOCK_IO_PROTOCOL *BlockIo;
316 EFI_DISK_IO_PROTOCOL *DiskIo;
317 UINT32 MediaId;
318 UINTN PartitionSize;
319 FASTBOOT_PARTITION_LIST *Entry;
320 CHAR16 PartitionNameUnicode[60];
321 BOOLEAN PartitionFound;
322 SPARSE_HEADER *SparseHeader;
323 CHUNK_HEADER *ChunkHeader;
324 UINTN Offset = 0;
325 UINT32 Chunk;
326
327
328 AsciiStrToUnicodeStr (PartitionName, PartitionNameUnicode);
329
330 PartitionFound = FALSE;
331 Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
332 while (!IsNull (&mPartitionListHead, &Entry->Link)) {
333 // Search the partition list for the partition named by PartitionName
334 if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
335 PartitionFound = TRUE;
336 break;
337 }
338
339 Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
340 }
341 if (!PartitionFound) {
342 return EFI_NOT_FOUND;
343 }
344
345 Status = gBS->OpenProtocol (
346 Entry->PartitionHandle,
347 &gEfiBlockIoProtocolGuid,
348 (VOID **) &BlockIo,
349 gImageHandle,
350 NULL,
351 EFI_OPEN_PROTOCOL_GET_PROTOCOL
352 );
353 if (EFI_ERROR (Status)) {
354 DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
355 return EFI_NOT_FOUND;
356 }
357
358 SparseHeader=(SPARSE_HEADER *)Image;
359
360 if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) {
361 DEBUG ((EFI_D_INFO, "Sparse Magic: 0x%x Major: %d Minor: %d fhs: %d chs: %d bs: %d tbs: %d tcs: %d checksum: %d \n",
362 SparseHeader->Magic, SparseHeader->MajorVersion, SparseHeader->MinorVersion, SparseHeader->FileHeaderSize,
363 SparseHeader->ChunkHeaderSize, SparseHeader->BlockSize, SparseHeader->TotalBlocks,
364 SparseHeader->TotalChunks, SparseHeader->ImageChecksum));
365 if (SparseHeader->MajorVersion != 1) {
366 DEBUG ((EFI_D_ERROR, "Sparse image version %d.%d not supported.\n",
367 SparseHeader->MajorVersion, SparseHeader->MinorVersion));
368 return EFI_INVALID_PARAMETER;
369 }
370
371 Size = SparseHeader->BlockSize * SparseHeader->TotalBlocks;
372 }
373
374 // Check image will fit on device
375 PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
376 if (PartitionSize < Size) {
377 DEBUG ((EFI_D_ERROR, "Partition not big enough.\n"));
378 DEBUG ((EFI_D_ERROR, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize, Size));
379
380 return EFI_VOLUME_FULL;
381 }
382
383 MediaId = BlockIo->Media->MediaId;
384
385 Status = gBS->OpenProtocol (
386 Entry->PartitionHandle,
387 &gEfiDiskIoProtocolGuid,
388 (VOID **) &DiskIo,
389 gImageHandle,
390 NULL,
391 EFI_OPEN_PROTOCOL_GET_PROTOCOL
392 );
393 ASSERT_EFI_ERROR (Status);
394
395 if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) {
396 Image += SparseHeader->FileHeaderSize;
397 for (Chunk = 0; Chunk < SparseHeader->TotalChunks; Chunk++) {
398 UINTN WriteSize;
399 ChunkHeader = (CHUNK_HEADER *)Image;
400 DEBUG ((EFI_D_INFO, "Chunk #%d - Type: 0x%x Size: %d TotalSize: %d Offset %d\n",
401 (Chunk+1), ChunkHeader->ChunkType, ChunkHeader->ChunkSize,
402 ChunkHeader->TotalSize, Offset));
403 Image += sizeof(CHUNK_HEADER);
404 WriteSize=(SparseHeader->BlockSize) * ChunkHeader->ChunkSize;
405 switch (ChunkHeader->ChunkType) {
406 case CHUNK_TYPE_RAW:
407 DEBUG ((EFI_D_INFO, "Writing %d at Offset %d\n", WriteSize, Offset));
408 Status = DiskIo->WriteDisk (DiskIo, MediaId, Offset, WriteSize, Image);
409 if (EFI_ERROR (Status)) {
410 return Status;
411 }
412 Image+=WriteSize;
413 break;
414 case CHUNK_TYPE_DONT_CARE:
415 break;
416 case CHUNK_TYPE_CRC32:
417 break;
418 default:
419 DEBUG ((EFI_D_ERROR, "Unknown Chunk Type: 0x%x"));
420 return EFI_PROTOCOL_ERROR;
421 }
422 Offset += WriteSize;
423 }
424 } else {
425 Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);
426 if (EFI_ERROR (Status)) {
427 return Status;
428 }
429 }
430
431 BlockIo->FlushBlocks(BlockIo);
432
433 return Status;
434 }
435
436 EFI_STATUS
ArmFastbootPlatformErasePartition(IN CHAR8 * Partition)437 ArmFastbootPlatformErasePartition (
438 IN CHAR8 *Partition
439 )
440 {
441 return EFI_SUCCESS;
442 }
443
444 EFI_STATUS
ArmFastbootPlatformGetVar(IN CHAR8 * Name,OUT CHAR8 * Value)445 ArmFastbootPlatformGetVar (
446 IN CHAR8 *Name,
447 OUT CHAR8 *Value
448 )
449 {
450 if (AsciiStrCmp (Name, "product")) {
451 AsciiStrCpy (Value, FixedPcdGetPtr (PcdFirmwareVendor));
452 } else {
453 *Value = '\0';
454 }
455 return EFI_SUCCESS;
456 }
457
458 EFI_STATUS
ArmFastbootPlatformOemCommand(IN CHAR8 * Command)459 ArmFastbootPlatformOemCommand (
460 IN CHAR8 *Command
461 )
462 {
463 CHAR16 CommandUnicode[65];
464
465 AsciiStrToUnicodeStr (Command, CommandUnicode);
466
467 if (AsciiStrCmp (Command, "Demonstrate") == 0) {
468 DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
469 return EFI_SUCCESS;
470 } else {
471 DEBUG ((EFI_D_ERROR,
472 "VExpress: Unrecognised Fastboot OEM command: %s\n",
473 CommandUnicode
474 ));
475 return EFI_NOT_FOUND;
476 }
477 }
478
479 FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {
480 ArmFastbootPlatformInit,
481 ArmFastbootPlatformUnInit,
482 ArmFastbootPlatformFlashPartition,
483 ArmFastbootPlatformErasePartition,
484 ArmFastbootPlatformGetVar,
485 ArmFastbootPlatformOemCommand
486 };
487
488 EFI_STATUS
489 EFIAPI
ArmAndroidFastbootPlatformEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)490 ArmAndroidFastbootPlatformEntryPoint (
491 IN EFI_HANDLE ImageHandle,
492 IN EFI_SYSTEM_TABLE *SystemTable
493 )
494 {
495 return gBS->InstallProtocolInterface (
496 &ImageHandle,
497 &gAndroidFastbootPlatformProtocolGuid,
498 EFI_NATIVE_INTERFACE,
499 &mPlatformProtocol
500 );
501 }
502