/*
 * libefivar - library for the manipulation of EFI variables
 * Copyright 2012-2015 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#ifndef _EFIVAR_DP_H
#define _EFIVAR_DP_H 1

#include <limits.h>

#define efidp_encode_bitfield_(name, shift, mask)			\
	(((name) << (shift)) & (mask))
#define efidp_decode_bitfield_(value, name, shift, mask)		\
	({ (name) = ((value) & (mask)) >> (shift); })

#define efidp_size_after(dp, field)					\
	(efidp_size((const_efidp)dp) -					\
	 (offsetof(__typeof__ (*(dp)), field) + sizeof((dp)->field)))

#define EFIVAR_PACKED __attribute__((__packed__))

/* Generic device path header */
typedef struct {
	uint8_t type;
	uint8_t subtype;
	uint16_t length;
} EFIVAR_PACKED efidp_header;

/* A little bit of housekeeping... */
typedef uint8_t efidp_boolean;

/* Each of the top-level types */
#define	EFIDP_HARDWARE_TYPE	0x01
#define EFIDP_ACPI_TYPE		0x02
#define EFIDP_MESSAGE_TYPE	0x03
#define EFIDP_MEDIA_TYPE	0x04
#define EFIDP_BIOS_BOOT_TYPE	0x05
#define EFIDP_END_TYPE		0x7f

/* Each hardware subtype */
#define EFIDP_HW_PCI	0x01
typedef struct {
	efidp_header	header;
	uint8_t		function;
	uint8_t		device;
} EFIVAR_PACKED efidp_pci;
extern ssize_t efidp_make_pci(uint8_t *buf, ssize_t size, uint8_t device,
			      uint8_t function);

#define EFIDP_HW_PCCARD	0x02
typedef struct {
	efidp_header	header;
	uint8_t		function;
} EFIVAR_PACKED efidp_pccard;

#define EFIDP_HW_MMIO		0x03
typedef struct {
	efidp_header	header;
	uint32_t	memory_type;
	uint64_t	starting_address;
	uint64_t	ending_address;
} EFIVAR_PACKED efidp_mmio;

#define EFIDP_HW_VENDOR		0x04
typedef struct {
	efidp_header	header;
	efi_guid_t	vendor_guid;
	uint8_t		vendor_data[];
} EFIVAR_PACKED efidp_hw_vendor;
typedef efidp_hw_vendor efidp_vendor_hw;
#define efidp_make_hw_vendor(buf, size, guid, data, data_size)		\
	efidp_make_vendor(buf, size, EFIDP_HARDWARE_TYPE,		\
			  EFIDP_HW_VENDOR, guid, data, data_size)

#define EDD10_HARDWARE_VENDOR_PATH_GUID \
	EFI_GUID(0xCF31FAC5,0xC24E,0x11d2,0x85F3,0x00,0xA0,0xC9,0x3E,0xC9,0x3B)
typedef struct {
	efidp_header	header;
	efi_guid_t	vendor_guid;
	uint32_t	hardware_device;
} EFIVAR_PACKED efidp_edd10;
extern ssize_t efidp_make_edd10(uint8_t *buf, ssize_t size,
				uint32_t hardware_device);

#define EFIDP_HW_CONTROLLER	0x05
typedef struct {
	efidp_header	header;
	uint32_t	controller;
} EFIVAR_PACKED efidp_controller;

#define EFIDP_HW_BMC		0x06
typedef struct {
	efidp_header	header;
	uint8_t		interface_type;
	uint64_t	base_addr;
} EFIVAR_PACKED efidp_bmc;

#define EFIDP_BMC_UNKNOWN	0x00
#define EFIDP_BMC_KEYBOARD	0x01
#define EFIDP_BMC_SMIC		0x02
#define EFIDP_BMC_BLOCK		0x03


/* Each ACPI subtype */
#define EFIDP_ACPI_HID		0x01
typedef struct {
	efidp_header	header;
	uint32_t	hid;
	uint32_t	uid;
} EFIVAR_PACKED efidp_acpi_hid;
extern ssize_t efidp_make_acpi_hid(uint8_t *buf, ssize_t size, uint32_t hid,
				   uint32_t uid);

#define EFIDP_ACPI_HID_EX	0x02
typedef struct {
	efidp_header	header;
	uint32_t	hid;
	uint32_t	uid;
	uint32_t	cid;
	/* three ascii string fields follow */
	char		hidstr[];
} EFIVAR_PACKED efidp_acpi_hid_ex;
extern ssize_t
efidp_make_acpi_hid_ex(uint8_t *buf, ssize_t size,
                       uint32_t hid, uint32_t uid, uint32_t cid,
                       const char *hidstr, const char *uidstr,
                       const char *cidstr);

#define EFIDP_PNP_EISA_ID_CONST		0x41d0
#define EFIDP_PNP_ACPI_ID_CONST		0x8e09
#define EFIDP_EISA_ID(_Name, _Num)	((uint32_t)((_Name) | (_Num) << 16))
#define EFIDP_EISA_PNP_ID(_PNPId)	EFIDP_EISA_ID(EFIDP_PNP_EISA_ID_CONST,\
							(_PNPId))
#define EFIDP_EFI_PNP_ID(_PNPId)	EFIDP_EISA_ID(EFIDP_PNP_EISA_ID_CONST,\
							(_PNPId))
#define EFIDP_EFI_ACPI_ID(_HID)		EFIDP_EISA_ID(EFIDP_PNP_ACPI_ID_CONST,\
						      (_HID))

#define EFIDP_PNP_EISA_ID_MASK		0xffff
#define EFIDP_EISA_ID_TO_NUM(_Id)	((_Id) >> 16)
#define EFIDP_ACPI_ID_MASK		0xffff
#define EFIDP_ACPI_ID_TO_NUM(_HID)	((_HID) >> 16)

