aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons/Lesson_62/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'Lessons/Lesson_62/README.md')
-rw-r--r--Lessons/Lesson_62/README.md520
1 files changed, 520 insertions, 0 deletions
diff --git a/Lessons/Lesson_62/README.md b/Lessons/Lesson_62/README.md
new file mode 100644
index 0000000..356762f
--- /dev/null
+++ b/Lessons/Lesson_62/README.md
@@ -0,0 +1,520 @@
+In this lesson we will talk about `DevicePath` concept in UEFI.
+
+A Device Path is used to define the programmatic path to a device in UEFI environment.
+
+Internally `Device path` consists from the so called `Device Path nodes` which directly follow each other in memory. At the very end of every `Device path` there is a `Device Path node` of a special `End` type.
+
+Basically it looks something like this:
+```
+Device path =
+ <Device Path node>
+ ...
+ <Device Path node>
+ <Device Path node with the End type>
+```
+There is a possibility to encode several so called `Device Path instances` in the Device path. In this case Device Path would look a little bit more complicated:
+```
+Device path =
+ <Device Path node>
+ ...
+ <Device Path node>
+ <Device Path node with the End instance type>
+ <Device Path node>
+ ...
+ <Device Path node>
+ <Device Path node with the End instance type>
+ <Device Path node>
+ ...
+ <Device Path node>
+ <Device Path node with the End type>
+```
+
+Now let's look at the definition of the `<Device Path node>`. Each `<Device Path node>` has a `EFI_DEVICE_PATH_PROTOCOL` header. The size of this header is only 4 bytes. And after it immediately follows data in a format that is dictated by the values of the header Type/SubType's fields.
+
+https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/DevicePath.h
+```
+#pragma pack(1)
+
+typedef struct {
+ UINT8 Type; ///< 0x01 Hardware Device Path.
+ ///< 0x02 ACPI Device Path.
+ ///< 0x03 Messaging Device Path.
+ ///< 0x04 Media Device Path.
+ ///< 0x05 BIOS Boot Specification Device Path.
+ ///< 0x7F End of Hardware Device Path.
+
+ UINT8 SubType; ///< Varies by Type
+
+ UINT8 Length[2]; ///< Specific Device Path data. Type and Sub-Type define
+ ///< type of data. Size of data is included in Length.
+} EFI_DEVICE_PATH_PROTOCOL;
+```
+
+Currently there are 6 main types of Device Paths. You can see them in the Type field comment above. Each of these types has several subtypes. And each of these subtypes has a unique well defined format for its data. And all of them are defined in the UEFI specification.
+
+For the glance you could look at the structures in the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/DevicePath.h
+Most of the structures in this file corresponds to different data formatting based on the Type/SybType fields.
+
+The `Length` field defines full size of the `Device Node` including the header. `Length[0]` defines the lower byte and `Length[1]` defines higher byte of the length.
+
+For example PCI device has:
+- a type `#define HARDWARE_DEVICE_PATH 0x01`
+- a subtype `#define HW_PCI_DP 0x01`
+
+And the full path is encoded as this:
+```
+///
+/// PCI Device Path.
+///
+typedef struct {
+ EFI_DEVICE_PATH_PROTOCOL Header;
+ ///
+ /// PCI Function Number.
+ ///
+ UINT8 Function;
+ ///
+ /// PCI Device Number.
+ ///
+ UINT8 Device;
+} PCI_DEVICE_PATH;
+```
+If we'll unwind the `EFI_DEVICE_PATH_PROTOCOL` structure it would look like this:
+```
+{
+ UINT8 Type = HARDWARE_DEVICE_PATH = 0x01
+ UINT8 SubType = HW_PCI_DP = 0x01
+ UINT8 Length[0] = 0x06
+ UINT8 Length[1] = 0x00
+ UINT8 Function;
+ UINT8 Device;
+}
+```
+
+# Create Device Path/Device Node statically and dynamically
+
+Let's try to write an application to investigate how it is possible to work with `Device Path` in UEFI.
+
+Create an application, and include `<Library/DevicePathLib.h>` header to the source code. This is the header for the UEFI Device Path library (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/DevicePathLib.h).
+
+Let's assume that we want to create a device path for the PCI device with just a single node of a type `PCI_DEVICE_PATH`. Our theoretical path would refer to the PCI device `Function=5`/`Device=3`.
+
+
+The code for the static device node with such characteristics can look like this:
+```
+#define EXAMPLE_PCI_FUNCTION 5
+#define EXAMPLE_PCI_DEVICE 3
+
+PCI_DEVICE_PATH PciDevicePathNodeStatic;
+PciDevicePathNodeStatic.Header.Type = HARDWARE_DEVICE_PATH;
+PciDevicePathNodeStatic.Header.SubType = HW_PCI_DP;
+PciDevicePathNodeStatic.Header.Length[0] = sizeof(PCI_DEVICE_PATH);
+PciDevicePathNodeStatic.Header.Length[1] = 0;
+PciDevicePathNodeStatic.Function = EXAMPLE_PCI_FUNCTION;
+PciDevicePathNodeStatic.Device = EXAMPLE_PCI_DEVICE;
+```
+or like this (which is effectively the same):
+```
+#define EXAMPLE_PCI_FUNCTION 5
+#define EXAMPLE_PCI_DEVICE 3
+
+PCI_DEVICE_PATH PciDevicePathNodeStatic = {
+{
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_PCI_DP,
+ {
+ (UINT8) (sizeof (PCI_DEVICE_PATH)),
+ (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8)
+ }
+ },
+ EXAMPLE_PCI_FUNCTION,
+ EXAMPLE_PCI_DEVICE;
+};
+```
+
+A DevicePathLib has a function to print DeviceNodes in a human readable text form:
+```
+/**
+ Converts a device node to its string representation.
+ @param DeviceNode A Pointer to the device node to be converted.
+ @param DisplayOnly If DisplayOnly is TRUE, then the shorter text representation
+ of the display node is used, where applicable. If DisplayOnly
+ is FALSE, then the longer text representation of the display node
+ is used.
+ @param AllowShortcuts If AllowShortcuts is TRUE, then the shortcut forms of text
+ representation for a device node can be used, where applicable.
+ @return A pointer to the allocated text representation of the device node or NULL if DeviceNode
+ is NULL or there was insufficient memory.
+**/
+CHAR16 *
+EFIAPI
+ConvertDeviceNodeToText (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DeviceNode,
+ IN BOOLEAN DisplayOnly,
+ IN BOOLEAN AllowShortcuts
+ );
+```
+If we would use it to print our Device node like this:
+```
+Print(L"PciDevicePathNodeStatic: %s\n", ConvertDeviceNodeToText((EFI_DEVICE_PATH_PROTOCOL*) &PciDevicePathNodeStatic, FALSE, FALSE));
+```
+We would get:
+```
+FS0:\> DevicePath.efi
+PciDevicePathNodeStatic: Pci(0x3,0x5)
+```
+
+Now let's try to create Device Path node dynamically. For this we can utilize `CreateDeviceNode` function from the library:
+```
+/**
+ Creates a device node.
+ This function creates a new device node in a newly allocated buffer of size
+ NodeLength and initializes the device path node header with NodeType and NodeSubType.
+ The new device path node is returned.
+ If NodeLength is smaller than a device path header, then NULL is returned.
+ If there is not enough memory to allocate space for the new device path, then
+ NULL is returned.
+ The memory is allocated from EFI boot services memory. It is the responsibility
+ of the caller to free the memory allocated.
+ @param NodeType The device node type for the new device node.
+ @param NodeSubType The device node sub-type for the new device node.
+ @param NodeLength The length of the new device node.
+ @return The new device path.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+CreateDeviceNode (
+ UINT8 NodeType,
+ UINT8 NodeSubType,
+ UINT16 NodeLength
+ )
+```
+
+The code would look like this:
+```
+EFI_DEVICE_PATH_PROTOCOL* PciDevicePathNodeDynamic = CreateDeviceNode(HARDWARE_DEVICE_PATH, HW_PCI_DP, sizeof(PCI_DEVICE_PATH));
+((PCI_DEVICE_PATH*)PciDevicePathNodeDynamic)->Function = EXAMPLE_PCI_FUNCTION;
+((PCI_DEVICE_PATH*)PciDevicePathNodeDynamic)->Device = EXAMPLE_PCI_DEVICE;
+Print(L"PciDevicePathNodeDynamic: %s\n", ConvertDeviceNodeToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathNodeDynamic, FALSE, FALSE));
+```
+And it would gave us exactly the same result:
+```
+FS0:\> DevicePath.efi
+PciDevicePathNodeStatic: Pci(0x3,0x5)
+PciDevicePathNodeDynamic: Pci(0x3,0x5)
+```
+
+Now let's try to create complete Device Path. For this we need to add Device Node with the End type right after the current one that we've created.
+
+Once again we start with a static declaration method. We need to create our own structure type which would include both `PCI_DEVICE_PATH` node and the `End` node:
+```
+#pragma pack(1)
+typedef struct {
+ PCI_DEVICE_PATH PciDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} FULL_PCI_DEVICE_PATH;
+#pragma pack()
+
+
+FULL_PCI_DEVICE_PATH PciDevicePathStatic = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_PCI_DP,
+ {
+ (UINT8) (sizeof (PCI_DEVICE_PATH)),
+ (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8)
+ }
+ },
+ EXAMPLE_PCI_FUNCTION,
+ EXAMPLE_PCI_DEVICE
+ },
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ (UINT8) (END_DEVICE_PATH_LENGTH),
+ (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
+ }
+ }
+};
+```
+
+To print Device path as a string we can use `ConvertDevicePathToText` function. This function looks pretty similar to the `ConvertDeviceNodeToText` but do not confuse them! If you'll try to perform `ConvertDevicePathToText` not on the device path, but on the device node, the system would probably hang.
+```
+/**
+ Converts a device path to its text representation.
+ @param DevicePath A Pointer to the device to be converted.
+ @param DisplayOnly If DisplayOnly is TRUE, then the shorter text representation
+ of the display node is used, where applicable. If DisplayOnly
+ is FALSE, then the longer text representation of the display node
+ is used.
+ @param AllowShortcuts If AllowShortcuts is TRUE, then the shortcut forms of text
+ representation for a device node can be used, where applicable.
+ @return A pointer to the allocated text representation of the device path or
+ NULL if DeviceNode is NULL or there was insufficient memory.
+**/
+CHAR16 *
+EFIAPI
+ConvertDevicePathToText (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN BOOLEAN DisplayOnly,
+ IN BOOLEAN AllowShortcuts
+ );
+```
+
+Usage as simple as before:
+```
+Print(L"PciDevicePathStatic: %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) &PciDevicePathStatic, FALSE, FALSE));
+```
+Which would once again give us the same result (as we've only added the `End` node, which has no valuable string representation):
+```
+PciDevicePathStatic: Pci(0x3,0x5)
+```
+
+At last let's create Device Path dynamically. For this task we can utilize `AppendDevicePathNode` function:
+```
+/**
+ Creates a new path by appending the device node to the device path.
+ This function creates a new device path by appending a copy of the device node specified by
+ DevicePathNode to a copy of the device path specified by DevicePath in an allocated buffer.
+ The end-of-device-path device node is moved after the end of the appended device node.
+ If DevicePathNode is NULL then a copy of DevicePath is returned.
+ If DevicePath is NULL then a copy of DevicePathNode, followed by an end-of-device path device
+ node is returned.
+ If both DevicePathNode and DevicePath are NULL then a copy of an end-of-device-path device node
+ is returned.
+ If there is not enough memory to allocate space for the new device path, then NULL is returned.
+ The memory is allocated from EFI boot services memory. It is the responsibility of the caller to
+ free the memory allocated.
+ @param DevicePath A pointer to a device path data structure.
+ @param DevicePathNode A pointer to a single device path node.
+ @retval NULL There is not enough memory for the new device path.
+ @retval Others A pointer to the new device path if success.
+ A copy of DevicePathNode followed by an end-of-device-path node
+ if both FirstDevicePath and SecondDevicePath are NULL.
+ A copy of an end-of-device-path node if both FirstDevicePath and SecondDevicePath are NULL.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+AppendDevicePathNode (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePathNode OPTIONAL
+ );
+```
+As the comment says, if we'll supply NULL in place of the first argument, the function can add the necessary Device Node End to our Device Node. Thus effectively creating Device Path from the Device Node.
+```
+EFI_DEVICE_PATH_PROTOCOL* PciDevicePathDynamic = AppendDevicePathNode((EFI_DEVICE_PATH_PROTOCOL*)NULL, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathNodeDynamic);
+```
+If you'll try to print it:
+```
+Print(L"PciDevicePathDynamic: %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathDynamic, FALSE, FALSE));
+```
+Once again you would get the same result:
+```
+PciDevicePathDynamic: Pci(0x3,0x5)
+```
+
+# Multinode Device Paths
+
+When you boot UEFI shell it prints mapping table which shows Device Paths for mappings:
+```
+UEFI Interactive Shell v2.2
+EDK II
+UEFI v2.70 (EDK II, 0x00010000)
+Mapping table
+ FS0: Alias(s):HD0a1:;BLK1:
+ PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
+ BLK0: Alias(s):
+ PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
+ BLK2: Alias(s):
+ PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
+Press ESC in 5 seconds to skip startup.nsh or any other key to continue.
+```
+
+In the `PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)` path each text between the `/` corresponds to the Device Path node. For example this particular path consist from 5 Device nodes (don't forget the Device End node).
+
+Sane path for the PCI device would have `PciRoot` node before the `Pci` node. But it is not an UEFI specification task to check these things. UEFI specification only defines a structure for the Device Path that was analyzed in the beginning of the lesson.
+
+Therefore is is possible to create Device Path from any Device Nodes.
+
+Let's use the `AppendDevicePathNode` function that we've already know to add 3 more PCI nodes to our path:
+```
+PciDevicePathDynamic = AppendDevicePathNode((EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathDynamic, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathNodeDynamic);
+PciDevicePathDynamic = AppendDevicePathNode((EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathDynamic, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathNodeDynamic);
+PciDevicePathDynamic = AppendDevicePathNode((EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathDynamic, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathNodeDynamic);
+Print(L"Complicated DevicePath (AppendDevicePathNode): %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathDynamic, FALSE, FALSE));
+```
+As you might guess the `Print` statement would print this:
+```
+Complicated DevicePath (AppendDevicePathNode): Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)
+```
+
+We can even concatenate two Device Paths with a help of the similar `AppendDevicePath` function. This function would correctly concatenate Device Path data nodes, so there would be only one Device Path End node in the final path:
+```
+/**
+ Creates a new device path by appending a second device path to a first device path.
+ This function creates a new device path by appending a copy of SecondDevicePath to a copy of
+ FirstDevicePath in a newly allocated buffer. Only the end-of-device-path device node from
+ SecondDevicePath is retained. The newly created device path is returned.
+ If FirstDevicePath is NULL, then it is ignored, and a duplicate of SecondDevicePath is returned.
+ If SecondDevicePath is NULL, then it is ignored, and a duplicate of FirstDevicePath is returned.
+ If both FirstDevicePath and SecondDevicePath are NULL, then a copy of an end-of-device-path is
+ returned.
+ If there is not enough memory for the newly allocated buffer, then NULL is returned.
+ The memory for the new device path is allocated from EFI boot services memory. It is the
+ responsibility of the caller to free the memory allocated.
+ @param FirstDevicePath A pointer to a device path data structure.
+ @param SecondDevicePath A pointer to a device path data structure.
+ @retval NULL If there is not enough memory for the newly allocated buffer.
+ @retval NULL If FirstDevicePath or SecondDevicePath is invalid.
+ @retval Others A pointer to the new device path if success.
+ Or a copy an end-of-device-path if both FirstDevicePath and SecondDevicePath are NULL.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+AppendDevicePath (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *FirstDevicePath OPTIONAL,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *SecondDevicePath OPTIONAL
+ );
+```
+We can use it like this to double the data nodes in our path:
+```
+PciDevicePathDynamic = AppendDevicePath((EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathDynamic, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathDynamic);
+Print(L"Complicated DevicePath (AppendDevicePath): %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathDynamic, FALSE, FALSE));
+```
+And this would give us 8 PCI nodes instead of 4:
+```
+Complicated DevicePath (AppendDevicePath): Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)/Pci(0x3,0x5)
+```
+
+We should not forget the internals of the `AppendDevicePathNode`/`AppendDevicePath` functions! Every call creates a new buffer, but we shouldn't forget to free the old buffer, that we no longer need. Else we would have a memory leak on every call. So the more correct code would look like this:
+```
+EFI_DEVICE_PATH_PROTOCOL* PciDevicePathDynamicMulti;
+EFI_DEVICE_PATH_PROTOCOL* TempPath;
+
+PciDevicePathDynamicMulti = AppendDevicePathNode((EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathDynamic, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathNodeDynamic);
+TempPath = PciDevicePathDynamicMulti;
+PciDevicePathDynamicMulti = AppendDevicePathNode((EFI_DEVICE_PATH_PROTOCOL*)TempPath, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathNodeDynamic);
+FreePool(TempPath);
+TempPath = PciDevicePathDynamicMulti;
+PciDevicePathDynamicMulti = AppendDevicePathNode((EFI_DEVICE_PATH_PROTOCOL*)TempPath, (EFI_DEVICE_PATH_PROTOCOL*)PciDevicePathNodeDynamic);
+FreePool(TempPath);
+Print(L"Complicated DevicePath (AppendDevicePathNode): %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathDynamicMulti, FALSE, FALSE));
+
+TempPath = PciDevicePathDynamicMulti;
+PciDevicePathDynamicMulti = AppendDevicePath((EFI_DEVICE_PATH_PROTOCOL*)TempPath, (EFI_DEVICE_PATH_PROTOCOL*)TempPath);
+FreePool(TempPath);
+Print(L"Complicated DevicePath (AppendDevicePath): %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathDynamicMulti, FALSE, FALSE));
+```
+
+# Iterate over Device Path
+
+The library presents several functions to iterate throught the device paths. Since the only way to iterate Device Path is to walk through its Device Nodes one by one until the Device End Node would be found, the library presents two functions: `IsDevicePathEnd` and `NextDevicePathNode`.
+```
+/**
+ Determines if a device path node is an end node of an entire device path.
+ Determines if a device path node specified by Node is an end node of an entire device path.
+ If Node represents the end of an entire device path, then TRUE is returned.
+ Otherwise, FALSE is returned.
+ If Node is NULL, then ASSERT().
+ @param Node A pointer to a device path node data structure.
+ @retval TRUE The device path node specified by Node is the end of an entire device path.
+ @retval FALSE The device path node specified by Node is not the end of an entire device path.
+**/
+BOOLEAN
+EFIAPI
+IsDevicePathEnd (
+ IN CONST VOID *Node
+ );
+```
+```
+/**
+ Returns a pointer to the next node in a device path.
+ Returns a pointer to the device path node that follows the device path node specified by Node.
+ If Node is NULL, then ASSERT().
+ @param Node A pointer to a device path node data structure.
+ @return a pointer to the device path node that follows the device path node specified by Node.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+NextDevicePathNode (
+ IN CONST VOID *Node
+ );
+```
+
+Let's count how many PCI nodes our current Device Path has:
+```
+EFI_DEVICE_PATH_PROTOCOL* TempDevicePathNode = PciDevicePathDynamicMulti;
+UINT8 PciNodeCount = 0;
+while (!IsDevicePathEnd(TempDevicePathNode)) {
+ if ( (DevicePathType(TempDevicePathNode) == HARDWARE_DEVICE_PATH) && (DevicePathSubType(TempDevicePathNode) == HW_PCI_DP) )
+ PciNodeCount++;
+ TempDevicePathNode = NextDevicePathNode(TempDevicePathNode);
+}
+Print(L"Last device path has %d PCI nodes\n", PciNodeCount);
+```
+Here we've used `DevicePathType` and `DevicePathSubType` functions which are simple functions to access Node fields.
+
+If you'll try this code you would see the same result that we've calculated earlier:
+```
+Last device path has 8 PCI nodes
+```
+
+# Create DevicePath/DeviceNode from text
+
+All along this lesson we've converted device paths and nodes to text strings with a help of `ConvertDevicePathToText`/`ConvertDeviceNodeToText` functions.
+
+But actually library represent an opposite way of things. We can initialize Device Path/Device node from the text strings. For this task library has `ConvertTextToDevicePath` and `ConvertTextToDeviceNode` functions.
+```
+/**
+ Convert text to the binary representation of a device path.
+ @param TextDevicePath TextDevicePath points to the text representation of a device
+ path. Conversion starts with the first character and continues
+ until the first non-device node character.
+ @return A pointer to the allocated device path or NULL if TextDeviceNode is NULL or
+ there was insufficient memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+ConvertTextToDevicePath (
+ IN CONST CHAR16 *TextDevicePath
+ );
+```
+```
+/**
+ Convert text to the binary representation of a device node.
+ @param TextDeviceNode TextDeviceNode points to the text representation of a device
+ node. Conversion starts with the first character and continues
+ until the first non-device node character.
+ @return A pointer to the EFI device node or NULL if TextDeviceNode is NULL or there was
+ insufficient memory or text unsupported.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+ConvertTextToDeviceNode (
+ IN CONST CHAR16 *TextDeviceNode
+ );
+```
+
+We don't need to check UEFI specification, we've already know how our path looks in string representation, so let's use our knowledge:
+```
+EFI_DEVICE_PATH_PROTOCOL* PciDevicePathNodeFromText = ConvertTextToDeviceNode(L"Pci(0x3,0x5)");
+Print(L"PciDevicePathNodeFromText: %s\n", ConvertDeviceNodeToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathNodeFromText, FALSE, FALSE));
+EFI_DEVICE_PATH_PROTOCOL* PciDevicePathFromText = ConvertTextToDevicePath(L"Pci(0x3,0x5)");
+Print(L"PciDevicePathFromText: %s\n", ConvertDevicePathToText((EFI_DEVICE_PATH_PROTOCOL*) PciDevicePathFromText, FALSE, FALSE));
+```
+This would get us the expected result:
+```
+PciDevicePathNodeFromText: Pci(0x3,0x5)
+PciDevicePathFromText: Pci(0x3,0x5)
+```
+
+# Don't forget to free allocated memory
+
+This section is just a remainder that you shouldn't forget to free memory that was allocated for dynamically created paths and nodes:
+```
+FreePool(PciDevicePathNodeDynamic);
+FreePool(PciDevicePathDynamic);
+FreePool(PciDevicePathNodeFromText);
+FreePool(PciDevicePathFromText);
+```