]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/ArmVExpressPkg/ArmVExpressFastBootDxe/ArmVExpressFastBoot.c
ArmPlatformPkg/ArmVExpressFastBootDxe: clean up code and comments
[mirror_edk2.git] / ArmPlatformPkg / ArmVExpressPkg / ArmVExpressFastBootDxe / ArmVExpressFastBoot.c
1 /** @file
2
3 Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
4 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 /*
17 Implementation of the Android Fastboot Platform protocol, to be used by the
18 Fastboot UEFI application, for ARM Versatile Express platforms.
19 */
20
21 #include <Protocol/AndroidFastbootPlatform.h>
22 #include <Protocol/BlockIo.h>
23 #include <Protocol/DiskIo.h>
24
25 #include <Library/BaseLib.h>
26 #include <Library/BaseMemoryLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/DevicePathLib.h>
29 #include <Library/MemoryAllocationLib.h>
30 #include <Library/UefiBootServicesTableLib.h>
31
32 #define FLASH_DEVICE_PATH_SIZE(DevPath) ( GetDevicePathSize (DevPath) - \
33 sizeof (EFI_DEVICE_PATH_PROTOCOL))
34
35 #define PARTITION_NAME_MAX_LENGTH 72/2
36
37 #define IS_ALPHA(Char) (((Char) <= L'z' && (Char) >= L'a') || \
38 ((Char) <= L'Z' && (Char) >= L'Z'))
39
40 typedef struct _FASTBOOT_PARTITION_LIST {
41 LIST_ENTRY Link;
42 CHAR16 PartitionName[PARTITION_NAME_MAX_LENGTH];
43 EFI_HANDLE PartitionHandle;
44 } FASTBOOT_PARTITION_LIST;
45
46 STATIC LIST_ENTRY mPartitionListHead;
47
48 /*
49 Helper to free the partition list
50 */
51 STATIC
52 VOID
53 FreePartitionList (
54 VOID
55 )
56 {
57 FASTBOOT_PARTITION_LIST *Entry;
58 FASTBOOT_PARTITION_LIST *NextEntry;
59
60 Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&mPartitionListHead);
61 while (!IsNull (&mPartitionListHead, &Entry->Link)) {
62 NextEntry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &Entry->Link);
63
64 RemoveEntryList (&Entry->Link);
65 FreePool (Entry);
66
67 Entry = NextEntry;
68 }
69 }
70 /*
71 Read the PartitionName fields from the GPT partition entries, putting them
72 into an allocated array that should later be freed.
73 */
74 STATIC
75 EFI_STATUS
76 ReadPartitionEntries (
77 IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
78 OUT EFI_PARTITION_ENTRY **PartitionEntries
79 )
80 {
81 UINTN EntrySize;
82 UINTN NumEntries;
83 UINTN BufferSize;
84 UINT32 MediaId;
85 EFI_PARTITION_TABLE_HEADER *GptHeader;
86 EFI_STATUS Status;
87
88 MediaId = BlockIo->Media->MediaId;
89
90 //
91 // Read size of Partition entry and number of entries from GPT header
92 //
93
94 GptHeader = AllocatePool (BlockIo->Media->BlockSize);
95 if (GptHeader == NULL) {
96 return EFI_OUT_OF_RESOURCES;
97 }
98
99 Status = BlockIo->ReadBlocks (BlockIo, MediaId, 1, BlockIo->Media->BlockSize, (VOID *) GptHeader);
100 if (EFI_ERROR (Status)) {
101 return Status;
102 }
103
104 // Check there is a GPT on the media
105 if (GptHeader->Header.Signature != EFI_PTAB_HEADER_ID ||
106 GptHeader->MyLBA != 1) {
107 DEBUG ((EFI_D_ERROR,
108 "Fastboot platform: No GPT on flash. "
109 "Fastboot on Versatile Express does not support MBR.\n"
110 ));
111 return EFI_DEVICE_ERROR;
112 }
113
114 EntrySize = GptHeader->SizeOfPartitionEntry;
115 NumEntries = GptHeader->NumberOfPartitionEntries;
116
117 FreePool (GptHeader);
118
119 ASSERT (EntrySize != 0);
120 ASSERT (NumEntries != 0);
121
122 BufferSize = ALIGN_VALUE (EntrySize * NumEntries, BlockIo->Media->BlockSize);
123 *PartitionEntries = AllocatePool (BufferSize);
124 if (PartitionEntries == NULL) {
125 return EFI_OUT_OF_RESOURCES;
126 }
127
128 Status = BlockIo->ReadBlocks (BlockIo, MediaId, 2, BufferSize, (VOID *) *PartitionEntries);
129 if (EFI_ERROR (Status)) {
130 FreePool (PartitionEntries);
131 return Status;
132 }
133
134 return Status;
135 }
136
137
138 /*
139 Do any initialisation that needs to be done in order to be able to respond to
140 commands.
141
142 @retval EFI_SUCCESS Initialised successfully.
143 @retval !EFI_SUCCESS Error in initialisation.
144 */
145 STATIC
146 EFI_STATUS
147 ArmFastbootPlatformInit (
148 VOID
149 )
150 {
151 EFI_STATUS Status;
152 EFI_DEVICE_PATH_PROTOCOL *FlashDevicePath;
153 EFI_DEVICE_PATH_PROTOCOL *FlashDevicePathDup;
154 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
155 EFI_DEVICE_PATH_PROTOCOL *NextNode;
156 HARDDRIVE_DEVICE_PATH *PartitionNode;
157 UINTN NumHandles;
158 EFI_HANDLE *AllHandles;
159 UINTN LoopIndex;
160 EFI_HANDLE FlashHandle;
161 EFI_BLOCK_IO_PROTOCOL *FlashBlockIo;
162 EFI_PARTITION_ENTRY *PartitionEntries;
163 FASTBOOT_PARTITION_LIST *Entry;
164
165 InitializeListHead (&mPartitionListHead);
166
167 //
168 // Get EFI_HANDLES for all the partitions on the block devices pointed to by
169 // PcdFastbootFlashDevicePath, also saving their GPT partition labels.
170 // We will use these labels as the key in ArmFastbootPlatformFlashPartition.
171 // There's no way to find all of a device's children, so we get every handle
172 // in the system supporting EFI_BLOCK_IO_PROTOCOL and then filter out ones
173 // that don't represent partitions on the flash device.
174 //
175
176 FlashDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdAndroidFastbootNvmDevicePath));
177
178 //
179 // Open the Disk IO protocol on the flash device - this will be used to read
180 // partition names out of the GPT entries
181 //
182 // Create another device path pointer because LocateDevicePath will modify it.
183 FlashDevicePathDup = FlashDevicePath;
184 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &FlashDevicePathDup, &FlashHandle);
185 if (EFI_ERROR (Status)) {
186 DEBUG ((EFI_D_ERROR, "Warning: Couldn't locate Android NVM device (status: %r)\n", Status));
187 // Failing to locate partitions should not prevent to do other Android FastBoot actions
188 return EFI_SUCCESS;
189 }
190
191 Status = gBS->OpenProtocol (
192 FlashHandle,
193 &gEfiBlockIoProtocolGuid,
194 (VOID **) &FlashBlockIo,
195 gImageHandle,
196 NULL,
197 EFI_OPEN_PROTOCOL_GET_PROTOCOL
198 );
199 if (EFI_ERROR (Status)) {
200 DEBUG ((EFI_D_ERROR, "Fastboot platform: Couldn't open Android NVM device (status: %r)\n", Status));
201 return EFI_DEVICE_ERROR;
202 }
203
204 // Read the GPT partition entry array into memory so we can get the partition names
205 Status = ReadPartitionEntries (FlashBlockIo, &PartitionEntries);
206 if (EFI_ERROR (Status)) {
207 DEBUG ((EFI_D_ERROR, "Warning: Failed to read partitions from Android NVM device (status: %r)\n", Status));
208 // Failing to locate partitions should not prevent to do other Android FastBoot actions
209 return EFI_SUCCESS;
210 }
211
212 // Get every Block IO protocol instance installed in the system
213 Status = gBS->LocateHandleBuffer (
214 ByProtocol,
215 &gEfiBlockIoProtocolGuid,
216 NULL,
217 &NumHandles,
218 &AllHandles
219 );
220 ASSERT_EFI_ERROR (Status);
221
222 // Filter out handles that aren't children of the flash device
223 for (LoopIndex = 0; LoopIndex < NumHandles; LoopIndex++) {
224 // Get the device path for the handle
225 Status = gBS->OpenProtocol (
226 AllHandles[LoopIndex],
227 &gEfiDevicePathProtocolGuid,
228 (VOID **) &DevicePath,
229 gImageHandle,
230 NULL,
231 EFI_OPEN_PROTOCOL_GET_PROTOCOL
232 );
233 ASSERT_EFI_ERROR (Status);
234
235 // Check if it is a sub-device of the flash device
236 if (!CompareMem (DevicePath, FlashDevicePath, FLASH_DEVICE_PATH_SIZE (FlashDevicePath))) {
237 // Device path starts with path of flash device. Check it isn't the flash
238 // device itself.
239 NextNode = NextDevicePathNode (DevicePath);
240 if (IsDevicePathEndType (NextNode)) {
241 continue;
242 }
243
244 // Assert that this device path node represents a partition.
245 ASSERT (NextNode->Type == MEDIA_DEVICE_PATH &&
246 NextNode->SubType == MEDIA_HARDDRIVE_DP);
247
248 PartitionNode = (HARDDRIVE_DEVICE_PATH *) NextNode;
249
250 // Assert that the partition type is GPT. ReadPartitionEntries checks for
251 // presence of a GPT, so we should never find MBR partitions.
252 // ("MBRType" is a misnomer - this field is actually called "Partition
253 // Format")
254 ASSERT (PartitionNode->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER);
255
256 // The firmware may install a handle for "partition 0", representing the
257 // whole device. Ignore it.
258 if (PartitionNode->PartitionNumber == 0) {
259 continue;
260 }
261
262 //
263 // Add the partition handle to the list
264 //
265
266 // Create entry
267 Entry = AllocatePool (sizeof (FASTBOOT_PARTITION_LIST));
268 if (Entry == NULL) {
269 Status = EFI_OUT_OF_RESOURCES;
270 FreePartitionList ();
271 goto Exit;
272 }
273
274 // Copy handle and partition name
275 Entry->PartitionHandle = AllHandles[LoopIndex];
276 StrnCpy (
277 Entry->PartitionName,
278 PartitionEntries[PartitionNode->PartitionNumber - 1].PartitionName, // Partition numbers start from 1.
279 PARTITION_NAME_MAX_LENGTH
280 );
281 InsertTailList (&mPartitionListHead, &Entry->Link);
282
283 // Print a debug message if the partition label is empty or looks like
284 // garbage.
285 if (!IS_ALPHA (Entry->PartitionName[0])) {
286 DEBUG ((EFI_D_ERROR,
287 "Warning: Partition %d doesn't seem to have a GPT partition label. "
288 "You won't be able to flash it with Fastboot.\n",
289 PartitionNode->PartitionNumber
290 ));
291 }
292 }
293 }
294
295 Exit:
296 FreePool (PartitionEntries);
297 FreePool (FlashDevicePath);
298 FreePool (AllHandles);
299 return Status;
300
301 }
302
303 /*
304 To be called when Fastboot is finished and we aren't rebooting or booting an
305 image. Undo initialisation, free resrouces.
306 */
307 STATIC
308 VOID
309 ArmFastbootPlatformUnInit (
310 VOID
311 )
312 {
313 FreePartitionList ();
314 }
315
316 /*
317 Flash the partition named (according to a platform-specific scheme)
318 PartitionName, with the image pointed to by Buffer, whose size is BufferSize.
319
320 @param[in] PartitionName Null-terminated name of partition to write.
321 @param[in] BufferSize Size of Buffer in byets.
322 @param[in] Buffer Data to write to partition.
323
324 @retval EFI_NOT_FOUND No such partition.
325 @retval EFI_DEVICE_ERROR Flashing failed.
326 */
327 STATIC
328 EFI_STATUS
329 ArmFastbootPlatformFlashPartition (
330 IN CHAR8 *PartitionName,
331 IN UINTN Size,
332 IN VOID *Image
333 )
334 {
335 EFI_STATUS Status;
336 EFI_BLOCK_IO_PROTOCOL *BlockIo;
337 EFI_DISK_IO_PROTOCOL *DiskIo;
338 UINT32 MediaId;
339 UINTN PartitionSize;
340 FASTBOOT_PARTITION_LIST *Entry;
341 CHAR16 PartitionNameUnicode[60];
342 BOOLEAN PartitionFound;
343
344 AsciiStrToUnicodeStr (PartitionName, PartitionNameUnicode);
345
346 PartitionFound = FALSE;
347 Entry = (FASTBOOT_PARTITION_LIST *) GetFirstNode (&(mPartitionListHead));
348 while (!IsNull (&mPartitionListHead, &Entry->Link)) {
349 // Search the partition list for the partition named by PartitionName
350 if (StrCmp (Entry->PartitionName, PartitionNameUnicode) == 0) {
351 PartitionFound = TRUE;
352 break;
353 }
354
355 Entry = (FASTBOOT_PARTITION_LIST *) GetNextNode (&mPartitionListHead, &(Entry)->Link);
356 }
357 if (!PartitionFound) {
358 return EFI_NOT_FOUND;
359 }
360
361 Status = gBS->OpenProtocol (
362 Entry->PartitionHandle,
363 &gEfiBlockIoProtocolGuid,
364 (VOID **) &BlockIo,
365 gImageHandle,
366 NULL,
367 EFI_OPEN_PROTOCOL_GET_PROTOCOL
368 );
369 if (EFI_ERROR (Status)) {
370 DEBUG ((EFI_D_ERROR, "Fastboot platform: couldn't open Block IO for flash: %r\n", Status));
371 return EFI_NOT_FOUND;
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 Status = DiskIo->WriteDisk (DiskIo, MediaId, 0, Size, Image);
396 if (EFI_ERROR (Status)) {
397 return Status;
398 }
399
400 BlockIo->FlushBlocks(BlockIo);
401
402 return Status;
403 }
404
405 /*
406 Erase the partition named PartitionName.
407
408 @param[in] PartitionName Null-terminated name of partition to erase.
409
410 @retval EFI_NOT_FOUND No such partition.
411 @retval EFI_DEVICE_ERROR Erasing failed.
412 */
413 STATIC
414 EFI_STATUS
415 ArmFastbootPlatformErasePartition (
416 IN CHAR8 *Partition
417 )
418 {
419 return EFI_SUCCESS;
420 }
421
422 /*
423 If the variable referred to by Name exists, copy it (as a null-terminated
424 string) into Value. If it doesn't exist, put the Empty string in Value.
425
426 Variable names and values may not be larger than 60 bytes, excluding the
427 terminal null character. This is a limitation of the Fastboot protocol.
428
429 The Fastboot application will handle platform-nonspecific variables
430 (Currently "version" is the only one of these.)
431
432 @param[in] Name Null-terminated name of Fastboot variable to retrieve.
433 @param[out] Value Caller-allocated buffer for null-terminated value of
434 variable.
435
436 @retval EFI_SUCCESS The variable was retrieved, or it doesn't exist.
437 @retval EFI_DEVICE_ERROR There was an error looking up the variable. This
438 does _not_ include the variable not existing.
439 */
440 STATIC
441 EFI_STATUS
442 ArmFastbootPlatformGetVar (
443 IN CHAR8 *Name,
444 OUT CHAR8 *Value
445 )
446 {
447 if (AsciiStrCmp (Name, "product")) {
448 AsciiStrCpy (Value, FixedPcdGetPtr (PcdFirmwareVendor));
449 } else {
450 *Value = '\0';
451 }
452 return EFI_SUCCESS;
453 }
454
455 /*
456 React to an OEM-specific command.
457
458 Future versions of this function might want to allow the platform to do some
459 extra communication with the host. A way to do this would be to add a function
460 to the FASTBOOT_TRANSPORT_PROTOCOL that allows the implementation of
461 DoOemCommand to replace the ReceiveEvent with its own, and to restore the old
462 one when it's finished.
463
464 However at the moment although the specification allows it, the AOSP fastboot
465 host application doesn't handle receiving any data from the client, and it
466 doesn't support a data phase for OEM commands.
467
468 @param[in] Command Null-terminated command string.
469
470 @retval EFI_SUCCESS The command executed successfully.
471 @retval EFI_NOT_FOUND The command wasn't recognised.
472 @retval EFI_DEVICE_ERROR There was an error executing the command.
473 */
474 STATIC
475 EFI_STATUS
476 ArmFastbootPlatformOemCommand (
477 IN CHAR8 *Command
478 )
479 {
480 CHAR16 CommandUnicode[65];
481
482 AsciiStrToUnicodeStr (Command, CommandUnicode);
483
484 if (AsciiStrCmp (Command, "Demonstrate") == 0) {
485 DEBUG ((EFI_D_ERROR, "ARM OEM Fastboot command 'Demonstrate' received.\n"));
486 return EFI_SUCCESS;
487 } else {
488 DEBUG ((EFI_D_ERROR,
489 "VExpress: Unrecognised Fastboot OEM command: %s\n",
490 CommandUnicode
491 ));
492 return EFI_NOT_FOUND;
493 }
494 }
495
496 STATIC FASTBOOT_PLATFORM_PROTOCOL mPlatformProtocol = {
497 ArmFastbootPlatformInit,
498 ArmFastbootPlatformUnInit,
499 ArmFastbootPlatformFlashPartition,
500 ArmFastbootPlatformErasePartition,
501 ArmFastbootPlatformGetVar,
502 ArmFastbootPlatformOemCommand
503 };
504
505 EFI_STATUS
506 EFIAPI
507 ArmAndroidFastbootPlatformEntryPoint (
508 IN EFI_HANDLE ImageHandle,
509 IN EFI_SYSTEM_TABLE *SystemTable
510 )
511 {
512 return gBS->InstallProtocolInterface (
513 &ImageHandle,
514 &gAndroidFastbootPlatformProtocolGuid,
515 EFI_NATIVE_INTERFACE,
516 &mPlatformProtocol
517 );
518 }