#define EFIDP_ACPI_PCI_ROOT_HID		EFIDP_EFI_PNP_ID(0x0a03)
#define EFIDP_ACPI_CONTAINER_0A05_HID	EFIDP_EFI_PNP_ID(0x0a05)
#define EFIDP_ACPI_CONTAINER_0A06_HID	EFIDP_EFI_PNP_ID(0x0a06)
#define EFIDP_ACPI_PCIE_ROOT_HID	EFIDP_EFI_PNP_ID(0x0a08)
#define EFIDP_ACPI_EC_HID		EFIDP_EFI_PNP_ID(0x0a09)
#define EFIDP_ACPI_FLOPPY_HID		EFIDP_EFI_PNP_ID(0x0604)
#define EFIDP_ACPI_KEYBOARD_HID		EFIDP_EFI_PNP_ID(0x0301)
#define EFIDP_ACPI_SERIAL_HID		EFIDP_EFI_PNP_ID(0x0501)
#define EFIDP_ACPI_PARALLEL_HID		EFIDP_EFI_PNP_ID(0x0401)
#define EFIDP_ACPI_NVDIMM_HID		EFIDP_EFI_ACPI_ID(0x0012)

#define EFIDP_ACPI_ADR		0x03
typedef struct {
	efidp_header	header;
	uint32_t	adr[];
} EFIVAR_PACKED efidp_acpi_adr;

#define EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_MASK		0x80000000
#define EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_SHIFT		31
#define EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_ACPI		0x1
#define EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_OTHER		0x0

#define EFIDP_ACPI_ADR_VGA_PIPE_ID_MASK			0x001C0000
#define EFIDP_ACPI_ADR_VGA_PIPE_ID_SHIFT		18

#define EFIDP_ACPI_ADR_NONVGA_OUTPUT_ID_MASK		0x00020000
#define EFIDP_ACPI_ADR_NONVGA_OUTPUT_ID_SHIFT		17

#define EFIDP_ACPI_ADR_FIRMWARE_DETECT_MASK		0x00010000
#define EFIDP_ACPI_ADR_FIRMWARE_DETECT_SHIFT		16

#define EFIDP_ACPI_ADR_VENDOR_INFO_MASK			0x0000f000
#define EFIDP_ACPI_ADR_VENDOR_INFO_SHIFT		12

#define EFIDP_ACPI_ADR_DISPLAY_TYPE_MASK		0x00000f00
#define EFIDP_ACPI_ADR_DISPLAY_TYPE_SHIFT		8
#define EFIDP_ACPI_ADR_DISPLAY_TYPE_OTHER		0
#define EFIDP_ACPI_ADR_DISPLAY_TYPE_VGA			1
#define EFIDP_ACPI_ADR_DISPLAY_TYPE_TV			2
#define EFIDP_ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL	3
#define EFIDP_ACPI_ADR_DISPLAY_TYPE_INTERNAL_DIGITAL	4

#define EFIDP_ACPI_ADR_DISPLAY_PORT_MASK		0x000000f0
#define EFIDP_ACPI_ADR_DISPLAY_PORT_SHIFT		4

#define EFIDP_ACPI_ADR_DISPLAY_INDEX_MASK		0x0000000f
#define EFIDP_ACPI_ADR_DISPLAY_INDEX_SHIFT		0

#define efidp_encode_acpi_display_adr(device_id_scheme, pipe_id,	\
				      nonvga_output,			\
				      firmware_can_detect, vendor_info,	\
				      type, port, index, adr)		\
	({								\
	((uint32_t)(adr)) = ((uint32_t)(				\
	 efidp_encode_bitfield_(device_id_scheme,			\
				EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_SHIFT,	\
				EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_MASK) |	\
	 efidp_encode_bitfield_(pipe_id,				\
				EFIDP_ACPI_ADR_VGA_PIPE_ID_SHIFT,	\
				EFIDP_ACPI_ADR_VGA_PIPE_ID_MASK) |	\
	 efidp_encode_bitfield_(nonvga_output,				\
				EFIDP_ACPI_ADR_NONVGA_OUTPUT_ID_SHIFT,	\
				EFIDP_ACPI_ADR_NONVGA_OUTPUT_ID_MASK) |	\
	 efidp_encode_bitfield_(firmware_can_detect,			\
				EFIDP_ACPI_ADR_FIRMWARE_DETECT_SHIFT,	\
				EFIDP_ACPI_ADR_FIRMWARE_DETECT_MASK) |	\
	 efidp_encode_bitfield_(vendor_info,				\
				EFIDP_ACPI_ADR_VENDOR_INFO_SHIFT,	\
				EFIDP_ACPI_ADR_VENDOR_INFO_MASK) |	\
	 efidp_encode_bitfield_(type,					\
				EFIDP_ACPI_ADR_DISPLAY_TYPE_SHIFT,	\
				EFIDP_ACPI_ADR_DISPLAY_TYPE_MASK) |	\
	 efidp_encode_bitfield_(port,					\
				EFIDP_ACPI_ADR_DISPLAY_PORT_SHIFT,	\
				EFIDP_ACPI_ADR_DISPLAY_PORT_MASK) |	\
	 efidp_encode_bitfield_(index,					\
				EFIDP_ACPI_ADR_DISPLAY_INDEX_SHIFT,	\
				EFIDP_ACPI_ADR_DISPLAY_INDEX_MASK)));	\
	(device_id_scheme == EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_ACPI);	\
	})

#define efidp_decode_acpi_display_adr(adr, device_id_scheme, pipe_id,	\
				      nonvga_output,			\
				      firmware_can_detect, vendor_info,	\
				      type, port, index) ({		\
	 efidp_decode_bitfield_(adr, *(device_id_scheme),		\
				EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_SHIFT,	\
				EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_MASK);	\
	 efidp_decode_bitfield_(adr, *(pipe_id),			\
				EFIDP_ACPI_ADR_VGA_PIPE_ID_SHIFT,	\
				EFIDP_ACPI_ADR_VGA_PIPE_ID_MASK);	\
	 efidp_decode_bitfield_(adr, *(nonvga_output),			\
				EFIDP_ACPI_ADR_NONVGA_OUTPUT_ID_SHIFT,	\
				EFIDP_ACPI_ADR_NONVGA_OUTPUT_ID_MASK);	\
	 efidp_decode_bitfield_(adr, *(firmware_can_detect),		\
				EFIDP_ACPI_ADR_FIRMWARE_DETECT_SHIFT,	\
				EFIDP_ACPI_ADR_FIRMWARE_DETECT_MASK);	\
	 efidp_decode_bitfield_(adr, *(vendor_info),			\
				EFIDP_ACPI_ADR_VENDOR_INFO_SHIFT,	\
				EFIDP_ACPI_ADR_VENDOR_INFO_MASK);	\
	 efidp_decode_bitfield_(adr, *(type),				\
				EFIDP_ACPI_ADR_DISPLAY_TYPE_SHIFT,	\
				EFIDP_ACPI_ADR_DISPLAY_TYPE_MASK);	\
	 efidp_decode_bitfield_(adr, *(port),				\
				EFIDP_ACPI_ADR_DISPLAY_PORT_SHIFT,	\
				EFIDP_ACPI_ADR_DISPLAY_PORT_MASK);	\
	 efidp_decode_bitfield_(adr, *(index),				\
				EFIDP_ACPI_ADR_DISPLAY_INDEX_SHIFT,	\
				EFIDP_ACPI_ADR_DISPLAY_INDEX_MASK);	\
	(*(device_id_scheme)) == EFIDP_ACPI_ADR_DEVICE_ID_SCHEME_ACPI;	\
	})

