mirror of
https://github.com/ipxe/ipxe
synced 2025-12-27 10:02:42 +03:00
[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>
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
/* Read parameters from disk address structure */
|
||||
copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
|
||||
lba = addr.lba;
|
||||
count = addr.count;
|
||||
buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
|
||||
/* 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 */
|
||||
memset ( &addr, 0, sizeof ( addr ) );
|
||||
copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize );
|
||||
lba = addr.lba;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -729,6 +764,117 @@ static int int13_extended_write ( struct int13_drive *int13,
|
||||
return int13_extended_rw ( int13, ix86, block_write );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@@ -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 ( ¶ms, 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 ( ¶ms.dpte, 0xff, sizeof ( params.dpte ) );
|
||||
if ( ( rc = int13_device_path_info ( int13, ¶ms.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, ¶ms, len );
|
||||
|
||||
/* Return drive parameters */
|
||||
if ( len > bufsize )
|
||||
len = bufsize;
|
||||
copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms, len );
|
||||
|
||||
copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms,
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user