1 /*++
2 
3 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 Module Name:
13 
14   RtDevicePath.c
15 
16 Abstract:
17 
18   Device Path services. The thing to remember is device paths are built out of
19   nodes. The device path is terminated by an end node that is length
20   sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is sizeof(EFI_DEVICE_PATH_PROTOCOL)
21   all over this file.
22 
23   The only place where multi-instance device paths are supported is in
24   environment varibles. Multi-instance device paths should never be placed
25   on a Handle.
26 
27 --*/
28 
29 #include "Tiano.h"
30 #include "EfiRuntimeLib.h"
31 #include "RtDevicePath.h"
32 #include EFI_GUID_DEFINITION (FrameworkDevicePath)
33 #include EFI_PROTOCOL_DEFINITION (DevicePath)
34 
35 STATIC
36 VOID *
InternalAllocatePool(IN UINTN AllocationSize)37 InternalAllocatePool (
38   IN  UINTN   AllocationSize
39   )
40 /*++
41 
42 Routine Description:
43 
44   Allocate BootServicesData pool.
45 
46 Arguments:
47 
48   AllocationSize  - The size to allocate
49 
50 Returns:
51 
52   Pointer of the buffer allocated.
53 
54 --*/
55 {
56   VOID  *Memory;
57 
58   Memory = NULL;
59   gBS->AllocatePool (EfiBootServicesData, AllocationSize, &Memory);
60   return Memory;
61 }
62 
63 STATIC
64 VOID *
InternalAllocateCopyPool(IN UINTN AllocationSize,IN VOID * Buffer)65 InternalAllocateCopyPool (
66   IN  UINTN   AllocationSize,
67   IN  VOID    *Buffer
68   )
69 /*++
70 
71 Routine Description:
72 
73   Allocate BootServicesData pool and use a buffer provided by
74   caller to fill it.
75 
76 Arguments:
77 
78   AllocationSize  - The size to allocate
79 
80   Buffer          - Buffer that will be filled into the buffer allocated
81 
82 Returns:
83 
84   Pointer of the buffer allocated.
85 
86 --*/
87 {
88   VOID  *Memory;
89 
90   Memory = NULL;
91   gBS->AllocatePool (EfiBootServicesData, AllocationSize, &Memory);
92   if (Memory != NULL) {
93     gBS->CopyMem (Memory, Buffer, AllocationSize);
94   }
95 
96   return Memory;
97 }
98 
99 STATIC
100 VOID *
InternalAllocateZeroPool(IN UINTN AllocationSize)101 InternalAllocateZeroPool (
102   IN  UINTN   AllocationSize
103   )
104 /*++
105 
106 Routine Description:
107 
108   Allocate BootServicesData pool and zero it.
109 
110 Arguments:
111 
112   AllocationSize  - The size to allocate
113 
114 Returns:
115 
116   Pointer of the buffer allocated.
117 
118 --*/
119 {
120   VOID  *Memory;
121 
122   Memory = InternalAllocatePool (AllocationSize);
123   if (Memory != NULL) {
124     gBS->SetMem (Memory, AllocationSize, 0);
125   }
126 
127   return Memory;
128 }
129 
130 EFI_DEVICE_PATH_PROTOCOL *
RtEfiDevicePathInstance(IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath,OUT UINTN * Size)131 RtEfiDevicePathInstance (
132   IN OUT EFI_DEVICE_PATH_PROTOCOL   **DevicePath,
133   OUT UINTN                         *Size
134   )
135 /*++
136 
137 Routine Description:
138   Function retrieves the next device path instance from a device path data structure.
139 
140 Arguments:
141   DevicePath           - A pointer to a device path data structure.
142 
143   Size                 - A pointer to the size of a device path instance in bytes.
144 
145 Returns:
146 
147   This function returns a pointer to the current device path instance.
148   In addition, it returns the size in bytes of the current device path instance in Size,
149   and a pointer to the next device path instance in DevicePath.
150   If there are no more device path instances in DevicePath, then DevicePath will be set to NULL.
151 
152 --*/
153 {
154   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
155   EFI_DEVICE_PATH_PROTOCOL  *ReturnValue;
156   UINT8                     Temp;
157 
158   if (*DevicePath == NULL) {
159     if (Size != NULL) {
160       *Size = 0;
161     }
162 
163     return NULL;
164   }
165 
166   //
167   // Find the end of the device path instance
168   //
169   DevPath = *DevicePath;
170   while (!IsDevicePathEndType (DevPath)) {
171     DevPath = NextDevicePathNode (DevPath);
172   }
173 
174   //
175   // Compute the size of the device path instance
176   //
177   if (Size != NULL) {
178     *Size = ((UINTN) DevPath - (UINTN) (*DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
179   }
180 
181   //
182   // Make a copy and return the device path instance
183   //
184   Temp              = DevPath->SubType;
185   DevPath->SubType  = END_ENTIRE_DEVICE_PATH_SUBTYPE;
186   ReturnValue       = RtEfiDuplicateDevicePath (*DevicePath);
187   DevPath->SubType  = Temp;
188 
189   //
190   // If DevPath is the end of an entire device path, then another instance
191   // does not follow, so *DevicePath is set to NULL.
192   //
193   if (DevicePathSubType (DevPath) == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
194     *DevicePath = NULL;
195   } else {
196     *DevicePath = NextDevicePathNode (DevPath);
197   }
198 
199   return ReturnValue;
200 }
201 
202 BOOLEAN
RtEfiIsDevicePathMultiInstance(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)203 RtEfiIsDevicePathMultiInstance (
204   IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath
205   )
206 /*++
207 
208 Routine Description:
209   Return TRUE is this is a multi instance device path.
210 
211 Arguments:
212   DevicePath  - A pointer to a device path data structure.
213 
214 
215 Returns:
216   TRUE - If DevicePath is multi instance. FALSE - If DevicePath is not multi
217   instance.
218 
219 --*/
220 {
221   EFI_DEVICE_PATH_PROTOCOL  *Node;
222 
223   if (DevicePath == NULL) {
224     return FALSE;
225   }
226 
227   Node = DevicePath;
228   while (!EfiIsDevicePathEnd (Node)) {
229     if (EfiIsDevicePathEndInstance (Node)) {
230       return TRUE;
231     }
232 
233     Node = EfiNextDevicePathNode (Node);
234   }
235 
236   return FALSE;
237 }
238 
239 UINTN
RtEfiDevicePathSize(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)240 RtEfiDevicePathSize (
241   IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath
242   )
243 /*++
244 
245 Routine Description:
246 
247   Calculate the space size of a device path.
248 
249 Arguments:
250 
251   DevicePath  - A specified device path
252 
253 Returns:
254 
255   The size.
256 
257 --*/
258 {
259   EFI_DEVICE_PATH_PROTOCOL  *Start;
260 
261   if (DevicePath == NULL) {
262     return 0;
263   }
264 
265   //
266   // Search for the end of the device path structure
267   //
268   Start = DevicePath;
269   while (!EfiIsDevicePathEnd (DevicePath)) {
270     DevicePath = EfiNextDevicePathNode (DevicePath);
271   }
272 
273   //
274   // Compute the size and add back in the size of the end device path structure
275   //
276   return ((UINTN) DevicePath - (UINTN) Start) + sizeof (EFI_DEVICE_PATH_PROTOCOL);
277 }
278 
279 EFI_DEVICE_PATH_PROTOCOL *
RtEfiDevicePathFromHandle(IN EFI_HANDLE Handle)280 RtEfiDevicePathFromHandle (
281   IN EFI_HANDLE  Handle
282   )
283 /*++
284 
285 Routine Description:
286 
287   Get the device path protocol interface installed on a specified handle.
288 
289 Arguments:
290 
291   Handle  - a specified handle
292 
293 Returns:
294 
295   The device path protocol interface installed on that handle.
296 
297 --*/
298 {
299   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
300 
301   DevicePath = NULL;
302   gBS->HandleProtocol (
303         Handle,
304         &gEfiDevicePathProtocolGuid,
305         (VOID *) &DevicePath
306         );
307   return DevicePath;
308 }
309 
310 EFI_DEVICE_PATH_PROTOCOL *
RtEfiDuplicateDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)311 RtEfiDuplicateDevicePath (
312   IN EFI_DEVICE_PATH_PROTOCOL   *DevicePath
313   )
314 /*++
315 
316 Routine Description:
317 
318   Duplicate a device path structure.
319 
320 Arguments:
321 
322   DevicePath  - The device path to duplicated.
323 
324 Returns:
325 
326   The duplicated device path.
327 
328 --*/
329 {
330   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
331   UINTN                     Size;
332 
333   if (DevicePath == NULL) {
334     return NULL;
335   }
336 
337   //
338   // Compute the size
339   //
340   Size = RtEfiDevicePathSize (DevicePath);
341   if (Size == 0) {
342     return NULL;
343   }
344 
345   //
346   // Allocate space for duplicate device path
347   //
348   NewDevicePath = InternalAllocateCopyPool (Size, DevicePath);
349 
350   return NewDevicePath;
351 }
352 
353 EFI_DEVICE_PATH_PROTOCOL *
RtEfiAppendDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * Src1,IN EFI_DEVICE_PATH_PROTOCOL * Src2)354 RtEfiAppendDevicePath (
355   IN EFI_DEVICE_PATH_PROTOCOL  *Src1,
356   IN EFI_DEVICE_PATH_PROTOCOL  *Src2
357   )
358 /*++
359 
360 Routine Description:
361   Function is used to append a Src1 and Src2 together.
362 
363 Arguments:
364   Src1  - A pointer to a device path data structure.
365 
366   Src2  - A pointer to a device path data structure.
367 
368 Returns:
369 
370   A pointer to the new device path is returned.
371   NULL is returned if space for the new device path could not be allocated from pool.
372   It is up to the caller to free the memory used by Src1 and Src2 if they are no longer needed.
373 
374 --*/
375 {
376   UINTN                     Size;
377   UINTN                     Size1;
378   UINTN                     Size2;
379   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
380   EFI_DEVICE_PATH_PROTOCOL  *SecondDevicePath;
381 
382   //
383   // If there's only 1 path, just duplicate it
384   //
385   if (!Src1) {
386     ASSERT (!IsDevicePathUnpacked (Src2));
387     return RtEfiDuplicateDevicePath (Src2);
388   }
389 
390   if (!Src2) {
391     ASSERT (!IsDevicePathUnpacked (Src1));
392     return RtEfiDuplicateDevicePath (Src1);
393   }
394 
395   //
396   // Allocate space for the combined device path. It only has one end node of
397   // length EFI_DEVICE_PATH_PROTOCOL
398   //
399   Size1         = RtEfiDevicePathSize (Src1);
400   Size2         = RtEfiDevicePathSize (Src2);
401   Size          = Size1 + Size2 - sizeof (EFI_DEVICE_PATH_PROTOCOL);
402 
403   NewDevicePath = InternalAllocateCopyPool (Size, Src1);
404 
405   if (NewDevicePath != NULL) {
406 
407     //
408     // Over write Src1 EndNode and do the copy
409     //
410     SecondDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) ((CHAR8 *) NewDevicePath + (Size1 - sizeof (EFI_DEVICE_PATH_PROTOCOL)));
411     EfiCopyMem (SecondDevicePath, Src2, Size2);
412   }
413 
414   return NewDevicePath;
415 }
416 
417 EFI_DEVICE_PATH_PROTOCOL *
RtEfiAppendDevicePathNode(IN EFI_DEVICE_PATH_PROTOCOL * Src1,IN EFI_DEVICE_PATH_PROTOCOL * Node)418 RtEfiAppendDevicePathNode (
419   IN EFI_DEVICE_PATH_PROTOCOL  *Src1,
420   IN EFI_DEVICE_PATH_PROTOCOL  *Node
421   )
422 /*++
423 
424 Routine Description:
425   Function is used to append a device path node to the end of another device path.
426 
427 Arguments:
428   Src1  - A pointer to a device path data structure.
429 
430   Node - A pointer to a device path data structure.
431 
432 Returns:
433   This function returns a pointer to the new device path.
434   If there is not enough temporary pool memory available to complete this function,
435   then NULL is returned.
436 
437 
438 --*/
439 {
440   EFI_DEVICE_PATH_PROTOCOL  *Temp;
441   EFI_DEVICE_PATH_PROTOCOL  *NextNode;
442   EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
443   UINTN                     NodeLength;
444 
445   //
446   // Build a Node that has a terminator on it
447   //
448   NodeLength  = DevicePathNodeLength (Node);
449 
450   Temp        = InternalAllocateCopyPool (NodeLength + sizeof (EFI_DEVICE_PATH_PROTOCOL), Node);
451   if (Temp == NULL) {
452     return NULL;
453   }
454 
455   //
456   // Add and end device path node to convert Node to device path
457   //
458   NextNode = NextDevicePathNode (Temp);
459   SetDevicePathEndNode (NextNode);
460 
461   //
462   // Append device paths
463   //
464   NewDevicePath = RtEfiAppendDevicePath (Src1, Temp);
465   gBS->FreePool (Temp);
466   return NewDevicePath;
467 }
468 
469 EFI_DEVICE_PATH_PROTOCOL *
RtEfiFileDevicePath(IN EFI_HANDLE Device OPTIONAL,IN CHAR16 * FileName)470 RtEfiFileDevicePath (
471   IN EFI_HANDLE               Device  OPTIONAL,
472   IN CHAR16                   *FileName
473   )
474 /*++
475 
476 Routine Description:
477 
478   This function allocates a device path for a file and appends it to an existiong
479   device path.
480 
481 Arguments:
482   Device     - A pointer to a device handle.
483 
484   FileName   - A pointer to a Null-terminated Unicodestring.
485 
486 Returns:
487   A device path contain the file name.
488 
489 --*/
490 {
491   UINTN                     Size;
492   FILEPATH_DEVICE_PATH      *FilePath;
493   EFI_DEVICE_PATH_PROTOCOL  *Eop;
494   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
495 
496   for (Size = 0; FileName[Size] != 0; Size++)
497     ;
498   Size        = (Size + 1) * 2;
499 
500   FilePath    = InternalAllocateZeroPool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));
501 
502   DevicePath  = NULL;
503 
504   if (FilePath != NULL) {
505 
506     //
507     // Build a file path
508     //
509     FilePath->Header.Type     = MEDIA_DEVICE_PATH;
510     FilePath->Header.SubType  = MEDIA_FILEPATH_DP;
511     SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
512     EfiCopyMem (FilePath->PathName, FileName, Size);
513     Eop = NextDevicePathNode (&FilePath->Header);
514     SetDevicePathEndNode (Eop);
515 
516     //
517     // Append file path to device's device path
518     //
519 
520     DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) FilePath;
521     if (Device != NULL) {
522       DevicePath = RtEfiAppendDevicePath (
523                     RtEfiDevicePathFromHandle (Device),
524                     DevicePath
525                     );
526 
527       gBS->FreePool (FilePath);
528     }
529   }
530 
531   return DevicePath;
532 }
533 
534 EFI_DEVICE_PATH_PROTOCOL *
RtEfiAppendDevicePathInstance(IN EFI_DEVICE_PATH_PROTOCOL * Src,IN EFI_DEVICE_PATH_PROTOCOL * Instance)535 RtEfiAppendDevicePathInstance (
536   IN EFI_DEVICE_PATH_PROTOCOL  *Src,
537   IN EFI_DEVICE_PATH_PROTOCOL  *Instance
538   )
539 /*++
540 
541 Routine Description:
542 
543   Append a device path instance to another.
544 
545 Arguments:
546 
547   Src       - The device path instance to be appended with.
548   Instance  - The device path instance appending the other.
549 
550 Returns:
551 
552   The contaction of these two.
553 
554 --*/
555 {
556   UINT8                     *Ptr;
557   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
558   UINTN                     SrcSize;
559   UINTN                     InstanceSize;
560 
561   if (Src == NULL) {
562     return RtEfiDuplicateDevicePath (Instance);
563   }
564 
565   SrcSize       = RtEfiDevicePathSize (Src);
566   InstanceSize  = RtEfiDevicePathSize (Instance);
567 
568   Ptr           = InternalAllocateCopyPool (SrcSize + InstanceSize, Src);
569   if (Ptr != NULL) {
570 
571     DevPath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
572 
573     while (!IsDevicePathEnd (DevPath)) {
574       DevPath = NextDevicePathNode (DevPath);
575     }
576     //
577     // Convert the End to an End Instance, since we are
578     //  appending another instacne after this one its a good
579     //  idea.
580     //
581     DevPath->SubType  = END_INSTANCE_DEVICE_PATH_SUBTYPE;
582 
583     DevPath           = NextDevicePathNode (DevPath);
584     EfiCopyMem (DevPath, Instance, InstanceSize);
585   }
586 
587   return (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
588 }
589 
590 VOID
591 EFIAPI
RtEfiInitializeFwVolDevicepathNode(IN MEDIA_FW_VOL_FILEPATH_DEVICE_PATH * FvDevicePathNode,IN EFI_GUID * NameGuid)592 RtEfiInitializeFwVolDevicepathNode (
593   IN  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH     *FvDevicePathNode,
594   IN EFI_GUID                               *NameGuid
595   )
596 /*++
597 
598 Routine Description:
599 
600   Initialize a Firmware Volume (FV) Media Device Path node.
601 
602 Arguments:
603 
604   FvDevicePathNode  - Pointer to a FV device path node to initialize
605   NameGuid          - FV file name to use in FvDevicePathNode
606 
607 Returns:
608 
609   None
610 
611 --*/
612 {
613   FvDevicePathNode->Header.Type     = MEDIA_DEVICE_PATH;
614   FvDevicePathNode->Header.SubType  = MEDIA_FV_FILEPATH_DP;
615   SetDevicePathNodeLength (&FvDevicePathNode->Header, sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH));
616 
617   EfiCopyMem (&FvDevicePathNode->NameGuid, NameGuid, sizeof(EFI_GUID));
618 }
619 
620 EFI_GUID *
621 EFIAPI
RtEfiGetNameGuidFromFwVolDevicePathNode(IN MEDIA_FW_VOL_FILEPATH_DEVICE_PATH * FvDevicePathNode)622 RtEfiGetNameGuidFromFwVolDevicePathNode (
623   IN  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH   *FvDevicePathNode
624   )
625 /*++
626 
627 Routine Description:
628 
629   Check to see if the Firmware Volume (FV) Media Device Path is valid.
630 
631 Arguments:
632 
633   FvDevicePathNode  - Pointer to FV device path to check
634 
635 Returns:
636 
637   NULL              - FvDevicePathNode is not valid.
638   Other             - FvDevicePathNode is valid and pointer to NameGuid was returned.
639 
640 --*/
641 {
642   if (DevicePathType (&FvDevicePathNode->Header) == MEDIA_DEVICE_PATH &&
643       DevicePathSubType (&FvDevicePathNode->Header) == MEDIA_FV_FILEPATH_DP) {
644     return &FvDevicePathNode->NameGuid;
645   }
646 
647   return NULL;
648 }
649 
650