#define EFIDP_ACPI_ADR_NVDIMM_NODE_CONTROLLER_MASK	0x0fff0000
#define EFIDP_ACPI_ADR_NVDIMM_NODE_CONTROLLER_SHIFT	16

#define EFIDP_ACPI_ADR_NVDIMM_SOCKET_ID_MASK		0x0000f000
#define EFIDP_ACPI_ADR_NVDIMM_SOCKET_ID_SHIFT		12

#define EFIDP_ACPI_ADR_NVDIMM_MEMORY_CONTROLLER_MASK	0x00000f00
#define EFIDP_ACPI_ADR_NVDIMM_MEMORY_CONTROLLER_SHIFT	8

#define EFIDP_ACPI_ADR_NVDIMM_MEMORY_CHANNEL_MASK	0x000000f0
#define EFIDP_ACPI_ADR_NVDIMM_MEMORY_CHANNEL_SHIFT	4

#define EFIDP_ACPI_ADR_NVDIMM_DIMM_MASK			0x0f00000f
#define EFIDP_ACPI_ADR_NVDIMM_DIMM_SHIFT		0

#define efidp_encode_acpi_nvdimm_adr(node_controller,			\
				     socket, memory_controller,		\
				     memory_channel, dimm, adr)		\
	({								\
	((uint32_t)(adr)) = ((uint32_t)(				\
	 efidp_encode_bitfield_(node_controller,			\
				EFIDP_ACPI_ADR_NVDIMM_NODE_CONTROLLER_SHIFT,\
				EFIDP_ACPI_ADR_NVDIMM_NODE_CONTROLLER_MASK) | \
	 efidp_encode_bitfield_(socket,					\
				EFIDP_ACPI_ADR_NVDIMM_SOCKET_ID_SHIFT,	\
				EFIDP_ACPI_ADR_NVDIMM_SOCKET_ID_MASK) |	\
	 efidp_encode_bitfield_(memory_controller,			\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CONTROLLER_SHIFT,\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CONTROLLER_MASK)|\
	 efidp_encode_bitfield_(memory_channel,				\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CHANNEL_SHIFT,\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CHANNEL_MASK) | \
	 efidp_encode_bitfield_(dimm,					\
				EFIDP_ACPI_ADR_NVDIMM_DIMM_SHIFT,	\
				EFIDP_ACPI_ADR_NVDIMM_DIMM_MASK));	\
	})

#define efidp_decode_acpi_nvdimm_adr(adr, node_controller, socket,	\
				     memory_controller, memory_channel,	\
				     dimm)				\
	({								\
	 efidp_decode_bitfield_(adr, *(node_controller),		\
				EFIDP_ACPI_ADR_NVDIMM_NODE_CONTROLLER_SHIFT,\
				EFIDP_ACPI_ADR_NVDIMM_NODE_CONTROLLER_MASK);\
	 efidp_decode_bitfield_(adr, *(socket),				\
				EFIDP_ACPI_ADR_NVDIMM_SOCKET_ID_SHIFT,	\
				EFIDP_ACPI_ADR_NVDIMM_SOCKET_ID_MASK);	\
	 efidp_decode_bitfield_(adr, *(memory_controller),		\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CONTROLLER_SHIFT,\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CONTROLLER_MASK);\
	 efidp_decode_bitfield_(adr, *(memory_channel),			\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CHANNEL_SHIFT,\
				EFIDP_ACPI_ADR_NVDIMM_MEMORY_CHANNEL_MASK);\
	 efidp_decode_bitfield_(adr, *(dimm),				\
				EFIDP_ACPI_ADR_NVDIMM_DIMM_SHIFT,	\
				EFIDP_ACPI_ADR_NVDIMM_DIMM_MASK);	\
	 0;								\
	})

/* Each messaging subtype */
#define EFIDP_MSG_ATAPI		0x01
typedef struct {
	efidp_header	header;
	uint8_t		primary;
	uint8_t		slave;
	uint16_t	lun;
} EFIVAR_PACKED efidp_atapi;
extern ssize_t efidp_make_atapi(uint8_t *buf, ssize_t size, uint16_t primary,
		uint16_t slave, uint16_t lun);

#define EFIDP_MSG_SCSI		0x02
typedef struct {
	efidp_header	header;
	uint16_t	target;
	uint16_t	lun;
} EFIVAR_PACKED efidp_scsi;
extern ssize_t efidp_make_scsi(uint8_t *buf, ssize_t size, uint16_t target,
			       uint16_t lun);

#define EFIDP_MSG_FIBRECHANNEL	0x03
typedef struct {
	efidp_header	header;
	uint32_t	reserved;
	uint64_t	wwn;
	uint64_t	lun;
} EFIVAR_PACKED efidp_fc;

#define EFIDP_MSG_FIBRECHANNELEX 0x15
typedef struct {
	efidp_header	header;
	uint32_t	reserved;
	uint8_t		wwn[8];
	uint8_t		lun[8];
} EFIVAR_PACKED efidp_fcex;

#define EFIDP_MSG_1394		0x04
typedef struct {
	efidp_header	header;
	uint32_t	reserved;
	uint64_t	guid;
} EFIVAR_PACKED efidp_1394;

#define EFIDP_MSG_USB		0x05
typedef struct {
	efidp_header	header;
	uint8_t		parent_port;
	uint8_t		interface;
} EFIVAR_PACKED efidp_usb;

#define EFIDP_MSG_USB_CLASS	0x0f
typedef struct {
	efidp_header	header;
	uint16_t	vendor_id;
	uint16_t	product_id;
	uint8_t		device_class;
	uint8_t		device_subclass;
	uint8_t		device_protocol;
} EFIVAR_PACKED efidp_usb_class;

