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