]> git.proxmox.com Git - mirror_edk2.git/blob - MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.c
Update copyright for files modified in this year
[mirror_edk2.git] / MdePkg / Library / UefiDevicePathLib / UefiDevicePathLib.c
1 /** @file
2 Device Path services. The thing to remember is device paths are built out of
3 nodes. The device path is terminated by an end node that is length
4 sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is sizeof(EFI_DEVICE_PATH_PROTOCOL)
5 all over this file.
6
7 The only place where multi-instance device paths are supported is in
8 environment varibles. Multi-instance device paths should never be placed
9 on a Handle.
10
11 Copyright (c) 2006 - 2008, Intel Corporation
12 All rights reserved. This program and the accompanying materials
13 are licensed and made available under the terms and conditions of the BSD License
14 which accompanies this distribution. The full text of the license may be found at
15 http://opensource.org/licenses/bsd-license.php
16
17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19
20 **/
21
22
23 #include <Uefi.h>
24
25 #include <Protocol/DevicePath.h>
26
27 #include <Library/DevicePathLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/DebugLib.h>
30 #include <Library/MemoryAllocationLib.h>
31 #include <Library/UefiBootServicesTableLib.h>
32 #include <Library/BaseLib.h>
33
34 //
35 // Template for an end-of-device path node.
36 //
37 GLOBAL_REMOVE_IF_UNREFERENCED CONST EFI_DEVICE_PATH_PROTOCOL mUefiDevicePathLibEndDevicePath = {
38 END_DEVICE_PATH_TYPE,
39 END_ENTIRE_DEVICE_PATH_SUBTYPE,
40 {
41 END_DEVICE_PATH_LENGTH,
42 0
43 }
44 };
45
46 /**
47 Returns the size of a device path in bytes.
48
49 This function returns the size, in bytes, of the device path data structure specified by
50 DevicePath including the end of device path node. If DevicePath is NULL, then 0 is returned.
51
52 @param DevicePath A pointer to a device path data structure.
53
54 @retval 0 If DevicePath is NULL.
55 @retval Others The size of a device path in bytes.
56
57 **/
58 UINTN
59 EFIAPI
60 GetDevicePathSize (
61 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
62 )
63 {
64 CONST EFI_DEVICE_PATH_PROTOCOL *Start;
65
66 if (DevicePath == NULL) {
67 return 0;
68 }
69
70 //
71 // Search for the end of the device path structure
72 //
73 Start = DevicePath;
74 while (!IsDevicePathEnd (DevicePath)) {
75 DevicePath = NextDevicePathNode (DevicePath);
76 }
77
78 //
79 // Compute the size and add back in the size of the end device path structure
80 //
81 return ((UINTN) DevicePath - (UINTN) Start) + DevicePathNodeLength (DevicePath);
82 }
83
84 /**
85 Creates a new device path by appending a second device path to a first device path.
86
87 This function allocates space for a new copy of the device path specified by DevicePath. If
88 DevicePath is NULL, then NULL is returned. If the memory is successfully allocated, then the
89 contents of DevicePath are copied to the newly allocated buffer, and a pointer to that buffer
90 is returned. Otherwise, NULL is returned.
91 The memory for the new device path is allocated from EFI boot services memory.
92 It is the responsibility of the caller to free the memory allocated.
93
94 @param DevicePath A pointer to a device path data structure.
95
96 @retval NULL If DevicePath is NULL.
97 @retval Others A pointer to the duplicated device path.
98
99 **/
100 EFI_DEVICE_PATH_PROTOCOL *
101 EFIAPI
102 DuplicateDevicePath (
103 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
104 )
105 {
106 UINTN Size;
107
108 //
109 // Compute the size
110 //
111 Size = GetDevicePathSize (DevicePath);
112 if (Size == 0) {
113 return NULL;
114 }
115
116 //
117 // Allocate space for duplicate device path
118 //
119
120 return AllocateCopyPool (Size, DevicePath);
121 }
122
123 /**
124 Creates a new device path by appending a second device path to a first device path.
125
126 This function creates a new device path by appending a copy of SecondDevicePath to a copy of
127 FirstDevicePath in a newly allocated buffer. Only the end-of-device-path device node from
128 SecondDevicePath is retained. The newly created device path is returned.
129 If FirstDevicePath is NULL, then it is ignored, and a duplicate of SecondDevicePath is returned.
130 If SecondDevicePath is NULL, then it is ignored, and a duplicate of FirstDevicePath is returned.
131 If both FirstDevicePath and SecondDevicePath are NULL, then a copy of an end-of-device-path is
132 returned.
133 If there is not enough memory for the newly allocated buffer, then NULL is returned.
134 The memory for the new device path is allocated from EFI boot services memory. It is the
135 responsibility of the caller to free the memory allocated.
136
137 @param FirstDevicePath A pointer to a device path data structure.
138 @param SecondDevicePath A pointer to a device path data structure.
139
140 @retval NULL If there is not enough memory for the newly allocated buffer.
141 @retval Others A pointer to the new device path if success.
142 Or a copy an end-of-device-path if both FirstDevicePath and SecondDevicePath are NULL.
143
144 **/
145 EFI_DEVICE_PATH_PROTOCOL *
146 EFIAPI
147 AppendDevicePath (
148 IN CONST EFI_DEVICE_PATH_PROTOCOL *FirstDevicePath, OPTIONAL
149 IN CONST EFI_DEVICE_PATH_PROTOCOL *SecondDevicePath OPTIONAL
150 )
151 {
152 UINTN Size;
153 UINTN Size1;
154 UINTN Size2;
155 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
156 EFI_DEVICE_PATH_PROTOCOL *DevicePath2;
157
158 //
159 // If there's only 1 path, just duplicate it.
160 //
161 if (FirstDevicePath == NULL) {
162 return DuplicateDevicePath ((SecondDevicePath != NULL) ? SecondDevicePath : &mUefiDevicePathLibEndDevicePath);
163 }
164
165 if (SecondDevicePath == NULL) {
166 return DuplicateDevicePath (FirstDevicePath);
167 }
168
169 //
170 // Allocate space for the combined device path. It only has one end node of
171 // length EFI_DEVICE_PATH_PROTOCOL.
172 //
173 Size1 = GetDevicePathSize (FirstDevicePath);
174 Size2 = GetDevicePathSize (SecondDevicePath);
175 Size = Size1 + Size2 - END_DEVICE_PATH_LENGTH;
176
177 NewDevicePath = AllocatePool (Size);
178
179 if (NewDevicePath != NULL) {
180 NewDevicePath = CopyMem (NewDevicePath, FirstDevicePath, Size1);
181 //
182 // Over write FirstDevicePath EndNode and do the copy
183 //
184 DevicePath2 = (EFI_DEVICE_PATH_PROTOCOL *) ((CHAR8 *) NewDevicePath +
185 (Size1 - END_DEVICE_PATH_LENGTH));
186 CopyMem (DevicePath2, SecondDevicePath, Size2);
187 }
188
189 return NewDevicePath;
190 }
191
192 /**
193 Creates a new path by appending the device node to the device path.
194
195 This function creates a new device path by appending a copy of the device node specified by
196 DevicePathNode to a copy of the device path specified by DevicePath in an allocated buffer.
197 The end-of-device-path device node is moved after the end of the appended device node.
198 If DevicePathNode is NULL then a copy of DevicePath is returned.
199 If DevicePath is NULL then a copy of DevicePathNode, followed by an end-of-device path device
200 node is returned.
201 If both DevicePathNode and DevicePath are NULL then a copy of an end-of-device-path device node
202 is returned.
203 If there is not enough memory to allocate space for the new device path, then NULL is returned.
204 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to
205 free the memory allocated.
206
207 @param DevicePath A pointer to a device path data structure.
208 @param DevicePathNode A pointer to a single device path node.
209
210 @retval NULL If there is not enough memory for the new device path.
211 @retval Others A pointer to the new device path if success.
212 A copy of DevicePathNode followed by an end-of-device-path node
213 if both FirstDevicePath and SecondDevicePath are NULL.
214 A copy of an end-of-device-path node if both FirstDevicePath and SecondDevicePath are NULL.
215
216 **/
217 EFI_DEVICE_PATH_PROTOCOL *
218 EFIAPI
219 AppendDevicePathNode (
220 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, OPTIONAL
221 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePathNode OPTIONAL
222 )
223 {
224 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
225 EFI_DEVICE_PATH_PROTOCOL *NextNode;
226 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
227 UINTN NodeLength;
228
229 if (DevicePathNode == NULL) {
230 return DuplicateDevicePath ((DevicePath != NULL) ? DevicePath : &mUefiDevicePathLibEndDevicePath);
231 }
232 //
233 // Build a Node that has a terminator on it
234 //
235 NodeLength = DevicePathNodeLength (DevicePathNode);
236
237 TempDevicePath = AllocatePool (NodeLength + END_DEVICE_PATH_LENGTH);
238 if (TempDevicePath == NULL) {
239 return NULL;
240 }
241 TempDevicePath = CopyMem (TempDevicePath, DevicePathNode, NodeLength);
242 //
243 // Add and end device path node to convert Node to device path
244 //
245 NextNode = NextDevicePathNode (TempDevicePath);
246 SetDevicePathEndNode (NextNode);
247 //
248 // Append device paths
249 //
250 NewDevicePath = AppendDevicePath (DevicePath, TempDevicePath);
251
252 FreePool (TempDevicePath);
253
254 return NewDevicePath;
255 }
256
257 /**
258 Creates a new device path by appending the specified device path instance to the specified device
259 path.
260
261 This function creates a new device path by appending a copy of the device path instance specified
262 by DevicePathInstance to a copy of the device path secified by DevicePath in a allocated buffer.
263 The end-of-device-path device node is moved after the end of the appended device path instance
264 and a new end-of-device-path-instance node is inserted between.
265 If DevicePath is NULL, then a copy if DevicePathInstance is returned.
266 If DevicePathInstance is NULL, then NULL is returned.
267 If there is not enough memory to allocate space for the new device path, then NULL is returned.
268 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to
269 free the memory allocated.
270
271 @param DevicePath A pointer to a device path data structure.
272 @param DevicePathInstance A pointer to a device path instance.
273
274 @return A pointer to the new device path.
275
276 **/
277 EFI_DEVICE_PATH_PROTOCOL *
278 EFIAPI
279 AppendDevicePathInstance (
280 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, OPTIONAL
281 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance OPTIONAL
282 )
283 {
284 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
285 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
286 UINTN SrcSize;
287 UINTN InstanceSize;
288
289 if (DevicePath == NULL) {
290 return DuplicateDevicePath (DevicePathInstance);
291 }
292
293 if (DevicePathInstance == NULL) {
294 return NULL;
295 }
296
297 SrcSize = GetDevicePathSize (DevicePath);
298 InstanceSize = GetDevicePathSize (DevicePathInstance);
299
300 NewDevicePath = AllocatePool (SrcSize + InstanceSize);
301 if (NewDevicePath != NULL) {
302
303 TempDevicePath = CopyMem (NewDevicePath, DevicePath, SrcSize);;
304
305 while (!IsDevicePathEnd (TempDevicePath)) {
306 TempDevicePath = NextDevicePathNode (TempDevicePath);
307 }
308
309 TempDevicePath->SubType = END_INSTANCE_DEVICE_PATH_SUBTYPE;
310 TempDevicePath = NextDevicePathNode (TempDevicePath);
311 CopyMem (TempDevicePath, DevicePathInstance, InstanceSize);
312 }
313
314 return NewDevicePath;
315 }
316
317 /**
318 Creates a copy of the current device path instance and returns a pointer to the next device path
319 instance.
320
321 This function creates a copy of the current device path instance. It also updates DevicePath to
322 point to the next device path instance in the device path (or NULL if no more) and updates Size
323 to hold the size of the device path instance copy.
324 If DevicePath is NULL, then NULL is returned.
325 If there is not enough memory to allocate space for the new device path, then NULL is returned.
326 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to
327 free the memory allocated.
328 If Size is NULL, then ASSERT().
329
330 @param DevicePath On input, this holds the pointer to the current device path
331 instance. On output, this holds the pointer to the next device
332 path instance or NULL if there are no more device path
333 instances in the device path pointer to a device path data
334 structure.
335 @param Size On output, this holds the size of the device path instance, in
336 bytes or zero, if DevicePath is NULL.
337
338 @return A pointer to the current device path instance.
339
340 **/
341 EFI_DEVICE_PATH_PROTOCOL *
342 EFIAPI
343 GetNextDevicePathInstance (
344 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
345 OUT UINTN *Size
346 )
347 {
348 EFI_DEVICE_PATH_PROTOCOL *DevPath;
349 EFI_DEVICE_PATH_PROTOCOL *ReturnValue;
350 UINT8 Temp;
351
352 ASSERT (Size != NULL);
353
354 if (DevicePath == NULL || *DevicePath == NULL) {
355 *Size = 0;
356 return NULL;
357 }
358
359 //
360 // Find the end of the device path instance
361 //
362 DevPath = *DevicePath;
363 while (!IsDevicePathEndType (DevPath)) {
364 DevPath = NextDevicePathNode (DevPath);
365 }
366
367 //
368 // Compute the size of the device path instance
369 //
370 *Size = ((UINTN) DevPath - (UINTN) (*DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
371
372 //
373 // Make a copy and return the device path instance
374 //
375 Temp = DevPath->SubType;
376 DevPath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
377 ReturnValue = DuplicateDevicePath (*DevicePath);
378 DevPath->SubType = Temp;
379
380 //
381 // If DevPath is the end of an entire device path, then another instance
382 // does not follow, so *DevicePath is set to NULL.
383 //
384 if (DevicePathSubType (DevPath) == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
385 *DevicePath = NULL;
386 } else {
387 *DevicePath = NextDevicePathNode (DevPath);
388 }
389
390 return ReturnValue;
391 }
392
393 /**
394 Creates a device node.
395
396 This function creates a new device node in a newly allocated buffer of size NodeLength and
397 initializes the device path node header with NodeType and NodeSubType. The new device path node
398 is returned.
399 If NodeLength is smaller than a device path header, then NULL is returned.
400 If there is not enough memory to allocate space for the new device path, then NULL is returned.
401 The memory is allocated from EFI boot services memory. It is the responsibility of the caller to
402 free the memory allocated.
403
404 @param NodeType The device node type for the new device node.
405 @param NodeSubType The device node sub-type for the new device node.
406 @param NodeLength The length of the new device node.
407
408 @return The new device path.
409
410 **/
411 EFI_DEVICE_PATH_PROTOCOL *
412 EFIAPI
413 CreateDeviceNode (
414 IN UINT8 NodeType,
415 IN UINT8 NodeSubType,
416 IN UINT16 NodeLength
417 )
418 {
419 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
420
421 if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
422 //
423 // NodeLength is less than the size of the header.
424 //
425 return NULL;
426 }
427
428 DevicePath = AllocateZeroPool (NodeLength);
429 if (DevicePath != NULL) {
430 DevicePath->Type = NodeType;
431 DevicePath->SubType = NodeSubType;
432 SetDevicePathNodeLength (DevicePath, NodeLength);
433 }
434
435 return DevicePath;
436 }
437
438 /**
439 Determines if a device path is single or multi-instance.
440
441 This function returns TRUE if the device path specified by DevicePath is multi-instance.
442 Otherwise, FALSE is returned. If DevicePath is NULL, then FALSE is returned.
443
444 @param DevicePath A pointer to a device path data structure.
445
446 @retval TRUE DevicePath is multi-instance.
447 @retval FALSE DevicePath is not multi-instance or DevicePath is NULL.
448
449 **/
450 BOOLEAN
451 EFIAPI
452 IsDevicePathMultiInstance (
453 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
454 )
455 {
456 CONST EFI_DEVICE_PATH_PROTOCOL *Node;
457
458 if (DevicePath == NULL) {
459 return FALSE;
460 }
461
462 Node = DevicePath;
463 while (!IsDevicePathEnd (Node)) {
464 if (IsDevicePathEndInstance (Node)) {
465 return TRUE;
466 }
467
468 Node = NextDevicePathNode (Node);
469 }
470
471 return FALSE;
472 }
473
474
475 /**
476 Retrieves the device path protocol from a handle.
477
478 This function returns the device path protocol from the handle specified by Handle. If Handle is
479 NULL or Handle does not contain a device path protocol, then NULL is returned.
480
481 @param Handle The handle from which to retrieve the device path protocol.
482
483 @return The device path protocol from the handle specified by Handle.
484
485 **/
486 EFI_DEVICE_PATH_PROTOCOL *
487 EFIAPI
488 DevicePathFromHandle (
489 IN EFI_HANDLE Handle
490 )
491 {
492 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
493 EFI_STATUS Status;
494
495 Status = gBS->HandleProtocol (
496 Handle,
497 &gEfiDevicePathProtocolGuid,
498 (VOID *) &DevicePath
499 );
500 if (EFI_ERROR (Status)) {
501 DevicePath = NULL;
502 }
503 return DevicePath;
504 }
505
506 /**
507 Allocates a device path for a file and appends it to an existing device path.
508
509 If Device is a valid device handle that contains a device path protocol, then a device path for
510 the file specified by FileName is allocated and appended to the device path associated with the
511 handle Device. The allocated device path is returned. If Device is NULL or Device is a handle
512 that does not support the device path protocol, then a device path containing a single device
513 path node for the file specified by FileName is allocated and returned.
514 The memory for the new device path is allocated from EFI boot services memory. It is the responsibility
515 of the caller to free the memory allocated.
516
517 If FileName is NULL, then ASSERT().
518 If FileName is not aligned on a 16-bit boundary, then ASSERT().
519
520 @param Device A pointer to a device handle. This parameter is optional and
521 may be NULL.
522 @param FileName A pointer to a Null-terminated Unicode string.
523
524 @return The allocated device path.
525
526 **/
527 EFI_DEVICE_PATH_PROTOCOL *
528 EFIAPI
529 FileDevicePath (
530 IN EFI_HANDLE Device, OPTIONAL
531 IN CONST CHAR16 *FileName
532 )
533 {
534 UINT16 Size;
535 FILEPATH_DEVICE_PATH *FilePath;
536 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
537 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
538
539 DevicePath = NULL;
540
541 Size = (UINT16) StrSize (FileName);
542
543 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH);
544 if (FileDevicePath != NULL) {
545 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
546 FilePath->Header.Type = MEDIA_DEVICE_PATH;
547 FilePath->Header.SubType = MEDIA_FILEPATH_DP;
548 CopyMem (&FilePath->PathName, FileName, Size);
549 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
550 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
551
552 if (Device != NULL) {
553 DevicePath = DevicePathFromHandle (Device);
554 }
555
556 DevicePath = AppendDevicePath (DevicePath, FileDevicePath);
557 FreePool (FileDevicePath);
558 }
559
560 return DevicePath;
561 }
562