#define EFIDP_USB_CLASS_AUDIO		0x01
#define EFIDP_USB_CLASS_CDC_CONTROL	0x02
#define EFIDP_USB_CLASS_HID		0x03
#define EFIDP_USB_CLASS_IMAGE		0x06
#define EFIDP_USB_CLASS_PRINTER		0x07
#define EFIDP_USB_CLASS_MASS_STORAGE	0x08
#define EFIDP_USB_CLASS_HUB		0x09
#define EFIDP_USB_CLASS_CDC_DATA	0x0a
#define EFIDP_USB_CLASS_SMARTCARD	0x0b
#define EFIDP_USB_CLASS_VIDEO		0x0e
#define EFIDP_USB_CLASS_DIAGNOSTIC	0xdc
#define EFIDP_USB_CLASS_WIRELESS	0xde
#define EFIDP_USB_CLASS_254		0xfe
#define EFIDP_USB_SUBCLASS_FW_UPDATE		0x01
#define EFIDP_USB_SUBCLASS_IRDA_BRIDGE		0x02
#define EFIDP_USB_SUBCLASS_TEST_AND_MEASURE	0x03

#define EFIDP_MSG_USB_WWID	0x10
typedef struct {
	efidp_header	header;
	uint16_t	interface;
	uint16_t	vendor_id;
	uint16_t	product_id;
	uint16_t	serial_number[];
} EFIVAR_PACKED efidp_usb_wwid;

#define EFIDP_MSG_LUN		0x11
typedef struct {
	efidp_header	header;
	uint8_t		lun;
} EFIVAR_PACKED efidp_lun;

#define EFIDP_MSG_SATA		0x12
typedef struct {
	efidp_header	header;
	uint16_t	hba_port;
	uint16_t	port_multiplier_port;
	uint16_t	lun;
} EFIVAR_PACKED efidp_sata;
#define SATA_HBA_DIRECT_CONNECT_FLAG	0x8000
extern ssize_t efidp_make_sata(uint8_t *buf, ssize_t size, uint16_t hba_port,
			       int16_t port_multiplier_port, uint16_t lun);

#define	EFIDP_MSG_I2O		0x06
typedef struct {
	efidp_header	header;
	uint32_t	target;
} EFIVAR_PACKED efidp_i2o;

#define EFIDP_MSG_MAC_ADDR	0x0b
typedef struct {
	efidp_header	header;
	uint8_t		mac_addr[32];
	uint8_t		if_type;
} EFIVAR_PACKED efidp_mac_addr;
extern ssize_t efidp_make_mac_addr(uint8_t *buf, ssize_t size,
				   uint8_t if_type,
				   const uint8_t * const mac_addr,
				   ssize_t mac_addr_size);

#define EFIDP_MSG_IPv4		0x0c

typedef struct {
	efidp_header	header;
	uint8_t		local_ipv4_addr[4];
	uint8_t		remote_ipv4_addr[4];
	uint16_t	local_port;
	uint16_t	remote_port;
	uint16_t	protocol;
	efidp_boolean	static_ip_addr;
	uint8_t		gateway[4];
	uint8_t		netmask[4];
} EFIVAR_PACKED efidp_ipv4_addr;
/* everything here is in host byte order */
extern ssize_t efidp_make_ipv4(uint8_t *buf, ssize_t size,
			       uint32_t local, uint32_t remote,
			       uint32_t gateway, uint32_t netmask,
			       uint16_t local_port, uint16_t remote_port,
			       uint16_t protocol, int is_static);

#define EFIDP_IPv4_ORIGIN_DHCP		0x00
#define EFIDP_IPv4_ORIGIN_STATIC	0x01

#define EFIDP_MSG_IPv6		0x0d
typedef struct {
	efidp_header	header;
	uint8_t		local_ipv6_addr[16];
	uint8_t		remote_ipv6_addr[16];
	uint16_t	local_port;
	uint16_t	remote_port;
	uint16_t	protocol;
	uint8_t		ip_addr_origin;
	uint8_t		prefix_length;
	uint8_t		gateway_ipv6_addr;
} EFIVAR_PACKED efidp_ipv6_addr;

#define EFIDP_IPv6_ORIGIN_MANUAL	0x00
#define EFIDP_IPv6_ORIGIN_AUTOCONF	0x01
#define EFIDP_IPv6_ORIGIN_STATEFUL	0x02

#define EFIDP_MSG_VLAN		0x14
typedef struct {
	efidp_header	header;
	uint16_t	vlan_id;
} EFIVAR_PACKED efidp_vlan;

#define EFIDP_MSG_INFINIBAND	0x09
typedef struct {
	efidp_header	header;
	uint32_t	resource_flags;
	uint64_t	port_gid[2];
	union {
		uint64_t	ioc_guid;
		uint64_t	service_id;
	};
	uint64_t	target_port_id;
	uint64_t	device_id;
} EFIVAR_PACKED efidp_infiniband;

#define EFIDP_INFINIBAND_RESOURCE_IOC_SERVICE	0x01
#define EFIDP_INFINIBAND_RESOURCE_EXTENDED_BOOT	0x02
#define EFIDP_INFINIBAND_RESOURCE_CONSOLE	0x04
#define EFIDP_INFINIBAND_RESOURCE_STORAGE	0x08
#define EFIDP_INFINIBAND_RESOURCE_NETWORK	0x10

#define EFIDP_MSG_UART		0x0e
typedef struct {
	efidp_header	header;
	uint32_t	reserved;
	uint64_t	baud_rate;
	uint8_t		data_bits;
	uint8_t		parity;
	uint8_t		stop_bits;
} EFIVAR_PACKED efidp_uart;

#define EFIDP_UART_PARITY_DEFAULT	0x00
#define EFIDP_UART_PARITY_NONE		0x01
#define EFIDP_UART_PARITY_EVEN		0x02
#define EFIDP_UART_PARITY_ODD		0x03
#define EFIDP_UART_PARITY_MARK		0x04
#define EFIDP_UART_PARITY_SPACE		0x05

#define EFIDP_UART_STOP_BITS_DEFAULT	0x00
#define EFIDP_UART_STOP_BITS_ONE	0x01
#define EFIDP_UART_STOP_BITS_ONEFIVE	0x02
#define EFIDP_UART_STOP_BITS_TWO	0x03

