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.
| Parameter | Type | Description |
|---|
type | DeviceProtocolType | The major device path category (e.g., Hardware, Media). |
subType | byte | The 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).
| Parameter | Type | Description |
|---|
reader | BinaryReader | Stream positioned at the start of the protocol data (after the 4-byte header). |
length | ushort | Total 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.
| Parameter | Type | Description |
|---|
writer | BinaryWriter | Stream 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
}
| Value | Byte | Description |
|---|
Hardware | 0x01 | Physical hardware device paths (PCI, memory-mapped, controller, vendor). |
ACPI | 0x02 | Devices enumerated by the ACPI namespace. |
Message | 0x03 | Messaging protocols such as USB, SCSI, NVMe, and networking. |
Media | 0x04 | Partitions and file paths on storage media. |
BIOS | 0x05 | BIOS Boot Specification device paths. |
End | 0x7F | Sentinel 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})";
}
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.
Every serialized device path node begins with a fixed 4-byte header that precedes the protocol data written by Serialize:| Offset | Size | Field | Description |
|---|
| 0 | 1 byte | Type | DeviceProtocolType value (e.g., 0x04 for Media). |
| 1 | 1 byte | SubType | Protocol-specific sub-type (e.g., 0x01 for Hard Drive). |
| 2–3 | 2 bytes | Length | Total node size in bytes, including this header (little-endian). Length = 4 + GetSerializationDataLength(). |
The value returned by GetSerializationDataLength() must therefore exclude these 4 header bytes.