UEFI News and Commentary

Monday, September 02, 2013

UEFI 2.4 Review, Part 8: NVMe Device Paths

This is the eighth part in a series examining in detail the changes made in the UEFI 2.4 specification. This time we focus on a series of related changes to supporting the NVMe specification, which "defines an optimized register interface, command set and feature set for PCI Express (PCIe®)-based Solid-State Drives (SSDs)."

Of course, if you want to boot from a device under UEFI, the standard mechanism is to write a driver that consumes the PCI I/O protocol and produces the Block I/O protocol. In fact, there is such a sample driver out on the tianocore.org EDK2 project repository (MdeModulePkg/Bus/Pci/NvmExpressDxe).

So if this support is standard (and both PCI I/O and Block I/O have been in the specification from day 1), then what is left to do? Good question. It all has to do with how UEFI represents hardware devices as device paths. A device path is a series of records (called nodes) that describe the logical path from the software running on the CPU through the various buses (like PCI and USB) and industry standard protocols (like SCSI or IPv4) until it reaches the logical or physical device. The UEFI firmware maintains a mapping between device paths and the handle associated with the device.

For example (using UEFI standard text representation of these binary records):

PciRoot(0)/PCI(1,0)/SAS(0x31000004CF13F6BD, 0, SAS)

This refers to a SAS (Serial-Attached SCSI) device attached to a SAS controller PCI device on device 1, function 0 on the first PCI root bus in the system. That device is further identified by 0x31000004cf13f6b (the SAS address), 0 (the SAS unit number) and the keyword SAS which refers to how the device has been configured.

Using the LocateDevicePath() function provided by UEFI, you can pass in a device path and the handle of the device (if any) will be returned to you. With that handle, you can then find other interfaces produced by the driver(s) managing that device, such as Block I/O.

What happens for devices based on newer or crazier technologies? Well, the UEFI specification allows you to create your own node type, called a "vendor-defined" type (see section of the UEFI specification). In that node, you place a GUID that you create to differentiate you from all other vendor-defined nodes. The UEFI firmware doesn't really care, it just performs binary matches. And, in fact, some so-called vendor-defined nodes have made there way into the specification (see SAS or VTF-100).

These nodes tend to get really long, because the GUID guarantees that it will take at least 16 extra bytes. So the UEFI specification added a new messaging device path node (type 23) which is much shorter, containing only the required bits to identify the device uniquely behind the NVMe(r) controller: the Namespace Identifier (4 bytes) and the Extended Unique Identifier (EUI-64). 16 bytes rather than the 32 bytes that would be required if the vendor-defined version was created.

Of course, it seems that the text representation of this node was left out of the update (see table 88). So, for now, NVM device paths look like this:

PciRoot(0)/PCI(1,0)/Msg(23, 000000000101010101010101)

I expect they'll make the text version prettier. But for now, the binary version is short and sweet and lets us find and boot from NVMe devices in UEFI 2.4.


Igor Sharovar said...

When will be real UEFI implementations, which will contain NVME bus driver? When could we start to use NVME UEFI boot?

Tim Lewis said...

Currently EDK2 already supports the standard PCI NVMe host bus adapter, at MdeModulePkg/Bus/Pci/NvmExpressDxe, including an implementation of pass-thru.

Igor Sharovar said...

Tim, I mean the real UEFI nvme implementation. Is there any available UEFI nvme boot from nvme devices? I spoke with some hardware vendors about nvme boot and they said such feature very likely would be available in one year for now.

Tim Lewis said...

Not sure what you mean. I have seen booting operating systems from NVMe today. From a UEFI 2.4 perspective, this is all done in the driver I mentioned. From an OS perspective, there must be an OS-level host controller adapter driver.

Leonid Myravyev said...

I can't understud how it work.

I created new device path whis it. I wrote it in Boot###. I fix BootOrder. But how can I add LOAD_FILE_PROTOCOL to this node? What does create that node? How Boot Manager get this node from device path?

Maybe there are any examples?

Tim Lewis said...

There are very few drivers which produce LOAD_FILE, usually only for OEM-specific cases, or PXE boot. Normally, after your driver creates a Block I/O, the function ConnectController() will be run by the BIOS when booting. This function will pass the handle of your device (on which you installed the device path and block I/O) to all other drivers. Normally, Disk I/O will be installed, then the Partition driver will find the partitions on the device and a new Block I/O and DIsk I/O will be installed for each partition. Most EDK2 implementations support El Torito (for CD/DVD) and FAT32.

davidm671 said...

Anyway on an older UEFI bios that doesnt support nvme booting to force boot the nvme drive? I have modded the bios on my p8Z68 to load the NVME ffs files from a more recent mainboard and now can see the Intel 750 in the bios boot menu but nothing happens when selected. I assume the correct boot handle isn't being returned. How can I force it?