#define EFIDP_PC_ANSI_GUID \
	EFI_GUID(0xe0c14753,0xf9be,0x11d2,0x9a0c,0x00,0x90,0x27,0x3f,0xc1,0x4d)
#define EFIDP_VT_100_GUID \
	EFI_GUID(0xdfa66065,0xb419,0x11d3,0x9a2d,0x00,0x90,0x27,0x3f,0xc1,0x4d)
#define EFIDP_VT_100_PLUS_GUID\
	EFI_GUID(0x7baec70b,0x57e0,0x4c76,0x8e87,0x2f,0x9e,0x28,0x08,0x83,0x43)
#define EFIDP_VT_UTF8_GUID\
	EFI_GUID(0xad15a0d6,0x8bec,0x4acf,0xa073,0xd0,0x1d,0xe7,0x7e,0x2d,0x88)

#define EFIDP_MSG_VENDOR	0x0a
typedef struct {
	efidp_header	header;
	efi_guid_t	vendor_guid;
	uint8_t		vendor_data[];
} EFIVAR_PACKED efidp_msg_vendor;
typedef efidp_msg_vendor efidp_vendor_msg;
#define efidp_make_msg_vendor(buf, size, guid, data, data_size)		\
	efidp_make_vendor(buf, size, EFIDP_MESSAGE_TYPE,		\
			  EFIDP_MSG_VENDOR, guid, data, data_size)

/* The next ones are phrased as vendor specific, but they're in the spec. */
#define EFIDP_MSG_UART_GUID \
	EFI_GUID(0x37499a9d,0x542f,0x4c89,0xa026,0x35,0xda,0x14,0x20,0x94,0xe4)
typedef struct {
	efidp_header	header;
	efi_guid_t	vendor_guid;
	uint32_t	flow_control_map;
} EFIVAR_PACKED efidp_uart_flow_control;

#define EFIDP_UART_FLOW_CONTROL_HARDWARE	0x1
#define EFIDP_UART_FLOW_CONTROL_XONXOFF		0x2

#define EFIDP_MSG_SAS_GUID \
	EFI_GUID(0xd487ddb4,0x008b,0x11d9,0xafdc,0x00,0x10,0x83,0xff,0xca,0x4d)
typedef struct {
	efidp_header	header;
	efi_guid_t	vendor_guid;
	uint32_t	reserved;
	uint64_t	sas_address;
	uint64_t	lun;
	uint8_t		device_topology_info;
	uint8_t		drive_bay_id; /* If EFIDP_SAS_TOPOLOGY_NEXTBYTE set */
	uint16_t	rtp;
} EFIVAR_PACKED efidp_sas;
extern ssize_t efidp_make_sas(uint8_t *buf, ssize_t size, uint64_t sas_address);

/* device_topology_info Bits 0:3 (enum) */
#define EFIDP_SAS_TOPOLOGY_MASK		0x02
#define EFIDP_SAS_TOPOLOGY_NONE		0x0
#define EFIDP_SAS_TOPOLOGY_THISBYTE	0x1
#define EFIDP_SAS_TOPOLOGY_NEXTBYTE	0x2

/* device_topology_info Bits 4:5 (enum) */
#define EFIDP_SAS_DEVICE_MASK		0x30
#define EFIDP_SAS_DEVICE_SHIFT		4
#define EFIDP_SAS_DEVICE_SAS_INTERNAL	0x0
#define EFIDP_SAS_DEVICE_SATA_INTERNAL	0x1
#define EFIDP_SAS_DEVICE_SAS_EXTERNAL	0x2
#define EFIDP_SAS_DEVICE_SATA_EXTERNAL	0x3

/* device_topology_info Bits 6:7 (enum) */
#define EFIDP_SAS_CONNECT_MASK		0x40
#define EFIDP_SAS_CONNECT_SHIFT		6
#define EFIDP_SAS_CONNECT_DIRECT	0x0
#define EFIDP_SAS_CONNECT_EXPANDER	0x1

#define EFIDP_MSG_SAS_EX	0x16
typedef struct {
	efidp_header	header;
	uint8_t		sas_address[8];
	uint8_t		lun[8];
	uint8_t		device_topology_info;
	uint8_t		drive_bay_id; /* If EFIDP_SAS_TOPOLOGY_NEXTBYTE set */
	uint16_t	rtp;
} EFIVAR_PACKED efidp_sas_ex;

#define EFIDP_MSG_DEBUGPORT_GUID \
	EFI_GUID(0xEBA4E8D2,0x3858,0x41EC,0xA281,0x26,0x47,0xBA,0x96,0x60,0xD0)

#define EFIDP_MSG_ISCSI		0x13
typedef struct {
	efidp_header	header;
	uint16_t	protocol;
	uint16_t	options;
	uint8_t		lun[8];
	uint16_t	tpgt;
	uint8_t		target_name[];
} EFIVAR_PACKED efidp_iscsi;

/* options bits 0:1 */
#define EFIDP_ISCSI_HEADER_DIGEST_SHIFT	0
#define EFIDP_ISCSI_NO_HEADER_DIGEST	0x0
#define EFIDP_ISCSI_HEADER_CRC32	0x2

/* option bits 2:3 */
#define EFIDP_ISCSI_DATA_DIGEST_SHIFT	2
#define EFIDP_ISCSI_NO_DATA_DIGEST	0x0
#define EFIDP_ISCSI_DATA_CRC32		0x2

/* option bits 4:9 */
#define EFIDP_ISCSI_RESERVED		0x0

/* option bits 10:11 */
#define EFIDP_ISCSI_AUTH_SHIFT		10
#define EFIDP_ISCSI_AUTH_CHAP		0x0
#define EFIDP_ISCSI_AUTH_NONE		0x2

/* option bit 12 */
#define EFIDP_ISCSI_CHAP_SHIFT		12
#define EFIDP_ISCSI_CHAP_BI		0x0
#define EFIDP_ISCSI_CHAP_UNI		0x1

#define EFIDP_ISCSI_MAX_TARGET_NAME_LEN		223

#define EFIDP_MSG_NVME		0x17
typedef struct {
	efidp_header	header;
	uint32_t	namespace_id;
	uint8_t		ieee_eui_64[8];
} EFIVAR_PACKED efidp_nvme;
extern ssize_t efidp_make_nvme(uint8_t *buf, ssize_t size,
			       uint32_t namespace_id, uint8_t *ieee_eui_64);

