david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[block] Replace gPXE block-device API with an iPXE asynchronous interface

The block device interface used in gPXE predates the invention of even
the old gPXE data-transfer interface, let alone the current iPXE
generic asynchronous interface mechanism.  Bring this old code up to
date, with the following benefits:

 o  Block device commands can be cancelled by the requestor.  The INT 13
    layer uses this to provide a global timeout on all INT 13 calls,
    with the result that an unexpected passive failure mode (such as
    an iSCSI target ACKing the request but never sending a response)
    will lead to a timeout that gets reported back to the INT 13 user,
    rather than simply freezing the system.

 o  INT 13,00 (reset drive) is now able to reset the underlying block
    device.  INT 13 users, such as DOS, that use INT 13,00 as a method
    for error recovery now have a chance of recovering.

 o  All block device commands are tagged, with a numerical tag that
    will show up in debugging output and in packet captures; this will
    allow easier interpretation of bug reports that include both
    sources of information.

 o  The extremely ugly hacks used to generate the boot firmware tables
    have been eradicated and replaced with a generic acpi_describe()
    method (exploiting the ability of iPXE interfaces to pass through
    methods to an underlying interface).  The ACPI tables are now
    built in a shared data block within .bss16, rather than each
    requiring dedicated space in .data16.

 o  The architecture-independent concept of a SAN device has been
    exposed to the iPXE core through the sanboot API, which provides
    calls to hook, unhook, boot, and describe SAN devices.  This
    allows for much more flexible usage patterns (such as hooking an
    empty SAN device and then running an OS installer via TFTP).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2010-09-03 16:11:51 +01:00
parent 46c7f99c66
commit 220495f8bf
47 changed files with 4904 additions and 3127 deletions

View File

@ -1,336 +0,0 @@
/*
* Copyright (C) 2007 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 );
/**
* @file
*
* El Torito bootable ISO image format
*
*/
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <bootsector.h>
#include <int13.h>
#include <ipxe/uaccess.h>
#include <ipxe/image.h>
#include <ipxe/segment.h>
#include <ipxe/ramdisk.h>
#include <ipxe/init.h>
#define ISO9660_BLKSIZE 2048
#define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE )
/** An El Torito Boot Record Volume Descriptor */
struct eltorito_vol_desc {
/** Boot record indicator; must be 0 */
uint8_t record_indicator;
/** ISO-9660 identifier; must be "CD001" */
uint8_t iso9660_id[5];
/** Version, must be 1 */
uint8_t version;
/** Boot system indicator; must be "EL TORITO SPECIFICATION" */
uint8_t system_indicator[32];
/** Unused */
uint8_t unused[32];
/** Boot catalog sector */
uint32_t sector;
} __attribute__ (( packed ));
/** An El Torito Boot Catalog Validation Entry */
struct eltorito_validation_entry {
/** Header ID; must be 1 */
uint8_t header_id;
/** Platform ID
*
* 0 = 80x86
* 1 = PowerPC
* 2 = Mac
*/
uint8_t platform_id;
/** Reserved */
uint16_t reserved;
/** ID string */
uint8_t id_string[24];
/** Checksum word */
uint16_t checksum;
/** Signature; must be 0xaa55 */
uint16_t signature;
} __attribute__ (( packed ));
/** A bootable entry in the El Torito Boot Catalog */
struct eltorito_boot_entry {
/** Boot indicator
*
* Must be @c ELTORITO_BOOTABLE for a bootable ISO image
*/
uint8_t indicator;
/** Media type
*
*/
uint8_t media_type;
/** Load segment */
uint16_t load_segment;
/** System type */
uint8_t filesystem;
/** Unused */
uint8_t reserved_a;
/** Sector count */
uint16_t length;
/** Starting sector */
uint32_t start;
/** Unused */
uint8_t reserved_b[20];
} __attribute__ (( packed ));
/** Boot indicator for a bootable ISO image */
#define ELTORITO_BOOTABLE 0x88
/** El Torito media types */
enum eltorito_media_type {
/** No emulation */
ELTORITO_NO_EMULATION = 0,
};
struct image_type eltorito_image_type __image_type ( PROBE_NORMAL );
/**
* Calculate 16-bit word checksum
*
* @v data Data to checksum
* @v len Length (in bytes, must be even)
* @ret sum Checksum
*/
static unsigned int word_checksum ( void *data, size_t len ) {
uint16_t *words;
uint16_t sum = 0;
for ( words = data ; len ; words++, len -= 2 ) {
sum += *words;
}
return sum;
}
/**
* Execute El Torito image
*
* @v image El Torito image
* @ret rc Return status code
*/
static int eltorito_exec ( struct image *image ) {
struct ramdisk ramdisk;
struct int13_drive int13_drive;
unsigned int load_segment = image->priv.ul;
unsigned int load_offset = ( load_segment ? 0 : 0x7c00 );
int rc;
memset ( &ramdisk, 0, sizeof ( ramdisk ) );
init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE );
memset ( &int13_drive, 0, sizeof ( int13_drive ) );
int13_drive.blockdev = &ramdisk.blockdev;
register_int13_drive ( &int13_drive );
if ( ( rc = call_bootsector ( load_segment, load_offset,
int13_drive.drive ) ) != 0 ) {
DBGC ( image, "ElTorito %p boot failed: %s\n",
image, strerror ( rc ) );
goto err;
}
rc = -ECANCELED; /* -EIMPOSSIBLE */
err:
unregister_int13_drive ( &int13_drive );
return rc;
}
/**
* Read and verify El Torito Boot Record Volume Descriptor
*
* @v image El Torito file
* @ret catalog_offset Offset of Boot Catalog
* @ret rc Return status code
*/
static int eltorito_read_voldesc ( struct image *image,
unsigned long *catalog_offset ) {
static const struct eltorito_vol_desc vol_desc_signature = {
.record_indicator = 0,
.iso9660_id = "CD001",
.version = 1,
.system_indicator = "EL TORITO SPECIFICATION",
};
struct eltorito_vol_desc vol_desc;
/* Sanity check */
if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
DBGC ( image, "ElTorito %p too short\n", image );
return -ENOEXEC;
}
/* Read and verify Boot Record Volume Descriptor */
copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
sizeof ( vol_desc ) );
if ( memcmp ( &vol_desc, &vol_desc_signature,
offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
DBGC ( image, "ElTorito %p invalid Boot Record Volume "
"Descriptor\n", image );
return -ENOEXEC;
}
*catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );
DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
image, *catalog_offset );
return 0;
}
/**
* Read and verify El Torito Boot Catalog
*
* @v image El Torito file
* @v catalog_offset Offset of Boot Catalog
* @ret boot_entry El Torito boot entry
* @ret rc Return status code
*/
static int eltorito_read_catalog ( struct image *image,
unsigned long catalog_offset,
struct eltorito_boot_entry *boot_entry ) {
struct eltorito_validation_entry validation_entry;
/* Sanity check */
if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
image, catalog_offset );
return -ENOEXEC;
}
/* Read and verify the Validation Entry of the Boot Catalog */
copy_from_user ( &validation_entry, image->data, catalog_offset,
sizeof ( validation_entry ) );
if ( word_checksum ( &validation_entry,
sizeof ( validation_entry ) ) != 0 ) {
DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
image );
return -ENOEXEC;
}
/* Read and verify the Initial/Default entry */
copy_from_user ( boot_entry, image->data,
( catalog_offset + sizeof ( validation_entry ) ),
sizeof ( *boot_entry ) );
if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
DBGC ( image, "ElTorito %p not bootable\n", image );
return -ENOEXEC;
}
if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
DBGC ( image, "ElTorito %p cannot support media type %d\n",
image, boot_entry->media_type );
return -ENOTSUP;
}
DBGC ( image, "ElTorito %p media type %d segment %04x\n",
image, boot_entry->media_type, boot_entry->load_segment );
return 0;
}
/**
* Load El Torito virtual disk image into memory
*
* @v image El Torito file
* @v boot_entry El Torito boot entry
* @ret rc Return status code
*/
static int eltorito_load_disk ( struct image *image,
struct eltorito_boot_entry *boot_entry ) {
unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
unsigned int load_segment;
userptr_t buffer;
int rc;
/* Sanity check */
if ( image->len < ( start + length ) ) {
DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
image );
return -ENOEXEC;
}
DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
image, start, length );
/* Calculate load address */
load_segment = boot_entry->load_segment;
buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );
/* Verify and prepare segment */
if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
image, strerror ( rc ) );
return rc;
}
/* Copy image to segment */
memcpy_user ( buffer, 0, image->data, start, length );
return 0;
}
/**
* Load El Torito image into memory
*
* @v image El Torito file
* @ret rc Return status code
*/
static int eltorito_load ( struct image *image ) {
struct eltorito_boot_entry boot_entry;
unsigned long bootcat_offset;
int rc;
/* Read Boot Record Volume Descriptor, if present */
if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 )
return rc;
/* This is an El Torito image, valid or otherwise */
if ( ! image->type )
image->type = &eltorito_image_type;
/* Read Boot Catalog */
if ( ( rc = eltorito_read_catalog ( image, bootcat_offset,
&boot_entry ) ) != 0 )
return rc;
/* Load Virtual Disk image */
if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 )
return rc;
/* Record load segment in image private data field */
image->priv.ul = boot_entry.load_segment;
return 0;
}
/** El Torito image type */
struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
.name = "El Torito",
.load = eltorito_load,
.exec = eltorito_exec,
};

View File

@ -1,3 +0,0 @@
#ifndef ELTORITO_PLATFORM
#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86
#endif /* ELTORITO_PLATFORM */

View File

@ -0,0 +1,14 @@
#ifndef _BITS_SANBOOT_H
#define _BITS_SANBOOT_H
/** @file
*
* i386-specific sanboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/bios_sanboot.h>
#endif /* _BITS_SANBOOT_H */

View File

