]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/ArmVExpressPkg/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c
4d0811cc5eaf137e3253261b4e1ced52788934bf
[mirror_edk2.git] / ArmPlatformPkg / ArmVExpressPkg / ArmVExpressFastBootDxe / ArmVExpressFastBoot.c
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
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
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
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
300 ArmFastbootPlatformUnInit (
301 VOID
302 )
303 {
304 FreePartitionList ();
305 }
306
307 EFI_STATUS
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
323 AsciiStrToUnicodeStr (PartitionName, PartitionNameUnicode);
324
325 PartitionFound = FALSE;
326 Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
327 while (!IsNull (&mPartitionListHead, &Entry->Link)) {
328 // Search the partition list for the partition named by PartitionName
329 if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
330 PartitionFound = TRUE;
331 break;
332 }
333
334 Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
335 }
336 if (!PartitionFound) {
337 return EFI_NOT_FOUND;
338 }
339
340 Status = gBS->OpenProtocol (
341 Entry->PartitionHandle,
342 &gEfiBlockIoProtocolGuid,
343 (VOID **) &BlockIo,
344 gImageHandle,
345 NULL,
346 EFI_OPEN_PROTOCOL_GET_PROTOCOL
347 );
348 if (EFI_ERROR (Status)) {
349 DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
350 return EFI_NOT_FOUND;
351 }
352
353 // Check image will fit on device
354 PartitionSize = (BlockIo->Media->LastBlock + 1) * BlockIo->Media->BlockSize;
355 if (PartitionSize < Size) {
356 DEBUG ((EFI_D_ERROR, "Partition not big enough.\n"));
357 DEBUG ((EFI_D_ERROR, "Partition Size:\t%d\nImage Size:\t%d\n", PartitionSize, Size));
358
359 return EFI_VOLUME_FULL;
360 }
361
362 MediaId = BlockIo->Media->MediaId;
363
364 Status = gBS->OpenProtocol (
365 Entry->PartitionHandle,
366 &gEfiDiskIoProtocolGuid,
367 (VOID **) &DiskIo,
368 gImageHandle,
369 NULL,
370 EFI_OPEN_PROTOCOL_GET_PROTOCOL
371 );
372 ASSERT_EFI_ERROR (Status);
373
374 Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);
375 if (EFI_ERROR (Status)) {
376 return Status;
377 }
378
379 BlockIo->FlushBlocks(BlockIo);
380
381 return Status;
382 }
383
384 EFI_STATUS
385 ArmFastbootPlatformErasePartition (
386 IN CHAR8 *Partition
387 )
388 {
389 return EFI_SUCCESS;
390 }
391
392 EFI_STATUS
393 ArmFastbootPlatformGetVar (
394 IN CHAR8 *Name,
395 OUT CHAR8 *Value
396 )
397 {
398 if (AsciiStrCmp (Name, "product")) {
399 AsciiStrCpy (Value, FixedPcdGetPtr (PcdFirmwareVendor));
400 } else {
401 *Value = '\0';
402 }
403 return EFI_SUCCESS;
404 }
405
406 EFI_STATUS
407 ArmFastbootPlatformOemCommand (
408 IN CHAR8 *Command
409 )
410 {
411 CHAR16 CommandUnicode[65];
412
413 AsciiStrToUnicodeStr (Command, CommandUnicode);
414
415 if (AsciiStrCmp (Command, "Demonstrate") == 0) {
416 DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
417 return EFI_SUCCESS;
418 } else {
419 DEBUG ((EFI_D_ERROR,
420 "VExpress: Unrecognised Fastboot OEM command: %s\n",
421 CommandUnicode
422 ));
423 return EFI_NOT_FOUND;
424 }
425 }
426
427 FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {
428 ArmFastbootPlatformInit,
429 ArmFastbootPlatformUnInit,
430 ArmFastbootPlatformFlashPartition,
431 ArmFastbootPlatformErasePartition,
432 ArmFastbootPlatformGetVar,
433 ArmFastbootPlatformOemCommand
434 };
435
436 EFI_STATUS
437 EFIAPI
438 ArmAndroidFastbootPlatformEntryPoint (
439 IN EFI_HANDLE ImageHandle,
440 IN EFI_SYSTEM_TABLE *SystemTable
441 )
442 {
443 return gBS->InstallProtocolInterface (
444 &ImageHandle,
445 &gAndroidFastbootPlatformProtocolGuid,
446 EFI_NATIVE_INTERFACE,
447 &mPlatformProtocol
448 );
449 }