#define EFIDP_MSG_URI		0x18
typedef struct {
	efidp_header	header;
	uint8_t		uri[];
} EFIVAR_PACKED efidp_uri;

#define EFIDP_MSG_UFS		0x19
typedef struct {
	efidp_header	header;
	uint8_t		target_id;
	uint8_t		lun;
} EFIVAR_PACKED efidp_ufs;

#define EFIDP_MSG_SD		0x1a
typedef struct {
	efidp_header	header;
	uint8_t		slot_number;
} EFIVAR_PACKED efidp_sd;

#define EFIDP_MSG_BT		0x1b
typedef struct {
	efidp_header	header;
	uint8_t		addr[6];
} EFIVAR_PACKED efidp_bt;

#define EFIDP_MSG_WIFI		0x1c
typedef struct {
	efidp_header	header;
	uint8_t		ssid[32];
} EFIVAR_PACKED efidp_wifi;

#define EFIDP_MSG_EMMC		0x1d
typedef struct {
	efidp_header	header;
	uint8_t		slot;
} EFIVAR_PACKED efidp_emmc;

extern ssize_t efidp_make_emmc(uint8_t *buf, ssize_t size, uint32_t slot_id);

#define EFIDP_MSG_BTLE		0x1e
typedef struct {
	efidp_header	header;
	uint8_t		addr[6];
	uint8_t		addr_type;
} EFIVAR_PACKED efidp_btle;

#define EFIDP_MSG_BTLE_ADDR_TYPE_PUBLIC	0
#define EFIDP_MSG_BTLE_ADDR_TYPE_RANDOM	1

#define EFIDP_MSG_DNS		0x1f
typedef struct {
	efidp_header	header;
	uint8_t		is_ipv6;
	efi_ip_addr_t	addrs[];
} EFIVAR_PACKED efidp_dns;

#define EFIDP_MSG_NVDIMM	0x20
typedef struct {
	efidp_header	header;
	efi_guid_t	uuid;
} EFIVAR_PACKED efidp_nvdimm;
extern ssize_t efidp_make_nvdimm(uint8_t *buf, ssize_t size, efi_guid_t *uuid);

/* Each media subtype */
#define EFIDP_MEDIA_HD		0x1
typedef struct {
	efidp_header	header;
	uint32_t	partition_number;
	uint64_t	start;
	uint64_t	size;
	uint8_t		signature[16];
	uint8_t		format;
	uint8_t		signature_type;
#ifdef __ia64
	uint8_t		padding[6]; /* Emperically needed */
#endif
} EFIVAR_PACKED EFIVAR_PACKED efidp_hd;
extern ssize_t efidp_make_hd(uint8_t *buf, ssize_t size, uint32_t num,
			     uint64_t part_start, uint64_t part_size,
			     uint8_t *signature, uint8_t format,
			     uint8_t signature_type);

#define EFIDP_HD_FORMAT_PCAT	0x01
#define EFIDP_HD_FORMAT_GPT	0x02

#define EFIDP_HD_SIGNATURE_NONE		0x00
#define EFIDP_HD_SIGNATURE_MBR		0x01
#define EFIDP_HD_SIGNATURE_GUID		0x02

#define EFIDP_MEDIA_CDROM	0x2
typedef struct {
	efidp_header	header;
	uint32_t	boot_catalog_entry;
	uint64_t	partition_rba;
	uint64_t	sectors;
} EFIVAR_PACKED efidp_cdrom;

#define EFIDP_MEDIA_VENDOR	0x3
typedef struct {
	efidp_header	header;
	efi_guid_t	vendor_guid;
	uint8_t		vendor_data[];
} EFIVAR_PACKED efidp_media_vendor;
typedef efidp_media_vendor efidp_vendor_media;
#define efidp_make_media_vendor(buf, size, guid, data, data_size)	\
	efidp_make_vendor(buf, size, EFIDP_MEDIA_TYPE,			\
			  EFIDP_MEDIA_VENDOR, guid, data, data_size)

#define EFIDP_MEDIA_FILE	0x4
typedef struct {
	efidp_header	header;
	uint16_t	name[];
} EFIVAR_PACKED efidp_file;
extern ssize_t efidp_make_file(uint8_t *buf, ssize_t size, char *filename);

#define EFIDP_MEDIA_PROTOCOL	0x5
typedef struct {
	efidp_header	header;
	efi_guid_t	protocol_guid;
} EFIVAR_PACKED efidp_protocol;

#define EFIDP_MEDIA_FIRMWARE_FILE	0x6
typedef struct {
	efidp_header	header;
	uint8_t		pi_info[];
} EFIVAR_PACKED efidp_firmware_file;

#define EFIDP_MEDIA_FIRMWARE_VOLUME	0x7
typedef struct {
	efidp_header	header;
	uint8_t		pi_info[];
} EFIVAR_PACKED efidp_firmware_volume;

#define EFIDP_MEDIA_RELATIVE_OFFSET	0x8
typedef struct {
	efidp_header	header;
	uint32_t	reserved;
	uint64_t	first_byte;
	uint64_t	last_byte;
} EFIVAR_PACKED efidp_relative_offset;

#define EFIDP_MEDIA_RAMDISK	0x9
typedef struct {
	efidp_header	header;
	uint64_t	start_addr;
	uint64_t	end_addr;
	efi_guid_t	disk_type_guid;
	uint16_t	instance_number;
} EFIVAR_PACKED efidp_ramdisk;

#define EFIDP_VIRTUAL_DISK_GUID \
	EFI_GUID(0x77AB535A,0x45FC,0x624B,0x5560,0xF7,0xB2,0x81,0xD1,0xF9,0x6E)
#define EFIDP_VIRTUAL_CD_GUID \
	EFI_GUID(0x3D5ABD30,0x4175,0x87CE,0x6D64,0xD2,0xAD,0xE5,0x23,0xC4,0xBB)
#define EFIDP_PERSISTENT_VIRTUAL_DISK_GUID \
	EFI_GUID(0x5CEA02C9,0x4D07,0x69D3,0x269F,0x44,0x96,0xFB,0xE0,0x96,0xF9)
#define EFIDP_PERSISTENT_VIRTUAL_CD_GUID \
	EFI_GUID(0x08018188,0x42CD,0xBB48,0x100F,0x53,0x87,0xD5,0x3D,0xED,0x3D)

