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