@ -13,8 +13,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/list.h>
#include <realmode.h>
struct block_device;
/**
* @defgroup int13ops INT 13 operation codes
* @{
@ -56,6 +54,8 @@ struct block_device;
#define INT13_STATUS_INVALID 0x01
/** Read error */
#define INT13_STATUS_READ_ERROR 0x04
/** Reset failed */
#define INT13_STATUS_RESET_FAILED 0x05
/** Write error */
#define INT13_STATUS_WRITE_ERROR 0xcc
@ -64,57 +64,6 @@ struct block_device;
/** Block size for non-extended INT 13 calls */
#define INT13_BLKSIZE 512
/** An INT 13 emulated drive */
struct int13_drive {
/** List of all registered drives */
struct list_head list;
/** Underlying block device */
struct block_device *blockdev;
/** BIOS in-use drive number (0x80-0xff) */
unsigned int drive;
/** BIOS natural drive number (0x80-0xff)
*
* This is the drive number that would have been assigned by
* 'naturally' appending the drive to the end of the BIOS
* drive list.
*
* If the emulated drive replaces a preexisting drive, this is
* the drive number that the preexisting drive gets remapped
* to.
*/
unsigned int natural_drive;
/** Number of cylinders
*
* The cylinder number field in an INT 13 call is ten bits
* wide, giving a maximum of 1024 cylinders. Conventionally,
* when the 7.8GB limit of a CHS address is exceeded, it is
* the number of cylinders that is increased beyond the
* addressable limit.
*/
unsigned int cylinders;
/** Number of heads
*
* The head number field in an INT 13 call is eight bits wide,
* giving a maximum of 256 heads. However, apparently all
* versions of MS-DOS up to and including Win95 fail with 256
* heads, so the maximum encountered in practice is 255.
*/
unsigned int heads;
/** Number of sectors per track
*
* The sector number field in an INT 13 call is six bits wide,
* giving a maximum of 63 sectors, since sector numbering
* (unlike head and cylinder numbering) starts at 1, not 0.
*/
unsigned int sectors_per_track;
/** Status of last operation */
int last_status;
};
/** An INT 13 disk address packet */
struct int13_disk_address {
/** Size of the packet, in bytes */
@ -147,7 +96,6 @@ struct int13_disk_parameters {
uint64_t sectors;
/** Bytes per sector */
uint16_t sector_size;
} __attribute__ (( packed ));
/**
@ -285,8 +233,7 @@ struct master_boot_record {
uint16_t signature;
} __attribute__ (( packed ));
extern void register_int13_drive ( struct int13_drive *drive );
extern void unregister_int13_drive ( struct int13_drive *drive );
extern int int13_boot ( unsigned int drive );
/** Use natural BIOS drive number */
#define INT13_USE_NATURAL_DRIVE 0xff
#endif /* INT13_H */

View File

@ -1,37 +0,0 @@
#ifndef _IPXE_ABFT_H
#define _IPXE_ABFT_H
/** @file
*
* AoE boot firmware table
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/acpi.h>
#include <ipxe/if_ether.h>
/** AoE boot firmware table signature */
#define ABFT_SIG "aBFT"
/**
* AoE Boot Firmware Table (aBFT)
*/
struct abft_table {
/** ACPI header */
struct acpi_description_header acpi;
/** AoE shelf */
uint16_t shelf;
/** AoE slot */
uint8_t slot;
/** Reserved */
uint8_t reserved_a;
/** MAC address */
uint8_t mac[ETH_ALEN];
} __attribute__ (( packed ));
extern void abft_fill_data ( struct aoe_session *aoe );
#endif /* _IPXE_ABFT_H */

View File

@ -0,0 +1,18 @@
#ifndef _IPXE_BIOS_SANBOOT_H
#define _IPXE_BIOS_SANBOOT_H
/** @file
*
* Standard PC-BIOS sanboot interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#ifdef SANBOOT_PCBIOS
#define SANBOOT_PREFIX_pcbios
#else
#define SANBOOT_PREFIX_pcbios __pcbios_
#endif
#endif /* _IPXE_BIOS_SANBOOT_H */

View File

@ -1,125 +0,0 @@
#ifndef _IPXE_SBFT_H
#define _IPXE_SBFT_H
/*
* Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
FILE_LICENCE ( BSD2 );
/** @file
*
* SRP boot firmware table
*
* The working draft specification for the SRP boot firmware table can
* be found at
*
* http://ipxe.org/wiki/srp/sbft
*
*/
#include <stdint.h>
#include <ipxe/acpi.h>
#include <ipxe/scsi.h>
#include <ipxe/srp.h>
#include <ipxe/ib_srp.h>
/** SRP Boot Firmware Table signature */
#define SBFT_SIG "sBFT"
/** An offset from the start of the sBFT */
typedef uint16_t sbft_off_t;
/**
* SRP Boot Firmware Table
*/
struct sbft_table {
/** ACPI header */
struct acpi_description_header acpi;
/** Offset to SCSI subtable */
sbft_off_t scsi_offset;
/** Offset to SRP subtable */
sbft_off_t srp_offset;
/** Offset to IB subtable, if present */
sbft_off_t ib_offset;
/** Reserved */
uint8_t reserved[6];
} __attribute__ (( packed ));
/**
* sBFT SCSI subtable
*/
struct sbft_scsi_subtable {
/** LUN */
struct scsi_lun lun;
} __attribute__ (( packed ));
/**
* sBFT SRP subtable
*/
struct sbft_srp_subtable {
/** Initiator and target ports */
struct srp_port_ids port_ids;
} __attribute__ (( packed ));
/**
* sBFT IB subtable
*/
struct sbft_ib_subtable {
/** Source GID */
struct ib_gid sgid;
/** Destination GID */
struct ib_gid dgid;
/** Service ID */
struct ib_gid_half service_id;
/** Partition key */
uint16_t pkey;
/** Reserved */
uint8_t reserved[6];
} __attribute__ (( packed ));
/**
* An sBFT created by iPXE
*/
struct ipxe_sbft {
/** The table header */
struct sbft_table table;
/** The SCSI subtable */
struct sbft_scsi_subtable scsi;
/** The SRP subtable */
struct sbft_srp_subtable srp;
/** The IB subtable */
struct sbft_ib_subtable ib;
} __attribute__ (( packed, aligned ( 16 ) ));
struct srp_device;
extern int sbft_fill_data ( struct srp_device *srp );
#endif /* _IPXE_SBFT_H */

View File

@ -1,62 +0,0 @@
/*
* Copyright (C) 2007 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 <realmode.h>
#include <ipxe/aoe.h>
#include <ipxe/netdevice.h>
#include <ipxe/abft.h>
/** @file
*
* AoE Boot Firmware Table
*
*/
#define abftab __use_data16 ( abftab )
/** The aBFT used by iPXE */
struct abft_table __data16 ( abftab ) __attribute__ (( aligned ( 16 ) )) = {
/* ACPI header */
.acpi = {
.signature = ABFT_SIG,
.length = sizeof ( abftab ),
.revision = 1,
.oem_id = "FENSYS",
.oem_table_id = "iPXE",
},
};
/**
* Fill in all variable portions of aBFT
*
* @v aoe AoE session
*/
void abft_fill_data ( struct aoe_session *aoe ) {
/* Fill in boot parameters */
abftab.shelf = aoe->major;
abftab.slot = aoe->minor;
memcpy ( abftab.mac, aoe->netdev->ll_addr, sizeof ( abftab.mac ) );
/* Update checksum */
acpi_fix_checksum ( &abftab.acpi );
DBG ( "AoE boot firmware table:\n" );
DBG_HD ( &abftab, sizeof ( abftab ) );
}

View File

@ -1,78 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ipxe/aoe.h>
#include <ipxe/ata.h>
#include <ipxe/netdevice.h>
#include <ipxe/sanboot.h>
#include <ipxe/abft.h>
#include <int13.h>
FILE_LICENCE ( GPL2_OR_LATER );
static int aoeboot ( const char *root_path ) {
struct ata_device *ata;
struct int13_drive *drive;
int rc;
ata = zalloc ( sizeof ( *ata ) );
if ( ! ata ) {
rc = -ENOMEM;
goto err_alloc_ata;
}
drive = zalloc ( sizeof ( *drive ) );
if ( ! drive ) {
rc = -ENOMEM;
goto err_alloc_drive;
}
/* FIXME: ugly, ugly hack */
struct net_device *netdev = last_opened_netdev();
if ( ( rc = aoe_attach ( ata, netdev, root_path ) ) != 0 ) {
printf ( "Could not attach AoE device: %s\n",
strerror ( rc ) );
goto err_attach;
}
if ( ( rc = init_atadev ( ata ) ) != 0 ) {
printf ( "Could not initialise AoE device: %s\n",
strerror ( rc ) );
goto err_init;
}
/* FIXME: ugly, ugly hack */
struct aoe_session *aoe =
container_of ( ata->backend, struct aoe_session, refcnt );
abft_fill_data ( aoe );
drive->blockdev = &ata->blockdev;
register_int13_drive ( drive );
printf ( "Registered as BIOS drive %#02x\n", drive->drive );
printf ( "Booting from BIOS drive %#02x\n", drive->drive );
rc = int13_boot ( drive->drive );
printf ( "Boot failed\n" );
/* Leave drive registered, if instructed to do so */
if ( keep_san() )
return rc;
printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
unregister_int13_drive ( drive );
err_init:
aoe_detach ( ata );
err_attach:
free ( drive );
err_alloc_drive:
free ( ata );
err_alloc_ata:
return rc;
}
struct sanboot_protocol aoe_sanboot_protocol __sanboot_protocol = {
.prefix = "aoe:",
.boot = aoeboot,
};

View File

@ -1,73 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ipxe/sanboot.h>
#include <int13.h>
#include <ipxe/srp.h>
#include <ipxe/sbft.h>
FILE_LICENCE ( GPL2_OR_LATER );
static int ib_srpboot ( const char *root_path ) {
struct scsi_device *scsi;
struct int13_drive *drive;
int rc;
scsi = zalloc ( sizeof ( *scsi ) );
if ( ! scsi ) {
rc = -ENOMEM;
goto err_alloc_scsi;
}
drive = zalloc ( sizeof ( *drive ) );
if ( ! drive ) {
rc = -ENOMEM;
goto err_alloc_drive;
}
if ( ( rc = srp_attach ( scsi, root_path ) ) != 0 ) {
printf ( "Could not attach IB_SRP device: %s\n",
strerror ( rc ) );
goto err_attach;
}
if ( ( rc = init_scsidev ( scsi ) ) != 0 ) {
printf ( "Could not initialise IB_SRP device: %s\n",
strerror ( rc ) );
goto err_init;
}
drive->blockdev = &scsi->blockdev;
/* FIXME: ugly, ugly hack */
struct srp_device *srp =
container_of ( scsi->backend, struct srp_device, refcnt );
sbft_fill_data ( srp );
register_int13_drive ( drive );
printf ( "Registered as BIOS drive %#02x\n", drive->drive );
printf ( "Booting from BIOS drive %#02x\n", drive->drive );
rc = int13_boot ( drive->drive );
printf ( "Boot failed\n" );
/* Leave drive registered, if instructed to do so */
if ( keep_san() )
return rc;
printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
unregister_int13_drive ( drive );
err_init:
srp_detach ( scsi );
err_attach:
free ( drive );
err_alloc_drive:
free ( scsi );
err_alloc_scsi:
return rc;
}
struct sanboot_protocol ib_srp_sanboot_protocol __sanboot_protocol = {
.prefix = "ib_srp:",
.boot = ib_srpboot,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ipxe/iscsi.h>
#include <ipxe/netdevice.h>
#include <ipxe/ibft.h>
#include <ipxe/sanboot.h>
#include <int13.h>
FILE_LICENCE ( GPL2_OR_LATER );
static int iscsiboot ( const char *root_path ) {
struct scsi_device *scsi;
struct int13_drive *drive;
int rc;
scsi = zalloc ( sizeof ( *scsi ) );
if ( ! scsi ) {
rc = -ENOMEM;
goto err_alloc_scsi;
}
drive = zalloc ( sizeof ( *drive ) );
if ( ! drive ) {
rc = -ENOMEM;
goto err_alloc_drive;
}
if ( ( rc = iscsi_attach ( scsi, root_path ) ) != 0 ) {
printf ( "Could not attach iSCSI device: %s\n",
strerror ( rc ) );
goto err_attach;
}
if ( ( rc = init_scsidev ( scsi ) ) != 0 ) {
printf ( "Could not initialise iSCSI device: %s\n",
strerror ( rc ) );
goto err_init;
}
drive->blockdev = &scsi->blockdev;
/* FIXME: ugly, ugly hack */
struct net_device *netdev = last_opened_netdev();
struct iscsi_session *iscsi =
container_of ( scsi->backend, struct iscsi_session, refcnt );
ibft_fill_data ( netdev, iscsi );
register_int13_drive ( drive );
printf ( "Registered as BIOS drive %#02x\n", drive->drive );
printf ( "Booting from BIOS drive %#02x\n", drive->drive );
rc = int13_boot ( drive->drive );
printf ( "Boot failed\n" );
/* Leave drive registered, if instructed to do so */
if ( keep_san() )
return rc;
printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
unregister_int13_drive ( drive );
err_init:
iscsi_detach ( scsi );
err_attach:
free ( drive );
err_alloc_drive:
free ( scsi );
err_alloc_scsi:
return rc;
}
struct sanboot_protocol iscsi_sanboot_protocol __sanboot_protocol = {
.prefix = "iscsi:",
.boot = iscsiboot,
};

View File

@ -1,26 +0,0 @@
#include <stdint.h>
#include <stdio.h>
#include <ipxe/settings.h>
#include <ipxe/dhcp.h>
#include <ipxe/init.h>
#include <ipxe/sanboot.h>
#include <usr/autoboot.h>
struct setting keep_san_setting __setting = {
.name = "keep-san",
.description = "Preserve SAN connection",
.tag = DHCP_EB_KEEP_SAN,
.type = &setting_type_int8,
};
int keep_san ( void ) {
int keep_san;
keep_san = fetch_intz_setting ( NULL, &keep_san_setting );
if ( ! keep_san )
return 0;
printf ( "Preserving connection to SAN disk\n" );
shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES;
return 1;
}

View File

@ -1,105 +0,0 @@
/*
* Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
FILE_LICENCE ( BSD2 );
/** @file
*
* SRP boot firmware table
*
*/
#include <assert.h>
#include <realmode.h>
#include <ipxe/srp.h>
#include <ipxe/ib_srp.h>
#include <ipxe/acpi.h>
#include <ipxe/sbft.h>
#define sbftab __use_data16 ( sbftab )
/** The sBFT used by iPXE */
struct ipxe_sbft __data16 ( sbftab ) = {
/* Table header */
.table = {
/* ACPI header */
.acpi = {
.signature = SBFT_SIG,
.length = sizeof ( sbftab ),
.revision = 1,
.oem_id = "FENSYS",
.oem_table_id = "iPXE",
},
.scsi_offset = offsetof ( typeof ( sbftab ), scsi ),
.srp_offset = offsetof ( typeof ( sbftab ), srp ),
.ib_offset = offsetof ( typeof ( sbftab ), ib ),
},
};
/**
* Fill in all variable portions of sBFT
*
* @v srp SRP device
* @ret rc Return status code
*/
int sbft_fill_data ( struct srp_device *srp ) {
struct sbft_scsi_subtable *sbft_scsi = &sbftab.scsi;
struct sbft_srp_subtable *sbft_srp = &sbftab.srp;
struct sbft_ib_subtable *sbft_ib = &sbftab.ib;
struct ib_srp_parameters *ib_params;
struct segoff rm_sbftab = {
.segment = rm_ds,
.offset = __from_data16 ( &sbftab ),
};
/* Fill in the SCSI subtable */
memcpy ( &sbft_scsi->lun, &srp->lun, sizeof ( sbft_scsi->lun ) );
/* Fill in the SRP subtable */
memcpy ( &sbft_srp->port_ids, &srp->port_ids,
sizeof ( sbft_srp->port_ids ) );
/* Fill in the IB subtable */
assert ( srp->transport == &ib_srp_transport );
ib_params = ib_srp_params ( srp );
memcpy ( &sbft_ib->sgid, &ib_params->sgid, sizeof ( sbft_ib->sgid ) );
memcpy ( &sbft_ib->dgid, &ib_params->dgid, sizeof ( sbft_ib->dgid ) );
memcpy ( &sbft_ib->service_id, &ib_params->service_id,
sizeof ( sbft_ib->service_id ) );
sbft_ib->pkey = ib_params->pkey;
/* Update checksum */
acpi_fix_checksum ( &sbftab.table.acpi );
DBGC ( &sbftab, "SRP Boot Firmware Table at %04x:%04x:\n",
rm_sbftab.segment, rm_sbftab.offset );
DBGC_HDA ( &sbftab, rm_sbftab, &sbftab, sizeof ( sbftab ) );
return 0;
}

View File

@ -0,0 +1,12 @@
#ifndef _BITS_SANBOOT_H
#define _BITS_SANBOOT_H
/** @file
*
* x86_64-specific sanboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#endif /* _BITS_SANBOOT_H */

View File

@ -133,7 +133,7 @@ REQUIRE_OBJECT ( slam );
*
*/
#ifdef SANBOOT_PROTO_ISCSI
REQUIRE_OBJECT ( iscsiboot );
REQUIRE_OBJECT ( iscsi );
#endif
/*

View File

@ -19,5 +19,5 @@ FILE_LICENCE ( GPL2_OR_LATER );
* Drag in Ethernet-specific protocols
*/
#ifdef SANBOOT_PROTO_AOE
REQUIRE_OBJECT ( aoeboot );
REQUIRE_OBJECT ( aoe );
#endif

View File

@ -19,5 +19,5 @@ FILE_LICENCE ( GPL2_OR_LATER );
* Drag in Infiniband-specific protocols
*/
#ifdef SANBOOT_PROTO_IB_SRP
REQUIRE_OBJECT ( ib_srpboot );
REQUIRE_OBJECT ( ib_srp );
#endif

View File

@ -15,6 +15,7 @@
#define NAP_EFIX86
#define UMALLOC_EFI
#define SMBIOS_EFI
#define SANBOOT_NULL
#define IMAGE_EFI /* EFI image support */
#define IMAGE_SCRIPT /* iPXE script image support */

View File

@ -13,6 +13,7 @@
#define UMALLOC_LINUX
#define NAP_LINUX
#define SMBIOS_LINUX
#define SANBOOT_NULL
#define DRIVERS_LINUX

View File

@ -17,6 +17,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define NAP_PCBIOS
#define UMALLOC_MEMTOP
#define SMBIOS_PCBIOS
#define SANBOOT_PCBIOS
#define IMAGE_ELF /* ELF image support */
#define IMAGE_MULTIBOOT /* MultiBoot image support */

16
src/config/sanboot.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef CONFIG_SANBOOT_H
#define CONFIG_SANBOOT_H
/** @file
*
* sanboot API configuration
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <config/defaults.h>
#include <config/local/sanboot.h>
#endif /* CONFIG_SANBOOT_H */

View File

@ -18,7 +18,9 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <errno.h>
#include <ipxe/acpi.h>
#include <ipxe/interface.h>
/** @file
*
@ -26,6 +28,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
/******************************************************************************
*
* Utility functions
*
******************************************************************************
*/
/**
* Fix up ACPI table checksum
*
@ -40,3 +49,37 @@ void acpi_fix_checksum ( struct acpi_description_header *acpi ) {
}
acpi->checksum -= sum;
}
/******************************************************************************
*
* Interface methods
*
******************************************************************************
*/
/**
* Describe object in an ACPI table
*
* @v intf Interface
* @v acpi ACPI table
* @v len Length of ACPI table
* @ret rc Return status code
*/
int acpi_describe ( struct interface *intf,
struct acpi_description_header *acpi, size_t len ) {
struct interface *dest;
acpi_describe_TYPE ( void * ) *op =
intf_get_dest_op ( intf, acpi_describe, &dest );
void *object = intf_object ( dest );
int rc;
if ( op ) {
rc = op ( object, acpi, len );
} else {
/* Default is to fail to describe */
rc = -EOPNOTSUPP;
}
intf_put ( dest );
return rc;
}

138
src/core/blockdev.c Normal file
View File

@ -0,0 +1,138 @@
/*
* 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/blockdev.h>
/** @file
*
* Block devices
*
*/
/**
* Read from block device
*
* @v control Control interface
* @v data Data interface
* @v lba Starting logical block address
* @v count Number of logical blocks
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
int block_read ( struct interface *control, struct interface *data,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len ) {
struct interface *dest;
block_read_TYPE ( void * ) *op =
intf_get_dest_op ( control, block_read, &dest );
void *object = intf_object ( dest );
int rc;
if ( op ) {
rc = op ( object, data, lba, count, buffer, len );
} else {
/* Default is to fail to issue the command */
rc = -EOPNOTSUPP;
}
intf_put ( dest );
return rc;
}
/**
* Write to block device
*
* @v control Control interface
* @v data Data interface
* @v lba Starting logical block address
* @v count Number of logical blocks
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
int block_write ( struct interface *control, struct interface *data,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len ) {
struct interface *dest;
block_write_TYPE ( void * ) *op =
intf_get_dest_op ( control, block_write, &dest );
void *object = intf_object ( dest );
int rc;
if ( op ) {
rc = op ( object, data, lba, count, buffer, len );
} else {
/* Default is to fail to issue the command */
rc = -EOPNOTSUPP;
}
intf_put ( dest );
return rc;
}
/**
* Read block device capacity
*
* @v control Control interface
* @v data Data interface
* @ret rc Return status code
*/
int block_read_capacity ( struct interface *control, struct interface *data ) {
struct interface *dest;
block_read_capacity_TYPE ( void * ) *op =
intf_get_dest_op ( control, block_read_capacity, &dest );
void *object = intf_object ( dest );
int rc;
if ( op ) {
rc = op ( object, data );
} else {
/* Default is to fail to issue the command */
rc = -EOPNOTSUPP;
}
intf_put ( dest );
return rc;
}
/**
* Report block device capacity
*
* @v intf Interface
* @v capacity Block device capacity
*/
void block_capacity ( struct interface *intf,
struct block_device_capacity *capacity ) {
struct interface *dest;
block_capacity_TYPE ( void * ) *op =
intf_get_dest_op ( intf, block_capacity, &dest );
void *object = intf_object ( dest );
if ( op ) {
op ( object, capacity );
} else {
/* Default is to do nothing */
}
intf_put ( dest );
}

44
src/core/null_sanboot.c Normal file
View File

@ -0,0 +1,44 @@
/*
* 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/sanboot.h>
static int null_san_hook ( struct uri *uri __unused,
unsigned int drive __unused ) {
return -EOPNOTSUPP;
}
static void null_san_unhook ( unsigned int drive __unused ) {
/* Do nothing */
}
static int null_san_boot ( unsigned int drive __unused ) {
return -EOPNOTSUPP;
}
static int null_san_describe ( unsigned int drive __unused ) {
return -EOPNOTSUPP;
}
PROVIDE_SANBOOT ( null, san_hook, null_san_hook );
PROVIDE_SANBOOT ( null, san_unhook, null_san_unhook );
PROVIDE_SANBOOT ( null, san_boot, null_san_boot );
PROVIDE_SANBOOT ( null, san_describe, null_san_describe );

View File