/* Each BIOS Boot subtype */
#define EFIDP_BIOS_BOOT	0x1
typedef struct {
	efidp_header	header;
	uint16_t	device_type;
	uint16_t	status;
	uint8_t		description[];
} EFIVAR_PACKED efidp_bios_boot;

#define EFIDP_BIOS_BOOT_DEVICE_TYPE_FLOPPY	1
#define EFIDP_BIOS_BOOT_DEVICE_TYPE_HD		2
#define EFIDP_BIOS_BOOT_DEVICE_TYPE_CDROM	3
#define EFIDP_BIOS_BOOT_DEVICE_TYPE_PCMCIA	4
#define EFIDP_BIOS_BOOT_DEVICE_TYPE_USB		5
#define EFIDP_BIOS_BOOT_DEVICE_TYPE_EMBEDDED_NET 6
#define EFIDP_BIOS_BOOT_DEVICE_TYPE_UNKNOWN	0xff

#define EFIDP_END_ENTIRE	0xff
#define EFIDP_END_INSTANCE	0x01

/* utility functions */
typedef union {
	struct {
		uint8_t type;
		uint8_t subtype;
		uint16_t length;
	};
	efidp_header header;
	efidp_pci pci;
	efidp_pccard pccard;
	efidp_mmio mmio;
	efidp_hw_vendor hw_vendor;
	efidp_controller controller;
	efidp_bmc bmc;
	efidp_acpi_hid acpi_hid;
	efidp_acpi_hid_ex acpi_hid_ex;
	efidp_acpi_adr acpi_adr;
	efidp_atapi atapi;
	efidp_scsi scsi;
	efidp_fc fc;
	efidp_fcex fcex;
	efidp_1394 firewire;
	efidp_usb usb;
	efidp_usb_class usb_class;
	efidp_usb_wwid usb_wwid;
	efidp_lun lun;
	efidp_sata sata;
	efidp_i2o i2o;
	efidp_mac_addr mac_addr;
	efidp_ipv4_addr ipv4_addr;
	efidp_ipv6_addr ipv6_addr;
	efidp_vlan vlan;
	efidp_infiniband infiniband;
	efidp_uart uart;
	efidp_msg_vendor msg_vendor;
	efidp_uart_flow_control uart_flow_control;
	efidp_sas sas;
	efidp_sas_ex sas_ex;
	efidp_iscsi iscsi;
	efidp_nvme nvme;
	efidp_uri uri;
	efidp_ufs ufs;
	efidp_sd sd;
	efidp_bt bt;
	efidp_wifi wifi;
	efidp_emmc emmc;
	efidp_btle btle;
	efidp_dns dns;
	efidp_nvdimm nvdimm;
	efidp_hd hd;
	efidp_cdrom cdrom;
	efidp_media_vendor media_vendor;
	efidp_file file;
	efidp_protocol protocol;
	efidp_firmware_file firmware_file;
	efidp_firmware_volume firmware_volume;
	efidp_relative_offset relative_offset;
	efidp_ramdisk ramdisk;
	efidp_bios_boot bios_boot;
} EFIVAR_PACKED efidp_data;
typedef efidp_data *efidp;
typedef const efidp_data *const_efidp;

extern int efidp_set_node_data(const_efidp dn, void *buf, size_t bufsize);
extern int efidp_duplicate_path(const_efidp dp, efidp *out);
extern int efidp_append_path(const_efidp dp0, const_efidp dp1, efidp *out);
extern int efidp_append_node(const_efidp dp, const_efidp dn, efidp *out);
extern int efidp_append_instance(const_efidp dp, const_efidp dpi, efidp *out);

/*
 * GCC/Clang complains that we check for null if we have a nonnull attribute,
 * even though older or other compilers might just ignore that attribute if
 * they don't support it.  Ugh.
 */
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wpointer-bool-conversion"
#elif defined(__GNUC__) && __GNUC__ >= 6
#pragma GCC diagnostic ignored "-Wnonnull-compare"
#endif

#if defined(__GNUC__) && !defined(__clang__)
#define ATTR_ARTIFICIAL __attribute__((__artificial__))
#else
#define ATTR_ARTIFICIAL
#endif

static inline int16_t
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1)))
__attribute__((__unused__))
efidp_type(const_efidp dp)
{
	if (!dp) {
		errno = EINVAL;
		return -1;
	}
	return (uint8_t)dp->type;
}

static inline int16_t
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1)))
__attribute__((__unused__))
efidp_subtype(const_efidp dp)
{
	if (!dp) {
		errno = EINVAL;
		return -1;
	}
	return (uint8_t)dp->subtype;
}

static inline ssize_t
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1)))
__attribute__((__unused__))
__attribute__((__warn_unused_result__))
efidp_node_size(const_efidp dn)
{
	if (!dn || dn->length < 4) {
		errno = EINVAL;
		return -1;
	}
	return dn->length;
}

static inline int
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1, 2)))
__attribute__((__unused__))
__attribute__((__warn_unused_result__))
efidp_next_node(const_efidp in, const_efidp *out)
{
	ssize_t sz;

	if (efidp_type(in) == EFIDP_END_TYPE &&
	    efidp_subtype(in) == EFIDP_END_ENTIRE)
		return 0;

	sz = efidp_node_size(in);
	if (sz < 0)
		return -1;

	/* I love you gcc. */
	*out = (const_efidp)(const efidp_header *)((const uint8_t *)in + sz);
	if (*out < in) {
		errno = EINVAL;
		return -1;
	}
	return 1;
}

static inline int
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1, 2)))
__attribute__((__unused__))
__attribute__((__warn_unused_result__))
efidp_next_instance(const_efidp in, const_efidp *out)
{
	ssize_t sz;

	if (efidp_type(in) != EFIDP_END_TYPE ||
			efidp_subtype(in) != EFIDP_END_INSTANCE) {
		errno = EINVAL;
		return -1;
	}

	sz = efidp_node_size(in);
	if (sz < 0)
		return -1;

	/* I love you gcc. */
	*out = (const_efidp)(const efidp_header *)((uint8_t *)in + sz);
	if (*out < in) {
		errno = EINVAL;
		return -1;
	}
	return 1;
}

