Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Rikitav/Unified.Firmware/llms.txt

Use this file to discover all available pages before exploring further.

DevicePathProtocolBase is the foundation of every device path node in Unified.Firmware. UEFI firmware uses device paths to unambiguously describe the physical or logical location of a boot device — from the PCI slot of a storage controller all the way down to the EFI application file on a specific GPT partition. Every concrete protocol class (such as PciProtocol, HardDriveProtocol, and FilePathProtocol) inherits from this abstract base and implements its binary marshalling contract so that full device path structures can be read from and written to UEFI NVRAM variables. Namespace: Unified.Firmware.BootService.Protocols Assembly: Unified.Firmware.BootService

Constructor

public abstract class DevicePathProtocolBase(DeviceProtocolType type, byte subType)
The primary constructor is a C# 12 primary constructor. Subclasses call it by passing their fixed Type and SubType values, establishing the node identity at construction time.
ParameterTypeDescription
typeDeviceProtocolTypeThe major device path category (e.g., Hardware, Media).
subTypebyteThe protocol-specific sub-type number within that category.

Properties

Type

public DeviceProtocolType Type { get; }
The major device path type assigned at construction. This value is written into the first byte of every serialized device path node header and is used during deserialization to dispatch to the correct registered protocol class.

SubType

public byte SubType { get; }
The protocol sub-type within the major Type category. Together, Type and SubType form the unique 2-byte key that identifies a specific protocol layout. For example, (Hardware, 1) identifies a PCI device path node.

Abstract Methods

GetSerializationDataLength

public abstract ushort GetSerializationDataLength();
Returns the number of bytes required to serialize the protocol’s own data fields. This count does not include the 4-byte node header (Type + SubType + Length). Implementations must return a value that accurately reflects the current object state, since it is used to write the Length field in the header before Serialize is called.

Deserialize

public abstract void Deserialize(BinaryReader reader, ushort length);
Reads the protocol’s data fields from a binary stream that has already been positioned past the 4-byte header. The length parameter carries the total node length as read from the header, which is useful for variable-length protocols (e.g., vendor-defined data or file paths).
ParameterTypeDescription
readerBinaryReaderStream positioned at the start of the protocol data (after the 4-byte header).
lengthushortTotal node length in bytes, including the 4-byte header.

Serialize

public abstract void Serialize(BinaryWriter writer);
Writes the protocol’s data fields to a binary stream. The caller is responsible for writing the 4-byte header before invoking this method. Implementations should write exactly GetSerializationDataLength() bytes.
ParameterTypeDescription
writerBinaryWriterStream to which the protocol data is written.

DeviceProtocolType Enum

DeviceProtocolType assigns a numeric category to each family of device path nodes. It is defined in Unified.Firmware.BootService.Protocols.
public enum DeviceProtocolType : byte
{
    Hardware = 0x01,  // Physical hardware on a system bus (PCI, memory-mapped, controller)
    ACPI     = 0x02,  // ACPI-enumerated devices
    Message  = 0x03,  // Messaging protocols (USB, SCSI, NVMe, network)
    Media    = 0x04,  // Partitions and file paths on storage media
    BIOS     = 0x05,  // BIOS Boot Specification device paths
    End      = 0x7F   // End of Hardware Device Path sentinel node
}
ValueByteDescription
Hardware0x01Physical hardware device paths (PCI, memory-mapped, controller, vendor).
ACPI0x02Devices enumerated by the ACPI namespace.
Message0x03Messaging protocols such as USB, SCSI, NVMe, and networking.
Media0x04Partitions and file paths on storage media.
BIOS0x05BIOS Boot Specification device paths.
End0x7FSentinel node marking the end of a device path structure.

DefineDevicePathProtocolAttribute

DefineDevicePathProtocolAttribute is a class-level attribute that registers a DevicePathProtocolBase subclass for automatic deserialization. When the library reads a raw device path node from NVRAM, it looks up the (Type, SubType) pair in a registry of all attributed classes and instantiates the matching type before calling Deserialize.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class DefineDevicePathProtocolAttribute(DeviceProtocolType type, byte subType) : Attribute
{
    public DeviceProtocolType Type { get; } = type;
    public byte SubType { get; }           = subType;
}
Apply it to any concrete protocol class to make it discoverable:
[DefineDevicePathProtocol(DeviceProtocolType.Hardware, 1)]
public sealed class PciProtocol() : DevicePathProtocolBase(DeviceProtocolType.Hardware, 1)
{
    // ...
}
Each (Type, SubType) pair must be unique across all registered classes in the application domain. Registering two classes with the same pair causes an ambiguous match during deserialization.

Implementing a Custom Protocol

The following example shows how to implement a fully functional custom device path node. The attribute registration ensures the class is resolved automatically when the library encounters (Hardware, 0x42) in a raw device path.
using System.IO;
using Unified.Firmware.BootService.Protocols;

// Register this class for automatic deserialization when
// a node with Type=Hardware, SubType=0x42 is encountered.
[DefineDevicePathProtocol(DeviceProtocolType.Hardware, 0x42)]
public sealed class MyCustomProtocol() : DevicePathProtocolBase(DeviceProtocolType.Hardware, 0x42)
{
    /// <summary>A 32-bit identifier specific to this protocol.</summary>
    public uint DeviceId { get; set; }

    /// <summary>An 8-bit flags field.</summary>
    public byte Flags { get; set; }

    // 4 bytes (uint) + 1 byte (byte) = 5 bytes of payload
    public override ushort GetSerializationDataLength() => 5;

    public override void Deserialize(BinaryReader reader, ushort length)
    {
        DeviceId = reader.ReadUInt32();
        Flags    = reader.ReadByte();
    }

    public override void Serialize(BinaryWriter writer)
    {
        writer.Write(DeviceId);
        writer.Write(Flags);
    }

    public override string ToString() => $"MyCustom(0x{DeviceId:X}, 0x{Flags:X})";
}

RawMediaDevicePath (Unknown Protocol Fallback)

When the deserializer encounters a (Type, SubType) pair for which no [DefineDevicePathProtocol]-attributed class is registered, it falls back to RawMediaDevicePath. This concrete class stores the entire node payload as a raw byte[] in its ProtocolData property, preserving the binary content without any interpretation.
// Produced automatically during deserialization for unrecognized nodes.
// Can also be constructed explicitly from raw bytes:
var raw = new RawMediaDevicePath(
    DeviceProtocolType.Hardware,
    subType: 0xFF,
    protocolData: new byte[] { 0x01, 0x02, 0x03 }
);
RawMediaDevicePath round-trips correctly through Serialize/Deserialize, so unrecognized nodes are never silently dropped when re-writing a device path back to firmware.

Binary Wire Format

Every serialized device path node begins with a fixed 4-byte header that precedes the protocol data written by Serialize:
OffsetSizeFieldDescription
01 byteTypeDeviceProtocolType value (e.g., 0x04 for Media).
11 byteSubTypeProtocol-specific sub-type (e.g., 0x01 for Hard Drive).
2–32 bytesLengthTotal node size in bytes, including this header (little-endian). Length = 4 + GetSerializationDataLength().
The value returned by GetSerializationDataLength() must therefore exclude these 4 header bytes.

Build docs developers (and LLMs) love