@ -19,12 +19,14 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/list.h>
#include <ipxe/interface.h>
#include <ipxe/blockdev.h>
#include <ipxe/process.h>
#include <ipxe/ata.h>
/** @file
@ -33,156 +35,625 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
static inline __attribute__ (( always_inline )) struct ata_device *
block_to_ata ( struct block_device *blockdev ) {
return container_of ( blockdev, struct ata_device, blockdev );
}
/******************************************************************************
*
* Interface methods
*
******************************************************************************
*/
/**
* Issue ATA command
*
* @v ata ATA device
* @v control ATA control interface
* @v data ATA data interface
* @v command ATA command
* @ret rc Return status code
* @ret tag Command tag, or negative error
*/
static inline __attribute__ (( always_inline )) int
ata_command ( struct ata_device *ata, struct ata_command *command ) {
int rc;
int ata_command ( struct interface *control, struct interface *data,
struct ata_cmd *command ) {
struct interface *dest;
ata_command_TYPE ( void * ) *op =
intf_get_dest_op ( control, ata_command, &dest );
void *object = intf_object ( dest );
int tag;
DBG ( "ATA cmd %02x dev %02x LBA%s %llx count %04x\n",
command->cb.cmd_stat, command->cb.device,
( command->cb.lba48 ? "48" : "" ),
( unsigned long long ) command->cb.lba.native,
command->cb.count.native );
/* Flag command as in-progress */
command->rc = -EINPROGRESS;
/* Issue ATA command */
if ( ( rc = ata->command ( ata, command ) ) != 0 ) {
/* Something went wrong with the issuing mechanism */
DBG ( "ATA could not issue command: %s\n", strerror ( rc ) );
return rc;
}
/* Wait for command to complete */
while ( command->rc == -EINPROGRESS )
step();
if ( ( rc = command->rc ) != 0 ) {
/* Something went wrong with the command execution */
DBG ( "ATA command failed: %s\n", strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Read block from ATA device
*
* @v blockdev Block device
* @v block LBA block number
* @v count Block count
* @v buffer Data buffer
* @ret rc Return status code
*/
static int ata_read ( struct block_device *blockdev, uint64_t block,
unsigned long count, userptr_t buffer ) {
struct ata_device *ata = block_to_ata ( blockdev );
struct ata_command command;
memset ( &command, 0, sizeof ( command ) );
command.cb.lba.native = block;
command.cb.count.native = count;
command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
command.cb.lba48 = ata->lba48;
if ( ! ata->lba48 )
command.cb.device |= command.cb.lba.bytes.low_prev;
command.cb.cmd_stat = ( ata->lba48 ? ATA_CMD_READ_EXT : ATA_CMD_READ );
command.data_in = buffer;
return ata_command ( ata, &command );
}
/**
* Write block to ATA device
*
* @v blockdev Block device
* @v block LBA block number
* @v count Block count
* @v buffer Data buffer
* @ret rc Return status code
*/
static int ata_write ( struct block_device *blockdev, uint64_t block,
unsigned long count, userptr_t buffer ) {
struct ata_device *ata = block_to_ata ( blockdev );
struct ata_command command;
memset ( &command, 0, sizeof ( command ) );
command.cb.lba.native = block;
command.cb.count.native = count;
command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
command.cb.lba48 = ata->lba48;
if ( ! ata->lba48 )
command.cb.device |= command.cb.lba.bytes.low_prev;
command.cb.cmd_stat = ( ata->lba48 ?
ATA_CMD_WRITE_EXT : ATA_CMD_WRITE );
command.data_out = buffer;
return ata_command ( ata, &command );
}
/**
* Identify ATA device
*
* @v blockdev Block device
* @ret rc Return status code
*/
static int ata_identify ( struct block_device *blockdev ) {
struct ata_device *ata = block_to_ata ( blockdev );
struct ata_command command;
struct ata_identity identity;
int rc;
/* Issue IDENTIFY */
memset ( &command, 0, sizeof ( command ) );
command.cb.count.native = 1;
command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
command.cb.cmd_stat = ATA_CMD_IDENTIFY;
command.data_in = virt_to_user ( &identity );
linker_assert ( sizeof ( identity ) == ATA_SECTOR_SIZE,
__ata_identity_bad_size__ );
if ( ( rc = ata_command ( ata, &command ) ) != 0 )
return rc;
/* Fill in block device parameters */
blockdev->blksize = ATA_SECTOR_SIZE;
if ( identity.supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) {
ata->lba48 = 1;
blockdev->blocks = le64_to_cpu ( identity.lba48_sectors );
if ( op ) {
tag = op ( object, data, command );
} else {
blockdev->blocks = le32_to_cpu ( identity.lba_sectors );
/* Default is to fail to issue the command */
tag = -EOPNOTSUPP;
}
return 0;
intf_put ( dest );
return tag;
}
static struct block_device_operations ata_operations = {
.read = ata_read,
.write = ata_write
/******************************************************************************
*
* ATA devices and commands
*
******************************************************************************
*/
/** List of all ATA commands */
static LIST_HEAD ( ata_commands );
/** An ATA device */
struct ata_device {
/** Reference count */
struct refcnt refcnt;
/** Block control interface */
struct interface block;
/** ATA control interface */
struct interface ata;
/** Device number
*
* Must be ATA_DEV_MASTER or ATA_DEV_SLAVE.
*/
unsigned int device;
/** Maximum number of blocks per single transfer */
unsigned int max_count;
/** Device uses LBA48 extended addressing */
int lba48;
};
/** An ATA command */
struct ata_command {
/** Reference count */
struct refcnt refcnt;
/** ATA device */
struct ata_device *atadev;
/** List of ATA commands */
struct list_head list;
/** Block data interface */
struct interface block;
/** ATA data interface */
struct interface ata;
/** Command type */
struct ata_command_type *type;
/** Command tag */
uint32_t tag;
/** Private data */
uint8_t priv[0];
};
/** An ATA command type */
struct ata_command_type {
/** Name */
const char *name;
/** Additional working space */
size_t priv_len;
/** Command for non-LBA48-capable devices */
uint8_t cmd_lba;
/** Command for LBA48-capable devices */
uint8_t cmd_lba48;
/**
* Calculate data-in buffer
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data_in Data-in buffer
* @ret data_in_len Data-in buffer length
*/
void ( * data_in ) ( struct ata_command *atacmd, userptr_t buffer,
size_t len, userptr_t *data_in,
size_t *data_in_len );
/**
* Calculate data-out buffer
*
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data_out Data-out buffer
* @ret data_out_len Data-out buffer length
*/
void ( * data_out ) ( struct ata_command *atacmd, userptr_t buffer,
size_t len, userptr_t *data_out,
size_t *data_out_len );
/**
* Handle ATA command completion
*
* @v atacmd ATA command
* @v rc Reason for completion
*/
void ( * done ) ( struct ata_command *atacmd, int rc );
};
/**
* Initialise ATA device
* Get reference to ATA device
*
* @v ata ATA device
* @ret rc Return status code
*
* Initialises an ATA device. The ata_device::command field and the
* @c ATA_FL_SLAVE portion of the ata_device::flags field must already
* be filled in. This function will configure ata_device::blockdev,
* including issuing an IDENTIFY DEVICE call to determine the block
* size and total device size.
* @v atadev ATA device
* @ret atadev ATA device
*/
int init_atadev ( struct ata_device *ata ) {
/** Fill in read and write methods, and get device capacity */
ata->blockdev.op = &ata_operations;
return ata_identify ( &ata->blockdev );
static inline __attribute__ (( always_inline )) struct ata_device *
atadev_get ( struct ata_device *atadev ) {
ref_get ( &atadev->refcnt );
return atadev;
}
/**
* Drop reference to ATA device
*
* @v atadev ATA device
*/
static inline __attribute__ (( always_inline )) void
atadev_put ( struct ata_device *atadev ) {
ref_put ( &atadev->refcnt );
}
/**
* Get reference to ATA command
*
* @v atacmd ATA command
* @ret atacmd ATA command
*/
static inline __attribute__ (( always_inline )) struct ata_command *
atacmd_get ( struct ata_command *atacmd ) {
ref_get ( &atacmd->refcnt );
return atacmd;
}
/**
* Drop reference to ATA command
*
* @v atacmd ATA command
*/
static inline __attribute__ (( always_inline )) void
atacmd_put ( struct ata_command *atacmd ) {
ref_put ( &atacmd->refcnt );
}
/**
* Get ATA command private data
*
* @v atacmd ATA command
* @ret priv Private data
*/
static inline __attribute__ (( always_inline )) void *
atacmd_priv ( struct ata_command *atacmd ) {
return atacmd->priv;
}
/**
* Free ATA command
*
* @v refcnt Reference count
*/
static void atacmd_free ( struct refcnt *refcnt ) {
struct ata_command *atacmd =
container_of ( refcnt, struct ata_command, refcnt );
/* Remove from list of commands */
list_del ( &atacmd->list );
atadev_put ( atacmd->atadev );
/* Free command */
free ( atacmd );
}
/**
* Close ATA command
*
* @v atacmd ATA command
* @v rc Reason for close
*/
static void atacmd_close ( struct ata_command *atacmd, int rc ) {
struct ata_device *atadev = atacmd->atadev;
if ( rc != 0 ) {
DBGC ( atadev, "ATA %p tag %08x closed: %s\n",
atadev, atacmd->tag, strerror ( rc ) );
}
/* Shut down interfaces */
intf_shutdown ( &atacmd->ata, rc );
intf_shutdown ( &atacmd->block, rc );
}
/**
* Handle ATA command completion
*
* @v atacmd ATA command
* @v rc Reason for close
*/
static void atacmd_done ( struct ata_command *atacmd, int rc ) {
/* Hand over to the command completion handler */
atacmd->type->done ( atacmd, rc );
}
/**
* Use provided data buffer for ATA command
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data Data buffer
* @ret data_len Data buffer length
*/
static void atacmd_data_buffer ( struct ata_command *atacmd __unused,
userptr_t buffer, size_t len,
userptr_t *data, size_t *data_len ) {
*data = buffer;
*data_len = len;
}
/**
* Use no data buffer for ATA command
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data Data buffer
* @ret data_len Data buffer length
*/
static void atacmd_data_none ( struct ata_command *atacmd __unused,
userptr_t buffer __unused, size_t len __unused,
userptr_t *data __unused,
size_t *data_len __unused ) {
/* Nothing to do */
}
/**
* Use private data buffer for ATA command
*
* @v atacmd ATA command
* @v buffer Available buffer
* @v len Available buffer length
* @ret data Data buffer
* @ret data_len Data buffer length
*/
static void atacmd_data_priv ( struct ata_command *atacmd,
userptr_t buffer __unused, size_t len __unused,
userptr_t *data, size_t *data_len ) {
*data = virt_to_user ( atacmd_priv ( atacmd ) );
*data_len = atacmd->type->priv_len;
}
/** ATA READ command type */
static struct ata_command_type atacmd_read = {
.name = "READ",
.cmd_lba = ATA_CMD_READ,
.cmd_lba48 = ATA_CMD_READ_EXT,
.data_in = atacmd_data_buffer,
.data_out = atacmd_data_none,
.done = atacmd_close,
};
/** ATA WRITE command type */
static struct ata_command_type atacmd_write = {
.name = "WRITE",
.cmd_lba = ATA_CMD_WRITE,
.cmd_lba48 = ATA_CMD_WRITE_EXT,
.data_in = atacmd_data_none,
.data_out = atacmd_data_buffer,
.done = atacmd_close,
};
/** ATA IDENTIFY private data */
struct ata_identify_private {
/** Identity data */
struct ata_identity identity;
};
/**
* Return ATA model string (for debugging)
*
* @v identify ATA identity data
* @ret model Model string
*/
static const char * ata_model ( struct ata_identity *identity ) {
static union {
uint16_t words[ sizeof ( identity->model ) / 2 ];
char text[ sizeof ( identity->model ) + 1 /* NUL */ ];
} buf;
unsigned int i;
for ( i = 0 ; i < ( sizeof ( identity->model ) / 2 ) ; i++ )
buf.words[i] = bswap_16 ( identity->model[i] );
return buf.text;
}
/**
* Handle ATA IDENTIFY command completion
*
* @v atacmd ATA command
* @v rc Reason for completion
*/
static void atacmd_identify_done ( struct ata_command *atacmd, int rc ) {
struct ata_device *atadev = atacmd->atadev;
struct ata_identify_private *priv = atacmd_priv ( atacmd );
struct ata_identity *identity = &priv->identity;
struct block_device_capacity capacity;
/* Close if command failed */
if ( rc != 0 ) {
atacmd_close ( atacmd, rc );
return;
}
/* Extract capacity */
if ( identity->supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) {
atadev->lba48 = 1;
capacity.blocks = le64_to_cpu ( identity->lba48_sectors );
} else {
capacity.blocks = le32_to_cpu ( identity->lba_sectors );
}
capacity.blksize = ATA_SECTOR_SIZE;
capacity.max_count = atadev->max_count;
DBGC ( atadev, "ATA %p is a %s\n", atadev, ata_model ( identity ) );
DBGC ( atadev, "ATA %p has %#llx blocks (%ld MB) and uses %s\n",
atadev, capacity.blocks,
( ( signed long ) ( capacity.blocks >> 11 ) ),
( atadev->lba48 ? "LBA48" : "LBA" ) );
/* Return capacity to caller */
block_capacity ( &atacmd->block, &capacity );
/* Close command */
atacmd_close ( atacmd, 0 );
}
/** ATA IDENTITY command type */
static struct ata_command_type atacmd_identify = {
.name = "IDENTIFY",
.priv_len = sizeof ( struct ata_identify_private ),
.cmd_lba = ATA_CMD_IDENTIFY,
.cmd_lba48 = ATA_CMD_IDENTIFY,
.data_in = atacmd_data_priv,
.data_out = atacmd_data_none,
.done = atacmd_identify_done,
};
/** ATA command block interface operations */
static struct interface_operation atacmd_block_op[] = {
INTF_OP ( intf_close, struct ata_command *, atacmd_close ),
};
/** ATA command block interface descriptor */
static struct interface_descriptor atacmd_block_desc =
INTF_DESC_PASSTHRU ( struct ata_command, block,
atacmd_block_op, ata );
/** ATA command ATA interface operations */
static struct interface_operation atacmd_ata_op[] = {
INTF_OP ( intf_close, struct ata_command *, atacmd_done ),
};
/** ATA command ATA interface descriptor */
static struct interface_descriptor atacmd_ata_desc =
INTF_DESC_PASSTHRU ( struct ata_command, ata,
atacmd_ata_op, block );
/**
* Create ATA command
*
* @v atadev ATA device
* @v block Block data interface
* @v type ATA command type
* @v lba Starting logical block address
* @v count Number of blocks to transfer
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
static int atadev_command ( struct ata_device *atadev,
struct interface *block,
struct ata_command_type *type,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len ) {
struct ata_command *atacmd;
struct ata_cmd command;
int tag;
int rc;
/* Allocate and initialise structure */
atacmd = zalloc ( sizeof ( *atacmd ) + type->priv_len );
if ( ! atacmd ) {
rc = -ENOMEM;
goto err_zalloc;
}
ref_init ( &atacmd->refcnt, atacmd_free );
intf_init ( &atacmd->block, &atacmd_block_desc, &atacmd->refcnt );
intf_init ( &atacmd->ata, &atacmd_ata_desc,
&atacmd->refcnt );
atacmd->atadev = atadev_get ( atadev );
list_add ( &atacmd->list, &ata_commands );
atacmd->type = type;
/* Sanity check */
if ( len != ( count * ATA_SECTOR_SIZE ) ) {
DBGC ( atadev, "ATA %p tag %08x buffer length mismatch (count "
"%d len %zd)\n", atadev, atacmd->tag, count, len );
rc = -EINVAL;
goto err_len;
}
/* Construct command */
memset ( &command, 0, sizeof ( command ) );
command.cb.lba.native = lba;
command.cb.count.native = count;
command.cb.device = ( atadev->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
command.cb.lba48 = atadev->lba48;
if ( ! atadev->lba48 )
command.cb.device |= command.cb.lba.bytes.low_prev;
command.cb.cmd_stat =
( atadev->lba48 ? type->cmd_lba48 : type->cmd_lba );
type->data_in ( atacmd, buffer, len,
&command.data_in, &command.data_in_len );
type->data_out ( atacmd, buffer, len,
&command.data_out, &command.data_out_len );
/* Issue command */
if ( ( tag = ata_command ( &atadev->ata, &atacmd->ata,
&command ) ) < 0 ) {
rc = tag;
DBGC ( atadev, "ATA %p tag %08x could not issue command: %s\n",
atadev, atacmd->tag, strerror ( rc ) );
goto err_command;
}
atacmd->tag = tag;
DBGC2 ( atadev, "ATA %p tag %08x %s cmd %02x dev %02x LBA%s %08llx "
"count %04x\n", atadev, atacmd->tag, atacmd->type->name,
command.cb.cmd_stat, command.cb.device,
( command.cb.lba48 ? "48" : "" ),
( unsigned long long ) command.cb.lba.native,
command.cb.count.native );
/* Attach to parent interface, mortalise self, and return */
intf_plug_plug ( &atacmd->block, block );
ref_put ( &atacmd->refcnt );
return 0;
err_command:
err_len:
atacmd_close ( atacmd, rc );
ref_put ( &atacmd->refcnt );
err_zalloc:
return rc;
}
/**
* Issue ATA block read
*
* @v atadev ATA device
* @v block Block data interface
* @v lba Starting logical block address
* @v count Number of blocks to transfer
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
static int atadev_read ( struct ata_device *atadev,
struct interface *block,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len ) {
return atadev_command ( atadev, block, &atacmd_read,
lba, count, buffer, len );
}
/**
* Issue ATA block write
*
* @v atadev ATA device
* @v block Block data interface
* @v lba Starting logical block address
* @v count Number of blocks to transfer
* @v buffer Data buffer
* @v len Length of data buffer
* @ret rc Return status code
*/
static int atadev_write ( struct ata_device *atadev,
struct interface *block,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len ) {
return atadev_command ( atadev, block, &atacmd_write,
lba, count, buffer, len );
}
/**
* Read ATA device capacity
*
* @v atadev ATA device
* @v block Block data interface
* @ret rc Return status code
*/
static int atadev_read_capacity ( struct ata_device *atadev,
struct interface *block ) {
struct ata_identity *identity;
assert ( atacmd_identify.priv_len == sizeof ( *identity ) );
assert ( atacmd_identify.priv_len == ATA_SECTOR_SIZE );
return atadev_command ( atadev, block, &atacmd_identify,
0, 1, UNULL, ATA_SECTOR_SIZE );
}
/**
* Close ATA device
*
* @v atadev ATA device
* @v rc Reason for close
*/
static void atadev_close ( struct ata_device *atadev, int rc ) {
struct ata_command *atacmd;
struct ata_command *tmp;
/* Shut down interfaces */
intf_shutdown ( &atadev->block, rc );
intf_shutdown ( &atadev->ata, rc );
/* Shut down any remaining commands */
list_for_each_entry_safe ( atacmd, tmp, &ata_commands, list ) {
if ( atacmd->atadev != atadev )
continue;
atacmd_get ( atacmd );
atacmd_close ( atacmd, rc );
atacmd_put ( atacmd );
}
}
/** ATA device block interface operations */
static struct interface_operation atadev_block_op[] = {
INTF_OP ( block_read, struct ata_device *, atadev_read ),
INTF_OP ( block_write, struct ata_device *, atadev_write ),
INTF_OP ( block_read_capacity, struct ata_device *,
atadev_read_capacity ),
INTF_OP ( intf_close, struct ata_device *, atadev_close ),
};
/** ATA device block interface descriptor */
static struct interface_descriptor atadev_block_desc =
INTF_DESC_PASSTHRU ( struct ata_device, block,
atadev_block_op, ata );
/** ATA device ATA interface operations */
static struct interface_operation atadev_ata_op[] = {
INTF_OP ( intf_close, struct ata_device *, atadev_close ),
};
/** ATA device ATA interface descriptor */
static struct interface_descriptor atadev_ata_desc =
INTF_DESC_PASSTHRU ( struct ata_device, ata,
atadev_ata_op, block );
/**
* Open ATA device
*
* @v block Block control interface
* @v ata ATA control interface
* @v device ATA device number
* @v max_count Maximum number of blocks per single transfer
* @ret rc Return status code
*/
int ata_open ( struct interface *block, struct interface *ata,
unsigned int device, unsigned int max_count ) {
struct ata_device *atadev;
/* Allocate and initialise structure */
atadev = zalloc ( sizeof ( *atadev ) );
if ( ! atadev )
return -ENOMEM;
ref_init ( &atadev->refcnt, NULL );
intf_init ( &atadev->block, &atadev_block_desc, &atadev->refcnt );
intf_init ( &atadev->ata, &atadev_ata_desc, &atadev->refcnt );
atadev->device = device;
atadev->max_count = max_count;
/* Attach to ATA and parent and interfaces, mortalise self,
* and return
*/
intf_plug_plug ( &atadev->ata, ata );
intf_plug_plug ( &atadev->block, block );
ref_put ( &atadev->refcnt );
return 0;
}

View File

@ -32,7 +32,6 @@ FILE_LICENCE ( BSD2 );
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <realmode.h>
#include <ipxe/pci.h>
#include <ipxe/acpi.h>
#include <ipxe/in.h>
@ -53,62 +52,36 @@ FILE_LICENCE ( BSD2 );
*
*/
#define ibftab __use_data16 ( ibftab )
/** The iBFT used by iPXE */
struct ipxe_ibft __data16 ( ibftab ) = {
/* Table header */
.table = {
/* ACPI header */
.acpi = {
.signature = IBFT_SIG,
.length = sizeof ( ibftab ),
.revision = 1,
.oem_id = "FENSYS",
.oem_table_id = "iPXE",
},
/* Control block */
.control = {
.header = {
.structure_id = IBFT_STRUCTURE_ID_CONTROL,
.version = 1,
.length = sizeof ( ibftab.table.control ),
.flags = 0,
},
.initiator = offsetof ( typeof ( ibftab ), initiator ),
.nic_0 = offsetof ( typeof ( ibftab ), nic ),
.target_0 = offsetof ( typeof ( ibftab ), target ),
},
},
/* iSCSI initiator information */
.initiator = {
.header = {
.structure_id = IBFT_STRUCTURE_ID_INITIATOR,
.version = 1,
.length = sizeof ( ibftab.initiator ),
.flags = ( IBFT_FL_INITIATOR_BLOCK_VALID |
IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ),
},
},
/* NIC information */
.nic = {
.header = {
.structure_id = IBFT_STRUCTURE_ID_NIC,
.version = 1,
.length = sizeof ( ibftab.nic ),
.flags = ( IBFT_FL_NIC_BLOCK_VALID |
IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ),
},
},
/* iSCSI target information */
.target = {
.header = {
.structure_id = IBFT_STRUCTURE_ID_TARGET,
.version = 1,
.length = sizeof ( ibftab.target ),
.flags = ( IBFT_FL_TARGET_BLOCK_VALID |
IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED ),
},
},
/**
* An iBFT created by iPXE
*
*/
struct ipxe_ibft {
/** The fixed section */
struct ibft_table table;
/** The Initiator section */
struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) ));
/** The NIC section */
struct ibft_nic nic __attribute__ (( aligned ( 16 ) ));
/** The Target section */
struct ibft_target target __attribute__ (( aligned ( 16 ) ));
/** Strings block */
char strings[0];
} __attribute__ (( packed, aligned ( 16 ) ));
/**
* iSCSI string block descriptor
*
* This is an internal structure that we use to keep track of the
* allocation of string data.
*/
struct ibft_strings {
/** The iBFT containing these strings */
struct ibft_table *table;
/** Offset of first free byte within iBFT */
size_t offset;
/** Total length of the iBFT */
size_t len;
};
/**
@ -132,9 +105,9 @@ static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) {
* @v setting Configuration setting
* @v tag DHCP option tag
*/
static void ibft_set_ipaddr_option ( struct ibft_ipaddr *ipaddr,
struct setting *setting ) {
struct in_addr in = { 0 };
static void ibft_set_ipaddr_setting ( struct ibft_ipaddr *ipaddr,
struct setting *setting ) {
struct in_addr in;
fetch_ipv4_setting ( NULL, setting, &in );
ibft_set_ipaddr ( ipaddr, in );
}
@ -156,22 +129,19 @@ static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) {
* @v strings iBFT string block descriptor
* @v string String field to fill in
* @v len Length of string to allocate (excluding NUL)
* @ret rc Return status code
* @ret dest String destination, or NULL
*/
static int ibft_alloc_string ( struct ibft_string_block *strings,
struct ibft_string *string, size_t len ) {
char *dest;
unsigned int remaining;
static char * ibft_alloc_string ( struct ibft_strings *strings,
struct ibft_string *string, size_t len ) {
dest = ( ( ( char * ) strings->table ) + strings->offset );
remaining = ( strings->table->acpi.length - strings->offset );
if ( len >= remaining )
return -ENOMEM;
if ( ( strings->offset + len ) >= strings->len )
return NULL;
string->offset = strings->offset;
string->length = len;
string->offset = cpu_to_le16 ( strings->offset );
string->len = cpu_to_le16 ( len );
strings->offset += ( len + 1 );
return 0;
return ( ( ( char * ) strings->table ) + string->offset );
}
/**
@ -182,18 +152,16 @@ static int ibft_alloc_string ( struct ibft_string_block *strings,
* @v data String to fill in, or NULL
* @ret rc Return status code
*/
static int ibft_set_string ( struct ibft_string_block *strings,
static int ibft_set_string ( struct ibft_strings *strings,
struct ibft_string *string, const char *data ) {
char *dest;
int rc;
if ( ! data )
return 0;
if ( ( rc = ibft_alloc_string ( strings, string,
strlen ( data ) ) ) != 0 )
return rc;
dest = ( ( ( char * ) strings->table ) + string->offset );
dest = ibft_alloc_string ( strings, string, strlen ( data ) );
if ( ! dest )
return -ENOBUFS;
strcpy ( dest, data );
return 0;
@ -207,24 +175,24 @@ static int ibft_set_string ( struct ibft_string_block *strings,
* @v setting Configuration setting
* @ret rc Return status code
*/
static int ibft_set_string_option ( struct ibft_string_block *strings,
struct ibft_string *string,
struct setting *setting ) {
static int ibft_set_string_setting ( struct ibft_strings *strings,
struct ibft_string *string,
struct setting *setting ) {
int len;
char *dest;
int rc;
len = fetch_setting_len ( NULL, setting );
if ( len < 0 ) {
string->offset = 0;
string->length = 0;
string->len = 0;
return 0;
}
if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 )
return rc;
dest = ( ( ( char * ) strings->table ) + string->offset );
dest = ibft_alloc_string ( strings, string, len );
if ( ! dest )
return -ENOBUFS;
fetch_string_setting ( NULL, setting, dest, ( len + 1 ) );
return 0;
}
@ -235,7 +203,7 @@ static int ibft_set_string_option ( struct ibft_string_block *strings,
* @v string String field
* @ret data String content (or "<empty>")
*/
static const char * ibft_string ( struct ibft_string_block *strings,
static const char * ibft_string ( struct ibft_strings *strings,
struct ibft_string *string ) {
return ( string->offset ?
( ( ( char * ) strings->table ) + string->offset ) : NULL );
@ -250,22 +218,29 @@ static const char * ibft_string ( struct ibft_string_block *strings,
* @ret rc Return status code
*/
static int ibft_fill_nic ( struct ibft_nic *nic,
struct ibft_string_block *strings,
struct ibft_strings *strings,
struct net_device *netdev ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct in_addr netmask_addr = { 0 };
unsigned int netmask_count = 0;
int rc;
/* Extract values from DHCP configuration */
ibft_set_ipaddr_option ( &nic->ip_address, &ip_setting );
/* Fill in common header */
nic->header.structure_id = IBFT_STRUCTURE_ID_NIC;
nic->header.version = 1;
nic->header.length = cpu_to_le16 ( sizeof ( *nic ) );
nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID |
IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED );
/* Extract values from configuration settings */
ibft_set_ipaddr_setting ( &nic->ip_address, &ip_setting );
DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) );
ibft_set_ipaddr_option ( &nic->gateway, &gateway_setting );
ibft_set_ipaddr_setting ( &nic->gateway, &gateway_setting );
DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
ibft_set_ipaddr_option ( &nic->dns[0], &dns_setting );
ibft_set_ipaddr_setting ( &nic->dns[0], &dns_setting );
DBG ( "iBFT NIC DNS = %s\n", ibft_ipaddr ( &nic->dns[0] ) );
if ( ( rc = ibft_set_string_option ( strings, &nic->hostname,
&hostname_setting ) ) != 0 )
if ( ( rc = ibft_set_string_setting ( strings, &nic->hostname,
&hostname_setting ) ) != 0 )
return rc;
DBG ( "iBFT NIC hostname = %s\n",
ibft_string ( strings, &nic->hostname ) );
@ -287,8 +262,8 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
return rc;
}
DBG ( "iBFT NIC MAC = %s\n", eth_ntoa ( nic->mac_address ) );
nic->pci_bus_dev_func = netdev->dev->desc.location;
DBG ( "iBFT NIC PCI = %04x\n", nic->pci_bus_dev_func );
nic->pci_bus_dev_func = cpu_to_le16 ( netdev->dev->desc.location );
DBG ( "iBFT NIC PCI = %04x\n", le16_to_cpu ( nic->pci_bus_dev_func ) );
return 0;
}
@ -301,10 +276,18 @@ static int ibft_fill_nic ( struct ibft_nic *nic,
* @ret rc Return status code
*/
static int ibft_fill_initiator ( struct ibft_initiator *initiator,
struct ibft_string_block *strings ) {
struct ibft_strings *strings ) {
const char *initiator_iqn = iscsi_initiator_iqn();
int rc;
/* Fill in common header */
initiator->header.structure_id = IBFT_STRUCTURE_ID_INITIATOR;
initiator->header.version = 1;
initiator->header.length = cpu_to_le16 ( sizeof ( *initiator ) );
initiator->header.flags = ( IBFT_FL_INITIATOR_BLOCK_VALID |
IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED );
/* Fill in hostname */
if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name,
initiator_iqn ) ) != 0 )
return rc;
@ -323,7 +306,7 @@ static int ibft_fill_initiator ( struct ibft_initiator *initiator,
* @ret rc Return status code
*/
static int ibft_fill_target_chap ( struct ibft_target *target,
struct ibft_string_block *strings,
struct ibft_strings *strings,
struct iscsi_session *iscsi ) {
int rc;
@ -356,7 +339,7 @@ static int ibft_fill_target_chap ( struct ibft_target *target,
* @ret rc Return status code
*/
static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
struct ibft_string_block *strings,
struct ibft_strings *strings,
struct iscsi_session *iscsi ) {
int rc;
@ -391,17 +374,27 @@ static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
* @ret rc Return status code
*/
static int ibft_fill_target ( struct ibft_target *target,
struct ibft_string_block *strings,
struct ibft_strings *strings,
struct iscsi_session *iscsi ) {
struct sockaddr_in *sin_target =
( struct sockaddr_in * ) &iscsi->target_sockaddr;
int rc;
/* Fill in common header */
target->header.structure_id = IBFT_STRUCTURE_ID_TARGET;
target->header.version = 1;
target->header.length = cpu_to_le16 ( sizeof ( *target ) );
target->header.flags = ( IBFT_FL_TARGET_BLOCK_VALID |
IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED );
/* Fill in Target values */
ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) );
target->socket = ntohs ( sin_target->sin_port );
target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) );
DBG ( "iBFT target port = %d\n", target->socket );
memcpy ( &target->boot_lun, &iscsi->lun, sizeof ( target->boot_lun ) );
DBG ( "iBFT target boot LUN = " SCSI_LUN_FORMAT "\n",
SCSI_LUN_DATA ( target->boot_lun ) );
if ( ( rc = ibft_set_string ( strings, &target->target_name,
iscsi->target_iqn ) ) != 0 )
return rc;
@ -417,35 +410,62 @@ static int ibft_fill_target ( struct ibft_target *target,
}
/**
* Fill in all variable portions of iBFT
* Fill in iBFT
*
* @v netdev Network device
* @v initiator_iqn Initiator IQN
* @v st_target Target socket address
* @v target_iqn Target IQN
* @v iscsi iSCSI session
* @v acpi ACPI table
* @v len Length of ACPI table
* @ret rc Return status code
*
*/
int ibft_fill_data ( struct net_device *netdev,
struct iscsi_session *iscsi ) {
struct ibft_string_block strings = {
.table = &ibftab.table,
.offset = offsetof ( typeof ( ibftab ), strings ),
int ibft_describe ( struct iscsi_session *iscsi,
struct acpi_description_header *acpi,
size_t len ) {
struct ipxe_ibft *ibft =
container_of ( acpi, struct ipxe_ibft, table.acpi );
struct ibft_strings strings = {
.table = &ibft->table,
.offset = offsetof ( typeof ( *ibft ), strings ),
.len = len,
};
struct net_device *netdev;
int rc;
/* Fill in NIC, Initiator and Target portions */
if ( ( rc = ibft_fill_nic ( &ibftab.nic, &strings, netdev ) ) != 0 )
/* Ugly hack. Now that we have a generic interface mechanism
* that can support ioctls, we can potentially eliminate this.
*/
netdev = last_opened_netdev();
if ( ! netdev ) {
DBGC ( iscsi, "iSCSI %p cannot guess network device\n",
iscsi );
return -ENODEV;
}
/* Fill in ACPI header */
ibft->table.acpi.signature = cpu_to_le32 ( IBFT_SIG );
ibft->table.acpi.length = cpu_to_le32 ( len );
ibft->table.acpi.revision = 1;
/* Fill in Control block */
ibft->table.control.header.structure_id = IBFT_STRUCTURE_ID_CONTROL;
ibft->table.control.header.version = 1;
ibft->table.control.header.length =
cpu_to_le16 ( sizeof ( ibft->table.control ) );
ibft->table.control.initiator =
cpu_to_le16 ( offsetof ( typeof ( *ibft ), initiator ) );
ibft->table.control.nic_0 =
cpu_to_le16 ( offsetof ( typeof ( *ibft ), nic ) );
ibft->table.control.target_0 =
cpu_to_le16 ( offsetof ( typeof ( *ibft ), target ) );
/* Fill in NIC, Initiator and Target blocks */
if ( ( rc = ibft_fill_nic ( &ibft->nic, &strings, netdev ) ) != 0 )
return rc;
if ( ( rc = ibft_fill_initiator ( &ibftab.initiator,
if ( ( rc = ibft_fill_initiator ( &ibft->initiator,
&strings ) ) != 0 )
return rc;
if ( ( rc = ibft_fill_target ( &ibftab.target, &strings,
if ( ( rc = ibft_fill_target ( &ibft->target, &strings,
iscsi ) ) != 0 )
return rc;
/* Update checksum */
acpi_fix_checksum ( &ibftab.table.acpi );
return 0;
}

View File

@ -1,97 +0,0 @@
/*
* Copyright (C) 2007 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 <ipxe/blockdev.h>
#include <ipxe/ramdisk.h>
/**
* @file
*
* RAM disks
*
*/
static inline __attribute__ (( always_inline )) struct ramdisk *
block_to_ramdisk ( struct block_device *blockdev ) {
return container_of ( blockdev, struct ramdisk, blockdev );
}
/**
* Read block
*
* @v blockdev Block device
* @v block Block number
* @v count Block count
* @v buffer Data buffer
* @ret rc Return status code
*/
static int ramdisk_read ( struct block_device *blockdev, uint64_t block,
unsigned long count, userptr_t buffer ) {
struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
unsigned long offset = ( block * blockdev->blksize );
unsigned long length = ( count * blockdev->blksize );
DBGC ( ramdisk, "RAMDISK %p reading [%lx,%lx)\n",
ramdisk, offset, length );
memcpy_user ( buffer, 0, ramdisk->data, offset, length );
return 0;
}
/**
* Write block
*
* @v blockdev Block device
* @v block Block number
* @v count Block count
* @v buffer Data buffer
* @ret rc Return status code
*/
static int ramdisk_write ( struct block_device *blockdev, uint64_t block,
unsigned long count, userptr_t buffer ) {
struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
unsigned long offset = ( block * blockdev->blksize );
unsigned long length = ( count * blockdev->blksize );
DBGC ( ramdisk, "RAMDISK %p writing [%lx,%lx)\n",
ramdisk, offset, length );
memcpy_user ( ramdisk->data, offset, buffer, 0, length );
return 0;
}
static struct block_device_operations ramdisk_operations = {
.read = ramdisk_read,
.write = ramdisk_write
};
int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
unsigned int blksize ) {
if ( ! blksize )
blksize = 512;
ramdisk->data = data;
ramdisk->blockdev.op = &ramdisk_operations;
ramdisk->blockdev.blksize = blksize;
ramdisk->blockdev.blocks = ( len / blksize );
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/interface.h>
/**
* An ACPI description header
@ -19,7 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
*/
struct acpi_description_header {
/** ACPI signature (4 ASCII characters) */
char signature[4];
uint32_t signature;
/** Length of table, in bytes, including header */
uint32_t length;
/** ACPI Specification minor version number */
@ -38,6 +39,25 @@ struct acpi_description_header {
uint32_t asl_compiler_revision;
} __attribute__ (( packed ));
/**
* Build ACPI signature
*
* @v a First character of ACPI signature
* @v b Second character of ACPI signature
* @v c Third character of ACPI signature
* @v d Fourth character of ACPI signature
* @ret signature ACPI signature
*/
#define ACPI_SIGNATURE( a, b, c, d ) \
( ( (a) << 0 ) | ( (b) << 8 ) | ( (c) << 16 ) | ( (d) << 24 ) )
extern int acpi_describe ( struct interface *interface,
struct acpi_description_header *acpi, size_t len );
#define acpi_describe_TYPE( object_type ) \
typeof ( int ( object_type, \
struct acpi_description_header *acpi, \
size_t len ) )
extern void acpi_fix_checksum ( struct acpi_description_header *acpi );
#endif /* _IPXE_ACPI_H */

View File

@ -14,10 +14,11 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/if_ether.h>
#include <ipxe/retry.h>
#include <ipxe/ata.h>
#include <ipxe/acpi.h>
/** An AoE config command */
struct aoecfg {
/** AoE Queue depth */
/** AoE queue depth */
uint16_t bufcnt;
/** ATA target firmware version */
uint16_t fwver;
@ -78,7 +79,7 @@ struct aoehdr {
/** Tag, in network byte order */
uint32_t tag;
/** Payload */
union aoecmd cmd[0];
union aoecmd payload[0];
} __attribute__ (( packed ));
#define AOE_VERSION 0x10 /**< Version 1 */
@ -93,58 +94,38 @@ struct aoehdr {
#define AOE_CMD_ATA 0x00 /**< Issue ATA command */
#define AOE_CMD_CONFIG 0x01 /**< Query Config Information */
#define AOE_TAG_MAGIC 0xebeb0000
#define AOE_ERR_BAD_COMMAND 1 /**< Unrecognised command code */
#define AOE_ERR_BAD_PARAMETER 2 /**< Bad argument parameter */
#define AOE_ERR_UNAVAILABLE 3 /**< Device unavailable */
#define AOE_ERR_CONFIG_EXISTS 4 /**< Config string present */
#define AOE_ERR_BAD_VERSION 5 /**< Unsupported version */
/** An AoE session */
struct aoe_session {
/** Reference counter */
struct refcnt refcnt;
/** List of all AoE sessions */
struct list_head list;
/** Network device */
struct net_device *netdev;
/** Major number */
uint16_t major;
/** Minor number */
uint8_t minor;
/** Target MAC address */
uint8_t target[ETH_ALEN];
/** Tag for current AoE command */
uint32_t tag;
/** Current AOE command */
uint8_t aoe_cmd_type;
/** Current ATA command */
struct ata_command *command;
/** Overall status of current ATA command */
unsigned int status;
/** Byte offset within command's data buffer */
unsigned int command_offset;
/** Return status code for command */
int rc;
/** Retransmission timer */
struct retry_timer timer;
};
#define AOE_STATUS_ERR_MASK 0x0f /**< Error portion of status code */
#define AOE_STATUS_PENDING 0x80 /**< Command pending */
/** AoE tag magic marker */
#define AOE_TAG_MAGIC 0x18ae0000
/** Maximum number of sectors per packet */
#define AOE_MAX_COUNT 2
extern void aoe_detach ( struct ata_device *ata );
extern int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
const char *root_path );
/** AoE boot firmware table signature */
#define ABFT_SIG ACPI_SIGNATURE ( 'a', 'B', 'F', 'T' )
/**
* AoE Boot Firmware Table (aBFT)
*/
struct abft_table {
/** ACPI header */
struct acpi_description_header acpi;
/** AoE shelf */
uint16_t shelf;
/** AoE slot */
uint8_t slot;
/** Reserved */
uint8_t reserved_a;
/** MAC address */
uint8_t mac[ETH_ALEN];
} __attribute__ (( packed ));
#endif /* _IPXE_AOE_H */

View File

@ -2,9 +2,8 @@
#define _IPXE_ATA_H
#include <stdint.h>
#include <ipxe/blockdev.h>
#include <ipxe/uaccess.h>
#include <ipxe/refcnt.h>
#include <ipxe/interface.h>
/** @file
*
@ -104,7 +103,7 @@ struct ata_cb {
uint8_t device;
/** Command/status register */
uint8_t cmd_stat;
/** LBA48 addressing flag */
/** Use LBA48 extended addressing */
int lba48;
};
@ -138,25 +137,8 @@ struct ata_cb {
/** "Identify" command */
#define ATA_CMD_IDENTIFY 0xec
/** An ATA command */
struct ata_command {
/** ATA command block */
struct ata_cb cb;
/** Data-out buffer (may be NULL)
*
* If non-NULL, this buffer must be ata_command::cb::count
* sectors in size.
*/
userptr_t data_out;
/** Data-in buffer (may be NULL)
*
* If non-NULL, this buffer must be ata_command::cb::count
* sectors in size.
*/
userptr_t data_in;
/** Command status code */
int rc;
};
/** Command completed in error */
#define ATA_STAT_ERR 0x01
/**
* Structure returned by ATA IDENTIFY command
@ -165,13 +147,15 @@ struct ata_command {
* so we implement only a few fields.
*/
struct ata_identity {
uint16_t ignore_a[60]; /* words 0-59 */
uint16_t ignore_a[27]; /* words 0-26 */
uint16_t model[20]; /* words 27-46 */
uint16_t ignore_b[13]; /* words 47-59 */
uint32_t lba_sectors; /* words 60-61 */
uint16_t ignore_b[21]; /* words 62-82 */
uint16_t ignore_c[21]; /* words 62-82 */
uint16_t supports_lba48; /* word 83 */
uint16_t ignore_c[16]; /* words 84-99 */
uint16_t ignore_d[16]; /* words 84-99 */
uint64_t lba48_sectors; /* words 100-103 */
uint16_t ignore_d[152]; /* words 104-255 */
uint16_t ignore_e[152]; /* words 104-255 */
};
/** Supports LBA48 flag */
@ -180,30 +164,41 @@ struct ata_identity {
/** ATA sector size */
#define ATA_SECTOR_SIZE 512
/** An ATA device */
struct ata_device {
/** Block device interface */
struct block_device blockdev;
/** Device number
/** An ATA command information unit */
struct ata_cmd {
/** ATA command block */
struct ata_cb cb;
/** Data-out buffer (may be NULL)
*
* Must be ATA_DEV_MASTER or ATA_DEV_SLAVE.
* If non-NULL, this buffer must be ata_command::cb::count
* sectors in size.
*/
int device;
/** LBA48 extended addressing */
int lba48;
/**
* Issue ATA command
userptr_t data_out;
/** Data-out buffer length
*
* @v ata ATA device
* @v command ATA command
* @ret rc Return status code
* Must be zero if @c data_out is NULL
*/
int ( * command ) ( struct ata_device *ata,
struct ata_command *command );
/** Backing device */
struct refcnt *backend;
size_t data_out_len;
/** Data-in buffer (may be NULL)
*
* If non-NULL, this buffer must be ata_command::cb::count
* sectors in size.
*/
userptr_t data_in;
/** Data-in buffer length
*
* Must be zero if @c data_in is NULL
*/
size_t data_in_len;
};
extern int init_atadev ( struct ata_device *ata );
extern int ata_command ( struct interface *control, struct interface *data,
struct ata_cmd *command );
#define ata_command_TYPE( object_type ) \
typeof ( int ( object_type, struct interface *data, \
struct ata_cmd *command ) )
extern int ata_open ( struct interface *block, struct interface *ata,
unsigned int device, unsigned int max_count );
#endif /* _IPXE_ATA_H */

View File

@ -10,44 +10,46 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <ipxe/uaccess.h>
#include <ipxe/interface.h>
struct block_device;
/** Block device operations */
struct block_device_operations {
/**
* Read block
*
* @v blockdev Block device
* @v block Block number
* @v count Block count
* @v buffer Data buffer
* @ret rc Return status code
*/
int ( * read ) ( struct block_device *blockdev, uint64_t block,
unsigned long count, userptr_t buffer );
/**
* Write block
*
* @v blockdev Block device
* @v block Block number
* @v count Block count
* @v buffer Data buffer
* @ret rc Return status code
*/
int ( * write ) ( struct block_device *blockdev, uint64_t block,
unsigned long count, userptr_t buffer );
};
/** A block device */
struct block_device {
/** Block device operations */
struct block_device_operations *op;
/** Block size */
size_t blksize;
/** Block device capacity */
struct block_device_capacity {
/** Total number of blocks */
uint64_t blocks;
/** Block size */
size_t blksize;
/** Maximum number of blocks per single transfer */
unsigned int max_count;
};
extern int block_read ( struct interface *control, struct interface *data,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len );
#define block_read_TYPE( object_type ) \
typeof ( int ( object_type, struct interface *data, \
uint64_t lba, unsigned int count, \
userptr_t buffer, size_t len ) )
extern int block_write ( struct interface *control, struct interface *data,
uint64_t lba, unsigned int count,
userptr_t buffer, size_t len );
#define block_write_TYPE( object_type ) \
typeof ( int ( object_type, struct interface *data, \
uint64_t lba, unsigned int count, \
userptr_t buffer, size_t len ) )
extern int block_read_capacity ( struct interface *control,
struct interface *data );
#define block_read_capacity_TYPE( object_type ) \
typeof ( int ( object_type, struct interface *data ) )
extern void block_capacity ( struct interface *intf,
struct block_device_capacity *capacity );
#define block_capacity_TYPE( object_type ) \
typeof ( void ( object_type, \
struct block_device_capacity *capacity ) )
#endif /* _IPXE_BLOCKDEV_H */

View File

@ -55,6 +55,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_bitmap ( ERRFILE_CORE | 0x000f0000 )
#define ERRFILE_base64 ( ERRFILE_CORE | 0x00100000 )
#define ERRFILE_base16 ( ERRFILE_CORE | 0x00110000 )
#define ERRFILE_blockdev ( ERRFILE_CORE | 0x00120000 )
#define ERRFILE_acpi ( ERRFILE_CORE | 0x00130000 )
#define ERRFILE_null_sanboot ( ERRFILE_CORE | 0x00140000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )

View File

@ -14,45 +14,35 @@ FILE_LICENCE ( BSD2 );
#include <ipxe/srp.h>
/** SRP initiator port identifier for Infiniband */
struct ib_srp_initiator_port_id {
/** Identifier extension */
struct ib_gid_half id_ext;
/** IB channel adapter GUID */
struct ib_gid_half hca_guid;
} __attribute__ (( packed ));
union ib_srp_initiator_port_id {
/** SRP version of port identifier */
union srp_port_id srp;
/** Infiniband version of port identifier */
struct {
/** Identifier extension */
struct ib_gid_half id_ext;
/** IB channel adapter GUID */
struct ib_gid_half hca_guid;
} __attribute__ (( packed )) ib;
};
/** SRP target port identifier for Infiniband */
struct ib_srp_target_port_id {
/** Identifier extension */
struct ib_gid_half id_ext;
/** I/O controller GUID */
struct ib_gid_half ioc_guid;
} __attribute__ (( packed ));
union ib_srp_target_port_id {
/** SRP version of port identifier */
union srp_port_id srp;
/** Infiniband version of port identifier */
struct {
/** Identifier extension */
struct ib_gid_half id_ext;
/** I/O controller GUID */
struct ib_gid_half ioc_guid;
} __attribute__ (( packed )) ib;
};
/**
* Get Infiniband-specific initiator port ID
*
* @v port_ids SRP port IDs
* @ret initiator_port_id Infiniband-specific initiator port ID
* sBFT Infiniband subtable
*/
static inline __always_inline struct ib_srp_initiator_port_id *
ib_srp_initiator_port_id ( struct srp_port_ids *port_ids ) {
return ( ( struct ib_srp_initiator_port_id * ) &port_ids->initiator );
}
/**
* Get Infiniband-specific target port ID
*
* @v port_ids SRP port IDs
* @ret target_port_id Infiniband-specific target port ID
*/
static inline __always_inline struct ib_srp_target_port_id *
ib_srp_target_port_id ( struct srp_port_ids *port_ids ) {
return ( ( struct ib_srp_target_port_id * ) &port_ids->target );
}
/** Infiniband-specific SRP parameters */
struct ib_srp_parameters {
struct sbft_ib_subtable {
/** Source GID */
struct ib_gid sgid;
/** Destination GID */
@ -61,19 +51,8 @@ struct ib_srp_parameters {
struct ib_gid_half service_id;
/** Partition key */
uint16_t pkey;
};
/**
* Get Infiniband-specific transport parameters
*
* @v srp SRP device
* @ret ib_params Infiniband-specific transport parameters
*/
static inline __always_inline struct ib_srp_parameters *
ib_srp_params ( struct srp_device *srp ) {
return srp_transport_priv ( srp );
}
extern struct srp_transport_type ib_srp_transport;
/** Reserved */
uint8_t reserved[6];
} __attribute__ (( packed ));
#endif /* _IPXE_IB_SRP_H */

View File

@ -43,10 +43,11 @@ FILE_LICENCE ( BSD2 );
#include <stdint.h>
#include <ipxe/acpi.h>
#include <ipxe/scsi.h>
#include <ipxe/in.h>
/** iSCSI Boot Firmware Table signature */
#define IBFT_SIG "iBFT"
#define IBFT_SIG ACPI_SIGNATURE ( 'i', 'B', 'F', 'T' )
/** An offset from the start of the iBFT */
typedef uint16_t ibft_off_t;
@ -57,7 +58,7 @@ typedef uint16_t ibft_size_t;
/** A string within the iBFT */
struct ibft_string {
/** Length of string */
ibft_size_t length;
ibft_size_t len;
/** Offset to string */
ibft_off_t offset;
} __attribute__ (( packed ));
@ -208,7 +209,7 @@ struct ibft_target {
/** TCP port */
uint16_t socket;
/** Boot LUN */
uint64_t boot_lun;
struct scsi_lun boot_lun;
/** CHAP type
*
* This is an IBFT_CHAP_XXX constant.
@ -260,43 +261,11 @@ struct ibft_table {
struct ibft_control control;
} __attribute__ (( packed ));
/**
* iSCSI string block descriptor
*
* This is an internal structure that we use to keep track of the
* allocation of string data.
*/
struct ibft_string_block {
/** The iBFT containing these strings */
struct ibft_table *table;
/** Offset of first free byte within iBFT */
unsigned int offset;
};
/** Amount of space reserved for strings in a iPXE iBFT */
#define IBFT_STRINGS_SIZE 384
/**
* An iBFT created by iPXE
*
*/
struct ipxe_ibft {
/** The fixed section */
struct ibft_table table;
/** The Initiator section */
struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) ));
/** The NIC section */
struct ibft_nic nic __attribute__ (( aligned ( 16 ) ));
/** The Target section */
struct ibft_target target __attribute__ (( aligned ( 16 ) ));
/** Strings block */
char strings[IBFT_STRINGS_SIZE];
} __attribute__ (( packed, aligned ( 16 ) ));
struct net_device;
struct iscsi_session;
struct net_device;
extern int ibft_fill_data ( struct net_device *netdev,
struct iscsi_session *iscsi );
extern int ibft_describe ( struct iscsi_session *iscsi,
struct acpi_description_header *acpi,
size_t len );
#endif /* _IPXE_IBFT_H */

View File

@ -90,6 +90,9 @@ struct iscsi_bhs_common {
/** Final PDU of a sequence */
#define ISCSI_FLAG_FINAL 0x80
/** iSCSI tag magic marker */
#define ISCSI_TAG_MAGIC 0x18ae0000
/**
* iSCSI basic header segment common request fields
*
@ -307,8 +310,10 @@ struct iscsi_bhs_scsi_response {
uint32_t maxcmdsn;
/** Expected data sequence number */
uint32_t expdatasn;
/** Reserved */
uint8_t reserved_b[8];
/** Bidirectional read residual count */
uint32_t bidi_residual_count;
/** Residual count */
uint32_t residual_count;
};
/** SCSI response opcode */
@ -320,13 +325,11 @@ struct iscsi_bhs_scsi_response {
/** SCSI target failure */
#define ISCSI_RESPONSE_TARGET_FAILURE 0x01
/** SCSI sense response code offset
*
* The SCSI response may contain unsolicited sense data in the data
* segment. If it does, this is the offset to the sense response code
* byte, which is the only byte we care about.
*/
#define ISCSI_SENSE_RESPONSE_CODE_OFFSET 2
/** Data overflow occurred */
#define ISCSI_RESPONSE_FLAG_OVERFLOW 0x20
/** Data underflow occurred */
#define ISCSI_RESPONSE_FLAG_UNDERFLOW 0x40
/**
* iSCSI data-in basic header segment
@ -497,6 +500,10 @@ struct iscsi_session {
/** Reference counter */
struct refcnt refcnt;
/** SCSI command-issuing interface */
struct interface control;
/** SCSI command interface */
struct interface data;
/** Transport-layer socket */
struct interface socket;
@ -506,10 +513,6 @@ struct iscsi_session {
unsigned int target_port;
/** Target IQN */
char *target_iqn;
/** Logical Unit Number (LUN) */
struct scsi_lun lun;
/** Target socket address (recorded only for iBFT) */
struct sockaddr target_sockaddr;
/** Session status
*
@ -517,12 +520,6 @@ struct iscsi_session {
* constants.
*/
int status;
/** Retry count
*
* Number of times that the connection has been retried.
* Reset upon a successful connection.
*/
int retry_count;
/** Initiator username (if any) */
char *initiator_username;
@ -542,12 +539,6 @@ struct iscsi_session {
/** CHAP response (used for both initiator and target auth) */
struct chap_response chap;
/** Target session identifying handle
*
* This is assigned by the target when we first log in, and
* must be reused on subsequent login attempts.
*/
uint16_t tsih;
/** Initiator task tag
*
* This is the tag of the current command. It is incremented
@ -560,15 +551,13 @@ struct iscsi_session {
* response to an R2T.
*/
uint32_t ttt;
/**
* Transfer offset
/** Transfer offset
*
* This is the offset for an in-progress sequence of data-out
* PDUs in response to an R2T.
*/
uint32_t transfer_offset;
/**
* Transfer length
/** Transfer length
*
* This is the length for an in-progress sequence of data-out
* PDUs in response to an R2T.
@ -609,18 +598,13 @@ struct iscsi_session {
/** Buffer for received data (not always used) */
void *rx_buffer;
/** Current SCSI command
*
* Set to NULL when command is complete.
*/
struct scsi_command *command;
/** Instant return code
*
* Set to a non-zero value if all requests should return
* immediately. This can be used to e.g. avoid retrying
* logins that are doomed to fail authentication.
*/
int instant_rc;
/** Current SCSI command, if any */
struct scsi_cmd *command;
/** Target socket address (for boot firmware table) */
struct sockaddr target_sockaddr;
/** SCSI LUN (for boot firmware table) */
struct scsi_lun lun;
};
/** iSCSI session is currently in the security negotiation phase */
@ -668,11 +652,6 @@ struct iscsi_session {
/** Target authenticated itself correctly */
#define ISCSI_STATUS_AUTH_REVERSE_OK 0x00040000
/** Maximum number of retries at connecting */
#define ISCSI_MAX_RETRIES 2
extern int iscsi_attach ( struct scsi_device *scsi, const char *root_path );
extern void iscsi_detach ( struct scsi_device *scsi );
extern const char * iscsi_initiator_iqn ( void );
#endif /* _IPXE_ISCSI_H */

View File

@ -0,0 +1,18 @@
#ifndef _IPXE_NULL_SANBOOT_H
#define _IPXE_NULL_SANBOOT_H
/** @file
*
* Standard do-nothing sanboot interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#ifdef SANBOOT_NULL
#define SANBOOT_PREFIX_null
#else
#define SANBOOT_PREFIX_null __null_
#endif
#endif /* _IPXE_NULL_SANBOOT_H */

View File

@ -1,24 +0,0 @@
#ifndef _IPXE_RAMDISK_H
#define _IPXE_RAMDISK_H
/**
* @file
*
* RAM disks
*
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/uaccess.h>
#include <ipxe/blockdev.h>
struct ramdisk {
struct block_device blockdev;
userptr_t data;
};
extern int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
unsigned int blksize );
#endif /* _IPXE_RAMDISK_H */

View File

@ -1,20 +1,93 @@
#ifndef _IPXE_SANBOOT_H
#define _IPXE_SANBOOT_H
/** @file
*
* iPXE sanboot API
*
* The sanboot API provides methods for hooking, unhooking,
* describing, and booting from SAN devices.
*
* The standard methods (readl()/writel() etc.) do not strictly check
* the type of the address parameter; this is because traditional
* usage does not necessarily provide the correct pointer type. For
* example, code written for ISA devices at fixed I/O addresses (such
* as the keyboard controller) tend to use plain integer constants for
* the address parameter.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/tables.h>
#include <ipxe/api.h>
#include <config/sanboot.h>
struct sanboot_protocol {
const char *prefix;
int ( * boot ) ( const char *root_path );
};
struct uri;
#define SANBOOT_PROTOCOLS \
__table ( struct sanboot_protocol, "sanboot_protocols" )
/**
* Calculate static inline sanboot API function name
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @ret _subsys_func Subsystem API function
*/
#define SANBOOT_INLINE( _subsys, _api_func ) \
SINGLE_API_INLINE ( SANBOOT_PREFIX_ ## _subsys, _api_func )
#define __sanboot_protocol __table_entry ( SANBOOT_PROTOCOLS, 01 )
/**
* Provide a sanboot API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
* @v _func Implementing function
*/
#define PROVIDE_SANBOOT( _subsys, _api_func, _func ) \
PROVIDE_SINGLE_API ( SANBOOT_PREFIX_ ## _subsys, _api_func, _func )
extern int keep_san ( void );
/**
* Provide a static inline sanboot API implementation
*
* @v _prefix Subsystem prefix
* @v _api_func API function
*/
#define PROVIDE_SANBOOT_INLINE( _subsys, _api_func ) \
PROVIDE_SINGLE_API_INLINE ( SANBOOT_PREFIX_ ## _subsys, _api_func )
/* Include all architecture-independent sanboot API headers */
#include <ipxe/null_sanboot.h>
/* Include all architecture-dependent sanboot API headers */
#include <bits/sanboot.h>
/**
* Hook SAN device
*
* @v uri URI
* @v drive Requested drive number
* @ret drive Assigned drive number, or negative error
*/
int san_hook ( struct uri *uri, unsigned int drive );
/**
* Unhook SAN device
*
* @v drive Drive number
*/
void san_unhook ( unsigned int drive );
/**
* Attempt to boot from a SAN device
*
* @v drive Drive number
* @ret rc Return status code
*/
int san_boot ( unsigned int drive );
/**
* Describe SAN device for SAN-booted operating system
*
* @v drive Drive number
* @ret rc Return status code
*/
int san_describe ( unsigned int drive );
#endif /* _IPXE_SANBOOT_H */

View File

@ -2,9 +2,8 @@
#define _IPXE_SCSI_H
#include <stdint.h>
#include <ipxe/blockdev.h>
#include <ipxe/uaccess.h>
#include <ipxe/refcnt.h>
#include <ipxe/interface.h>
/** @file
*
@ -14,6 +13,9 @@
FILE_LICENCE ( GPL2_OR_LATER );
/** Maximum block for READ/WRITE (10) commands */
#define SCSI_MAX_BLOCK_10 0xffffffffULL
/**
* @defgroup scsiops SCSI operation codes
* @{
@ -214,8 +216,27 @@ union scsi_cdb {
/** @} */
/** A SCSI command */
struct scsi_command {
/** A SCSI LUN
*
* This is a four-level LUN as specified by SAM-2, in big-endian
* order.
*/
struct scsi_lun {
uint16_t u16[4];
} __attribute__ (( packed ));
/** printf() format for dumping a scsi_lun */
#define SCSI_LUN_FORMAT "%04x-%04x-%04x-%04x"
/** printf() parameters for dumping a scsi_lun */
#define SCSI_LUN_DATA(lun) \
ntohs ( (lun).u16[0] ), ntohs ( (lun).u16[1] ), \
ntohs ( (lun).u16[2] ), ntohs ( (lun).u16[3] )
/** A SCSI command information unit */
struct scsi_cmd {
/** LUN */
struct scsi_lun lun;
/** CDB for this command */
union scsi_cdb cdb;
/** Data-out buffer (may be NULL) */
@ -232,50 +253,43 @@ struct scsi_command {
* Must be zero if @c data_in is NULL
*/
size_t data_in_len;
};
/** SCSI sense data */
struct scsi_sns {
/** Response code */
uint8_t code;
/** Reserved */
uint8_t reserved;
/** Sense key */
uint8_t key;
/** Information */
uint32_t info;
};
/** A SCSI response information unit */
struct scsi_rsp {
/** SCSI status code */
uint8_t status;
/** SCSI sense response code */
uint8_t sense_response;
/** Command status code */
int rc;
/** Data overrun (or negative underrun) */
ssize_t overrun;
/** Autosense data (if any) */
struct scsi_sns sense;
};
/** A SCSI LUN
*
* This is a four-level LUN as specified by SAM-2, in big-endian
* order.
*/
struct scsi_lun {
uint16_t u16[4];
} __attribute__ (( packed ));
/** A SCSI device */
struct scsi_device {
/** Block device interface */
struct block_device blockdev;
/**
* Issue SCSI command
*
* @v scsi SCSI device
* @v command SCSI command
* @ret rc Return status code
*
* Note that a successful return status code indicates only
* that the SCSI command was issued. The caller must check
* the status field in the command structure to see when the
* command completes and whether, for example, the device
* returned CHECK CONDITION or some other non-success status
* code.
*/
int ( * command ) ( struct scsi_device *scsi,
struct scsi_command *command );
/** Backing device */
struct refcnt *backend;
};
extern int scsi_detached_command ( struct scsi_device *scsi,
struct scsi_command *command );
extern int init_scsidev ( struct scsi_device *scsi );
extern int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun );
extern int scsi_command ( struct interface *control, struct interface *data,
struct scsi_cmd *command );
#define scsi_command_TYPE( object_type ) \
typeof ( int ( object_type, struct interface *data, \
struct scsi_cmd *command ) )
extern void scsi_response ( struct interface *intf, struct scsi_rsp *response );
#define scsi_response_TYPE( object_type ) \
typeof ( void ( object_type, struct scsi_rsp *response ) )
extern int scsi_open ( struct interface *block, struct interface *scsi,
struct scsi_lun *lun );
#endif /* _IPXE_SCSI_H */

View File

@ -14,6 +14,7 @@ FILE_LICENCE ( BSD2 );
#include <ipxe/iobuf.h>
#include <ipxe/xfer.h>
#include <ipxe/scsi.h>
#include <ipxe/acpi.h>
/*****************************************************************************
*
@ -23,21 +24,18 @@ FILE_LICENCE ( BSD2 );
*/
/** An SRP information unit tag */
struct srp_tag {
union srp_tag {
uint8_t bytes[8];
uint32_t dwords[2];
} __attribute__ (( packed ));
/** An SRP port ID */
struct srp_port_id {
uint8_t bytes[16];
} __attribute__ (( packed ));
/** SRP tag magic marker */
#define SRP_TAG_MAGIC 0x69505845
/** An SRP port ID pair */
struct srp_port_ids {
/** Initiator port ID */
struct srp_port_id initiator;
/** Target port ID */
struct srp_port_id target;
/** An SRP port ID */
union srp_port_id {
uint8_t bytes[16];
uint32_t dwords[4];
} __attribute__ (( packed ));
/** SRP information unit common fields */
@ -47,7 +45,7 @@ struct srp_common {
/** Reserved */
uint8_t reserved0[7];
/** Tag */
struct srp_tag tag;
union srp_tag tag;
} __attribute__ (( packed ));
/*****************************************************************************
@ -67,7 +65,7 @@ struct srp_login_req {
/** Reserved */
uint8_t reserved0[7];
/** Tag */
struct srp_tag tag;
union srp_tag tag;
/** Requested maximum initiator to target IU length */
uint32_t max_i_t_iu_len;
/** Reserved */
@ -87,8 +85,10 @@ struct srp_login_req {
uint8_t flags;
/** Reserved */
uint8_t reserved2[5];
/** Initiator and target port identifiers */
struct srp_port_ids port_ids;
/** Initiator port identifier */
union srp_port_id initiator;
/** Target port identifier */
union srp_port_id target;
} __attribute__ (( packed ));
/** Type of an SRP login request */
@ -137,7 +137,7 @@ struct srp_login_rsp {
/** Request limit delta */
uint32_t request_limit_delta;
/** Tag */
struct srp_tag tag;
union srp_tag tag;
/** Maximum initiator to target IU length */
uint32_t max_i_t_iu_len;
/** Maximum target to initiator IU length */
@ -205,7 +205,7 @@ struct srp_login_rej {
*/
uint32_t reason;
/** Tag */
struct srp_tag tag;
union srp_tag tag;
/** Reserved */
uint8_t reserved1[8];
/** Supported buffer formats
@ -265,7 +265,7 @@ struct srp_i_logout {
/** Reserved */
uint8_t reserved0[7];
/** Tag */
struct srp_tag tag;
union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP initiator logout request */
@ -299,7 +299,7 @@ struct srp_t_logout {
*/
uint32_t reason;
/** Tag */
struct srp_tag tag;
union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP target logout request */
@ -355,7 +355,7 @@ struct srp_tsk_mgmt {
/** Reserved */
uint8_t reserved0[6];
/** Tag */
struct srp_tag tag;
union srp_tag tag;
/** Reserved */
uint8_t reserved1[4];
/** Logical unit number */
@ -370,7 +370,7 @@ struct srp_tsk_mgmt {
/** Reserved */
uint8_t reserved3[1];
/** Tag of task to be managed */
struct srp_tag managed_tag;
union srp_tag managed_tag;
/** Reserved */
uint8_t reserved4[8];
} __attribute__ (( packed ));
@ -432,7 +432,7 @@ struct srp_cmd {
/** Data-in buffer descriptor count */
uint8_t data_in_buffer_count;
/** Tag */
struct srp_tag tag;
union srp_tag tag;
/** Reserved */
uint8_t reserved1[4];
/** Logical unit number */
@ -526,7 +526,7 @@ struct srp_rsp {
/** Request limit delta */
uint32_t request_limit_delta;
/** Tag */
struct srp_tag tag;
union srp_tag tag;
/** Reserved */
uint8_t reserved1[2];
/** Valid fields
@ -580,9 +580,9 @@ struct srp_rsp {
* @v rsp SCSI response
* @ret response_data Response data, or NULL if not present
*/
static inline void * srp_rsp_response_data ( struct srp_rsp *rsp ) {
static inline const void * srp_rsp_response_data ( const struct srp_rsp *rsp ) {
return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ?
( ( ( void * ) rsp ) + sizeof ( *rsp ) ) : NULL );
( ( ( const void * ) rsp ) + sizeof ( *rsp ) ) : NULL );
}
/**
@ -591,7 +591,7 @@ static inline void * srp_rsp_response_data ( struct srp_rsp *rsp ) {
* @v rsp SCSI response
* @ret response_data_len Response data length
*/
static inline size_t srp_rsp_response_data_len ( struct srp_rsp *rsp ) {
static inline size_t srp_rsp_response_data_len ( const struct srp_rsp *rsp ) {
return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ?
ntohl ( rsp->response_data_len ) : 0 );
}
@ -602,9 +602,9 @@ static inline size_t srp_rsp_response_data_len ( struct srp_rsp *rsp ) {
* @v rsp SCSI response
* @ret sense_data Sense data, or NULL if not present
*/
static inline void * srp_rsp_sense_data ( struct srp_rsp *rsp ) {
static inline const void * srp_rsp_sense_data ( const struct srp_rsp *rsp ) {
return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ?
( ( ( void * ) rsp ) + sizeof ( *rsp ) +
( ( ( const void * ) rsp ) + sizeof ( *rsp ) +
srp_rsp_response_data_len ( rsp ) ) : NULL );
}
@ -614,7 +614,7 @@ static inline void * srp_rsp_sense_data ( struct srp_rsp *rsp ) {
* @v rsp SCSI response
* @ret sense_data_len Sense data length
*/
static inline size_t srp_rsp_sense_data_len ( struct srp_rsp *rsp ) {
static inline size_t srp_rsp_sense_data_len ( const struct srp_rsp *rsp ) {
return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ?
ntohl ( rsp->sense_data_len ) : 0 );
}
@ -644,7 +644,7 @@ struct srp_cred_req {
/** Request limit delta */
uint32_t request_limit_delta;
/** Tag */
struct srp_tag tag;
union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP credit request */
@ -670,7 +670,7 @@ struct srp_cred_rsp {
/** Reserved */
uint8_t reserved0[7];
/** Tag */
struct srp_tag tag;
union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP credit response */
@ -701,7 +701,7 @@ struct srp_aer_req {
/** Request limit delta */
uint32_t request_limit_delta;
/** Tag */
struct srp_tag tag;
union srp_tag tag;
/** Reserved */
uint8_t reserved1[4];
/** Logical unit number */
@ -757,7 +757,7 @@ struct srp_aer_rsp {
/** Reserved */
uint8_t reserved0[7];
/** Tag */
struct srp_tag tag;
union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP asynchronous event response */
@ -765,104 +765,65 @@ struct srp_aer_rsp {
/*****************************************************************************
*
* Information units
* SRP boot firmware table
*
* The working draft specification for the SRP boot firmware table can
* be found at
*
* http://ipxe.org/wiki/srp/sbft
*
*****************************************************************************
*/
/** Maximum length of any initiator-to-target IU that we will send
*
* The longest IU is a SRP_CMD with no additional CDB and two direct
* data buffer descriptors, which comes to 80 bytes.
/** SRP Boot Firmware Table signature */
#define SBFT_SIG ACPI_SIGNATURE ( 's', 'B', 'F', 'T' )
/** An offset from the start of the sBFT */
typedef uint16_t sbft_off_t;
/**
* SRP Boot Firmware Table
*/
#define SRP_MAX_I_T_IU_LEN 80
struct sbft_table {
/** ACPI header */
struct acpi_description_header acpi;
/** Offset to SCSI subtable */
sbft_off_t scsi_offset;
/** Offset to SRP subtable */
sbft_off_t srp_offset;
/** Offset to IB subtable, if present */
sbft_off_t ib_offset;
/** Reserved */
uint8_t reserved[6];
} __attribute__ (( packed ));
/**
* sBFT SCSI subtable
*/
struct sbft_scsi_subtable {
/** LUN */
struct scsi_lun lun;
} __attribute__ (( packed ));
/**
* sBFT SRP subtable
*/
struct sbft_srp_subtable {
/** Initiator port identifier */
union srp_port_id initiator;
/** Target port identifier */
union srp_port_id target;
} __attribute__ (( packed ));
/*****************************************************************************
*
* SRP device
* SRP devices
*
*****************************************************************************
*/
struct srp_device;
/** An SRP transport type */
struct srp_transport_type {
/** Length of transport private data */
size_t priv_len;
/** Parse root path
*
* @v srp SRP device
* @v root_path Root path
* @ret Return status code
*/
int ( * parse_root_path ) ( struct srp_device *srp,
const char *root_path );
/** Connect SRP session
*
* @v srp SRP device
* @ret rc Return status code
*
* This method should open the underlying socket.
*/
int ( * connect ) ( struct srp_device *srp );
};
/** An SRP device */
struct srp_device {
/** Reference count */
struct refcnt refcnt;
/** Initiator and target port IDs */
struct srp_port_ids port_ids;
/** Logical unit number */
struct scsi_lun lun;
/** Memory handle */
uint32_t memory_handle;
/** Current state
*
* This is the bitwise-OR of zero or more @c SRP_STATE_XXX
* flags.
*/
unsigned int state;
/** Retry counter */
unsigned int retry_count;
/** Current SCSI command */
struct scsi_command *command;
/** Underlying data transfer interface */
struct interface socket;
/** Transport type */
struct srp_transport_type *transport;
/** Transport private data */
char transport_priv[0];
};
/**
* Get SRP transport private data
*
* @v srp SRP device
* @ret priv SRP transport private data
*/
static inline __always_inline void *
srp_transport_priv ( struct srp_device *srp ) {
return ( ( void * ) srp->transport_priv );
}
/** SRP state flags */
enum srp_state {
/** Underlying socket is open */
SRP_STATE_SOCKET_OPEN = 0x0001,
/** Session is logged in */
SRP_STATE_LOGGED_IN = 0x0002,
};
/** Maximum number of SRP retry attempts */
#define SRP_MAX_RETRIES 3
extern int srp_attach ( struct scsi_device *scsi, const char *root_path );
extern void srp_detach ( struct scsi_device *scsi );
extern int srp_open ( struct interface *block, struct interface *socket,
union srp_port_id *initiator, union srp_port_id *target,
uint32_t memory_handle, struct scsi_lun *lun );
#endif /* _IPXE_SRP_H */

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,11 @@ FILE_LICENCE ( BSD2 );
#include <stdlib.h>
#include <errno.h>
#include <ipxe/interface.h>
#include <ipxe/uri.h>
#include <ipxe/open.h>
#include <ipxe/base16.h>
#include <ipxe/acpi.h>
#include <ipxe/srp.h>
#include <ipxe/infiniband.h>
#include <ipxe/ib_cmrc.h>
@ -56,6 +60,192 @@ FILE_LICENCE ( BSD2 );
#define EINFO_EINVAL_RP_TOO_SHORT __einfo_uniqify \
( EINFO_EINVAL, 0x04, "Root path too short" )
/******************************************************************************
*
* IB SRP devices
*
******************************************************************************
*/
/** An Infiniband SRP device */
struct ib_srp_device {
/** Reference count */
struct refcnt refcnt;
/** SRP transport interface */
struct interface srp;
/** CMRC interface */
struct interface cmrc;
/** Infiniband device */
struct ib_device *ibdev;
/** Destination GID (for boot firmware table) */
struct ib_gid dgid;
/** Service ID (for boot firmware table) */
struct ib_gid_half service_id;
};
/**
* Free IB SRP device
*
* @v refcnt Reference count
*/
static void ib_srp_free ( struct refcnt *refcnt ) {
struct ib_srp_device *ib_srp =
container_of ( refcnt, struct ib_srp_device, refcnt );
ibdev_put ( ib_srp->ibdev );
free ( ib_srp );
}
/**
* Close IB SRP device
*
* @v ib_srp IB SRP device
* @v rc Reason for close
*/
static void ib_srp_close ( struct ib_srp_device *ib_srp, int rc ) {
/* Shut down interfaces */
intf_shutdown ( &ib_srp->cmrc, rc );
intf_shutdown ( &ib_srp->srp, rc );
}
/**
* Describe IB SRP device in an ACPI table
*
* @v srpdev SRP device
* @v acpi ACPI table
* @v len Length of ACPI table
* @ret rc Return status code
*/
static int ib_srp_describe ( struct ib_srp_device *ib_srp,
struct acpi_description_header *acpi,
size_t len ) {
struct ib_device *ibdev = ib_srp->ibdev;
struct sbft_table *sbft =
container_of ( acpi, struct sbft_table, acpi );
struct sbft_ib_subtable *ib_sbft;
size_t used;
/* Sanity check */
if ( acpi->signature != SBFT_SIG )
return -EINVAL;
/* Append IB subtable to existing table */
used = le32_to_cpu ( sbft->acpi.length );
sbft->ib_offset = cpu_to_le16 ( used );
ib_sbft = ( ( ( void * ) sbft ) + used );
used += sizeof ( *ib_sbft );
if ( used > len )
return -ENOBUFS;
sbft->acpi.length = cpu_to_le32 ( used );
/* Populate subtable */
memcpy ( &ib_sbft->sgid, &ibdev->gid, sizeof ( ib_sbft->sgid ) );
memcpy ( &ib_sbft->dgid, &ib_srp->dgid, sizeof ( ib_sbft->dgid ) );
memcpy ( &ib_sbft->service_id, &ib_srp->service_id,
sizeof ( ib_sbft->service_id ) );
ib_sbft->pkey = cpu_to_le16 ( ibdev->pkey );
return 0;
}
/** IB SRP CMRC interface operations */
static struct interface_operation ib_srp_cmrc_op[] = {
INTF_OP ( intf_close, struct ib_srp_device *, ib_srp_close ),
};
/** IB SRP CMRC interface descriptor */
static struct interface_descriptor ib_srp_cmrc_desc =
INTF_DESC_PASSTHRU ( struct ib_srp_device, cmrc, ib_srp_cmrc_op, srp );
/** IB SRP SRP interface operations */
static struct interface_operation ib_srp_srp_op[] = {
INTF_OP ( acpi_describe, struct ib_srp_device *, ib_srp_describe ),
INTF_OP ( intf_close, struct ib_srp_device *, ib_srp_close ),
};
/** IB SRP SRP interface descriptor */
static struct interface_descriptor ib_srp_srp_desc =
INTF_DESC_PASSTHRU ( struct ib_srp_device, srp, ib_srp_srp_op, cmrc );
/**
* Open IB SRP device
*
* @v block Block control interface
* @v ibdev Infiniband device
* @v dgid Destination GID
* @v service_id Service ID
* @v initiator Initiator port ID
* @v target Target port ID
* @v lun SCSI LUN
* @ret rc Return status code
*/
static int ib_srp_open ( struct interface *block, struct ib_device *ibdev,
struct ib_gid *dgid, struct ib_gid_half *service_id,
union srp_port_id *initiator,
union srp_port_id *target, struct scsi_lun *lun ) {
struct ib_srp_device *ib_srp;
int rc;
/* Allocate and initialise structure */
ib_srp = zalloc ( sizeof ( *ib_srp ) );
if ( ! ib_srp ) {
rc = -ENOMEM;
goto err_zalloc;
}
ref_init ( &ib_srp->refcnt, ib_srp_free );
intf_init ( &ib_srp->srp, &ib_srp_srp_desc, &ib_srp->refcnt );
intf_init ( &ib_srp->cmrc, &ib_srp_cmrc_desc, &ib_srp->refcnt );
ib_srp->ibdev = ibdev_get ( ibdev );
DBGC ( ib_srp, "IBSRP %p created for %08x%08x%08x%08x:%08x%08x\n",
ib_srp, ntohl ( dgid->u.dwords[0] ),
ntohl ( dgid->u.dwords[1] ), ntohl ( dgid->u.dwords[2] ),
ntohl ( dgid->u.dwords[3] ), ntohl ( service_id->u.dwords[0] ),
ntohl ( service_id->u.dwords[1] ) );
/* Preserve parameters required for boot firmware table */
memcpy ( &ib_srp->dgid, dgid, sizeof ( ib_srp->dgid ) );
memcpy ( &ib_srp->service_id, service_id,
sizeof ( ib_srp->service_id ) );
/* Open CMRC socket */
if ( ( rc = ib_cmrc_open ( &ib_srp->cmrc, ibdev, dgid,
service_id ) ) != 0 ) {
DBGC ( ib_srp, "IBSRP %p could not open CMRC socket: %s\n",
ib_srp, strerror ( rc ) );
goto err_cmrc_open;
}
/* Attach SRP device to parent interface */
if ( ( rc = srp_open ( block, &ib_srp->srp, initiator, target,
ibdev->rdma_key, lun ) ) != 0 ) {
DBGC ( ib_srp, "IBSRP %p could not create SRP device: %s\n",
ib_srp, strerror ( rc ) );
goto err_srp_open;
}
/* Mortalise self and return */
ref_put ( &ib_srp->refcnt );
return 0;
err_srp_open:
err_cmrc_open:
ib_srp_close ( ib_srp, rc );
ref_put ( &ib_srp->refcnt );
err_zalloc:
return rc;
}
/******************************************************************************
*
* IB SRP URIs
*
******************************************************************************
*/
/** IB SRP parse flags */
enum ib_srp_parse_flags {
IB_SRP_PARSE_REQUIRED = 0x0000,
@ -65,12 +255,20 @@ enum ib_srp_parse_flags {
/** IB SRP root path parameters */
struct ib_srp_root_path {
/** Source GID */
struct ib_gid sgid;
/** Initiator port ID */
union ib_srp_initiator_port_id initiator;
/** Destination GID */
struct ib_gid dgid;
/** Partition key */
uint16_t pkey;
/** Service ID */
struct ib_gid_half service_id;
/** SCSI LUN */
struct scsi_lun *lun;
/** SRP port IDs */
struct srp_port_ids *port_ids;
/** IB SRP parameters */
struct ib_srp_parameters *ib;
struct scsi_lun lun;
/** Target port ID */
union ib_srp_target_port_id target;
};
/**
@ -99,7 +297,6 @@ static int ib_srp_parse_byte_string ( const char *rp_comp, uint8_t *bytes,
decoded_size = base16_decode ( rp_comp, bytes );
if ( decoded_size < 0 )
return decoded_size;
assert ( decoded_size == size );
return 0;
}
@ -125,19 +322,6 @@ static int ib_srp_parse_integer ( const char *rp_comp, int default_value ) {
return value;
}
/**
* Parse IB SRP root path literal component
*
* @v rp_comp Root path component string
* @v rp IB SRP root path
* @ret rc Return status code
*/
static int ib_srp_parse_literal ( const char *rp_comp __unused,
struct ib_srp_root_path *rp __unused ) {
/* Ignore */
return 0;
}
/**
* Parse IB SRP root path source GID
*
@ -151,10 +335,10 @@ static int ib_srp_parse_sgid ( const char *rp_comp,
/* Default to the GID of the last opened Infiniband device */
if ( ( ibdev = last_opened_ibdev() ) != NULL )
memcpy ( &rp->ib->sgid, &ibdev->gid, sizeof ( rp->ib->sgid ) );
memcpy ( &rp->sgid, &ibdev->gid, sizeof ( rp->sgid ) );
return ib_srp_parse_byte_string ( rp_comp, rp->ib->sgid.u.bytes,
( sizeof ( rp->ib->sgid ) |
return ib_srp_parse_byte_string ( rp_comp, rp->sgid.u.bytes,
( sizeof ( rp->sgid ) |
IB_SRP_PARSE_OPTIONAL ) );
}
@ -167,11 +351,10 @@ static int ib_srp_parse_sgid ( const char *rp_comp,
*/
static int ib_srp_parse_initiator_id_ext ( const char *rp_comp,
struct ib_srp_root_path *rp ) {
struct ib_srp_initiator_port_id *port_id =
ib_srp_initiator_port_id ( rp->port_ids );
union ib_srp_initiator_port_id *port_id = &rp->initiator;
return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
( sizeof ( port_id->id_ext ) |
return ib_srp_parse_byte_string ( rp_comp, port_id->ib.id_ext.u.bytes,
( sizeof ( port_id->ib.id_ext ) |
IB_SRP_PARSE_OPTIONAL ) );
}
@ -184,15 +367,14 @@ static int ib_srp_parse_initiator_id_ext ( const char *rp_comp,
*/
static int ib_srp_parse_initiator_hca_guid ( const char *rp_comp,
struct ib_srp_root_path *rp ) {
struct ib_srp_initiator_port_id *port_id =
ib_srp_initiator_port_id ( rp->port_ids );
union ib_srp_initiator_port_id *port_id = &rp->initiator;
/* Default to the GUID portion of the source GID */
memcpy ( &port_id->hca_guid, &rp->ib->sgid.u.half[1],
sizeof ( port_id->hca_guid ) );
memcpy ( &port_id->ib.hca_guid, &rp->sgid.u.half[1],
sizeof ( port_id->ib.hca_guid ) );
return ib_srp_parse_byte_string ( rp_comp, port_id->hca_guid.u.bytes,
( sizeof ( port_id->hca_guid ) |
return ib_srp_parse_byte_string ( rp_comp, port_id->ib.hca_guid.u.bytes,
( sizeof ( port_id->ib.hca_guid ) |
IB_SRP_PARSE_OPTIONAL ) );
}
@ -205,8 +387,8 @@ static int ib_srp_parse_initiator_hca_guid ( const char *rp_comp,
*/
static int ib_srp_parse_dgid ( const char *rp_comp,
struct ib_srp_root_path *rp ) {
return ib_srp_parse_byte_string ( rp_comp, rp->ib->dgid.u.bytes,
( sizeof ( rp->ib->dgid ) |
return ib_srp_parse_byte_string ( rp_comp, rp->dgid.u.bytes,
( sizeof ( rp->dgid ) |
IB_SRP_PARSE_REQUIRED ) );
}
@ -223,7 +405,7 @@ static int ib_srp_parse_pkey ( const char *rp_comp,
if ( ( pkey = ib_srp_parse_integer ( rp_comp, IB_PKEY_DEFAULT ) ) < 0 )
return pkey;
rp->ib->pkey = pkey;
rp->pkey = pkey;
return 0;
}
@ -236,8 +418,8 @@ static int ib_srp_parse_pkey ( const char *rp_comp,
*/
static int ib_srp_parse_service_id ( const char *rp_comp,
struct ib_srp_root_path *rp ) {
return ib_srp_parse_byte_string ( rp_comp, rp->ib->service_id.u.bytes,
( sizeof ( rp->ib->service_id ) |
return ib_srp_parse_byte_string ( rp_comp, rp->service_id.u.bytes,
( sizeof ( rp->service_id ) |
IB_SRP_PARSE_REQUIRED ) );
}
@ -250,7 +432,7 @@ static int ib_srp_parse_service_id ( const char *rp_comp,
*/
static int ib_srp_parse_lun ( const char *rp_comp,
struct ib_srp_root_path *rp ) {
return scsi_parse_lun ( rp_comp, rp->lun );
return scsi_parse_lun ( rp_comp, &rp->lun );
}
/**
@ -262,11 +444,10 @@ static int ib_srp_parse_lun ( const char *rp_comp,
*/
static int ib_srp_parse_target_id_ext ( const char *rp_comp,
struct ib_srp_root_path *rp ) {
struct ib_srp_target_port_id *port_id =
ib_srp_target_port_id ( rp->port_ids );
union ib_srp_target_port_id *port_id = &rp->target;
return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
( sizeof ( port_id->id_ext ) |
return ib_srp_parse_byte_string ( rp_comp, port_id->ib.id_ext.u.bytes,
( sizeof ( port_id->ib.id_ext ) |
IB_SRP_PARSE_REQUIRED ) );
}
@ -279,11 +460,10 @@ static int ib_srp_parse_target_id_ext ( const char *rp_comp,
*/
static int ib_srp_parse_target_ioc_guid ( const char *rp_comp,
struct ib_srp_root_path *rp ) {
struct ib_srp_target_port_id *port_id =
ib_srp_target_port_id ( rp->port_ids );
union ib_srp_target_port_id *port_id = &rp->target;
return ib_srp_parse_byte_string ( rp_comp, port_id->ioc_guid.u.bytes,
( sizeof ( port_id->ioc_guid ) |
return ib_srp_parse_byte_string ( rp_comp, port_id->ib.ioc_guid.u.bytes,
( sizeof ( port_id->ib.ioc_guid ) |
IB_SRP_PARSE_REQUIRED ) );
}
@ -301,7 +481,6 @@ struct ib_srp_root_path_parser {
/** IB SRP root path components */
static struct ib_srp_root_path_parser ib_srp_rp_parser[] = {
{ ib_srp_parse_literal },
{ ib_srp_parse_sgid },
{ ib_srp_parse_initiator_id_ext },
{ ib_srp_parse_initiator_hca_guid },
@ -320,18 +499,13 @@ static struct ib_srp_root_path_parser ib_srp_rp_parser[] = {
/**
* Parse IB SRP root path
*
* @v srp SRP device
* @v rp_string Root path
* @v rp_string Root path string
* @v rp IB SRP root path
* @ret rc Return status code
*/
static int ib_srp_parse_root_path ( struct srp_device *srp,
const char *rp_string ) {
struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
struct ib_srp_root_path rp = {
.lun = &srp->lun,
.port_ids = &srp->port_ids,
.ib = ib_params,
};
static int ib_srp_parse_root_path ( const char *rp_string,
struct ib_srp_root_path *rp ) {
struct ib_srp_root_path_parser *parser;
char rp_string_copy[ strlen ( rp_string ) + 1 ];
char *rp_comp[IB_SRP_NUM_RP_COMPONENTS];
char *rp_string_tmp = rp_string_copy;
@ -346,8 +520,8 @@ static int ib_srp_parse_root_path ( struct srp_device *srp,
break;
for ( ; *rp_string_tmp != ':' ; rp_string_tmp++ ) {
if ( ! *rp_string_tmp ) {
DBGC ( srp, "SRP %p root path \"%s\" too "
"short\n", srp, rp_string );
DBG ( "IBSRP root path \"%s\" too short\n",
rp_string );
return -EINVAL_RP_TOO_SHORT;
}
}
@ -356,11 +530,11 @@ static int ib_srp_parse_root_path ( struct srp_device *srp,
/* Parse root path components */
for ( i = 0 ; i < IB_SRP_NUM_RP_COMPONENTS ; i++ ) {
if ( ( rc = ib_srp_rp_parser[i].parse ( rp_comp[i],
&rp ) ) != 0 ) {
DBGC ( srp, "SRP %p could not parse \"%s\" in root "
"path \"%s\": %s\n", srp, rp_comp[i],
rp_string, strerror ( rc ) );
parser = &ib_srp_rp_parser[i];
if ( ( rc = parser->parse ( rp_comp[i], rp ) ) != 0 ) {
DBG ( "IBSRP could not parse \"%s\" in root path "
"\"%s\": %s\n", rp_comp[i], rp_string,
strerror ( rc ) );
return rc;
}
}
@ -369,41 +543,42 @@ static int ib_srp_parse_root_path ( struct srp_device *srp,
}
/**
* Connect IB SRP session
* Open IB SRP URI
*
* @v srp SRP device
* @v parent Parent interface
* @v uri URI
* @ret rc Return status code
*/
static int ib_srp_connect ( struct srp_device *srp ) {
struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
static int ib_srp_open_uri ( struct interface *parent, struct uri *uri ) {
struct ib_srp_root_path rp;
struct ib_device *ibdev;
int rc;
/* Parse URI */
if ( ! uri->opaque )
return -EINVAL;
memset ( &rp, 0, sizeof ( rp ) );
if ( ( rc = ib_srp_parse_root_path ( uri->opaque, &rp ) ) != 0 )
return rc;
/* Identify Infiniband device */
ibdev = find_ibdev ( &ib_params->sgid );
ibdev = find_ibdev ( &rp.sgid );
if ( ! ibdev ) {
DBGC ( srp, "SRP %p could not identify Infiniband device\n",
srp );
DBG ( "IBSRP could not identify Infiniband device\n" );
return -ENODEV;
}
/* Configure remaining SRP parameters */
srp->memory_handle = ibdev->rdma_key;
/* Open CMRC socket */
if ( ( rc = ib_cmrc_open ( &srp->socket, ibdev, &ib_params->dgid,
&ib_params->service_id ) ) != 0 ) {
DBGC ( srp, "SRP %p could not open CMRC socket: %s\n",
srp, strerror ( rc ) );
/* Open IB SRP device */
if ( ( rc = ib_srp_open ( parent, ibdev, &rp.dgid, &rp.service_id,
&rp.initiator.srp, &rp.target.srp,
&rp.lun ) ) != 0 )
return rc;
}
return 0;
}
/** IB SRP transport type */
struct srp_transport_type ib_srp_transport = {
.priv_len = sizeof ( struct ib_srp_parameters ),
.parse_root_path = ib_srp_parse_root_path,
.connect = ib_srp_connect,
/** IB SRP URI opener */
struct uri_opener ib_srp_uri_opener __uri_opener = {
.scheme = "ib_srp",
.open = ib_srp_open_uri,
};

View File

@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/vsprintf.h>
#include <ipxe/socket.h>
#include <ipxe/iobuf.h>
#include <ipxe/uri.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/scsi.h>
@ -38,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/features.h>
#include <ipxe/base16.h>
#include <ipxe/base64.h>
#include <ipxe/ibft.h>
#include <ipxe/iscsi.h>
/** @file
@ -127,6 +129,36 @@ static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) {
iscsi->rx_buffer = NULL;
}
/**
* Receive PDU data into buffer
*
* @v iscsi iSCSI session
* @v data Data to receive
* @v len Length of data
* @ret rc Return status code
*
* This can be used when the RX PDU type handler wishes to buffer up
* all received data and process the PDU as a single unit. The caller
* is repsonsible for calling iscsi_rx_buffered_data_done() after
* processing the data.
*/
static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi,
const void *data, size_t len ) {
/* Allocate buffer on first call */
if ( ! iscsi->rx_buffer ) {
iscsi->rx_buffer = malloc ( iscsi->rx_len );
if ( ! iscsi->rx_buffer )
return -ENOMEM;
}
/* Copy data to buffer */
assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len );
memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len );
return 0;
}
/**
* Free iSCSI session
*
@ -144,9 +176,44 @@ static void iscsi_free ( struct refcnt *refcnt ) {
free ( iscsi->target_password );
chap_finish ( &iscsi->chap );
iscsi_rx_buffered_data_done ( iscsi );
free ( iscsi->command );
free ( iscsi );
}
/**
* Shut down iSCSI interface
*
* @v iscsi iSCSI session
* @v rc Reason for close
*/
static void iscsi_close ( struct iscsi_session *iscsi, int rc ) {
/* A TCP graceful close is still an error from our point of view */
if ( rc == 0 )
rc = -ECONNRESET;
DBGC ( iscsi, "iSCSI %p closed: %s\n", iscsi, strerror ( rc ) );
/* Stop transmission process */
process_del ( &iscsi->process );
/* Shut down interfaces */
intf_shutdown ( &iscsi->socket, rc );
intf_shutdown ( &iscsi->control, rc );
intf_shutdown ( &iscsi->data, rc );
}
/**
* Assign new iSCSI initiator task tag
*
* @v iscsi iSCSI session
*/
static void iscsi_new_itt ( struct iscsi_session *iscsi ) {
static uint16_t itt_idx;
iscsi->itt = ( ISCSI_TAG_MAGIC | (++itt_idx) );
}
/**
* Open iSCSI transport-layer connection
*
@ -180,7 +247,7 @@ static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_REQUIRED;
/* Assign fresh initiator task tag */
iscsi->itt++;
iscsi_new_itt ( iscsi );
/* Initiate login */
iscsi_start_login ( iscsi );
@ -220,21 +287,34 @@ static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
*
* @v iscsi iSCSI session
* @v rc Return status code
* @v rsp SCSI response, if any
*
* Note that iscsi_scsi_done() will not close the connection, and must
* therefore be called only when the internal state machines are in an
* appropriate state, otherwise bad things may happen on the next call
* to iscsi_issue(). The general rule is to call iscsi_scsi_done()
* only at the end of receiving a PDU; at this point the TX and RX
* engines should both be idle.
* to iscsi_scsi_command(). The general rule is to call
* iscsi_scsi_done() only at the end of receiving a PDU; at this point
* the TX and RX engines should both be idle.
*/
static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc ) {
static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc,
struct scsi_rsp *rsp ) {
uint32_t itt = iscsi->itt;
assert ( iscsi->tx_state == ISCSI_TX_IDLE );
assert ( iscsi->command != NULL );
iscsi->command->rc = rc;
/* Clear command */
free ( iscsi->command );
iscsi->command = NULL;
/* Send SCSI response, if any */
scsi_response ( &iscsi->data, rsp );
/* Close SCSI command, if this is still the same command. (It
* is possible that the command interface has already been
* closed as a result of the SCSI response we sent.)
*/
if ( iscsi->itt == itt )
intf_restart ( &iscsi->data, rc );
}
/****************************************************************************
@ -268,8 +348,9 @@ static void iscsi_start_command ( struct iscsi_session *iscsi ) {
if ( iscsi->command->data_out )
command->flags |= ISCSI_COMMAND_FLAG_WRITE;
/* lengths left as zero */
command->lun = iscsi->lun;
command->itt = htonl ( ++iscsi->itt );
memcpy ( &command->lun, &iscsi->command->lun,
sizeof ( command->lun ) );
command->itt = htonl ( iscsi->itt );
command->exp_len = htonl ( iscsi->command->data_in_len |
iscsi->command->data_out_len );
command->cmdsn = htonl ( iscsi->cmdsn );
@ -297,28 +378,39 @@ static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi,
size_t remaining ) {
struct iscsi_bhs_scsi_response *response
= &iscsi->rx_bhs.scsi_response;
int sense_offset;
struct scsi_rsp rsp;
uint32_t residual_count;
int rc;
/* Capture the sense response code as it floats past, if present */
sense_offset = ISCSI_SENSE_RESPONSE_CODE_OFFSET - iscsi->rx_offset;
if ( ( sense_offset >= 0 ) && len ) {
iscsi->command->sense_response =
* ( ( char * ) data + sense_offset );
/* Buffer up the PDU data */
if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) {
DBGC ( iscsi, "iSCSI %p could not buffer login response: %s\n",
iscsi, strerror ( rc ) );
return rc;
}
/* Wait for whole SCSI response to arrive */
if ( remaining )
return 0;
/* Record SCSI status code */
iscsi->command->status = response->status;
/* Parse SCSI response and discard buffer */
memset ( &rsp, 0, sizeof ( rsp ) );
rsp.status = response->status;
residual_count = ntohl ( response->residual_count );
if ( response->flags & ISCSI_DATA_FLAG_OVERFLOW ) {
rsp.overrun = residual_count;
} else if ( response->flags & ISCSI_DATA_FLAG_UNDERFLOW ) {
rsp.overrun = -(residual_count);
}
if ( ISCSI_DATA_LEN ( response->lengths ) )
memcpy ( &rsp.sense, ( iscsi->rx_buffer + 2 ),
sizeof ( rsp.sense ) );
iscsi_rx_buffered_data_done ( iscsi );
/* Check for errors */
if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
return -EIO;
/* Mark as completed */
iscsi_scsi_done ( iscsi, 0 );
iscsi_scsi_done ( iscsi, 0, &rsp );
return 0;
}
@ -352,9 +444,8 @@ static int iscsi_rx_data_in ( struct iscsi_session *iscsi,
if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) {
assert ( ( offset + len ) == iscsi->command->data_in_len );
assert ( data_in->flags & ISCSI_FLAG_FINAL );
iscsi->command->status = data_in->status;
/* iSCSI cannot return an error status via a data-in */
iscsi_scsi_done ( iscsi, 0 );
iscsi_scsi_done ( iscsi, 0, NULL );
}
return 0;
@ -412,7 +503,7 @@ static void iscsi_start_data_out ( struct iscsi_session *iscsi,
if ( len == remaining )
data_out->flags = ( ISCSI_FLAG_FINAL );
ISCSI_SET_LENGTHS ( data_out->lengths, 0, len );
data_out->lun = iscsi->lun;
data_out->lun = iscsi->command->lun;
data_out->itt = htonl ( iscsi->itt );
data_out->ttt = htonl ( iscsi->ttt );
data_out->expstatsn = htonl ( iscsi->statsn + 1 );
@ -592,6 +683,19 @@ static void iscsi_start_login ( struct iscsi_session *iscsi ) {
struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
int len;
switch ( iscsi->status & ISCSI_LOGIN_CSG_MASK ) {
case ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION:
DBGC ( iscsi, "iSCSI %p entering security negotiation\n",
iscsi );
break;
case ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION:
DBGC ( iscsi, "iSCSI %p entering operational negotiation\n",
iscsi );
break;
default:
assert ( 0 );
}
/* Construct BHS and initiate transmission */
iscsi_start_tx ( iscsi );
request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
@ -604,7 +708,7 @@ static void iscsi_start_login ( struct iscsi_session *iscsi ) {
request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
IANA_EN_FEN_SYSTEMS );
/* isid_iana_qual left as zero */
request->tsih = htons ( iscsi->tsih );
/* tsih left as zero */
request->itt = htonl ( iscsi->itt );
/* cid left as zero */
request->cmdsn = htonl ( iscsi->cmdsn );
@ -1011,36 +1115,6 @@ static int iscsi_handle_strings ( struct iscsi_session *iscsi,
return 0;
}
/**
* Receive PDU data into buffer
*
* @v iscsi iSCSI session
* @v data Data to receive
* @v len Length of data
* @ret rc Return status code
*
* This can be used when the RX PDU type handler wishes to buffer up
* all received data and process the PDU as a single unit. The caller
* is repsonsible for calling iscsi_rx_buffered_data_done() after
* processing the data.
*/
static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi,
const void *data, size_t len ) {
/* Allocate buffer on first call */
if ( ! iscsi->rx_buffer ) {
iscsi->rx_buffer = malloc ( iscsi->rx_len );
if ( ! iscsi->rx_buffer )
return -ENOMEM;
}
/* Copy data to buffer */
assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len );
memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len );
return 0;
}
/**
* Convert iSCSI response status to return status code
*
@ -1119,7 +1193,6 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
response->status_class, response->status_detail );
rc = iscsi_status_to_rc ( response->status_class,
response->status_detail );
iscsi->instant_rc = rc;
return rc;
}
@ -1160,14 +1233,9 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
return -EPROTO;
}
/* Reset retry count */
iscsi->retry_count = 0;
/* Record TSIH for future reference */
iscsi->tsih = ntohl ( response->tsih );
/* Send the actual SCSI command */
iscsi_start_command ( iscsi );
/* Notify SCSI layer of window change */
DBGC ( iscsi, "iSCSI %p entering full feature phase\n", iscsi );
xfer_window_changed ( &iscsi->control );
return 0;
}
@ -1187,13 +1255,18 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
* be in transit at any one time.
*/
static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
assert ( iscsi->tx_state == ISCSI_TX_IDLE );
assert ( ! process_running ( &iscsi->process ) );
/* Initialise TX BHS */
memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) );
/* Flag TX engine to start transmitting */
iscsi->tx_state = ISCSI_TX_BHS;
/* Start transmission process */
process_add ( &iscsi->process );
}
/**
@ -1273,6 +1346,9 @@ static int iscsi_tx_data_padding ( struct iscsi_session *iscsi ) {
static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
/* Stop transmission process */
process_del ( &iscsi->process );
switch ( common->opcode & ISCSI_OPCODE_MASK ) {
case ISCSI_OPCODE_DATA_OUT:
iscsi_data_out_done ( iscsi );
@ -1305,9 +1381,6 @@ static void iscsi_tx_step ( struct process *process ) {
/* Select fragment to transmit */
while ( 1 ) {
switch ( iscsi->tx_state ) {
case ISCSI_TX_IDLE:
/* Stop processing */
return;
case ISCSI_TX_BHS:
tx = iscsi_tx_bhs;
tx_len = sizeof ( iscsi->tx_bhs );
@ -1328,6 +1401,10 @@ static void iscsi_tx_step ( struct process *process ) {
tx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
next_state = ISCSI_TX_IDLE;
break;
case ISCSI_TX_IDLE:
/* Stop processing */
iscsi_tx_done ( iscsi );
return;
default:
assert ( 0 );
return;
@ -1343,13 +1420,13 @@ static void iscsi_tx_step ( struct process *process ) {
if ( ( rc = tx ( iscsi ) ) != 0 ) {
DBGC ( iscsi, "iSCSI %p could not transmit: %s\n",
iscsi, strerror ( rc ) );
/* Transmission errors are fatal */
iscsi_close ( iscsi, rc );
return;
}
/* Move to next state */
iscsi->tx_state = next_state;
if ( next_state == ISCSI_TX_IDLE )
iscsi_tx_done ( iscsi );
}
}
@ -1495,8 +1572,6 @@ static int iscsi_socket_deliver ( struct iscsi_session *iscsi,
remaining ) ) != 0 ) {
DBGC ( iscsi, "iSCSI %p could not process received "
"data: %s\n", iscsi, strerror ( rc ) );
iscsi_close_connection ( iscsi, rc );
iscsi_scsi_done ( iscsi, rc );
goto done;
}
@ -1518,41 +1593,14 @@ static int iscsi_socket_deliver ( struct iscsi_session *iscsi,
done:
/* Free I/O buffer */
free_iob ( iobuf );
/* Destroy session on error */
if ( rc != 0 )
iscsi_close ( iscsi, rc );
return rc;
}
/**
* Handle stream connection closure
*
* @v iscsi iSCSI session
* @v rc Reason for close
*
*/
static void iscsi_socket_close ( struct iscsi_session *iscsi, int rc ) {
/* Even a graceful close counts as an error for iSCSI */
if ( ! rc )
rc = -ECONNRESET;
/* Close session cleanly */
iscsi_close_connection ( iscsi, rc );
/* Retry connection if within the retry limit, otherwise fail */
if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
DBGC ( iscsi, "iSCSI %p retrying connection (retry #%d)\n",
iscsi, iscsi->retry_count );
if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
DBGC ( iscsi, "iSCSI %p could not reconnect: %s\n",
iscsi, strerror ( rc ) );
iscsi_scsi_done ( iscsi, rc );
}
} else {
DBGC ( iscsi, "iSCSI %p retry count exceeded\n", iscsi );
iscsi->instant_rc = rc;
iscsi_scsi_done ( iscsi, rc );
}
}
/**
* Handle redirection event
*
@ -1582,13 +1630,12 @@ static int iscsi_vredirect ( struct iscsi_session *iscsi, int type,
return xfer_vreopen ( &iscsi->socket, type, args );
}
/** iSCSI socket interface operations */
static struct interface_operation iscsi_socket_operations[] = {
INTF_OP ( xfer_deliver, struct iscsi_session *, iscsi_socket_deliver ),
INTF_OP ( xfer_vredirect, struct iscsi_session *, iscsi_vredirect ),
INTF_OP ( intf_close, struct iscsi_session *, iscsi_socket_close ),
INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ),
};
/** iSCSI socket interface descriptor */
@ -1602,54 +1649,100 @@ static struct interface_descriptor iscsi_socket_desc =
*/
/**
* Issue SCSI command
* Check iSCSI flow-control window
*
* @v scsi SCSI device
* @v command SCSI command
* @ret rc Return status code
* @v iscsi iSCSI session
* @ret len Length of window
*/
static int iscsi_command ( struct scsi_device *scsi,
struct scsi_command *command ) {
struct iscsi_session *iscsi =
container_of ( scsi->backend, struct iscsi_session, refcnt );
int rc;
static size_t iscsi_scsi_window ( struct iscsi_session *iscsi ) {
/* Abort immediately if we have a recorded permanent failure */
if ( iscsi->instant_rc )
return iscsi->instant_rc;
/* Record SCSI command */
iscsi->command = command;
/* Issue command or open connection as appropriate */
if ( iscsi->status ) {
iscsi_start_command ( iscsi );
if ( ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) ==
ISCSI_STATUS_FULL_FEATURE_PHASE ) &&
( iscsi->command == NULL ) ) {
/* We cannot handle concurrent commands */
return 1;
} else {
if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
iscsi->command = NULL;
return rc;
}
return 0;
}
return 0;
}
/**
* Shut down iSCSI interface
* Issue iSCSI SCSI command
*
* @v scsi SCSI device
* @v iscsi iSCSI session
* @v parent Parent interface
* @v command SCSI command
* @ret tag Command tag, or negative error
*/
void iscsi_detach ( struct scsi_device *scsi ) {
struct iscsi_session *iscsi =
container_of ( scsi->backend, struct iscsi_session, refcnt );
static int iscsi_scsi_command ( struct iscsi_session *iscsi,
struct interface *parent,
struct scsi_cmd *command ) {
iscsi_close_connection ( iscsi, 0 );
process_del ( &iscsi->process );
scsi->command = scsi_detached_command;
ref_put ( scsi->backend );
scsi->backend = NULL;
/* This iSCSI implementation cannot handle multiple concurrent
* commands or commands arriving before login is complete.
*/
if ( iscsi_scsi_window ( iscsi ) == 0 ) {
DBGC ( iscsi, "iSCSI %p cannot handle concurrent commands\n",
iscsi );
return -EOPNOTSUPP;
}
/* Store command */
iscsi->command = malloc ( sizeof ( *command ) );
if ( ! iscsi->command )
return -ENOMEM;
memcpy ( iscsi->command, command, sizeof ( *command ) );
/* Assign new ITT */
iscsi_new_itt ( iscsi );
/* Start sending command */
iscsi_start_command ( iscsi );
/* Attach to parent interface and return */
intf_plug_plug ( &iscsi->data, parent );
return iscsi->itt;
}
/** iSCSI SCSI command-issuing interface operations */
static struct interface_operation iscsi_control_op[] = {
INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ),
INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ),
INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ),
INTF_OP ( acpi_describe, struct iscsi_session *, ibft_describe ),
};
/** iSCSI SCSI command-issuing interface descriptor */
static struct interface_descriptor iscsi_control_desc =
INTF_DESC ( struct iscsi_session, control, iscsi_control_op );
/**
* Close iSCSI command
*
* @v iscsi iSCSI session
* @v rc Reason for close
*/
static void iscsi_command_close ( struct iscsi_session *iscsi, int rc ) {
/* Restart interface */
intf_restart ( &iscsi->data, rc );
/* Treat unsolicited command closures mid-command as fatal,
* because we have no code to handle partially-completed PDUs.
*/
if ( iscsi->command != NULL )
iscsi_close ( iscsi, ( ( rc == 0 ) ? -ECANCELED : rc ) );
}
/** iSCSI SCSI command interface operations */
static struct interface_operation iscsi_data_op[] = {
INTF_OP ( intf_close, struct iscsi_session *, iscsi_command_close ),
};
/** iSCSI SCSI command interface descriptor */
static struct interface_descriptor iscsi_data_desc =
INTF_DESC ( struct iscsi_session, data, iscsi_data_op );
/****************************************************************************
*
* Instantiator
@ -1658,8 +1751,7 @@ void iscsi_detach ( struct scsi_device *scsi ) {
/** iSCSI root path components (as per RFC4173) */
enum iscsi_root_path_component {
RP_LITERAL = 0,
RP_SERVERNAME,
RP_SERVERNAME = 0,
RP_PROTOCOL,
RP_PORT,
RP_LUN,
@ -1779,60 +1871,95 @@ static int iscsi_set_auth ( struct iscsi_session *iscsi,
}
/**
* Attach iSCSI interface
* Open iSCSI URI
*
* @v scsi SCSI device
* @v root_path iSCSI root path (as per RFC4173)
* @v parent Parent interface
* @v uri URI
* @ret rc Return status code
*/
int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
static int iscsi_open ( struct interface *parent, struct uri *uri ) {
struct iscsi_session *iscsi;
int rc;
/* Sanity check */
if ( ! uri->opaque ) {
rc = -EINVAL;
goto err_sanity_uri;
}
/* Allocate and initialise structure */
iscsi = zalloc ( sizeof ( *iscsi ) );
if ( ! iscsi )
return -ENOMEM;
if ( ! iscsi ) {
rc = -ENOMEM;
goto err_zalloc;
}
ref_init ( &iscsi->refcnt, iscsi_free );
intf_init ( &iscsi->control, &iscsi_control_desc, &iscsi->refcnt );
intf_init ( &iscsi->data, &iscsi_data_desc, &iscsi->refcnt );
intf_init ( &iscsi->socket, &iscsi_socket_desc, &iscsi->refcnt );
process_init ( &iscsi->process, iscsi_tx_step, &iscsi->refcnt );
process_init_stopped ( &iscsi->process, iscsi_tx_step,
&iscsi->refcnt );
/* Parse root path */
if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
goto err;
if ( ( rc = iscsi_parse_root_path ( iscsi, uri->opaque ) ) != 0 )
goto err_parse_root_path;
/* Set fields not specified by root path */
if ( ( rc = iscsi_set_auth ( iscsi,
iscsi_initiator_username,
iscsi_initiator_password,
iscsi_target_username,
iscsi_target_password ) ) != 0 )
goto err;
goto err_set_auth;
/* Sanity checks */
if ( ! iscsi->target_address ) {
DBGC ( iscsi, "iSCSI %p does not yet support discovery\n",
iscsi );
rc = -ENOTSUP_DISCOVERY;
goto err;
goto err_sanity_address;
}
if ( ! iscsi->target_iqn ) {
DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n",
iscsi, root_path );
iscsi, uri->opaque );
rc = -EINVAL;
goto err;
goto err_sanity_iqn;
}
/* Attach parent interface, mortalise self, and return */
scsi->backend = ref_get ( &iscsi->refcnt );
scsi->command = iscsi_command;
/* Open socket */
if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 )
goto err_open_connection;
/* Attach SCSI device to parent interface */
if ( ( rc = scsi_open ( parent, &iscsi->control,
&iscsi->lun ) ) != 0 ) {
DBGC ( iscsi, "iSCSI %p could not create SCSI device: %s\n",
iscsi, strerror ( rc ) );
goto err_scsi_open;
}
/* Mortalise self, and return */
ref_put ( &iscsi->refcnt );
return 0;
err:
err_scsi_open:
err_open_connection:
err_sanity_iqn:
err_sanity_address:
err_set_auth:
err_parse_root_path:
iscsi_close ( iscsi, rc );
ref_put ( &iscsi->refcnt );
err_zalloc:
err_sanity_uri:
return rc;
}
/** iSCSI URI opener */
struct uri_opener iscsi_uri_opener __uri_opener = {
.scheme = "iscsi",
.open = iscsi_open,
};
/****************************************************************************
*
* Settings

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/image.h>
#include <ipxe/sanboot.h>
#include <ipxe/uri.h>
#include <ipxe/init.h>
#include <usr/ifmgmt.h>
#include <usr/route.h>
#include <usr/dhcpmgmt.h>
@ -109,6 +110,14 @@ int boot_next_server_and_filename ( struct in_addr next_server,
return rc;
}
/** The "keep-san" setting */
struct setting keep_san_setting __setting = {
.name = "keep-san",
.description = "Preserve SAN connection",
.tag = DHCP_EB_KEEP_SAN,
.type = &setting_type_int8,
};
/**
* Boot using root path
*
@ -116,17 +125,62 @@ int boot_next_server_and_filename ( struct in_addr next_server,
* @ret rc Return status code
*/
int boot_root_path ( const char *root_path ) {
struct sanboot_protocol *sanboot;
struct uri *uri;
int drive;
int rc;
/* Quick hack */
for_each_table_entry ( sanboot, SANBOOT_PROTOCOLS ) {
if ( strncmp ( root_path, sanboot->prefix,
strlen ( sanboot->prefix ) ) == 0 ) {
return sanboot->boot ( root_path );
}
/* Parse URI */
uri = parse_uri ( root_path );
if ( ! uri ) {
printf ( "Could not parse \"%s\"\n", root_path );
rc = -ENOMEM;
goto err_parse_uri;
}
return -ENOTSUP;
/* Hook SAN device */
if ( ( drive = san_hook ( uri, 0 ) ) < 0 ) {
rc = drive;
printf ( "Could not open SAN device: %s\n",
strerror ( rc ) );
goto err_open;
}
printf ( "Registered as SAN device %#02x\n", drive );
/* Describe SAN device */
if ( ( rc = san_describe ( drive ) ) != 0 ) {
printf ( "Could not describe SAN device %#02x: %s\n",
drive, strerror ( rc ) );
goto err_describe;
}
printf ( "Booting from SAN device %#02x\n", drive );
rc = san_boot ( drive );
printf ( "Boot from SAN device %#02x failed: %s\n",
drive, strerror ( rc ) );
/* Leave drive registered, if instructed to do so */
if ( fetch_intz_setting ( NULL, &keep_san_setting ) != 0 ) {
printf ( "Preserving connection to SAN device %#02x\n",
drive );
shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES;
goto err_keep_san;
}
/* Unhook SAN deivce */
printf ( "Unregistering SAN device %#02x\n", drive );
san_unhook ( drive );
/* Drop URI reference */
uri_put ( uri );
return 0;
err_keep_san:
err_describe:
err_open:
uri_put ( uri );
err_parse_uri:
return rc;
}
/**