static inline int
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1)))
__attribute__((__unused__))
__attribute__((__warn_unused_result__))
efidp_is_multiinstance(const_efidp dn)
{
	while (1) {
		const_efidp next = NULL;
		int rc;

		rc = efidp_next_node(dn, &next);
		if (rc < 0) {
			errno = EINVAL;
			return -1;
		} else if (rc == 0) {
			return 0;
		}

		dn = next;
		if (efidp_type(dn) == EFIDP_END_TYPE &&
		    efidp_subtype(dn) == EFIDP_END_INSTANCE)
			return 1;
		if (efidp_type(dn) == EFIDP_END_TYPE &&
		    efidp_subtype(dn) == EFIDP_END_ENTIRE)
			return 0;
	}

	return 0;
}

static inline int
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1, 2)))
__attribute__((__unused__))
__attribute__((__warn_unused_result__))
efidp_get_next_end(const_efidp in, const_efidp *out)
{
	while (1) {
		const_efidp next;
		ssize_t sz;

		if (efidp_type(in) == EFIDP_END_TYPE) {
			*out = in;
			return 0;
		}

		sz = efidp_node_size(in);
		if (sz < 0)
			break;

		next = (const_efidp)(const efidp_header *)((uint8_t *)in + sz);
		if (next < in) {
			errno = EINVAL;
			return -1;
		}
		in = next;
	}
	return -1;
}

static inline ssize_t
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1)))
__attribute__((__unused__))
__attribute__((__warn_unused_result__))
efidp_size(const_efidp dp)
{
	ssize_t ret = 0;
	if (!dp) {
		errno = EINVAL;
		return -1;
	}

	if (efidp_type(dp) == EFIDP_END_TYPE &&
	    efidp_subtype(dp) == EFIDP_END_ENTIRE)
		return efidp_node_size(dp);

	while (1) {
		int rc;
		ssize_t sz;
		const_efidp next = NULL;

		sz = efidp_node_size(dp);
		if (sz < 0)
			return sz;
		ret += sz;

		rc = efidp_next_instance(dp, &next);
		if (rc < 0) {
			rc = efidp_next_node(dp, &next);
			if (rc == 0)
				break;
		}
		if (rc < 0)
			return rc;

		dp = next;
	}
	return ret;
}

static inline ssize_t
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1)))
__attribute__((__unused__))
__attribute__((__warn_unused_result__))
efidp_instance_size(const_efidp dpi)
{
	ssize_t ret = 0;
	while (1) {
		ssize_t sz;
		const_efidp next = NULL;
		int rc;

		sz = efidp_node_size(dpi);
		if (sz < 0)
			return sz;
		ret += sz;

		if (efidp_type(dpi) == EFIDP_END_TYPE)
			break;

		rc = efidp_next_node(dpi, &next);
		if (rc < 0)
			return rc;
		dpi = next;
	}
	return ret;
}

static inline int
ATTR_ARTIFICIAL
__attribute__((__nonnull__(1)))
__attribute__((__unused__))
efidp_is_valid(const_efidp dp, ssize_t limit)
{
	efidp_header *hdr = (efidp_header *)dp;
	/* just to make it so I'm not checking for negatives everywhere,
	 * limit this at a truly absurdly large size. */
	if (limit < 0)
		limit = INT_MAX;

	while (limit > 0 && hdr) {
		efidp_header *next;
		if (limit < (int64_t)(sizeof (efidp_header)))
			return 0;

		switch (hdr->type) {
		case EFIDP_HARDWARE_TYPE:
			if (hdr->subtype != EFIDP_HW_VENDOR &&
			    hdr->length > 1024) {
				errno = EINVAL;
				efi_error("invalid hardware node");
				return 0;
			}
			break;
		case EFIDP_ACPI_TYPE:
			if (hdr->length > 1024) {
				errno = EINVAL;
				efi_error("invalid ACPI node");
				return 0;
			}
			break;
		case EFIDP_MESSAGE_TYPE:
			if (hdr->subtype != EFIDP_MSG_VENDOR &&
			    hdr->length > 1024) {
				errno = EINVAL;
				efi_error("invalid message node");
				return 0;
			}
			break;
		case EFIDP_MEDIA_TYPE:
			if (hdr->subtype != EFIDP_MEDIA_VENDOR &&
			    hdr->length > 1024) {
				errno = EINVAL;
				efi_error("invalid media node");
				return 0;
			}
			break;
		case EFIDP_BIOS_BOOT_TYPE:
			break;
		case EFIDP_END_TYPE:
			if (hdr->length > 4) {
				errno = EINVAL;
				efi_error("invalid end node");
				return 0;
			}
			break;
		default:
			errno = EINVAL;
			efi_error("invalid device path node type");
			return 0;
		}

		if (limit < hdr->length) {
			errno = EINVAL;
			efi_error("device path node length overruns buffer");
			return 0;
		}
		limit -= hdr->length;

		if (hdr->type != EFIDP_END_TYPE &&
		    hdr->type != EFIDP_END_ENTIRE)
			break;

		next = (efidp_header *)((uint8_t *)hdr + hdr->length);
		if (next < hdr) {
			errno = EINVAL;
			return -1;
		}
		hdr = next;
	}
	if (limit < 0) {
		errno = EINVAL;
		efi_error("device path node length overruns buffer");
		return 0;
	}
	return 1;
}

#if defined(__GNUC__) && __GNUC__ >= 6
#pragma GCC diagnostic pop
#endif

/* and now, printing and parsing */
extern ssize_t efidp_parse_device_node(char *path, efidp out, size_t size);
extern ssize_t efidp_parse_device_path(char *path, efidp out, size_t size);
extern ssize_t efidp_format_device_path(char *buf, size_t size, const_efidp dp,
				       ssize_t limit);
extern ssize_t efidp_make_vendor(uint8_t *buf, ssize_t size, uint8_t type,
				 uint8_t subtype,  efi_guid_t vendor_guid,
				 void *data, size_t data_size);
extern ssize_t efidp_make_generic(uint8_t *buf, ssize_t size, uint8_t type,
				  uint8_t subtype, ssize_t total_size);
#define efidp_make_end_entire(buf, size)				\
	efidp_make_generic(buf, size, EFIDP_END_TYPE, EFIDP_END_ENTIRE,	\
			   sizeof (efidp_header));
#define efidp_make_end_instance(buf, size)				\
	efidp_make_generic(buf, size, EFIDP_END_TYPE,			\
			   EFIDP_END_INSTANCE, sizeof (efidp_header));

#endif /* _EFIVAR_DP_H */
