summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2010-09-22 01:12:23 +0200
committerMichael Brown2010-09-22 18:09:56 +0200
commit3c35ae2f3b67fcd8f1ded0431fbaca05fa7fd3a0 (patch)
tree32a19df0bb5fcb3c7fe9bc3b2db886adbbee04fe
parent[aoe] Fail immediately when network device is closed (diff)
downloadipxe-3c35ae2f3b67fcd8f1ded0431fbaca05fa7fd3a0.tar.gz
ipxe-3c35ae2f3b67fcd8f1ded0431fbaca05fa7fd3a0.tar.xz
ipxe-3c35ae2f3b67fcd8f1ded0431fbaca05fa7fd3a0.zip
[int13] Add infrastructure to support EDD version 4.0
Support the extensions mandated by EDD 4.0, including: o the ability to specify a flat physical address in a disk address packet, o the ability to specify a sector count greater than 127 in a disk address packet, o support for all functions within the Fixed Disk Access and EDD Support subsets, o the ability to describe a device using EDD Device Path Information. This implementation is based on draft revision 3 of the EDD 4.0 specification, with reference to the EDD 3.0 specification. It is possible that this implementation may need to change in order to conform to the final published EDD 4.0 specification. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/arch/i386/include/int13.h31
-rw-r--r--src/arch/i386/interface/pcbios/int13.c239
-rw-r--r--src/core/device.c27
-rw-r--r--src/core/edd.c56
-rw-r--r--src/include/ipxe/device.h6
-rw-r--r--src/include/ipxe/edd.h193
-rw-r--r--src/include/ipxe/errfile.h1
7 files changed, 525 insertions, 28 deletions
diff --git a/src/arch/i386/include/int13.h b/src/arch/i386/include/int13.h
index 2a0f19cf..06755724 100644
--- a/src/arch/i386/include/int13.h
+++ b/src/arch/i386/include/int13.h
@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/list.h>
+#include <ipxe/edd.h>
#include <realmode.h>
/**
@@ -36,6 +37,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define INT13_EXTENDED_READ 0x42
/** Extended write */
#define INT13_EXTENDED_WRITE 0x43
+/** Verify sectors */
+#define INT13_EXTENDED_VERIFY 0x44
+/** Extended seek */
+#define INT13_EXTENDED_SEEK 0x47
/** Get extended drive parameters */
#define INT13_GET_EXTENDED_PARAMETERS 0x48
/** Get CD-ROM status / terminate emulation */
@@ -68,16 +73,22 @@ FILE_LICENCE ( GPL2_OR_LATER );
struct int13_disk_address {
/** Size of the packet, in bytes */
uint8_t bufsize;
- /** Reserved, must be zero */
- uint8_t reserved;
+ /** Reserved */
+ uint8_t reserved_a;
/** Block count */
- uint16_t count;
+ uint8_t count;
+ /** Reserved */
+ uint8_t reserved_b;
/** Data buffer */
struct segoff buffer;
/** Starting block number */
uint64_t lba;
- /** Data buffer (EDD-3.0 only) */
+ /** Data buffer (EDD 3.0+ only) */
uint64_t buffer_phys;
+ /** Block count (EDD 4.0+ only) */
+ uint32_t long_count;
+ /** Reserved */
+ uint32_t reserved_c;
} __attribute__ (( packed ));
/** INT 13 disk parameters */
@@ -96,6 +107,10 @@ struct int13_disk_parameters {
uint64_t sectors;
/** Bytes per sector */
uint16_t sector_size;
+ /** Device parameter table extension */
+ struct segoff dpte;
+ /** Device path information */
+ struct edd_device_path_information dpi;
} __attribute__ (( packed ));
/**
@@ -147,6 +162,8 @@ struct int13_disk_parameters {
#define INT13_EXTENSION_REMOVABLE 0x02
/** EDD functions supported */
#define INT13_EXTENSION_EDD 0x04
+/** 64-bit extensions are present */
+#define INT13_EXTENSION_64BIT 0x08
/** @} */
@@ -166,6 +183,12 @@ struct int13_disk_parameters {
/** @} */
+/** Maximum number of sectors for which CHS geometry is allowed to be valid
+ *
+ * This number is taken from the EDD specification.
+ */
+#define INT13_MAX_CHS_SECTORS 15482880
+
/** Bootable CD-ROM specification packet */
struct int13_cdrom_specification {
/** Size of packet in bytes */
diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c
index 97110fde..e0acc07c 100644
--- a/src/arch/i386/interface/pcbios/int13.c
+++ b/src/arch/i386/interface/pcbios/int13.c
@@ -35,6 +35,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/timer.h>
#include <ipxe/acpi.h>
#include <ipxe/sanboot.h>
+#include <ipxe/device.h>
+#include <ipxe/pci.h>
#include <realmode.h>
#include <bios.h>
#include <biosint.h>
@@ -654,8 +656,10 @@ static int int13_extension_check ( struct int13_drive *int13 __unused,
if ( ix86->regs.bx == 0x55aa ) {
DBGC2 ( int13, "INT13 extensions installation check\n" );
ix86->regs.bx = 0xaa55;
- ix86->regs.cx = INT13_EXTENSION_LINEAR;
- return INT13_EXTENSION_VER_1_X;
+ ix86->regs.cx = ( INT13_EXTENSION_LINEAR |
+ INT13_EXTENSION_EDD |
+ INT13_EXTENSION_64BIT );
+ return INT13_EXTENSION_VER_3_0;
} else {
return -INT13_STATUS_INVALID;
}
@@ -678,25 +682,56 @@ static int int13_extended_rw ( struct int13_drive *int13,
userptr_t buffer,
size_t len ) ) {
struct int13_disk_address addr;
+ uint8_t bufsize;
uint64_t lba;
unsigned long count;
userptr_t buffer;
int rc;
+ /* Get buffer size */
+ get_real ( bufsize, ix86->segs.ds,
+ ( ix86->regs.si + offsetof ( typeof ( addr ), bufsize ) ) );
+ if ( bufsize < offsetof ( typeof ( addr ), buffer_phys ) ) {
+ DBGC2 ( int13, "<invalid buffer size %#02x\n>\n", bufsize );
+ return -INT13_STATUS_INVALID;
+ }
+
/* Read parameters from disk address structure */
- copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
+ memset ( &addr, 0, sizeof ( addr ) );
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize );
lba = addr.lba;
- count = addr.count;
- buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
+ DBGC2 ( int13, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) );
+ if ( ( addr.count == 0xff ) ||
+ ( ( addr.buffer.segment == 0xffff ) &&
+ ( addr.buffer.offset == 0xffff ) ) ) {
+ buffer = phys_to_user ( addr.buffer_phys );
+ DBGC2 ( int13, "%08llx",
+ ( ( unsigned long long ) addr.buffer_phys ) );
+ } else {
+ buffer = real_to_user ( addr.buffer.segment,
+ addr.buffer.offset );
+ DBGC2 ( int13, "%04x:%04x", addr.buffer.segment,
+ addr.buffer.offset );
+ }
+ if ( addr.count <= 0x7f ) {
+ count = addr.count;
+ } else if ( addr.count == 0xff ) {
+ count = addr.long_count;
+ } else {
+ DBGC2 ( int13, " <invalid count %#02x>\n", addr.count );
+ return -INT13_STATUS_INVALID;
+ }
+ DBGC2 ( int13, " (count %ld)\n", count );
- DBGC2 ( int13, "LBA %08llx <-> %04x:%04x (count %ld)\n",
- ( ( unsigned long long ) lba ), addr.buffer.segment,
- addr.buffer.offset, count );
-
/* Read from / write to block device */
if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) {
DBGC ( int13, "INT13 drive %02x extended I/O failed: %s\n",
int13->drive, strerror ( rc ) );
+ /* Record that no blocks were transferred successfully */
+ addr.count = 0;
+ put_real ( addr.count, ix86->segs.ds,
+ ( ix86->regs.si +
+ offsetof ( typeof ( addr ), count ) ) );
return -INT13_STATUS_READ_ERROR;
}
@@ -730,6 +765,117 @@ static int int13_extended_write ( struct int13_drive *int13,
}
/**
+ * INT 13, 44 - Verify sectors
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+static int int13_extended_verify ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ struct int13_disk_address addr;
+ uint64_t lba;
+ unsigned long count;
+
+ /* Read parameters from disk address structure */
+ if ( DBG_EXTRA ) {
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
+ sizeof ( addr ));
+ lba = addr.lba;
+ count = addr.count;
+ DBGC2 ( int13, "Verify: LBA %08llx (count %ld)\n",
+ ( ( unsigned long long ) lba ), count );
+ }
+
+ /* We have no mechanism for verifying sectors */
+ return -INT13_STATUS_INVALID;
+}
+
+/**
+ * INT 13, 44 - Extended seek
+ *
+ * @v int13 Emulated drive
+ * @v ds:si Disk address packet
+ * @ret status Status code
+ */
+int int13_extended_seek ( struct int13_drive *int13,
+ struct i386_all_regs *ix86 ) {
+ struct int13_disk_address addr;
+ uint64_t lba;
+ unsigned long count;
+
+ /* Read parameters from disk address structure */
+ if ( DBG_EXTRA ) {
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
+ sizeof ( addr ));
+ lba = addr.lba;
+ count = addr.count;
+ DBGC2 ( int13, "Seek: LBA %08llx (count %ld)\n",
+ ( ( unsigned long long ) lba ), count );
+ }
+
+ /* Ignore and return success */
+ return 0;
+}
+
+/**
+ * Build device path information
+ *
+ * @v int13 Emulated drive
+ * @v dpi Device path information
+ * @ret rc Return status code
+ */
+static int int13_device_path_info ( struct int13_drive *int13,
+ struct edd_device_path_information *dpi ) {
+ struct device *device;
+ struct device_description *desc;
+ unsigned int i;
+ uint8_t sum = 0;
+ int rc;
+
+ /* Get underlying hardware device */
+ device = identify_device ( &int13->block );
+ if ( ! device ) {
+ DBGC ( int13, "INT13 drive %02x cannot identify hardware "
+ "device\n", int13->drive );
+ return -ENODEV;
+ }
+
+ /* Fill in bus type and interface path */
+ desc = &device->desc;
+ switch ( desc->bus_type ) {
+ case BUS_TYPE_PCI:
+ dpi->host_bus_type.type = EDD_BUS_TYPE_PCI;
+ dpi->interface_path.pci.bus = PCI_BUS ( desc->location );
+ dpi->interface_path.pci.slot = PCI_SLOT ( desc->location );
+ dpi->interface_path.pci.function = PCI_FUNC ( desc->location );
+ dpi->interface_path.pci.channel = 0xff; /* unused */
+ break;
+ default:
+ DBGC ( int13, "INT13 drive %02x unrecognised bus type %d\n",
+ int13->drive, desc->bus_type );
+ return -ENOTSUP;
+ }
+
+ /* Get EDD block device description */
+ if ( ( rc = edd_describe ( &int13->block, &dpi->interface_type,
+ &dpi->device_path ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x cannot identify block device: "
+ "%s\n", int13->drive, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Fill in common fields and fix checksum */
+ dpi->key = EDD_DEVICE_PATH_INFO_KEY;
+ dpi->len = sizeof ( *dpi );
+ for ( i = 0 ; i < sizeof ( *dpi ) ; i++ )
+ sum += *( ( ( uint8_t * ) dpi ) + i );
+ dpi->checksum -= sum;
+
+ return 0;
+}
+
+/**
* INT 13, 48 - Get extended parameters
*
* @v int13 Emulated drive
@@ -738,21 +884,62 @@ static int int13_extended_write ( struct int13_drive *int13,
*/
static int int13_get_extended_parameters ( struct int13_drive *int13,
struct i386_all_regs *ix86 ) {
- struct int13_disk_parameters params = {
- .bufsize = sizeof ( params ),
- .flags = INT13_FL_DMA_TRANSPARENT,
- .cylinders = int13->cylinders,
- .heads = int13->heads,
- .sectors_per_track = int13->sectors_per_track,
- .sectors = int13->capacity.blocks,
- .sector_size = int13->capacity.blksize,
- };
-
- DBGC2 ( int13, "Get extended drive parameters to %04x:%04x\n",
- ix86->segs.ds, ix86->regs.si );
+ struct int13_disk_parameters params;
+ struct segoff address;
+ size_t len = sizeof ( params );
+ uint16_t bufsize;
+ int rc;
+
+ /* Get buffer size */
+ get_real ( bufsize, ix86->segs.ds,
+ ( ix86->regs.si + offsetof ( typeof ( params ), bufsize )));
+
+ DBGC2 ( int13, "Get extended drive parameters to %04x:%04x+%02x\n",
+ ix86->segs.ds, ix86->regs.si, bufsize );
+
+ /* Build drive parameters */
+ memset ( &params, 0, sizeof ( params ) );
+ params.flags = INT13_FL_DMA_TRANSPARENT;
+ if ( ( int13->cylinders < 1024 ) &&
+ ( int13->capacity.blocks <= INT13_MAX_CHS_SECTORS ) ) {
+ params.flags |= INT13_FL_CHS_VALID;
+ }
+ params.cylinders = int13->cylinders;
+ params.heads = int13->heads;
+ params.sectors_per_track = int13->sectors_per_track;
+ params.sectors = int13->capacity.blocks;
+ params.sector_size = int13->capacity.blksize;
+ memset ( &params.dpte, 0xff, sizeof ( params.dpte ) );
+ if ( ( rc = int13_device_path_info ( int13, &params.dpi ) ) != 0 ) {
+ DBGC ( int13, "INT13 drive %02x could not provide device "
+ "path information: %s\n",
+ int13->drive, strerror ( rc ) );
+ len = offsetof ( typeof ( params ), dpi );
+ }
+
+ /* Calculate returned "buffer size" (which will be less than
+ * the length actually copied if device path information is
+ * present).
+ */
+ if ( bufsize < offsetof ( typeof ( params ), dpte ) )
+ return -INT13_STATUS_INVALID;
+ if ( bufsize < offsetof ( typeof ( params ), dpi ) ) {
+ params.bufsize = offsetof ( typeof ( params ), dpte );
+ } else {
+ params.bufsize = offsetof ( typeof ( params ), dpi );
+ }
+
+ DBGC ( int13, "INT 13 drive %02x described using extended "
+ "parameters:\n", int13->drive );
+ address.segment = ix86->segs.ds;
+ address.offset = ix86->regs.si;
+ DBGC_HDA ( int13, address, &params, len );
+
+ /* Return drive parameters */
+ if ( len > bufsize )
+ len = bufsize;
+ copy_to_real ( ix86->segs.ds, ix86->regs.si, &params, len );
- copy_to_real ( ix86->segs.ds, ix86->regs.si, &params,
- sizeof ( params ) );
return 0;
}
@@ -814,6 +1001,12 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
case INT13_EXTENDED_WRITE:
status = int13_extended_write ( int13, ix86 );
break;
+ case INT13_EXTENDED_VERIFY:
+ status = int13_extended_verify ( int13, ix86 );
+ break;
+ case INT13_EXTENDED_SEEK:
+ status = int13_extended_seek ( int13, ix86 );
+ break;
case INT13_GET_EXTENDED_PARAMETERS:
status = int13_get_extended_parameters ( int13, ix86 );
break;
diff --git a/src/core/device.c b/src/core/device.c
index 1a227fd9..cb2c23b0 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -21,8 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <ipxe/list.h>
#include <ipxe/tables.h>
-#include <ipxe/device.h>
#include <ipxe/init.h>
+#include <ipxe/interface.h>
+#include <ipxe/device.h>
/**
* @file
@@ -105,3 +106,27 @@ struct startup_fn startup_devices __startup_fn ( STARTUP_NORMAL ) = {
.startup = probe_devices,
.shutdown = remove_devices,
};
+
+/**
+ * Identify a device behind an interface
+ *
+ * @v intf Interface
+ * @ret device Device, or NULL
+ */
+struct device * identify_device ( struct interface *intf ) {
+ struct interface *dest;
+ identify_device_TYPE ( void * ) *op =
+ intf_get_dest_op ( intf, identify_device, &dest );
+ void *object = intf_object ( dest );
+ void *device;
+
+ if ( op ) {
+ device = op ( object );
+ } else {
+ /* Default is to return NULL */
+ device = NULL;
+ }
+
+ intf_put ( dest );
+ return device;
+}
diff --git a/src/core/edd.c b/src/core/edd.c
new file mode 100644
index 00000000..8ba24b17
--- /dev/null
+++ b/src/core/edd.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <ipxe/interface.h>
+#include <ipxe/edd.h>
+
+/** @file
+ *
+ * Enhanced Disk Drive specification
+ *
+ */
+
+/**
+ * Describe a disk device using EDD
+ *
+ * @v intf Interface
+ * @v type EDD interface type
+ * @v path EDD device path
+ * @ret rc Return status code
+ */
+int edd_describe ( struct interface *intf, struct edd_interface_type *type,
+ union edd_device_path *path ) {
+ struct interface *dest;
+ edd_describe_TYPE ( void * ) *op =
+ intf_get_dest_op ( intf, edd_describe, &dest );
+ void *object = intf_object ( dest );
+ int rc;
+
+ if ( op ) {
+ rc = op ( object, type, path );
+ } else {
+ /* Default is to not support this operation */
+ rc = -ENOTSUP;
+ }
+
+ intf_put ( dest );
+ return rc;
+}
diff --git a/src/include/ipxe/device.h b/src/include/ipxe/device.h
index 85675300..068268ba 100644
--- a/src/include/ipxe/device.h
+++ b/src/include/ipxe/device.h
@@ -13,6 +13,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/list.h>
#include <ipxe/tables.h>
+struct interface;
+
/** A hardware device description */
struct device_description {
/** Bus type
@@ -110,4 +112,8 @@ struct root_driver {
/** Declare a root device */
#define __root_device __table_entry ( ROOT_DEVICES, 01 )
+extern struct device * identify_device ( struct interface *intf );
+#define identify_device_TYPE( object_type ) \
+ typeof ( struct device * ( object_type ) )
+
#endif /* _IPXE_DEVICE_H */
diff --git a/src/include/ipxe/edd.h b/src/include/ipxe/edd.h
new file mode 100644
index 00000000..0c25593d
--- /dev/null
+++ b/src/include/ipxe/edd.h
@@ -0,0 +1,193 @@
+#ifndef _IPXE_EDD_H
+#define _IPXE_EDD_H
+
+/** @file
+ *
+ * Enhanced Disk Drive specification
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <ipxe/interface.h>
+
+/** An EDD host bus type */
+struct edd_host_bus_type {
+ /** Type */
+ uint32_t type;
+} __attribute__ (( packed ));
+
+/** EDD bus type */
+#define EDD_BUS_TYPE_FIXED( a, b, c, d, ... ) \
+ ( ( (a) << 0 ) | ( (b) << 8 ) | ( (c) << 16 ) | ( (d) << 24 ) )
+#define EDD_BUS_TYPE( ... ) \
+ EDD_BUS_TYPE_FIXED ( __VA_ARGS__, ' ', ' ', ' ', ' ' )
+/** EDD PCI bus type */
+#define EDD_BUS_TYPE_PCI EDD_BUS_TYPE ( 'P', 'C', 'I' )
+/** EDD ISA bus type */
+#define EDD_BUS_TYPE_ISA EDD_BUS_TYPE ( 'I', 'S', 'A' )
+/** EDD PCI-X bus type */
+#define EDD_BUS_TYPE_PCIX EDD_BUS_TYPE ( 'P', 'C', 'I', 'X' )
+/** EDD Infiniband bus type */
+#define EDD_BUS_TYPE_IBND EDD_BUS_TYPE ( 'I', 'B', 'N', 'D' )
+/** EDD PCI Express bus type */
+#define EDD_BUS_TYPE_XPRS EDD_BUS_TYPE ( 'X', 'P', 'R', 'S' )
+/** EDD HyperTransport bus type */
+#define EDD_BUS_TYPE_HTPT EDD_BUS_TYPE ( 'H', 'T', 'P', 'T' )
+
+/** An EDD interface type */
+struct edd_interface_type {
+ /** Type */
+ uint64_t type;
+} __attribute__ (( packed ));
+
+/** EDD interface type */
+#define EDD_INTF_TYPE_FIXED( a, b, c, d, e, f, g, h, ... ) \
+ ( ( ( ( uint64_t ) (a) ) << 0 ) | ( ( ( uint64_t ) (b) ) << 8 ) | \
+ ( ( ( uint64_t ) (c) ) << 16 ) | ( ( ( uint64_t ) (d) ) << 24 ) | \
+ ( ( ( uint64_t ) (e) ) << 32 ) | ( ( ( uint64_t ) (f) ) << 40 ) | \
+ ( ( ( uint64_t ) (g) ) << 48 ) | ( ( ( uint64_t ) (h) ) << 56 ) )
+#define EDD_INTF_TYPE( ... ) \
+ EDD_INTF_TYPE_FIXED ( __VA_ARGS__, \
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' )
+/** EDD ATA interface type */
+#define EDD_INTF_TYPE_ATA EDD_INTF_TYPE ( 'A', 'T', 'A' )
+/** EDD ATAPI interface type */
+#define EDD_INTF_TYPE_ATAPI EDD_INTF_TYPE ( 'A', 'T', 'A', 'P', 'I' )
+/** EDD SCSI interface type */
+#define EDD_INTF_TYPE_SCSI EDD_INTF_TYPE ( 'S', 'C', 'S', 'I' )
+/** EDD USB interface type */
+#define EDD_INTF_TYPE_USB EDD_INTF_TYPE ( 'U', 'S', 'B' )
+/** EDD 1394 interface type */
+#define EDD_INTF_TYPE_1394 EDD_INTF_TYPE ( '1', '3', '9', '4' )
+/** EDD Fibre Channel interface type */
+#define EDD_INTF_TYPE_FIBRE EDD_INTF_TYPE ( 'F', 'I', 'B', 'R', 'E' )
+/** EDD I2O interface type */
+#define EDD_INTF_TYPE_I2O EDD_INTF_TYPE ( 'I', '2', 'O' )
+/** EDD RAID interface type */
+#define EDD_INTF_TYPE_RAID EDD_INTF_TYPE ( 'R', 'A', 'I', 'D' )
+/** EDD SATA interface type */
+#define EDD_INTF_TYPE_SATA EDD_INTF_TYPE ( 'S', 'A', 'T', 'A' )
+/** EDD SAS interface type */
+#define EDD_INTF_TYPE_SAS EDD_INTF_TYPE ( 'S', 'A', 'S' )
+
+/** An EDD interface path */
+union edd_interface_path {
+ /** Legacy bus type */
+ struct {
+ /** Base address */
+ uint16_t base;
+ } __attribute__ (( packed )) legacy;
+ /** PCI, PCI-X, PCI Express, or HyperTransport bus type */
+ struct {
+ /** Bus */
+ uint8_t bus;
+ /** Slot */
+ uint8_t slot;
+ /** Function */
+ uint8_t function;
+ /** Channel number */
+ uint8_t channel;
+ } __attribute__ (( packed )) pci;
+ /** Padding */
+ uint8_t pad[8];
+} __attribute__ (( packed ));
+
+/** An EDD device path */
+union edd_device_path {
+ /** ATA interface type */
+ struct {
+ /** Slave */
+ uint8_t slave;
+ } __attribute__ (( packed )) ata;
+ /** ATAPI interface type */
+ struct {
+ /** Slave */
+ uint8_t slave;
+ /** Logical Unit Number */
+ uint8_t lun;
+ } __attribute__ (( packed )) atapi;
+ /** SCSI interface type */
+ struct {
+ /** SCSI ID */
+ uint16_t id;
+ /** Logical Unit Number */
+ uint64_t lun;
+ } __attribute__ (( packed )) scsi;
+ /** USB interface type */
+ struct {
+ /** Serial number */
+ uint64_t serial;
+ } __attribute__ (( packed )) usb;
+ /** IEEE1394 interface type */
+ struct {
+ /** GUID */
+ uint64_t guid;
+ } __attribute__ (( packed )) ieee1394;
+ /** Fibre Channel interface type */
+ struct {
+ /** WWN */
+ uint64_t wwn;
+ /** Logical Unit Number */
+ uint64_t lun;
+ } __attribute__ (( packed )) fibre;
+ /** I2O interface type */
+ struct {
+ /** Identity tag */
+ uint64_t tag;
+ } __attribute__ (( packed )) i2o;
+ /** RAID interface type */
+ struct {
+ /** Array number */
+ uint32_t array;
+ } __attribute__ (( packed )) raid;
+ /** SATA interface type */
+ struct {
+ /** Port number */
+ uint8_t port;
+ /** Port multiplier number */
+ uint8_t multiplier;
+ } __attribute__ (( packed )) sata;
+ /** SAS interface type */
+ struct {
+ /** Address */
+ uint64_t address;
+ } __attribute__ (( packed )) sas;
+ /** Padding */
+ uint8_t pad[16];
+} __attribute__ (( packed ));
+
+/** EDD device path information */
+struct edd_device_path_information {
+ /** Key */
+ uint16_t key;
+ /** Length of this structure */
+ uint8_t len;
+ /** Reserved */
+ uint8_t reserved_a[3];
+ /** Host bus type */
+ struct edd_host_bus_type host_bus_type;
+ /** Interface type */
+ struct edd_interface_type interface_type;
+ /** Interface path */
+ union edd_interface_path interface_path;
+ /** Device path */
+ union edd_device_path device_path;
+ /** Reserved */
+ uint8_t reserved_b;
+ /** Checksum */
+ uint8_t checksum;
+} __attribute__ (( packed ));
+
+/** EDD device path information key */
+#define EDD_DEVICE_PATH_INFO_KEY 0xbedd
+
+extern int edd_describe ( struct interface *intf,
+ struct edd_interface_type *type,
+ union edd_device_path *path );
+#define edd_describe_TYPE( object_type ) \
+ typeof ( int ( object_type, struct edd_interface_type *type, \
+ union edd_device_path *path ) )
+
+#endif /* _IPXE_EDD_H */
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index 8101560c..10bc49d8 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -58,6 +58,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_blockdev ( ERRFILE_CORE | 0x00120000 )
#define ERRFILE_acpi ( ERRFILE_CORE | 0x00130000 )
#define ERRFILE_null_sanboot ( ERRFILE_CORE | 0x00140000 )
+#define ERRFILE_edd ( ERRFILE_CORE | 0x00150000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )