diff --git a/src/arch/i386/image/eltorito.c b/src/arch/i386/image/eltorito.c deleted file mode 100644 index a416d15b..00000000 --- a/src/arch/i386/image/eltorito.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown . - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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, -}; diff --git a/src/arch/i386/include/bits/eltorito.h b/src/arch/i386/include/bits/eltorito.h deleted file mode 100644 index d43e9aac..00000000 --- a/src/arch/i386/include/bits/eltorito.h +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef ELTORITO_PLATFORM -#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86 -#endif /* ELTORITO_PLATFORM */ diff --git a/src/arch/i386/include/bits/sanboot.h b/src/arch/i386/include/bits/sanboot.h new file mode 100644 index 00000000..9c77a4d4 --- /dev/null +++ b/src/arch/i386/include/bits/sanboot.h @@ -0,0 +1,14 @@ +#ifndef _BITS_SANBOOT_H +#define _BITS_SANBOOT_H + +/** @file + * + * i386-specific sanboot API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include + +#endif /* _BITS_SANBOOT_H */ diff --git a/src/arch/i386/include/int13.h b/src/arch/i386/include/int13.h index ed085d57..2a0f19cf 100644 --- a/src/arch/i386/include/int13.h +++ b/src/arch/i386/include/int13.h @@ -13,8 +13,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -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 */ diff --git a/src/arch/i386/include/ipxe/abft.h b/src/arch/i386/include/ipxe/abft.h deleted file mode 100644 index ca963020..00000000 --- a/src/arch/i386/include/ipxe/abft.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _IPXE_ABFT_H -#define _IPXE_ABFT_H - -/** @file - * - * AoE boot firmware table - * - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include -#include - -/** 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 */ diff --git a/src/arch/i386/include/ipxe/bios_sanboot.h b/src/arch/i386/include/ipxe/bios_sanboot.h new file mode 100644 index 00000000..09794548 --- /dev/null +++ b/src/arch/i386/include/ipxe/bios_sanboot.h @@ -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 */ diff --git a/src/arch/i386/include/ipxe/sbft.h b/src/arch/i386/include/ipxe/sbft.h deleted file mode 100644 index f9910624..00000000 --- a/src/arch/i386/include/ipxe/sbft.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef _IPXE_SBFT_H -#define _IPXE_SBFT_H - -/* - * Copyright (C) 2009 Fen Systems Ltd . - * 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 -#include -#include -#include -#include - -/** 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 */ diff --git a/src/arch/i386/interface/pcbios/abft.c b/src/arch/i386/interface/pcbios/abft.c deleted file mode 100644 index 9aa2f098..00000000 --- a/src/arch/i386/interface/pcbios/abft.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown . - * - * 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 -#include -#include -#include - -/** @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 ) ); -} diff --git a/src/arch/i386/interface/pcbios/aoeboot.c b/src/arch/i386/interface/pcbios/aoeboot.c deleted file mode 100644 index 388cd140..00000000 --- a/src/arch/i386/interface/pcbios/aoeboot.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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, -}; diff --git a/src/arch/i386/interface/pcbios/ib_srpboot.c b/src/arch/i386/interface/pcbios/ib_srpboot.c deleted file mode 100644 index 711c1e93..00000000 --- a/src/arch/i386/interface/pcbios/ib_srpboot.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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, -}; diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index f72dc5fa..97110fde 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include @@ -26,6 +27,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -41,6 +50,77 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** + * Overall timeout for INT 13 commands (independent of underlying device + * + * Underlying devices should ideally never become totally stuck. + * However, if they do, then the INT 13 mechanism provides no means + * for the caller to cancel the operation, and the machine appears to + * hang. Use an overall timeout for all commands to avoid this + * problem and bounce timeout failures to the caller. + */ +#define INT13_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC ) + +/** An INT 13 emulated drive */ +struct int13_drive { + /** Reference count */ + struct refcnt refcnt; + /** List of all registered drives */ + struct list_head list; + + /** Block device URI */ + struct uri *uri; + /** Underlying block device interface */ + struct interface block; + + /** 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; + + /** Block device capacity */ + struct block_device_capacity capacity; + + /** 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; + + /** Underlying device status, if in error */ + int block_rc; + /** Status of last operation */ + int last_status; +}; + /** Vector for chaining to other INT 13 handlers */ static struct segoff __text16 ( int13_vector ); #define int13_vector __use_text16 ( int13_vector ) @@ -49,7 +129,7 @@ static struct segoff __text16 ( int13_vector ); extern void int13_wrapper ( void ); /** List of registered emulated drives */ -static LIST_HEAD ( drives ); +static LIST_HEAD ( int13s ); /** * Number of BIOS drives @@ -60,19 +140,309 @@ static LIST_HEAD ( drives ); */ static uint8_t num_drives; +/** An INT 13 command */ +struct int13_command { + /** Status */ + int rc; + /** INT 13 drive */ + struct int13_drive *int13; + /** Underlying block device interface */ + struct interface block; + /** Command timeout timer */ + struct retry_timer timer; +}; + +/** + * Record INT 13 drive capacity + * + * @v command INT 13 command + * @v capacity Block device capacity + */ +static void int13_command_capacity ( struct int13_command *command, + struct block_device_capacity *capacity ) { + memcpy ( &command->int13->capacity, capacity, + sizeof ( command->int13->capacity ) ); +} + +/** + * Close INT 13 command + * + * @v command INT 13 command + * @v rc Reason for close + */ +static void int13_command_close ( struct int13_command *command, int rc ) { + intf_restart ( &command->block, rc ); + stop_timer ( &command->timer ); + command->rc = rc; +} + +/** + * Handle INT 13 command timer expiry + * + * @v timer Timer + */ +static void int13_command_expired ( struct retry_timer *timer, + int over __unused ) { + struct int13_command *command = + container_of ( timer, struct int13_command, timer ); + + int13_command_close ( command, -ETIMEDOUT ); +} + +/** INT 13 command interface operations */ +static struct interface_operation int13_command_op[] = { + INTF_OP ( intf_close, struct int13_command *, int13_command_close ), + INTF_OP ( block_capacity, struct int13_command *, + int13_command_capacity ), +}; + +/** INT 13 command interface descriptor */ +static struct interface_descriptor int13_command_desc = + INTF_DESC ( struct int13_command, block, int13_command_op ); + +/** + * Prepare to issue INT 13 command + * + * @v command INT 13 command + * @v int13 Emulated drive + * @ret rc Return status code + */ +static int int13_command_start ( struct int13_command *command, + struct int13_drive *int13 ) { + + /* Sanity check */ + assert ( command->int13 == NULL ); + assert ( ! timer_running ( &command->timer ) ); + + /* Initialise command */ + command->rc = -EINPROGRESS; + command->int13 = int13; + start_timer_fixed ( &command->timer, INT13_COMMAND_TIMEOUT ); + + /* Wait for block control interface to become ready */ + while ( ( command->rc == -EINPROGRESS ) && + ( xfer_window ( &int13->block ) == 0 ) ) { + step(); + } + + return ( ( command->rc == -EINPROGRESS ) ? + int13->block_rc : command->rc ); +} + +/** + * Wait for INT 13 command to complete + * + * @v command INT 13 command + * @ret rc Return status code + */ +static int int13_command_wait ( struct int13_command *command ) { + + /* Sanity check */ + assert ( timer_running ( &command->timer ) ); + + /* Wait for command to complete */ + while ( command->rc == -EINPROGRESS ) + step(); + + assert ( ! timer_running ( &command->timer ) ); + return command->rc; +} + +/** + * Terminate INT 13 command + * + * @v command INT 13 command + */ +static void int13_command_stop ( struct int13_command *command ) { + stop_timer ( &command->timer ); + command->int13 = NULL; +} + +/** The single active INT 13 command */ +static struct int13_command int13_command = { + .block = INTF_INIT ( int13_command_desc ), + .timer = TIMER_INIT ( int13_command_expired ), +}; + +/** + * Read from or write to INT 13 drive + * + * @v int13 Emulated drive + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v block_rw Block read/write method + * @ret rc Return status code + */ +static int int13_rw ( struct int13_drive *int13, uint64_t lba, + unsigned int count, userptr_t buffer, + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) ) { + struct int13_command *command = &int13_command; + unsigned int frag_count; + size_t frag_len; + int rc; + + while ( count ) { + + /* Determine fragment length */ + frag_count = count; + if ( frag_count > int13->capacity.max_count ) + frag_count = int13->capacity.max_count; + frag_len = ( int13->capacity.blksize * frag_count ); + + /* Issue command */ + if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) || + ( ( rc = block_rw ( &int13->block, &command->block, lba, + frag_count, buffer, + frag_len ) ) != 0 ) || + ( ( rc = int13_command_wait ( command ) ) != 0 ) ) { + int13_command_stop ( command ); + return rc; + } + int13_command_stop ( command ); + + /* Move to next fragment */ + lba += frag_count; + count -= frag_count; + buffer = userptr_add ( buffer, frag_len ); + } + + return 0; +} + +/** + * Read INT 13 drive capacity + * + * @v int13 Emulated drive + * @ret rc Return status code + */ +static int int13_read_capacity ( struct int13_drive *int13 ) { + struct int13_command *command = &int13_command; + int rc; + + /* Issue command */ + if ( ( ( rc = int13_command_start ( command, int13 ) ) != 0 ) || + ( ( rc = block_read_capacity ( &int13->block, + &command->block ) ) != 0 ) || + ( ( rc = int13_command_wait ( command ) ) != 0 ) ) { + int13_command_stop ( command ); + return rc; + } + + int13_command_stop ( command ); + return 0; +} + +/** + * Guess INT 13 drive geometry + * + * @v int13 Emulated drive + * @ret rc Return status code + * + * Guesses the drive geometry by inspecting the partition table. + */ +static int int13_guess_geometry ( struct int13_drive *int13 ) { + struct master_boot_record mbr; + struct partition_table_entry *partition; + unsigned int guessed_heads = 255; + unsigned int guessed_sectors_per_track = 63; + unsigned long blocks; + unsigned long blocks_per_cyl; + unsigned int i; + int rc; + + /* Don't even try when the blksize is invalid for C/H/S access */ + if ( int13->capacity.blksize != INT13_BLKSIZE ) + return 0; + + /* Read partition table */ + if ( ( rc = int13_rw ( int13, 0, 1, virt_to_user ( &mbr ), + block_read ) ) != 0 ) { + DBGC ( int13, "INT13 drive %02x could not read partition " + "table to guess geometry: %s\n", + int13->drive, strerror ( rc ) ); + return rc; + } + + /* Scan through partition table and modify guesses for heads + * and sectors_per_track if we find any used partitions. + */ + for ( i = 0 ; i < 4 ; i++ ) { + partition = &mbr.partitions[i]; + if ( ! partition->type ) + continue; + guessed_heads = ( PART_HEAD ( partition->chs_end ) + 1 ); + guessed_sectors_per_track = PART_SECTOR ( partition->chs_end ); + DBGC ( int13, "INT13 drive %02x guessing C/H/S xx/%d/%d based " + "on partition %d\n", int13->drive, guessed_heads, + guessed_sectors_per_track, ( i + 1 ) ); + } + + /* Apply guesses if no geometry already specified */ + if ( ! int13->heads ) + int13->heads = guessed_heads; + if ( ! int13->sectors_per_track ) + int13->sectors_per_track = guessed_sectors_per_track; + if ( ! int13->cylinders ) { + /* Avoid attempting a 64-bit divide on a 32-bit system */ + blocks = ( ( int13->capacity.blocks <= ULONG_MAX ) ? + int13->capacity.blocks : ULONG_MAX ); + blocks_per_cyl = ( int13->heads * int13->sectors_per_track ); + assert ( blocks_per_cyl != 0 ); + int13->cylinders = ( blocks / blocks_per_cyl ); + if ( int13->cylinders > 1024 ) + int13->cylinders = 1024; + } + + return 0; +} + +/** + * Open (or reopen) INT 13 emulated drive underlying block device + * + * @v int13 Emulated drive + * @ret rc Return status code + */ +static int int13_reopen_block ( struct int13_drive *int13 ) { + int rc; + + /* Close any existing block device */ + intf_restart ( &int13->block, -ECONNRESET ); + + /* Open block device */ + if ( ( rc = xfer_open_uri ( &int13->block, int13->uri ) ) != 0 ) { + DBGC ( int13, "INT13 drive %02x could not reopen block " + "device: %s\n", int13->drive, strerror ( rc ) ); + int13->block_rc = rc; + return rc; + } + + /* Clear block device error status */ + int13->block_rc = 0; + + /* Read device capacity */ + if ( ( rc = int13_read_capacity ( int13 ) ) != 0 ) + return rc; + + return 0; +} + /** * Update BIOS drive count */ static void int13_set_num_drives ( void ) { - struct int13_drive *drive; + struct int13_drive *int13; /* Get current drive count */ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); /* Ensure count is large enough to cover all of our emulated drives */ - list_for_each_entry ( drive, &drives, list ) { - if ( num_drives <= ( drive->drive & 0x7f ) ) - num_drives = ( ( drive->drive & 0x7f ) + 1 ); + list_for_each_entry ( int13, &int13s, list ) { + if ( num_drives <= ( int13->drive & 0x7f ) ) + num_drives = ( ( int13->drive & 0x7f ) + 1 ); } /* Update current drive count */ @@ -96,48 +466,56 @@ static void int13_check_num_drives ( void ) { /** * INT 13, 00 - Reset disk system * - * @v drive Emulated drive + * @v int13 Emulated drive * @ret status Status code */ -static int int13_reset ( struct int13_drive *drive __unused, +static int int13_reset ( struct int13_drive *int13, struct i386_all_regs *ix86 __unused ) { - DBG ( "Reset drive\n" ); + int rc; + + DBGC2 ( int13, "Reset drive\n" ); + + /* Reopen underlying block device */ + if ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) + return -INT13_STATUS_RESET_FAILED; + return 0; } /** * INT 13, 01 - Get status of last operation * - * @v drive Emulated drive + * @v int13 Emulated drive * @ret status Status code */ -static int int13_get_last_status ( struct int13_drive *drive, +static int int13_get_last_status ( struct int13_drive *int13, struct i386_all_regs *ix86 __unused ) { - DBG ( "Get status of last operation\n" ); - return drive->last_status; + DBGC2 ( int13, "Get status of last operation\n" ); + return int13->last_status; } /** * Read / write sectors * - * @v drive Emulated drive + * @v int13 Emulated drive * @v al Number of sectors to read or write (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number * @v cl (bits 5:0) Sector number * @v dh Head number * @v es:bx Data buffer - * @v io Read / write method + * @v block_rw Block read/write method * @ret status Status code * @ret al Number of sectors read or written */ -static int int13_rw_sectors ( struct int13_drive *drive, +static int int13_rw_sectors ( struct int13_drive *int13, struct i386_all_regs *ix86, - int ( * io ) ( struct block_device *blockdev, - uint64_t block, - unsigned long count, - userptr_t buffer ) ) { - struct block_device *blockdev = drive->blockdev; + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, + unsigned int count, + userptr_t buffer, + size_t len ) ) { unsigned int cylinder, head, sector; unsigned long lba; unsigned int count; @@ -145,30 +523,33 @@ static int int13_rw_sectors ( struct int13_drive *drive, int rc; /* Validate blocksize */ - if ( blockdev->blksize != INT13_BLKSIZE ) { - DBG ( "Invalid blocksize (%zd) for non-extended read/write\n", - blockdev->blksize ); + if ( int13->capacity.blksize != INT13_BLKSIZE ) { + DBGC ( int13, "\nINT 13 drive %02x invalid blocksize (%zd) " + "for non-extended read/write\n", + int13->drive, int13->capacity.blksize ); return -INT13_STATUS_INVALID; } /* Calculate parameters */ cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch ); - assert ( cylinder < drive->cylinders ); + assert ( cylinder < int13->cylinders ); head = ix86->regs.dh; - assert ( head < drive->heads ); + assert ( head < int13->heads ); sector = ( ix86->regs.cl & 0x3f ); - assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) ); - lba = ( ( ( ( cylinder * drive->heads ) + head ) - * drive->sectors_per_track ) + sector - 1 ); + assert ( ( sector >= 1 ) && ( sector <= int13->sectors_per_track ) ); + lba = ( ( ( ( cylinder * int13->heads ) + head ) + * int13->sectors_per_track ) + sector - 1 ); count = ix86->regs.al; buffer = real_to_user ( ix86->segs.es, ix86->regs.bx ); - DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder, - head, sector, lba, ix86->segs.es, ix86->regs.bx, count ); + DBGC2 ( int13, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n", + cylinder, head, sector, lba, ix86->segs.es, ix86->regs.bx, + count ); /* Read from / write to block device */ - if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) { - DBG ( "INT 13 failed: %s\n", strerror ( rc ) ); + if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) { + DBGC ( int13, "INT13 drive %02x I/O failed: %s\n", + int13->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; } @@ -178,7 +559,7 @@ static int int13_rw_sectors ( struct int13_drive *drive, /** * INT 13, 02 - Read sectors * - * @v drive Emulated drive + * @v int13 Emulated drive * @v al Number of sectors to read (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number @@ -188,16 +569,16 @@ static int int13_rw_sectors ( struct int13_drive *drive, * @ret status Status code * @ret al Number of sectors read */ -static int int13_read_sectors ( struct int13_drive *drive, +static int int13_read_sectors ( struct int13_drive *int13, struct i386_all_regs *ix86 ) { - DBG ( "Read: " ); - return int13_rw_sectors ( drive, ix86, drive->blockdev->op->read ); + DBGC2 ( int13, "Read: " ); + return int13_rw_sectors ( int13, ix86, block_read ); } /** * INT 13, 03 - Write sectors * - * @v drive Emulated drive + * @v int13 Emulated drive * @v al Number of sectors to write (must be nonzero) * @v ch Low bits of cylinder number * @v cl (bits 7:6) High bits of cylinder number @@ -207,16 +588,16 @@ static int int13_read_sectors ( struct int13_drive *drive, * @ret status Status code * @ret al Number of sectors written */ -static int int13_write_sectors ( struct int13_drive *drive, +static int int13_write_sectors ( struct int13_drive *int13, struct i386_all_regs *ix86 ) { - DBG ( "Write: " ); - return int13_rw_sectors ( drive, ix86, drive->blockdev->op->write ); + DBGC2 ( int13, "Write: " ); + return int13_rw_sectors ( int13, ix86, block_write ); } /** * INT 13, 08 - Get drive parameters * - * @v drive Emulated drive + * @v int13 Emulated drive * @ret status Status code * @ret ch Low bits of maximum cylinder number * @ret cl (bits 7:6) High bits of maximum cylinder number @@ -224,13 +605,13 @@ static int int13_write_sectors ( struct int13_drive *drive, * @ret dh Maximum head number * @ret dl Number of drives */ -static int int13_get_parameters ( struct int13_drive *drive, +static int int13_get_parameters ( struct int13_drive *int13, struct i386_all_regs *ix86 ) { - unsigned int max_cylinder = drive->cylinders - 1; - unsigned int max_head = drive->heads - 1; - unsigned int max_sector = drive->sectors_per_track; /* sic */ + unsigned int max_cylinder = int13->cylinders - 1; + unsigned int max_head = int13->heads - 1; + unsigned int max_sector = int13->sectors_per_track; /* sic */ - DBG ( "Get drive parameters\n" ); + DBGC2 ( int13, "Get drive parameters\n" ); ix86->regs.ch = ( max_cylinder & 0xff ); ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector ); @@ -242,18 +623,18 @@ static int int13_get_parameters ( struct int13_drive *drive, /** * INT 13, 15 - Get disk type * - * @v drive Emulated drive + * @v int13 Emulated drive * @ret ah Type code * @ret cx:dx Sector count * @ret status Status code / disk type */ -static int int13_get_disk_type ( struct int13_drive *drive, +static int int13_get_disk_type ( struct int13_drive *int13, struct i386_all_regs *ix86 ) { uint32_t blocks; - DBG ( "Get disk type\n" ); - blocks = ( ( drive->blockdev->blocks <= 0xffffffffUL ) ? - drive->blockdev->blocks : 0xffffffffUL ); + DBGC2 ( int13, "Get disk type\n" ); + blocks = ( ( int13->capacity.blocks <= 0xffffffffUL ) ? + int13->capacity.blocks : 0xffffffffUL ); ix86->regs.cx = ( blocks >> 16 ); ix86->regs.dx = ( blocks & 0xffff ); return INT13_DISK_TYPE_HDD; @@ -262,16 +643,16 @@ static int int13_get_disk_type ( struct int13_drive *drive, /** * INT 13, 41 - Extensions installation check * - * @v drive Emulated drive + * @v int13 Emulated drive * @v bx 0x55aa * @ret bx 0xaa55 * @ret cx Extensions API support bitmap * @ret status Status code / API version */ -static int int13_extension_check ( struct int13_drive *drive __unused, +static int int13_extension_check ( struct int13_drive *int13 __unused, struct i386_all_regs *ix86 ) { if ( ix86->regs.bx == 0x55aa ) { - DBG ( "INT 13 extensions installation check\n" ); + DBGC2 ( int13, "INT13 extensions installation check\n" ); ix86->regs.bx = 0xaa55; ix86->regs.cx = INT13_EXTENSION_LINEAR; return INT13_EXTENSION_VER_1_X; @@ -283,18 +664,19 @@ static int int13_extension_check ( struct int13_drive *drive __unused, /** * Extended read / write * - * @v drive Emulated drive + * @v int13 Emulated drive * @v ds:si Disk address packet - * @v io Read / write method + * @v block_rw Block read/write method * @ret status Status code */ -static int int13_extended_rw ( struct int13_drive *drive, +static int int13_extended_rw ( struct int13_drive *int13, struct i386_all_regs *ix86, - int ( * io ) ( struct block_device *blockdev, - uint64_t block, - unsigned long count, - userptr_t buffer ) ) { - struct block_device *blockdev = drive->blockdev; + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, + unsigned int count, + userptr_t buffer, + size_t len ) ) { struct int13_disk_address addr; uint64_t lba; unsigned long count; @@ -307,12 +689,14 @@ static int int13_extended_rw ( struct int13_drive *drive, count = addr.count; buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset ); - DBG ( "LBA %#llx <-> %04x:%04x (count %ld)\n", (unsigned long long)lba, - addr.buffer.segment, addr.buffer.offset, count ); + DBGC2 ( int13, "LBA %08llx <-> %04x:%04x (count %ld)\n", + ( ( unsigned long long ) lba ), addr.buffer.segment, + addr.buffer.offset, count ); /* Read from / write to block device */ - if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) { - DBG ( "INT 13 failed: %s\n", strerror ( rc ) ); + if ( ( rc = int13_rw ( int13, lba, count, buffer, block_rw ) ) != 0 ) { + DBGC ( int13, "INT13 drive %02x extended I/O failed: %s\n", + int13->drive, strerror ( rc ) ); return -INT13_STATUS_READ_ERROR; } @@ -322,50 +706,50 @@ static int int13_extended_rw ( struct int13_drive *drive, /** * INT 13, 42 - Extended read * - * @v drive Emulated drive + * @v int13 Emulated drive * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_read ( struct int13_drive *drive, +static int int13_extended_read ( struct int13_drive *int13, struct i386_all_regs *ix86 ) { - DBG ( "Extended read: " ); - return int13_extended_rw ( drive, ix86, drive->blockdev->op->read ); + DBGC2 ( int13, "Extended read: " ); + return int13_extended_rw ( int13, ix86, block_read ); } /** * INT 13, 43 - Extended write * - * @v drive Emulated drive + * @v int13 Emulated drive * @v ds:si Disk address packet * @ret status Status code */ -static int int13_extended_write ( struct int13_drive *drive, +static int int13_extended_write ( struct int13_drive *int13, struct i386_all_regs *ix86 ) { - DBG ( "Extended write: " ); - return int13_extended_rw ( drive, ix86, drive->blockdev->op->write ); + DBGC2 ( int13, "Extended write: " ); + return int13_extended_rw ( int13, ix86, block_write ); } /** * INT 13, 48 - Get extended parameters * - * @v drive Emulated drive + * @v int13 Emulated drive * @v ds:si Drive parameter table * @ret status Status code */ -static int int13_get_extended_parameters ( struct int13_drive *drive, +static int int13_get_extended_parameters ( struct int13_drive *int13, struct i386_all_regs *ix86 ) { struct int13_disk_parameters params = { .bufsize = sizeof ( params ), .flags = INT13_FL_DMA_TRANSPARENT, - .cylinders = drive->cylinders, - .heads = drive->heads, - .sectors_per_track = drive->sectors_per_track, - .sectors = drive->blockdev->blocks, - .sector_size = drive->blockdev->blksize, + .cylinders = int13->cylinders, + .heads = int13->heads, + .sectors_per_track = int13->sectors_per_track, + .sectors = int13->capacity.blocks, + .sector_size = int13->capacity.blksize, }; - DBG ( "Get extended drive parameters to %04x:%04x\n", - ix86->segs.ds, ix86->regs.si ); + DBGC2 ( int13, "Get extended drive parameters to %04x:%04x\n", + ix86->segs.ds, ix86->regs.si ); copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms, sizeof ( params ) ); @@ -379,72 +763,74 @@ static int int13_get_extended_parameters ( struct int13_drive *drive, static __asmcall void int13 ( struct i386_all_regs *ix86 ) { int command = ix86->regs.ah; unsigned int bios_drive = ix86->regs.dl; - struct int13_drive *drive; + struct int13_drive *int13; int status; /* Check BIOS hasn't killed off our drive */ int13_check_num_drives(); - list_for_each_entry ( drive, &drives, list ) { + list_for_each_entry ( int13, &int13s, list ) { - if ( bios_drive != drive->drive ) { + if ( bios_drive != int13->drive ) { /* Remap any accesses to this drive's natural number */ - if ( bios_drive == drive->natural_drive ) { - DBG ( "INT 13,%04x (%02x) remapped to " - "(%02x)\n", ix86->regs.ax, - bios_drive, drive->drive ); - ix86->regs.dl = drive->drive; + if ( bios_drive == int13->natural_drive ) { + DBGC2 ( int13, "INT13,%02x (%02x) remapped to " + "(%02x)\n", ix86->regs.ah, + bios_drive, int13->drive ); + ix86->regs.dl = int13->drive; return; } continue; } - DBG ( "INT 13,%04x (%02x): ", ix86->regs.ax, drive->drive ); + DBGC2 ( int13, "INT13,%02x (%02x): ", + ix86->regs.ah, int13->drive ); switch ( command ) { case INT13_RESET: - status = int13_reset ( drive, ix86 ); + status = int13_reset ( int13, ix86 ); break; case INT13_GET_LAST_STATUS: - status = int13_get_last_status ( drive, ix86 ); + status = int13_get_last_status ( int13, ix86 ); break; case INT13_READ_SECTORS: - status = int13_read_sectors ( drive, ix86 ); + status = int13_read_sectors ( int13, ix86 ); break; case INT13_WRITE_SECTORS: - status = int13_write_sectors ( drive, ix86 ); + status = int13_write_sectors ( int13, ix86 ); break; case INT13_GET_PARAMETERS: - status = int13_get_parameters ( drive, ix86 ); + status = int13_get_parameters ( int13, ix86 ); break; case INT13_GET_DISK_TYPE: - status = int13_get_disk_type ( drive, ix86 ); + status = int13_get_disk_type ( int13, ix86 ); break; case INT13_EXTENSION_CHECK: - status = int13_extension_check ( drive, ix86 ); + status = int13_extension_check ( int13, ix86 ); break; case INT13_EXTENDED_READ: - status = int13_extended_read ( drive, ix86 ); + status = int13_extended_read ( int13, ix86 ); break; case INT13_EXTENDED_WRITE: - status = int13_extended_write ( drive, ix86 ); + status = int13_extended_write ( int13, ix86 ); break; case INT13_GET_EXTENDED_PARAMETERS: - status = int13_get_extended_parameters ( drive, ix86 ); + status = int13_get_extended_parameters ( int13, ix86 ); break; default: - DBG ( "*** Unrecognised INT 13 ***\n" ); + DBGC2 ( int13, "*** Unrecognised INT13 ***\n" ); status = -INT13_STATUS_INVALID; break; } /* Store status for INT 13,01 */ - drive->last_status = status; + int13->last_status = status; /* Negative status indicates an error */ if ( status < 0 ) { status = -status; - DBG ( "INT 13 returning failure status %x\n", status ); + DBGC ( int13, "INT13,%02x (%02x) failed with status " + "%02x\n", ix86->regs.ah, int13->drive, status ); } else { ix86->flags &= ~CF; } @@ -461,7 +847,7 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) { * Hook INT 13 handler * */ -static void hook_int13 ( void ) { +static void int13_hook_vector ( void ) { /* Assembly wrapper to call int13(). int13() sets OF if we * should not chain to the previous handler. (The wrapper * clears CF and OF before calling int13()). @@ -517,136 +903,188 @@ static void hook_int13 ( void ) { /** * Unhook INT 13 handler */ -static void unhook_int13 ( void ) { +static void int13_unhook_vector ( void ) { unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper, &int13_vector ); } /** - * Guess INT 13 drive geometry + * Handle INT 13 emulated drive underlying block device closing * - * @v drive Emulated drive - * - * Guesses the drive geometry by inspecting the partition table. + * @v int13 Emulated drive + * @v rc Reason for close */ -static void guess_int13_geometry ( struct int13_drive *drive ) { - struct master_boot_record mbr; - struct partition_table_entry *partition; - unsigned int guessed_heads = 255; - unsigned int guessed_sectors_per_track = 63; - unsigned long blocks; - unsigned long blocks_per_cyl; - unsigned int i; +static void int13_block_close ( struct int13_drive *int13, int rc ) { - /* Don't even try when the blksize is invalid for C/H/S access */ - if ( drive->blockdev->blksize != INT13_BLKSIZE ) - return; + /* Any closing is an error from our point of view */ + if ( rc == 0 ) + rc = -ENOTCONN; - /* Scan through partition table and modify guesses for heads - * and sectors_per_track if we find any used partitions. + DBGC ( int13, "INT13 drive %02x went away: %s\n", + int13->drive, strerror ( rc ) ); + + /* Record block device error code */ + int13->block_rc = rc; + + /* Shut down interfaces */ + intf_restart ( &int13->block, rc ); + + /* Further INT 13 calls will fail immediately. The caller may + * use INT 13,00 to reset the drive. */ - if ( drive->blockdev->op->read ( drive->blockdev, 0, 1, - virt_to_user ( &mbr ) ) == 0 ) { - for ( i = 0 ; i < 4 ; i++ ) { - partition = &mbr.partitions[i]; - if ( ! partition->type ) - continue; - guessed_heads = - ( PART_HEAD ( partition->chs_end ) + 1 ); - guessed_sectors_per_track = - PART_SECTOR ( partition->chs_end ); - DBG ( "Guessing C/H/S xx/%d/%d based on partition " - "%d\n", guessed_heads, - guessed_sectors_per_track, ( i + 1 ) ); - } - } else { - DBG ( "Could not read partition table to guess geometry\n" ); - } +} - /* Apply guesses if no geometry already specified */ - if ( ! drive->heads ) - drive->heads = guessed_heads; - if ( ! drive->sectors_per_track ) - drive->sectors_per_track = guessed_sectors_per_track; - if ( ! drive->cylinders ) { - /* Avoid attempting a 64-bit divide on a 32-bit system */ - blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ? - drive->blockdev->blocks : ULONG_MAX ); - blocks_per_cyl = ( drive->heads * drive->sectors_per_track ); - assert ( blocks_per_cyl != 0 ); - drive->cylinders = ( blocks / blocks_per_cyl ); - if ( drive->cylinders > 1024 ) - drive->cylinders = 1024; - } +/** INT 13 drive interface operations */ +static struct interface_operation int13_block_op[] = { + INTF_OP ( intf_close, struct int13_drive *, int13_block_close ), +}; + +/** INT 13 drive interface descriptor */ +static struct interface_descriptor int13_block_desc = + INTF_DESC ( struct int13_drive, block, int13_block_op ); + +/** + * Free INT 13 emulated drive + * + * @v refcnt Reference count + */ +static void int13_free ( struct refcnt *refcnt ) { + struct int13_drive *int13 = + container_of ( refcnt, struct int13_drive, refcnt ); + + uri_put ( int13->uri ); + free ( int13 ); } /** - * Register INT 13 emulated drive + * Hook INT 13 emulated drive * - * @v drive Emulated drive + * @v uri URI + * @v drive Requested drive number + * @ret drive Assigned drive number, or negative error * * Registers the drive with the INT 13 emulation subsystem, and hooks * the INT 13 interrupt vector (if not already hooked). - * - * The underlying block device must be valid. A drive number and - * geometry will be assigned if left blank. */ -void register_int13_drive ( struct int13_drive *drive ) { +static int int13_hook ( struct uri *uri, unsigned int drive ) { + struct int13_drive *int13; uint8_t num_drives; + unsigned int natural_drive; + int rc; - /* Give drive a default geometry if none specified */ - guess_int13_geometry ( drive ); - - /* Assign natural drive number */ + /* Calculate drive number */ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); - drive->natural_drive = ( num_drives | 0x80 ); + natural_drive = ( num_drives | 0x80 ); + if ( drive == INT13_USE_NATURAL_DRIVE ) + drive = natural_drive; + drive |= 0x80; - /* Assign drive number */ - if ( ( drive->drive & 0xff ) == 0xff ) { - /* Drive number == -1 => use natural drive number */ - drive->drive = drive->natural_drive; - } else { - /* Use specified drive number (+0x80 if necessary) */ - drive->drive |= 0x80; + /* Check that drive number is not in use */ + list_for_each_entry ( int13, &int13s, list ) { + if ( int13->drive == drive ) { + rc = -EADDRINUSE; + goto err_in_use; + } } - DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S " - "geometry %d/%d/%d\n", drive->drive, drive->natural_drive, - drive->cylinders, drive->heads, drive->sectors_per_track ); + /* Allocate and initialise structure */ + int13 = zalloc ( sizeof ( *int13 ) ); + if ( ! int13 ) { + rc = -ENOMEM; + goto err_zalloc; + } + ref_init ( &int13->refcnt, int13_free ); + intf_init ( &int13->block, &int13_block_desc, &int13->refcnt ); + int13->uri = uri_get ( uri ); + int13->drive = drive; + int13->natural_drive = natural_drive; + + /* Open block device interface */ + if ( ( rc = int13_reopen_block ( int13 ) ) != 0 ) + goto err_reopen_block; + + /* Give drive a default geometry */ + if ( ( rc = int13_guess_geometry ( int13 ) ) != 0 ) + goto err_guess_geometry; + + DBGC ( int13, "INT13 drive %02x (naturally %02x) registered with C/H/S " + "geometry %d/%d/%d\n", int13->drive, int13->natural_drive, + int13->cylinders, int13->heads, int13->sectors_per_track ); /* Hook INT 13 vector if not already hooked */ - if ( list_empty ( &drives ) ) - hook_int13(); + if ( list_empty ( &int13s ) ) + int13_hook_vector(); /* Add to list of emulated drives */ - list_add ( &drive->list, &drives ); + list_add ( &int13->list, &int13s ); /* Update BIOS drive count */ int13_set_num_drives(); + + return int13->drive; + + err_guess_geometry: + err_reopen_block: + intf_shutdown ( &int13->block, rc ); + ref_put ( &int13->refcnt ); + err_zalloc: + err_in_use: + return rc; } /** - * Unregister INT 13 emulated drive + * Find INT 13 emulated drive by drive number * - * @v drive Emulated drive + * @v drive Drive number + * @ret int13 Emulated drive, or NULL + */ +static struct int13_drive * int13_find ( unsigned int drive ) { + struct int13_drive *int13; + + list_for_each_entry ( int13, &int13s, list ) { + if ( int13->drive == drive ) + return int13; + } + return NULL; +} + +/** + * Unhook INT 13 emulated drive + * + * @v drive Drive number * * Unregisters the drive from the INT 13 emulation subsystem. If this * is the last emulated drive, the INT 13 vector is unhooked (if * possible). */ -void unregister_int13_drive ( struct int13_drive *drive ) { - /* Remove from list of emulated drives */ - list_del ( &drive->list ); +static void int13_unhook ( unsigned int drive ) { + struct int13_drive *int13; - /* Should adjust BIOS drive count, but it's difficult to do so - * reliably. + /* Find drive */ + int13 = int13_find ( drive ); + if ( ! int13 ) { + DBG ( "INT13 cannot find emulated drive %02x\n", drive ); + return; + } + + /* Shut down interfaces */ + intf_shutdown ( &int13->block, 0 ); + + /* Remove from list of emulated drives */ + list_del ( &int13->list ); + + /* Should adjust BIOS drive count, but it's difficult + * to do so reliably. */ - DBG ( "Unregistered INT13 drive %02x\n", drive->drive ); + DBGC ( int13, "INT13 drive %02x unregsitered\n", int13->drive ); /* Unhook INT 13 vector if no more drives */ - if ( list_empty ( &drives ) ) - unhook_int13(); + if ( list_empty ( &int13s ) ) + int13_unhook_vector(); + + /* Drop list's reference to drive */ + ref_put ( &int13->refcnt ); } /** @@ -662,13 +1100,13 @@ void unregister_int13_drive ( struct int13_drive *drive ) { * * Note that this function can never return success, by definition. */ -int int13_boot ( unsigned int drive ) { +static int int13_boot ( unsigned int drive ) { struct memory_map memmap; int status, signature; int discard_c, discard_d; int rc; - DBG ( "Booting from INT 13 drive %02x\n", drive ); + DBG ( "INT13 drive %02x booting\n", drive ); /* Use INT 13 to read the boot sector */ __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t" @@ -692,8 +1130,8 @@ int int13_boot ( unsigned int drive ) { /* Check signature is correct */ if ( signature != be16_to_cpu ( 0x55aa ) ) { - DBG ( "Invalid disk signature %#04x (should be 0x55aa)\n", - cpu_to_be16 ( signature ) ); + DBG ( "INT13 drive %02x invalid disk signature %#04x (should " + "be 0x55aa)\n", drive, cpu_to_be16 ( signature ) ); return -ENOEXEC; } @@ -706,10 +1144,74 @@ int int13_boot ( unsigned int drive ) { /* Jump to boot sector */ if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) { - DBG ( "INT 13 drive %02x boot returned: %s\n", + DBG ( "INT13 drive %02x boot returned: %s\n", drive, strerror ( rc ) ); return rc; } return -ECANCELED; /* -EIMPOSSIBLE */ } + +/** A boot firmware table generated by iPXE */ +union xbft_table { + /** ACPI header */ + struct acpi_description_header acpi; + /** Padding */ + char pad[768]; +}; + +/** The boot firmware table generated by iPXE */ +static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) )); +#define xbftab __use_data16 ( xbftab ) + +/** + * Describe INT 13 emulated drive for SAN-booted operating system + * + * @v drive Drive number + * @ret rc Return status code + */ +static int int13_describe ( unsigned int drive ) { + struct int13_drive *int13; + struct segoff xbft_address; + int rc; + + /* Find drive */ + int13 = int13_find ( drive ); + if ( ! int13 ) { + DBG ( "INT13 cannot find emulated drive %02x\n", drive ); + return -ENODEV; + } + + /* Clear table */ + memset ( &xbftab, 0, sizeof ( xbftab ) ); + + /* Fill in common parameters */ + strncpy ( xbftab.acpi.oem_id, "FENSYS", + sizeof ( xbftab.acpi.oem_id ) ); + strncpy ( xbftab.acpi.oem_table_id, "iPXE", + sizeof ( xbftab.acpi.oem_table_id ) ); + + /* Fill in remaining parameters */ + if ( ( rc = acpi_describe ( &int13->block, &xbftab.acpi, + sizeof ( xbftab ) ) ) != 0 ) { + DBGC ( int13, "INT13 drive %02x could not create ACPI " + "description: %s\n", int13->drive, strerror ( rc ) ); + return rc; + } + + /* Fix up ACPI checksum */ + acpi_fix_checksum ( &xbftab.acpi ); + xbft_address.segment = rm_ds; + xbft_address.offset = __from_data16 ( &xbftab ); + DBGC ( int13, "INT13 drive %02x described using boot firmware " + "table:\n", int13->drive ); + DBGC_HDA ( int13, xbft_address, &xbftab, + le32_to_cpu ( xbftab.acpi.length ) ); + + return 0; +} + +PROVIDE_SANBOOT ( pcbios, san_hook, int13_hook ); +PROVIDE_SANBOOT ( pcbios, san_unhook, int13_unhook ); +PROVIDE_SANBOOT ( pcbios, san_boot, int13_boot ); +PROVIDE_SANBOOT ( pcbios, san_describe, int13_describe ); diff --git a/src/arch/i386/interface/pcbios/iscsiboot.c b/src/arch/i386/interface/pcbios/iscsiboot.c deleted file mode 100644 index 12c5566d..00000000 --- a/src/arch/i386/interface/pcbios/iscsiboot.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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, -}; diff --git a/src/arch/i386/interface/pcbios/keepsan.c b/src/arch/i386/interface/pcbios/keepsan.c deleted file mode 100644 index b2a93177..00000000 --- a/src/arch/i386/interface/pcbios/keepsan.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -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; -} diff --git a/src/arch/i386/interface/pcbios/sbft.c b/src/arch/i386/interface/pcbios/sbft.c deleted file mode 100644 index 6d51020a..00000000 --- a/src/arch/i386/interface/pcbios/sbft.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2009 Fen Systems Ltd . - * 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 -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/arch/x86_64/include/bits/sanboot.h b/src/arch/x86_64/include/bits/sanboot.h new file mode 100644 index 00000000..d33d03cb --- /dev/null +++ b/src/arch/x86_64/include/bits/sanboot.h @@ -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 */ diff --git a/src/config/config.c b/src/config/config.c index fcf27f04..46df6bd2 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -133,7 +133,7 @@ REQUIRE_OBJECT ( slam ); * */ #ifdef SANBOOT_PROTO_ISCSI -REQUIRE_OBJECT ( iscsiboot ); +REQUIRE_OBJECT ( iscsi ); #endif /* diff --git a/src/config/config_ethernet.c b/src/config/config_ethernet.c index 51c197d7..f6cb87bc 100644 --- a/src/config/config_ethernet.c +++ b/src/config/config_ethernet.c @@ -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 diff --git a/src/config/config_infiniband.c b/src/config/config_infiniband.c index 1b8c53a9..432e621d 100644 --- a/src/config/config_infiniband.c +++ b/src/config/config_infiniband.c @@ -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 diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 35e5fde9..9c3f3ff3 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -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 */ diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index 901097fd..647dc0a5 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -13,6 +13,7 @@ #define UMALLOC_LINUX #define NAP_LINUX #define SMBIOS_LINUX +#define SANBOOT_NULL #define DRIVERS_LINUX diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index 246a8cd9..d2ffc3d2 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -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 */ diff --git a/src/config/sanboot.h b/src/config/sanboot.h new file mode 100644 index 00000000..1d7f5f17 --- /dev/null +++ b/src/config/sanboot.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_SANBOOT_H +#define CONFIG_SANBOOT_H + +/** @file + * + * sanboot API configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include + +#include + +#endif /* CONFIG_SANBOOT_H */ diff --git a/src/core/acpi.c b/src/core/acpi.c index d573d2e9..223765f7 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -18,7 +18,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include #include +#include /** @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; +} diff --git a/src/core/blockdev.c b/src/core/blockdev.c new file mode 100644 index 00000000..182765e3 --- /dev/null +++ b/src/core/blockdev.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * 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 +#include +#include + +/** @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 ); +} diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c new file mode 100644 index 00000000..9cdb1629 --- /dev/null +++ b/src/core/null_sanboot.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * 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 +#include + +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 ); diff --git a/src/drivers/block/ata.c b/src/drivers/block/ata.c index c59e788a..0197483b 100644 --- a/src/drivers/block/ata.c +++ b/src/drivers/block/ata.c @@ -19,12 +19,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include #include +#include +#include #include -#include #include /** @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; } diff --git a/src/arch/i386/interface/pcbios/ibft.c b/src/drivers/block/ibft.c similarity index 63% rename from src/arch/i386/interface/pcbios/ibft.c rename to src/drivers/block/ibft.c index 63dba65e..adf1d7d5 100644 --- a/src/arch/i386/interface/pcbios/ibft.c +++ b/src/drivers/block/ibft.c @@ -32,7 +32,6 @@ FILE_LICENCE ( BSD2 ); #include #include #include -#include #include #include #include @@ -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 "") */ -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; } diff --git a/src/drivers/block/ramdisk.c b/src/drivers/block/ramdisk.c deleted file mode 100644 index 55c0d2d5..00000000 --- a/src/drivers/block/ramdisk.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown . - * - * 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 -#include - -/** - * @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; -} diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index 94e283b6..d7f7b4db 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -23,8 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include -#include #include /** @file @@ -33,308 +33,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Maximum number of dummy "read capacity (10)" operations +/** Maximum number of command retries */ +#define SCSICMD_MAX_RETRIES 10 + +/****************************************************************************** * - * These are issued at connection setup to draw out various useless - * power-on messages. + * Utility functions + * + ****************************************************************************** */ -#define SCSI_MAX_DUMMY_READ_CAP 10 - -static inline __attribute__ (( always_inline )) struct scsi_device * -block_to_scsi ( struct block_device *blockdev ) { - return container_of ( blockdev, struct scsi_device, blockdev ); -} - -/** - * Handle SCSI command with no backing device - * - * @v scsi SCSI device - * @v command SCSI command - * @ret rc Return status code - */ -int scsi_detached_command ( struct scsi_device *scsi __unused, - struct scsi_command *command __unused ) { - return -ENODEV; -} - -/** - * Issue SCSI command - * - * @v scsi SCSI device - * @v command SCSI command - * @ret rc Return status code - */ -static int scsi_command ( struct scsi_device *scsi, - struct scsi_command *command ) { - int rc; - - DBGC2 ( scsi, "SCSI %p " SCSI_CDB_FORMAT "\n", - scsi, SCSI_CDB_DATA ( command->cdb ) ); - - /* Clear sense response code before issuing command */ - command->sense_response = 0; - - /* Flag command as in-progress */ - command->rc = -EINPROGRESS; - - /* Issue SCSI command */ - if ( ( rc = scsi->command ( scsi, command ) ) != 0 ) { - /* Something went wrong with the issuing mechanism */ - DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " err %s\n", - scsi, SCSI_CDB_DATA ( command->cdb ), 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 */ - DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " err %s\n", - scsi, SCSI_CDB_DATA ( command->cdb ), strerror ( rc ) ); - return rc; - } - - /* Check for SCSI errors */ - if ( command->status != 0 ) { - DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " status %02x sense " - "%02x\n", scsi, SCSI_CDB_DATA ( command->cdb ), - command->status, command->sense_response ); - return -EIO; - } - - return 0; -} - -/** - * Read block from SCSI device using READ (10) - * - * @v blockdev Block device - * @v block LBA block number - * @v count Block count - * @v buffer Data buffer - * @ret rc Return status code - */ -static int scsi_read_10 ( struct block_device *blockdev, uint64_t block, - unsigned long count, userptr_t buffer ) { - struct scsi_device *scsi = block_to_scsi ( blockdev ); - struct scsi_command command; - struct scsi_cdb_read_10 *cdb = &command.cdb.read10; - - /* Issue READ (10) */ - memset ( &command, 0, sizeof ( command ) ); - cdb->opcode = SCSI_OPCODE_READ_10; - cdb->lba = cpu_to_be32 ( block ); - cdb->len = cpu_to_be16 ( count ); - command.data_in = buffer; - command.data_in_len = ( count * blockdev->blksize ); - return scsi_command ( scsi, &command ); -} - -/** - * Read block from SCSI device using READ (16) - * - * @v blockdev Block device - * @v block LBA block number - * @v count Block count - * @v buffer Data buffer - * @ret rc Return status code - */ -static int scsi_read_16 ( struct block_device *blockdev, uint64_t block, - unsigned long count, userptr_t buffer ) { - struct scsi_device *scsi = block_to_scsi ( blockdev ); - struct scsi_command command; - struct scsi_cdb_read_16 *cdb = &command.cdb.read16; - - /* Issue READ (16) */ - memset ( &command, 0, sizeof ( command ) ); - cdb->opcode = SCSI_OPCODE_READ_16; - cdb->lba = cpu_to_be64 ( block ); - cdb->len = cpu_to_be32 ( count ); - command.data_in = buffer; - command.data_in_len = ( count * blockdev->blksize ); - return scsi_command ( scsi, &command ); -} - -/** - * Write block to SCSI device using WRITE (10) - * - * @v blockdev Block device - * @v block LBA block number - * @v count Block count - * @v buffer Data buffer - * @ret rc Return status code - */ -static int scsi_write_10 ( struct block_device *blockdev, uint64_t block, - unsigned long count, userptr_t buffer ) { - struct scsi_device *scsi = block_to_scsi ( blockdev ); - struct scsi_command command; - struct scsi_cdb_write_10 *cdb = &command.cdb.write10; - - /* Issue WRITE (10) */ - memset ( &command, 0, sizeof ( command ) ); - cdb->opcode = SCSI_OPCODE_WRITE_10; - cdb->lba = cpu_to_be32 ( block ); - cdb->len = cpu_to_be16 ( count ); - command.data_out = buffer; - command.data_out_len = ( count * blockdev->blksize ); - return scsi_command ( scsi, &command ); -} - -/** - * Write block to SCSI device using WRITE (16) - * - * @v blockdev Block device - * @v block LBA block number - * @v count Block count - * @v buffer Data buffer - * @ret rc Return status code - */ -static int scsi_write_16 ( struct block_device *blockdev, uint64_t block, - unsigned long count, userptr_t buffer ) { - struct scsi_device *scsi = block_to_scsi ( blockdev ); - struct scsi_command command; - struct scsi_cdb_write_16 *cdb = &command.cdb.write16; - - /* Issue WRITE (16) */ - memset ( &command, 0, sizeof ( command ) ); - cdb->opcode = SCSI_OPCODE_WRITE_16; - cdb->lba = cpu_to_be64 ( block ); - cdb->len = cpu_to_be32 ( count ); - command.data_out = buffer; - command.data_out_len = ( count * blockdev->blksize ); - return scsi_command ( scsi, &command ); -} - -/** - * Read capacity of SCSI device via READ CAPACITY (10) - * - * @v blockdev Block device - * @ret rc Return status code - */ -static int scsi_read_capacity_10 ( struct block_device *blockdev ) { - struct scsi_device *scsi = block_to_scsi ( blockdev ); - struct scsi_command command; - struct scsi_cdb_read_capacity_10 *cdb = &command.cdb.readcap10; - struct scsi_capacity_10 capacity; - int rc; - - /* Issue READ CAPACITY (10) */ - memset ( &command, 0, sizeof ( command ) ); - cdb->opcode = SCSI_OPCODE_READ_CAPACITY_10; - command.data_in = virt_to_user ( &capacity ); - command.data_in_len = sizeof ( capacity ); - - if ( ( rc = scsi_command ( scsi, &command ) ) != 0 ) - return rc; - - /* Fill in block device fields */ - blockdev->blksize = be32_to_cpu ( capacity.blksize ); - blockdev->blocks = ( be32_to_cpu ( capacity.lba ) + 1 ); - - return 0; -} - -/** - * Read capacity of SCSI device via READ CAPACITY (16) - * - * @v blockdev Block device - * @ret rc Return status code - */ -static int scsi_read_capacity_16 ( struct block_device *blockdev ) { - struct scsi_device *scsi = block_to_scsi ( blockdev ); - struct scsi_command command; - struct scsi_cdb_read_capacity_16 *cdb = &command.cdb.readcap16; - struct scsi_capacity_16 capacity; - int rc; - - /* Issue READ CAPACITY (16) */ - memset ( &command, 0, sizeof ( command ) ); - cdb->opcode = SCSI_OPCODE_SERVICE_ACTION_IN; - cdb->service_action = SCSI_SERVICE_ACTION_READ_CAPACITY_16; - cdb->len = cpu_to_be32 ( sizeof ( capacity ) ); - command.data_in = virt_to_user ( &capacity ); - command.data_in_len = sizeof ( capacity ); - - if ( ( rc = scsi_command ( scsi, &command ) ) != 0 ) - return rc; - - /* Fill in block device fields */ - blockdev->blksize = be32_to_cpu ( capacity.blksize ); - blockdev->blocks = ( be64_to_cpu ( capacity.lba ) + 1 ); - return 0; -} - -static struct block_device_operations scsi_operations_16 = { - .read = scsi_read_16, - .write = scsi_write_16, -}; - -static struct block_device_operations scsi_operations_10 = { - .read = scsi_read_10, - .write = scsi_write_10, -}; - -/** - * Initialise SCSI device - * - * @v scsi SCSI device - * @ret rc Return status code - * - * Initialises a SCSI device. The scsi_device::command and - * scsi_device::lun fields must already be filled in. This function - * will configure scsi_device::blockdev, including issuing a READ - * CAPACITY call to determine the block size and total device size. - */ -int init_scsidev ( struct scsi_device *scsi ) { - unsigned int i; - int rc; - - /* Issue some theoretically extraneous READ CAPACITY (10) - * commands, solely in order to draw out the "CHECK CONDITION - * (power-on occurred)", "CHECK CONDITION (reported LUNs data - * has changed)" etc. that some dumb targets insist on sending - * as an error at start of day. The precise command that we - * use is unimportant; we just need to provide the target with - * an opportunity to send its responses. - */ - for ( i = 0 ; i < SCSI_MAX_DUMMY_READ_CAP ; i++ ) { - if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) == 0 ) - break; - DBGC ( scsi, "SCSI %p ignoring start-of-day error (#%d)\n", - scsi, ( i + 1 ) ); - } - - /* Try READ CAPACITY (10), which is a mandatory command, first. */ - scsi->blockdev.op = &scsi_operations_10; - if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) != 0 ) { - DBGC ( scsi, "SCSI %p could not READ CAPACITY (10): %s\n", - scsi, strerror ( rc ) ); - return rc; - } - - /* If capacity range was exceeded (i.e. capacity.lba was - * 0xffffffff, meaning that blockdev->blocks is now zero), use - * READ CAPACITY (16) instead. READ CAPACITY (16) is not - * mandatory, so we can't just use it straight off. - */ - if ( scsi->blockdev.blocks == 0 ) { - scsi->blockdev.op = &scsi_operations_16; - if ( ( rc = scsi_read_capacity_16 ( &scsi->blockdev ) ) != 0 ){ - DBGC ( scsi, "SCSI %p could not READ CAPACITY (16): " - "%s\n", scsi, strerror ( rc ) ); - return rc; - } - } - - DBGC ( scsi, "SCSI %p using READ/WRITE (%d) commands\n", scsi, - ( ( scsi->blockdev.op == &scsi_operations_10 ) ? 10 : 16 ) ); - DBGC ( scsi, "SCSI %p capacity is %ld MB (%#llx blocks)\n", scsi, - ( ( unsigned long ) ( scsi->blockdev.blocks >> 11 ) ), - scsi->blockdev.blocks ); - - return 0; -} /** * Parse SCSI LUN @@ -364,3 +71,700 @@ int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) { return 0; } + +/****************************************************************************** + * + * Interface methods + * + ****************************************************************************** + */ + +/** + * Issue SCSI command + * + * @v control SCSI control interface + * @v data SCSI data interface + * @v command SCSI command + * @ret tag Command tag, or negative error + */ +int scsi_command ( struct interface *control, struct interface *data, + struct scsi_cmd *command ) { + struct interface *dest; + scsi_command_TYPE ( void * ) *op = + intf_get_dest_op ( control, scsi_command, &dest ); + void *object = intf_object ( dest ); + int tap; + + if ( op ) { + tap = op ( object, data, command ); + } else { + /* Default is to fail to issue the command */ + tap = -EOPNOTSUPP; + } + + intf_put ( dest ); + return tap; +} + +/** + * Report SCSI response + * + * @v interface SCSI command interface + * @v response SCSI response + */ +void scsi_response ( struct interface *intf, struct scsi_rsp *response ) { + struct interface *dest; + scsi_response_TYPE ( void * ) *op = + intf_get_dest_op ( intf, scsi_response, &dest ); + void *object = intf_object ( dest ); + + if ( op ) { + op ( object, response ); + } else { + /* Default is to ignore the response */ + } + + intf_put ( dest ); +} + +/****************************************************************************** + * + * SCSI devices and commands + * + ****************************************************************************** + */ + +/** A SCSI device */ +struct scsi_device { + /** Reference count */ + struct refcnt refcnt; + /** Block control interface */ + struct interface block; + /** SCSI control interface */ + struct interface scsi; + + /** SCSI LUN */ + struct scsi_lun lun; + + /** List of commands */ + struct list_head cmds; +}; + +/** A SCSI command */ +struct scsi_command { + /** Reference count */ + struct refcnt refcnt; + /** SCSI device */ + struct scsi_device *scsidev; + /** List of SCSI commands */ + struct list_head list; + + /** Block data interface */ + struct interface block; + /** SCSI data interface */ + struct interface scsi; + + /** Command type */ + struct scsi_command_type *type; + /** Starting logical block address */ + uint64_t lba; + /** Number of blocks */ + unsigned int count; + /** Data buffer */ + userptr_t buffer; + /** Length of data buffer */ + size_t len; + /** Command tag */ + uint32_t tag; + + /** Retry count */ + unsigned int retries; + + /** Private data */ + uint8_t priv[0]; +}; + +/** A SCSI command type */ +struct scsi_command_type { + /** Name */ + const char *name; + /** Additional working space */ + size_t priv_len; + /** + * Construct SCSI command IU + * + * @v scsicmd SCSI command + * @v command SCSI command IU + */ + void ( * cmd ) ( struct scsi_command *scsicmd, + struct scsi_cmd *command ); + /** + * Handle SCSI command completion + * + * @v scsicmd SCSI command + * @v rc Reason for completion + */ + void ( * done ) ( struct scsi_command *scsicmd, int rc ); +}; + +/** + * Get reference to SCSI device + * + * @v scsidev SCSI device + * @ret scsidev SCSI device + */ +static inline __attribute__ (( always_inline )) struct scsi_device * +scsidev_get ( struct scsi_device *scsidev ) { + ref_get ( &scsidev->refcnt ); + return scsidev; +} + +/** + * Drop reference to SCSI device + * + * @v scsidev SCSI device + */ +static inline __attribute__ (( always_inline )) void +scsidev_put ( struct scsi_device *scsidev ) { + ref_put ( &scsidev->refcnt ); +} + +/** + * Get reference to SCSI command + * + * @v scsicmd SCSI command + * @ret scsicmd SCSI command + */ +static inline __attribute__ (( always_inline )) struct scsi_command * +scsicmd_get ( struct scsi_command *scsicmd ) { + ref_get ( &scsicmd->refcnt ); + return scsicmd; +} + +/** + * Drop reference to SCSI command + * + * @v scsicmd SCSI command + */ +static inline __attribute__ (( always_inline )) void +scsicmd_put ( struct scsi_command *scsicmd ) { + ref_put ( &scsicmd->refcnt ); +} + +/** + * Get SCSI command private data + * + * @v scsicmd SCSI command + * @ret priv Private data + */ +static inline __attribute__ (( always_inline )) void * +scsicmd_priv ( struct scsi_command *scsicmd ) { + return scsicmd->priv; +} + +/** + * Free SCSI command + * + * @v refcnt Reference count + */ +static void scsicmd_free ( struct refcnt *refcnt ) { + struct scsi_command *scsicmd = + container_of ( refcnt, struct scsi_command, refcnt ); + + /* Remove from list of commands */ + list_del ( &scsicmd->list ); + scsidev_put ( scsicmd->scsidev ); + + /* Free command */ + free ( scsicmd ); +} + +/** + * Close SCSI command + * + * @v scsicmd SCSI command + * @v rc Reason for close + */ +static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) { + struct scsi_device *scsidev = scsicmd->scsidev; + + if ( rc != 0 ) { + DBGC ( scsidev, "SCSI %p tag %08x closed: %s\n", + scsidev, scsicmd->tag, strerror ( rc ) ); + } + + /* Shut down interfaces */ + intf_shutdown ( &scsicmd->scsi, rc ); + intf_shutdown ( &scsicmd->block, rc ); +} + +/** + * Construct and issue SCSI command + * + * @ret rc Return status code + */ +static int scsicmd_command ( struct scsi_command *scsicmd ) { + struct scsi_device *scsidev = scsicmd->scsidev; + struct scsi_cmd command; + int tag; + int rc; + + /* Construct command */ + memset ( &command, 0, sizeof ( command ) ); + memcpy ( &command.lun, &scsidev->lun, sizeof ( command.lun ) ); + scsicmd->type->cmd ( scsicmd, &command ); + + /* Issue command */ + if ( ( tag = scsi_command ( &scsidev->scsi, &scsicmd->scsi, + &command ) ) < 0 ) { + rc = tag; + DBGC ( scsidev, "SCSI %p could not issue command: %s\n", + scsidev, strerror ( rc ) ); + return rc; + } + + /* Record tag */ + if ( scsicmd->tag ) { + DBGC ( scsidev, "SCSI %p tag %08x is now tag %08x\n", + scsidev, scsicmd->tag, tag ); + } + scsicmd->tag = tag; + DBGC2 ( scsidev, "SCSI %p tag %08x %s " SCSI_CDB_FORMAT "\n", + scsidev, scsicmd->tag, scsicmd->type->name, + SCSI_CDB_DATA ( command.cdb ) ); + + return 0; +} + +/** + * Handle SCSI command completion + * + * @v scsicmd SCSI command + * @v rc Reason for close + */ +static void scsicmd_done ( struct scsi_command *scsicmd, int rc ) { + struct scsi_device *scsidev = scsicmd->scsidev; + + /* Restart SCSI interface */ + intf_restart ( &scsicmd->scsi, rc ); + + /* SCSI targets have an annoying habit of returning occasional + * pointless "error" messages such as "power-on occurred", so + * we have to be prepared to retry commands. + */ + if ( ( rc != 0 ) && ( scsicmd->retries++ < SCSICMD_MAX_RETRIES ) ) { + /* Retry command */ + DBGC ( scsidev, "SCSI %p tag %08x failed: %s\n", + scsidev, scsicmd->tag, strerror ( rc ) ); + DBGC ( scsidev, "SCSI %p tag %08x retrying (retry %d)\n", + scsidev, scsicmd->tag, scsicmd->retries ); + if ( ( rc = scsicmd_command ( scsicmd ) ) == 0 ) + return; + } + + /* If we didn't (successfully) reissue the command, hand over + * to the command completion handler. + */ + scsicmd->type->done ( scsicmd, rc ); +} + +/** + * Handle SCSI response + * + * @v scsicmd SCSI command + * @v response SCSI response + */ +static void scsicmd_response ( struct scsi_command *scsicmd, + struct scsi_rsp *response ) { + struct scsi_device *scsidev = scsicmd->scsidev; + size_t overrun; + size_t underrun; + + if ( response->status == 0 ) { + scsicmd_done ( scsicmd, 0 ); + } else { + DBGC ( scsidev, "SCSI %p tag %08x status %02x", + scsidev, scsicmd->tag, response->status ); + if ( response->overrun > 0 ) { + overrun = response->overrun; + DBGC ( scsidev, " overrun +%zd", overrun ); + } else if ( response->overrun < 0 ) { + underrun = -(response->overrun); + DBGC ( scsidev, " underrun -%zd", underrun ); + } + DBGC ( scsidev, " sense %02x:%02x:%08x\n", + response->sense.code, response->sense.key, + ntohl ( response->sense.info ) ); + scsicmd_done ( scsicmd, -EIO ); + } +} + +/** + * Construct SCSI READ command + * + * @v scsicmd SCSI command + * @v command SCSI command IU + */ +static void scsicmd_read_cmd ( struct scsi_command *scsicmd, + struct scsi_cmd *command ) { + + if ( ( scsicmd->lba + scsicmd->count ) > SCSI_MAX_BLOCK_10 ) { + /* Use READ (16) */ + command->cdb.read16.opcode = SCSI_OPCODE_READ_16; + command->cdb.read16.lba = cpu_to_be64 ( scsicmd->lba ); + command->cdb.read16.len = cpu_to_be32 ( scsicmd->count ); + } else { + /* Use READ (10) */ + command->cdb.read10.opcode = SCSI_OPCODE_READ_10; + command->cdb.read10.lba = cpu_to_be32 ( scsicmd->lba ); + command->cdb.read10.len = cpu_to_be16 ( scsicmd->count ); + } + command->data_in = scsicmd->buffer; + command->data_in_len = scsicmd->len; +} + +/** SCSI READ command type */ +static struct scsi_command_type scsicmd_read = { + .name = "READ", + .cmd = scsicmd_read_cmd, + .done = scsicmd_close, +}; + +/** + * Construct SCSI WRITE command + * + * @v scsicmd SCSI command + * @v command SCSI command IU + */ +static void scsicmd_write_cmd ( struct scsi_command *scsicmd, + struct scsi_cmd *command ) { + + if ( ( scsicmd->lba + scsicmd->count ) > SCSI_MAX_BLOCK_10 ) { + /* Use WRITE (16) */ + command->cdb.write16.opcode = SCSI_OPCODE_WRITE_16; + command->cdb.write16.lba = cpu_to_be64 ( scsicmd->lba ); + command->cdb.write16.len = cpu_to_be32 ( scsicmd->count ); + } else { + /* Use WRITE (10) */ + command->cdb.write10.opcode = SCSI_OPCODE_WRITE_10; + command->cdb.write10.lba = cpu_to_be32 ( scsicmd->lba ); + command->cdb.write10.len = cpu_to_be16 ( scsicmd->count ); + } + command->data_out = scsicmd->buffer; + command->data_out_len = scsicmd->len; +} + +/** SCSI WRITE command type */ +static struct scsi_command_type scsicmd_write = { + .name = "WRITE", + .cmd = scsicmd_write_cmd, + .done = scsicmd_close, +}; + +/** SCSI READ CAPACITY private data */ +struct scsi_read_capacity_private { + /** Use READ CAPACITY (16) */ + int use16; + /** Data buffer for READ CAPACITY commands */ + union { + /** Data buffer for READ CAPACITY (10) */ + struct scsi_capacity_10 capacity10; + /** Data buffer for READ CAPACITY (16) */ + struct scsi_capacity_16 capacity16; + } capacity; +}; + +/** + * Construct SCSI READ CAPACITY command + * + * @v scsicmd SCSI command + * @v command SCSI command IU + */ +static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd, + struct scsi_cmd *command ) { + struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd ); + struct scsi_cdb_read_capacity_16 *readcap16 = &command->cdb.readcap16; + struct scsi_cdb_read_capacity_10 *readcap10 = &command->cdb.readcap10; + struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16; + struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10; + + if ( priv->use16 ) { + /* Use READ CAPACITY (16) */ + readcap16->opcode = SCSI_OPCODE_SERVICE_ACTION_IN; + readcap16->service_action = + SCSI_SERVICE_ACTION_READ_CAPACITY_16; + readcap16->len = cpu_to_be32 ( sizeof ( *capacity16 ) ); + command->data_in = virt_to_user ( capacity16 ); + command->data_in_len = sizeof ( *capacity16 ); + } else { + /* Use READ CAPACITY (10) */ + readcap10->opcode = SCSI_OPCODE_READ_CAPACITY_10; + command->data_in = virt_to_user ( capacity10 ); + command->data_in_len = sizeof ( *capacity10 ); + } +} + +/** + * Handle SCSI READ CAPACITY command completion + * + * @v scsicmd SCSI command + * @v rc Reason for completion + */ +static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd, + int rc ) { + struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd ); + struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16; + struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10; + struct block_device_capacity capacity; + + /* Close if command failed */ + if ( rc != 0 ) { + scsicmd_close ( scsicmd, rc ); + return; + } + + /* Extract capacity */ + if ( priv->use16 ) { + capacity.blocks = ( be64_to_cpu ( capacity16->lba ) + 1 ); + capacity.blksize = be32_to_cpu ( capacity16->blksize ); + } else { + capacity.blocks = ( be32_to_cpu ( capacity10->lba ) + 1 ); + capacity.blksize = be32_to_cpu ( capacity10->blksize ); + + /* If capacity range was exceeded (i.e. capacity.lba + * was 0xffffffff, meaning that blockdev->blocks is + * now zero), use READ CAPACITY (16) instead. READ + * CAPACITY (16) is not mandatory, so we can't just + * use it straight off. + */ + if ( capacity.blocks == 0 ) { + priv->use16 = 1; + if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 ) { + scsicmd_close ( scsicmd, rc ); + return; + } + return; + } + } + capacity.max_count = -1U; + + /* Return capacity to caller */ + block_capacity ( &scsicmd->block, &capacity ); + + /* Close command */ + scsicmd_close ( scsicmd, 0 ); +} + +/** SCSI READ CAPACITY command type */ +static struct scsi_command_type scsicmd_read_capacity = { + .name = "READ CAPACITY", + .priv_len = sizeof ( struct scsi_read_capacity_private ), + .cmd = scsicmd_read_capacity_cmd, + .done = scsicmd_read_capacity_done, +}; + +/** SCSI command block interface operations */ +static struct interface_operation scsicmd_block_op[] = { + INTF_OP ( intf_close, struct scsi_command *, scsicmd_close ), +}; + +/** SCSI command block interface descriptor */ +static struct interface_descriptor scsicmd_block_desc = + INTF_DESC_PASSTHRU ( struct scsi_command, block, + scsicmd_block_op, scsi ); + +/** SCSI command SCSI interface operations */ +static struct interface_operation scsicmd_scsi_op[] = { + INTF_OP ( intf_close, struct scsi_command *, scsicmd_done ), + INTF_OP ( scsi_response, struct scsi_command *, scsicmd_response ), +}; + +/** SCSI command SCSI interface descriptor */ +static struct interface_descriptor scsicmd_scsi_desc = + INTF_DESC_PASSTHRU ( struct scsi_command, scsi, + scsicmd_scsi_op, block ); + +/** + * Create SCSI command + * + * @v scsidev SCSI device + * @v block Block data interface + * @v type SCSI 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 scsidev_command ( struct scsi_device *scsidev, + struct interface *block, + struct scsi_command_type *type, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) { + struct scsi_command *scsicmd; + int rc; + + /* Allocate and initialise structure */ + scsicmd = zalloc ( sizeof ( *scsicmd ) + type->priv_len ); + if ( ! scsicmd ) { + rc = -ENOMEM; + goto err_zalloc; + } + ref_init ( &scsicmd->refcnt, scsicmd_free ); + intf_init ( &scsicmd->block, &scsicmd_block_desc, &scsicmd->refcnt ); + intf_init ( &scsicmd->scsi, &scsicmd_scsi_desc, + &scsicmd->refcnt ); + scsicmd->scsidev = scsidev_get ( scsidev ); + list_add ( &scsicmd->list, &scsidev->cmds ); + scsicmd->type = type; + scsicmd->lba = lba; + scsicmd->count = count; + scsicmd->buffer = buffer; + scsicmd->len = len; + + /* Issue SCSI command */ + if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 ) + goto err_command; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &scsicmd->block, block ); + ref_put ( &scsicmd->refcnt ); + return 0; + + err_command: + scsicmd_close ( scsicmd, rc ); + ref_put ( &scsicmd->refcnt ); + err_zalloc: + return rc; +} + +/** + * Issue SCSI block read + * + * @v scsidev SCSI 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 scsidev_read ( struct scsi_device *scsidev, + struct interface *block, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) { + return scsidev_command ( scsidev, block, &scsicmd_read, + lba, count, buffer, len ); +} + +/** + * Issue SCSI block write + * + * @v scsidev SCSI 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 scsidev_write ( struct scsi_device *scsidev, + struct interface *block, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) { + return scsidev_command ( scsidev, block, &scsicmd_write, + lba, count, buffer, len ); +} + +/** + * Read SCSI device capacity + * + * @v scsidev SCSI device + * @v block Block data interface + * @ret rc Return status code + */ +static int scsidev_read_capacity ( struct scsi_device *scsidev, + struct interface *block ) { + return scsidev_command ( scsidev, block, &scsicmd_read_capacity, + 0, 0, UNULL, 0 ); +} + +/** + * Close SCSI device + * + * @v scsidev SCSI device + * @v rc Reason for close + */ +static void scsidev_close ( struct scsi_device *scsidev, int rc ) { + struct scsi_command *scsicmd; + struct scsi_command *tmp; + + /* Shut down interfaces */ + intf_shutdown ( &scsidev->block, rc ); + intf_shutdown ( &scsidev->scsi, rc ); + + /* Shut down any remaining commands */ + list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) { + scsicmd_get ( scsicmd ); + scsicmd_close ( scsicmd, rc ); + scsicmd_put ( scsicmd ); + } +} + +/** SCSI device block interface operations */ +static struct interface_operation scsidev_block_op[] = { + INTF_OP ( block_read, struct scsi_device *, scsidev_read ), + INTF_OP ( block_write, struct scsi_device *, scsidev_write ), + INTF_OP ( block_read_capacity, struct scsi_device *, + scsidev_read_capacity ), + INTF_OP ( intf_close, struct scsi_device *, scsidev_close ), +}; + +/** SCSI device block interface descriptor */ +static struct interface_descriptor scsidev_block_desc = + INTF_DESC_PASSTHRU ( struct scsi_device, block, + scsidev_block_op, scsi ); + +/** SCSI device SCSI interface operations */ +static struct interface_operation scsidev_scsi_op[] = { + INTF_OP ( intf_close, struct scsi_device *, scsidev_close ), +}; + +/** SCSI device SCSI interface descriptor */ +static struct interface_descriptor scsidev_scsi_desc = + INTF_DESC_PASSTHRU ( struct scsi_device, scsi, + scsidev_scsi_op, block ); + +/** + * Open SCSI device + * + * @v block Block control interface + * @v scsi SCSI control interface + * @v lun SCSI LUN + * @ret rc Return status code + */ +int scsi_open ( struct interface *block, struct interface *scsi, + struct scsi_lun *lun ) { + struct scsi_device *scsidev; + + /* Allocate and initialise structure */ + scsidev = zalloc ( sizeof ( *scsidev ) ); + if ( ! scsidev ) + return -ENOMEM; + ref_init ( &scsidev->refcnt, NULL ); + intf_init ( &scsidev->block, &scsidev_block_desc, &scsidev->refcnt ); + intf_init ( &scsidev->scsi, &scsidev_scsi_desc, &scsidev->refcnt ); + INIT_LIST_HEAD ( &scsidev->cmds ); + memcpy ( &scsidev->lun, lun, sizeof ( scsidev->lun ) ); + DBGC ( scsidev, "SCSI %p created for LUN " SCSI_LUN_FORMAT "\n", + scsidev, SCSI_LUN_DATA ( scsidev->lun ) ); + + /* Attach to SCSI and parent and interfaces, mortalise self, + * and return + */ + intf_plug_plug ( &scsidev->scsi, scsi ); + intf_plug_plug ( &scsidev->block, block ); + ref_put ( &scsidev->refcnt ); + return 0; +} diff --git a/src/drivers/block/srp.c b/src/drivers/block/srp.c index a8deab15..4b592e95 100644 --- a/src/drivers/block/srp.c +++ b/src/drivers/block/srp.c @@ -36,7 +36,6 @@ FILE_LICENCE ( BSD2 ); #include #include #include -#include #include /** @@ -48,334 +47,555 @@ FILE_LICENCE ( BSD2 ); FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 ); -/** Tag to be used for next SRP IU */ -static unsigned int srp_tag = 0; +/** 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. + */ +#define SRP_MAX_I_T_IU_LEN 80 -static void srp_login ( struct srp_device *srp ); -static void srp_cmd ( struct srp_device *srp ); +/** An SRP device */ +struct srp_device { + /** Reference count */ + struct refcnt refcnt; + + /** SCSI command issuing interface */ + struct interface scsi; + /** Underlying data transfer interface */ + struct interface socket; + + /** RDMA memory handle */ + uint32_t memory_handle; + /** Login completed successfully */ + int logged_in; + + /** Initiator port ID (for boot firmware table) */ + union srp_port_id initiator; + /** Target port ID (for boot firmware table) */ + union srp_port_id target; + /** SCSI LUN (for boot firmware table) */ + struct scsi_lun lun; + + /** List of active commands */ + struct list_head commands; +}; + +/** An SRP command */ +struct srp_command { + /** Reference count */ + struct refcnt refcnt; + /** SRP device */ + struct srp_device *srpdev; + /** List of active commands */ + struct list_head list; + + /** SCSI command interface */ + struct interface scsi; + /** Command tag */ + uint32_t tag; +}; /** - * Mark SRP SCSI command as complete + * Get reference to SRP device * - * @v srp SRP device - * @v rc Status code + * @v srpdev SRP device + * @ret srpdev SRP device */ -static void srp_scsi_done ( struct srp_device *srp, int rc ) { - if ( srp->command ) - srp->command->rc = rc; - srp->command = NULL; +static inline __attribute__ (( always_inline )) struct srp_device * +srpdev_get ( struct srp_device *srpdev ) { + ref_get ( &srpdev->refcnt ); + return srpdev; } /** - * Handle SRP session failure + * Drop reference to SRP device * - * @v srp SRP device - * @v rc Reason for failure + * @v srpdev SRP device */ -static void srp_fail ( struct srp_device *srp, int rc ) { +static inline __attribute__ (( always_inline )) void +srpdev_put ( struct srp_device *srpdev ) { + ref_put ( &srpdev->refcnt ); +} - /* Close underlying socket */ - intf_restart ( &srp->socket, rc ); +/** + * Get reference to SRP command + * + * @v srpcmd SRP command + * @ret srpcmd SRP command + */ +static inline __attribute__ (( always_inline )) struct srp_command * +srpcmd_get ( struct srp_command *srpcmd ) { + ref_get ( &srpcmd->refcnt ); + return srpcmd; +} - /* Clear session state */ - srp->state = 0; +/** + * Drop reference to SRP command + * + * @v srpcmd SRP command + */ +static inline __attribute__ (( always_inline )) void +srpcmd_put ( struct srp_command *srpcmd ) { + ref_put ( &srpcmd->refcnt ); +} - /* If we have reached the retry limit, report the failure */ - if ( srp->retry_count >= SRP_MAX_RETRIES ) { - srp_scsi_done ( srp, rc ); - return; +/** + * Free SRP command + * + * @v refcnt Reference count + */ +static void srpcmd_free ( struct refcnt *refcnt ) { + struct srp_command *srpcmd = + container_of ( refcnt, struct srp_command, refcnt ); + + assert ( list_empty ( &srpcmd->list ) ); + + srpdev_put ( srpcmd->srpdev ); + free ( srpcmd ); +} + +/** + * Close SRP command + * + * @v srpcmd SRP command + * @v rc Reason for close + */ +static void srpcmd_close ( struct srp_command *srpcmd, int rc ) { + struct srp_device *srpdev = srpcmd->srpdev; + + if ( rc != 0 ) { + DBGC ( srpdev, "SRP %p tag %08x closed: %s\n", + srpdev, srpcmd->tag, strerror ( rc ) ); } - /* Otherwise, increment the retry count and try to reopen the - * connection - */ - srp->retry_count++; - srp_login ( srp ); + /* Remove from list of commands */ + if ( ! list_empty ( &srpcmd->list ) ) { + list_del ( &srpcmd->list ); + INIT_LIST_HEAD ( &srpcmd->list ); + srpcmd_put ( srpcmd ); + } + + /* Shut down interfaces */ + intf_shutdown ( &srpcmd->scsi, rc ); } /** - * Initiate SRP login + * Close SRP device * - * @v srp SRP device + * @v srpdev SRP device + * @v rc Reason for close */ -static void srp_login ( struct srp_device *srp ) { +static void srpdev_close ( struct srp_device *srpdev, int rc ) { + struct srp_command *srpcmd; + struct srp_command *tmp; + + if ( rc != 0 ) { + DBGC ( srpdev, "SRP %p closed: %s\n", + srpdev, strerror ( rc ) ); + } + + /* Shut down interfaces */ + intf_shutdown ( &srpdev->socket, rc ); + intf_shutdown ( &srpdev->scsi, rc ); + + /* Shut down any active commands */ + list_for_each_entry_safe ( srpcmd, tmp, &srpdev->commands, list ) { + srpcmd_get ( srpcmd ); + srpcmd_close ( srpcmd, rc ); + srpcmd_put ( srpcmd ); + } +} + +/** + * Identify SRP command by tag + * + * @v srpdev SRP device + * @v tag Command tag + * @ret srpcmd SRP command, or NULL + */ +static struct srp_command * srp_find_tag ( struct srp_device *srpdev, + uint32_t tag ) { + struct srp_command *srpcmd; + + list_for_each_entry ( srpcmd, &srpdev->commands, list ) { + if ( srpcmd->tag == tag ) + return srpcmd; + } + return NULL; +} + +/** + * Choose an SRP command tag + * + * @v srpdev SRP device + * @ret tag New tag, or negative error + */ +static int srp_new_tag ( struct srp_device *srpdev ) { + static uint16_t tag_idx; + unsigned int i; + + for ( i = 0 ; i < 65536 ; i++ ) { + tag_idx++; + if ( srp_find_tag ( srpdev, tag_idx ) == NULL ) + return tag_idx; + } + return -EADDRINUSE; +} + +/** + * Transmit SRP login request + * + * @v srpdev SRP device + * @v initiator Initiator port ID + * @v target Target port ID + * @v tag Command tag + * @ret rc Return status code + */ +static int srp_login ( struct srp_device *srpdev, union srp_port_id *initiator, + union srp_port_id *target, uint32_t tag ) { struct io_buffer *iobuf; struct srp_login_req *login_req; int rc; - assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ); - - /* Open underlying socket */ - if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) { - DBGC ( srp, "SRP %p could not open socket: %s\n", - srp, strerror ( rc ) ); - goto err; - } - srp->state |= SRP_STATE_SOCKET_OPEN; - /* Allocate I/O buffer */ - iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err; - } + iobuf = xfer_alloc_iob ( &srpdev->socket, sizeof ( *login_req ) ); + if ( ! iobuf ) + return -ENOMEM; /* Construct login request IU */ login_req = iob_put ( iobuf, sizeof ( *login_req ) ); memset ( login_req, 0, sizeof ( *login_req ) ); login_req->type = SRP_LOGIN_REQ; - login_req->tag.dwords[1] = htonl ( ++srp_tag ); + login_req->tag.dwords[0] = htonl ( SRP_TAG_MAGIC ); + login_req->tag.dwords[1] = htonl ( tag ); login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN ); login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD; - memcpy ( &login_req->port_ids, &srp->port_ids, - sizeof ( login_req->port_ids ) ); + memcpy ( &login_req->initiator, initiator, + sizeof ( login_req->initiator ) ); + memcpy ( &login_req->target, target, sizeof ( login_req->target ) ); - DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n", - srp, ntohl ( login_req->tag.dwords[0] ), - ntohl ( login_req->tag.dwords[1] ) ); - DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC ( srpdev, "SRP %p tag %08x LOGIN_REQ:\n", srpdev, tag ); + DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) ); /* Send login request IU */ - if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { - DBGC ( srp, "SRP %p could not send login request: %s\n", - srp, strerror ( rc ) ); - goto err; + if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) { + DBGC ( srpdev, "SRP %p tag %08x could not send LOGIN_REQ: " + "%s\n", srpdev, tag, strerror ( rc ) ); + return rc; } - return; - - err: - srp_fail ( srp, rc ); + return 0; } /** - * Handle SRP login response + * Receive SRP login response * - * @v srp SRP device - * @v iobuf I/O buffer + * @v srpdev SRP device + * @v data SRP IU + * @v len Length of SRP IU * @ret rc Return status code */ -static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) { - struct srp_login_rsp *login_rsp = iobuf->data; - int rc; - - DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n", - srp, ntohl ( login_rsp->tag.dwords[0] ), - ntohl ( login_rsp->tag.dwords[1] ) ); +static int srp_login_rsp ( struct srp_device *srpdev, + const void *data, size_t len ) { + const struct srp_login_rsp *login_rsp = data; /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) { - DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n", - srp, iob_len ( iobuf ) ); - rc = -EINVAL; - goto out; + if ( len < sizeof ( *login_rsp ) ) { + DBGC ( srpdev, "SRP %p LOGIN_RSP too short (%zd bytes)\n", + srpdev, len ); + return -EINVAL; } - - DBGC ( srp, "SRP %p logged in\n", srp ); + DBGC ( srpdev, "SRP %p tag %08x LOGIN_RSP:\n", + srpdev, ntohl ( login_rsp->tag.dwords[1] ) ); + DBGC_HDA ( srpdev, 0, data, len ); /* Mark as logged in */ - srp->state |= SRP_STATE_LOGGED_IN; + srpdev->logged_in = 1; + DBGC ( srpdev, "SRP %p logged in\n", srpdev ); - /* Reset error counter */ - srp->retry_count = 0; + /* Notify of window change */ + xfer_window_changed ( &srpdev->scsi ); - /* Issue pending command */ - srp_cmd ( srp ); - - rc = 0; - out: - free_iob ( iobuf ); - return rc; + return 0; } /** - * Handle SRP login rejection + * Receive SRP login rejection * - * @v srp SRP device - * @v iobuf I/O buffer + * @v srpdev SRP device + * @v data SRP IU + * @v len Length of SRP IU * @ret rc Return status code */ -static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) { - struct srp_login_rej *login_rej = iobuf->data; - int rc; - - DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n", - srp, ntohl ( login_rej->tag.dwords[0] ), - ntohl ( login_rej->tag.dwords[1] ) ); +static int srp_login_rej ( struct srp_device *srpdev, + const void *data, size_t len ) { + const struct srp_login_rej *login_rej = data; /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) { - DBGC ( srp, "SRP %p RX login rejection too short (%zd " - "bytes)\n", srp, iob_len ( iobuf ) ); - rc = -EINVAL; - goto out; + if ( len < sizeof ( *login_rej ) ) { + DBGC ( srpdev, "SRP %p LOGIN_REJ too short (%zd bytes)\n", + srpdev, len ); + return -EINVAL; } + DBGC ( srpdev, "SRP %p tag %08x LOGIN_REJ:\n", + srpdev, ntohl ( login_rej->tag.dwords[1] ) ); + DBGC_HDA ( srpdev, 0, data, len ); /* Login rejection always indicates an error */ - DBGC ( srp, "SRP %p login rejected (reason %08x)\n", - srp, ntohl ( login_rej->reason ) ); - rc = -EPERM; - - out: - free_iob ( iobuf ); - return rc; + DBGC ( srpdev, "SRP %p login rejected (reason %08x)\n", + srpdev, ntohl ( login_rej->reason ) ); + return -EPERM; } /** * Transmit SRP SCSI command * - * @v srp SRP device + * @v srpdev SRP device + * @v command SCSI command + * @v tag Command tag + * @ret rc Return status code */ -static void srp_cmd ( struct srp_device *srp ) { +static int srp_cmd ( struct srp_device *srpdev, + struct scsi_cmd *command, + uint32_t tag ) { struct io_buffer *iobuf; struct srp_cmd *cmd; struct srp_memory_descriptor *data_out; struct srp_memory_descriptor *data_in; int rc; - assert ( srp->state & SRP_STATE_LOGGED_IN ); + /* Sanity check */ + if ( ! srpdev->logged_in ) { + DBGC ( srpdev, "SRP %p tag %08x cannot send CMD before " + "login completes\n", srpdev, tag ); + return -EBUSY; + } /* Allocate I/O buffer */ - iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err; - } + iobuf = xfer_alloc_iob ( &srpdev->socket, SRP_MAX_I_T_IU_LEN ); + if ( ! iobuf ) + return -ENOMEM; /* Construct base portion */ cmd = iob_put ( iobuf, sizeof ( *cmd ) ); memset ( cmd, 0, sizeof ( *cmd ) ); cmd->type = SRP_CMD; - cmd->tag.dwords[1] = htonl ( ++srp_tag ); - cmd->lun = srp->lun; - memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) ); + cmd->tag.dwords[0] = htonl ( SRP_TAG_MAGIC ); + cmd->tag.dwords[1] = htonl ( tag ); + memcpy ( &cmd->lun, &command->lun, sizeof ( cmd->lun ) ); + memcpy ( &cmd->cdb, &command->cdb, sizeof ( cmd->cdb ) ); /* Construct data-out descriptor, if present */ - if ( srp->command->data_out ) { + if ( command->data_out ) { cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT; data_out = iob_put ( iobuf, sizeof ( *data_out ) ); data_out->address = - cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) ); - data_out->handle = ntohl ( srp->memory_handle ); - data_out->len = ntohl ( srp->command->data_out_len ); + cpu_to_be64 ( user_to_phys ( command->data_out, 0 ) ); + data_out->handle = ntohl ( srpdev->memory_handle ); + data_out->len = ntohl ( command->data_out_len ); } /* Construct data-in descriptor, if present */ - if ( srp->command->data_in ) { + if ( command->data_in ) { cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT; data_in = iob_put ( iobuf, sizeof ( *data_in ) ); data_in->address = - cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) ); - data_in->handle = ntohl ( srp->memory_handle ); - data_in->len = ntohl ( srp->command->data_in_len ); + cpu_to_be64 ( user_to_phys ( command->data_in, 0 ) ); + data_in->handle = ntohl ( srpdev->memory_handle ); + data_in->len = ntohl ( command->data_in_len ); } - DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp, - ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) ); - DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); + DBGC2 ( srpdev, "SRP %p tag %08x CMD " SCSI_CDB_FORMAT "\n", + srpdev, tag, SCSI_CDB_DATA ( cmd->cdb ) ); /* Send IU */ - if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { - DBGC ( srp, "SRP %p could not send command: %s\n", - srp, strerror ( rc ) ); - goto err; + if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) { + DBGC ( srpdev, "SRP %p tag %08x could not send CMD: %s\n", + srpdev, tag, strerror ( rc ) ); + return rc; } - return; - - err: - srp_fail ( srp, rc ); + return 0; } /** - * Handle SRP SCSI response + * Receive SRP SCSI response * - * @v srp SRP device - * @v iobuf I/O buffer + * @v srpdev SRP device + * @v data SRP IU + * @v len Length of SRP IU * @ret rc Returns status code */ -static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) { - struct srp_rsp *rsp = iobuf->data; - int rc; - - DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp, - ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) ); +static int srp_rsp ( struct srp_device *srpdev, + const void *data, size_t len ) { + const struct srp_rsp *rsp = data; + struct srp_command *srpcmd; + struct scsi_rsp response; + const void *sense; + ssize_t data_out_residual_count; + ssize_t data_in_residual_count; /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) { - DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n", - srp, iob_len ( iobuf ) ); - rc = -EINVAL; - goto out; + if ( len < sizeof ( *rsp ) ) { + DBGC ( srpdev, "SRP %p RSP too short (%zd bytes)\n", + srpdev, len ); + return -EINVAL; + } + DBGC2 ( srpdev, "SRP %p tag %08x RSP stat %02x dores %08x dires " + "%08x valid %02x%s%s%s%s%s%s\n", + srpdev, ntohl ( rsp->tag.dwords[1] ), rsp->status, + ntohl ( rsp->data_out_residual_count ), + ntohl ( rsp->data_in_residual_count ), rsp->valid, + ( ( rsp->valid & SRP_RSP_VALID_DIUNDER ) ? " diunder" : "" ), + ( ( rsp->valid & SRP_RSP_VALID_DIOVER ) ? " diover" : "" ), + ( ( rsp->valid & SRP_RSP_VALID_DOUNDER ) ? " dounder" : "" ), + ( ( rsp->valid & SRP_RSP_VALID_DOOVER ) ? " doover" : "" ), + ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ? " sns" : "" ), + ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ? " rsp" : "" ) ); + + /* Identify command by tag */ + srpcmd = srp_find_tag ( srpdev, ntohl ( rsp->tag.dwords[1] ) ); + if ( ! srpcmd ) { + DBGC ( srpdev, "SRP %p tag %08x unrecognised RSP\n", + srpdev, ntohl ( rsp->tag.dwords[1] ) ); + return -ENOENT; } - /* Report SCSI errors */ - if ( rsp->status != 0 ) { - DBGC ( srp, "SRP %p response status %02x\n", - srp, rsp->status ); - if ( srp_rsp_sense_data ( rsp ) ) { - DBGC ( srp, "SRP %p sense data:\n", srp ); - DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ), - srp_rsp_sense_data_len ( rsp ) ); - } - } - if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) { - DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n", - srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER ) - ? "under" : "over" ), - ntohl ( rsp->data_out_residual_count ) ); - } - if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) { - DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n", - srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER ) - ? "under" : "over" ), - ntohl ( rsp->data_in_residual_count ) ); - } - srp->command->status = rsp->status; + /* Hold command reference for remainder of function */ + srpcmd_get ( srpcmd ); - /* Mark SCSI command as complete */ - srp_scsi_done ( srp, 0 ); + /* Build SCSI response */ + memset ( &response, 0, sizeof ( response ) ); + response.status = rsp->status; + data_out_residual_count = ntohl ( rsp->data_out_residual_count ); + data_in_residual_count = ntohl ( rsp->data_in_residual_count ); + if ( rsp->valid & SRP_RSP_VALID_DOOVER ) { + response.overrun = data_out_residual_count; + } else if ( rsp->valid & SRP_RSP_VALID_DOUNDER ) { + response.overrun = -(data_out_residual_count); + } else if ( rsp->valid & SRP_RSP_VALID_DIOVER ) { + response.overrun = data_in_residual_count; + } else if ( rsp->valid & SRP_RSP_VALID_DIUNDER ) { + response.overrun = -(data_in_residual_count); + } + sense = srp_rsp_sense_data ( rsp ); + if ( sense ) + memcpy ( &response.sense, sense, sizeof ( response.sense ) ); - rc = 0; - out: - free_iob ( iobuf ); + /* Report SCSI response */ + scsi_response ( &srpcmd->scsi, &response ); + + /* Close SCSI command */ + srpcmd_close ( srpcmd, 0 ); + + /* Drop temporary command reference */ + srpcmd_put ( srpcmd ); + + return 0; +} + +/** + * Receive SRP unrecognised response IU + * + * @v srpdev SRP device + * @v data SRP IU + * @v len Length of SRP IU + * @ret rc Returns status code + */ +static int srp_unrecognised ( struct srp_device *srpdev, + const void *data, size_t len ) { + const struct srp_common *common = data; + + DBGC ( srpdev, "SRP %p tag %08x unrecognised IU type %02x:\n", + srpdev, ntohl ( common->tag.dwords[1] ), common->type ); + DBGC_HDA ( srpdev, 0, data, len ); + + return -ENOTSUP; +} + +/** SRP command SCSI interface operations */ +static struct interface_operation srpcmd_scsi_op[] = { + INTF_OP ( intf_close, struct srp_command *, srpcmd_close ), +}; + +/** SRP command SCSI interface descriptor */ +static struct interface_descriptor srpcmd_scsi_desc = + INTF_DESC ( struct srp_command, scsi, srpcmd_scsi_op ); + +/** + * Issue SRP SCSI command + * + * @v srpdev SRP device + * @v parent Parent interface + * @v command SCSI command + * @ret tag Command tag, or negative error + */ +static int srpdev_scsi_command ( struct srp_device *srpdev, + struct interface *parent, + struct scsi_cmd *command ) { + struct srp_command *srpcmd; + int tag; + int rc; + + /* Allocate command tag */ + tag = srp_new_tag ( srpdev ); + if ( tag < 0 ) { + rc = tag; + goto err_tag; + } + + /* Allocate and initialise structure */ + srpcmd = zalloc ( sizeof ( *srpcmd ) ); + if ( ! srpcmd ) { + rc = -ENOMEM; + goto err_zalloc; + } + ref_init ( &srpcmd->refcnt, srpcmd_free ); + intf_init ( &srpcmd->scsi, &srpcmd_scsi_desc, &srpcmd->refcnt ); + srpcmd->srpdev = srpdev_get ( srpdev ); + list_add ( &srpcmd->list, &srpdev->commands ); + srpcmd->tag = tag; + + /* Send command IU */ + if ( ( rc = srp_cmd ( srpdev, command, srpcmd->tag ) ) != 0 ) + goto err_cmd; + + /* Attach to parent interface, leave reference with command + * list, and return. + */ + intf_plug_plug ( &srpcmd->scsi, parent ); + return srpcmd->tag; + + err_cmd: + srpcmd_close ( srpcmd, rc ); + err_zalloc: + err_tag: return rc; } /** - * Handle SRP unrecognised response + * Receive data from SRP socket * - * @v srp SRP device - * @v iobuf I/O buffer - * @ret rc Returns status code - */ -static int srp_unrecognised ( struct srp_device *srp, - struct io_buffer *iobuf ) { - struct srp_common *common = iobuf->data; - - DBGC ( srp, "SRP %p RX unrecognised IU tag %08x%08x type %02x\n", - srp, ntohl ( common->tag.dwords[0] ), - ntohl ( common->tag.dwords[1] ), common->type ); - - free_iob ( iobuf ); - return -ENOTSUP; -} - -/** - * Receive data from underlying socket - * - * @v srp SRP device + * @v srpdev SRP device * @v iobuf Datagram I/O buffer * @v meta Data transfer metadata * @ret rc Return status code */ -static int srp_xfer_deliver ( struct srp_device *srp, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { +static int srpdev_deliver ( struct srp_device *srpdev, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { struct srp_common *common = iobuf->data; - int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf ); + int ( * type ) ( struct srp_device *srp, const void *data, size_t len ); int rc; + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *common ) ) { + DBGC ( srpdev, "SRP %p IU too short (%zd bytes)\n", + srpdev, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + /* Determine IU type */ switch ( common->type ) { case SRP_LOGIN_RSP: @@ -393,114 +613,170 @@ static int srp_xfer_deliver ( struct srp_device *srp, } /* Handle IU */ - if ( ( rc = type ( srp, iobuf ) ) != 0 ) + if ( ( rc = type ( srpdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 ) goto err; + free_iob ( iobuf ); return 0; err: - srp_fail ( srp, rc ); + DBGC ( srpdev, "SRP %p closing due to received IU (%s):\n", + srpdev, strerror ( rc ) ); + DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) ); + free_iob ( iobuf ); + srpdev_close ( srpdev, rc ); return rc; } -/** SRP data transfer interface operations */ -static struct interface_operation srp_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct srp_device *, srp_xfer_deliver ), - INTF_OP ( intf_close, struct srp_device *, srp_fail ), -}; - -/** SRP data transfer interface descriptor */ -static struct interface_descriptor srp_xfer_desc = - INTF_DESC ( struct srp_device, socket, srp_xfer_operations ); +/** + * Check SRP device flow-control window + * + * @v srpdev SRP device + * @ret len Length of window + */ +static size_t srpdev_window ( struct srp_device *srpdev ) { + return ( srpdev->logged_in ? ~( ( size_t ) 0 ) : 0 ); +} /** - * Issue SCSI command via SRP + * A (transport-independent) 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; +} __attribute__ (( packed, aligned ( 16 ) )); + +/** + * Describe SRP device in an ACPI table * - * @v scsi SCSI device - * @v command SCSI command + * @v srpdev SRP device + * @v acpi ACPI table + * @v len Length of ACPI table * @ret rc Return status code */ -static int srp_command ( struct scsi_device *scsi, - struct scsi_command *command ) { - struct srp_device *srp = - container_of ( scsi->backend, struct srp_device, refcnt ); - - /* Store SCSI command */ - if ( srp->command ) { - DBGC ( srp, "SRP %p cannot handle concurrent SCSI commands\n", - srp ); - return -EBUSY; - } - srp->command = command; - - /* Log in or issue command as appropriate */ - if ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ) { - srp_login ( srp ); - } else if ( srp->state & SRP_STATE_LOGGED_IN ) { - srp_cmd ( srp ); - } else { - /* Still waiting for login; do nothing */ - } - - return 0; -} - -/** - * Attach SRP device - * - * @v scsi SCSI device - * @v root_path Root path - */ -int srp_attach ( struct scsi_device *scsi, const char *root_path ) { - struct srp_transport_type *transport; - struct srp_device *srp; +static int srpdev_describe ( struct srp_device *srpdev, + struct acpi_description_header *acpi, + size_t len ) { + struct ipxe_sbft *sbft = + container_of ( acpi, struct ipxe_sbft, table.acpi ); int rc; - /* Hard-code an IB SRP back-end for now */ - transport = &ib_srp_transport; + /* Sanity check */ + if ( len < sizeof ( *sbft ) ) + return -ENOBUFS; - /* Allocate and initialise structure */ - srp = zalloc ( sizeof ( *srp ) + transport->priv_len ); - if ( ! srp ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &srp->refcnt, NULL ); - intf_init ( &srp->socket, &srp_xfer_desc, &srp->refcnt ); - srp->transport = transport; - DBGC ( srp, "SRP %p using %s\n", srp, root_path ); + /* Populate table */ + sbft->table.acpi.signature = cpu_to_le32 ( SBFT_SIG ); + sbft->table.acpi.length = cpu_to_le32 ( sizeof ( *sbft ) ); + sbft->table.acpi.revision = 1; + sbft->table.scsi_offset = + cpu_to_le16 ( offsetof ( typeof ( *sbft ), scsi ) ); + memcpy ( &sbft->scsi.lun, &srpdev->lun, sizeof ( sbft->scsi.lun ) ); + sbft->table.srp_offset = + cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) ); + memcpy ( &sbft->srp.initiator, &srpdev->initiator, + sizeof ( sbft->srp.initiator ) ); + memcpy ( &sbft->srp.target, &srpdev->target, + sizeof ( sbft->srp.target ) ); - /* Parse root path */ - if ( ( rc = transport->parse_root_path ( srp, root_path ) ) != 0 ) { - DBGC ( srp, "SRP %p could not parse root path: %s\n", - srp, strerror ( rc ) ); - goto err_parse_root_path; + /* Ask transport layer to describe transport-specific portions */ + if ( ( rc = acpi_describe ( &srpdev->socket, acpi, len ) ) != 0 ) { + DBGC ( srpdev, "SRP %p cannot describe transport layer: %s\n", + srpdev, strerror ( rc ) ); + return rc; } - /* Attach parent interface, mortalise self, and return */ - scsi->backend = ref_get ( &srp->refcnt ); - scsi->command = srp_command; - ref_put ( &srp->refcnt ); return 0; - - err_parse_root_path: - ref_put ( &srp->refcnt ); - err_alloc: - return rc; } +/** SRP device socket interface operations */ +static struct interface_operation srpdev_socket_op[] = { + INTF_OP ( xfer_deliver, struct srp_device *, srpdev_deliver ), + INTF_OP ( intf_close, struct srp_device *, srpdev_close ), +}; + +/** SRP device socket interface descriptor */ +static struct interface_descriptor srpdev_socket_desc = + INTF_DESC ( struct srp_device, socket, srpdev_socket_op ); + +/** SRP device SCSI interface operations */ +static struct interface_operation srpdev_scsi_op[] = { + INTF_OP ( scsi_command, struct srp_device *, srpdev_scsi_command ), + INTF_OP ( xfer_window, struct srp_device *, srpdev_window ), + INTF_OP ( intf_close, struct srp_device *, srpdev_close ), + INTF_OP ( acpi_describe, struct srp_device *, srpdev_describe ), +}; + +/** SRP device SCSI interface descriptor */ +static struct interface_descriptor srpdev_scsi_desc = + INTF_DESC ( struct srp_device, scsi, srpdev_scsi_op ); + /** - * Detach SRP device + * Open SRP device * - * @v scsi SCSI device + * @v block Block control interface + * @v socket Socket interface + * @v initiator Initiator port ID + * @v target Target port ID + * @v memory_handle RDMA memory handle + * @v lun SCSI LUN + * @ret rc Return status code */ -void srp_detach ( struct scsi_device *scsi ) { - struct srp_device *srp = - container_of ( scsi->backend, struct srp_device, refcnt ); +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 ) { + struct srp_device *srpdev; + int tag; + int rc; - /* Close socket */ - intf_shutdown ( &srp->socket, 0 ); - scsi->command = scsi_detached_command; - ref_put ( scsi->backend ); - scsi->backend = NULL; + /* Allocate and initialise structure */ + srpdev = zalloc ( sizeof ( *srpdev ) ); + if ( ! srpdev ) { + rc = -ENOMEM; + goto err_zalloc; + } + ref_init ( &srpdev->refcnt, NULL ); + intf_init ( &srpdev->scsi, &srpdev_scsi_desc, &srpdev->refcnt ); + intf_init ( &srpdev->socket, &srpdev_socket_desc, &srpdev->refcnt ); + INIT_LIST_HEAD ( &srpdev->commands ); + srpdev->memory_handle = memory_handle; + DBGC ( srpdev, "SRP %p %08x%08x%08x%08x->%08x%08x%08x%08x\n", srpdev, + ntohl ( initiator->dwords[0] ), ntohl ( initiator->dwords[1] ), + ntohl ( initiator->dwords[2] ), ntohl ( initiator->dwords[3] ), + ntohl ( target->dwords[0] ), ntohl ( target->dwords[1] ), + ntohl ( target->dwords[2] ), ntohl ( target->dwords[3] ) ); + + /* Preserve parameters required for boot firmware table */ + memcpy ( &srpdev->initiator, initiator, sizeof ( srpdev->initiator ) ); + memcpy ( &srpdev->target, target, sizeof ( srpdev->target ) ); + memcpy ( &srpdev->lun, lun, sizeof ( srpdev->lun ) ); + + /* Attach to socket interface and initiate login */ + intf_plug_plug ( &srpdev->socket, socket ); + tag = srp_new_tag ( srpdev ); + assert ( tag >= 0 ); /* Cannot fail when no commands in progress */ + if ( ( rc = srp_login ( srpdev, initiator, target, tag ) ) != 0 ) + goto err_login; + + /* Attach SCSI device to parent interface */ + if ( ( rc = scsi_open ( block, &srpdev->scsi, lun ) ) != 0 ) { + DBGC ( srpdev, "SRP %p could not create SCSI device: %s\n", + srpdev, strerror ( rc ) ); + goto err_scsi_open; + } + + /* Mortalise self and return */ + ref_put ( &srpdev->refcnt ); + return 0; + + err_scsi_open: + err_login: + srpdev_close ( srpdev, rc ); + ref_put ( &srpdev->refcnt ); + err_zalloc: + return rc; } diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index eabbec39..282b6d92 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include /** * 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 */ diff --git a/src/include/ipxe/aoe.h b/src/include/ipxe/aoe.h index d2a12858..60f3bd95 100644 --- a/src/include/ipxe/aoe.h +++ b/src/include/ipxe/aoe.h @@ -14,10 +14,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** 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 */ diff --git a/src/include/ipxe/ata.h b/src/include/ipxe/ata.h index 043e3705..b7f02d65 100644 --- a/src/include/ipxe/ata.h +++ b/src/include/ipxe/ata.h @@ -2,9 +2,8 @@ #define _IPXE_ATA_H #include -#include #include -#include +#include /** @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 */ diff --git a/src/include/ipxe/blockdev.h b/src/include/ipxe/blockdev.h index 5c1ab136..9f0a9f78 100644 --- a/src/include/ipxe/blockdev.h +++ b/src/include/ipxe/blockdev.h @@ -10,44 +10,46 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include #include +#include -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 */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index ad32ed1b..d78f2822 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -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 ) diff --git a/src/include/ipxe/ib_srp.h b/src/include/ipxe/ib_srp.h index b773f964..902bef56 100644 --- a/src/include/ipxe/ib_srp.h +++ b/src/include/ipxe/ib_srp.h @@ -14,45 +14,35 @@ FILE_LICENCE ( BSD2 ); #include /** 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 */ diff --git a/src/arch/i386/include/ipxe/ibft.h b/src/include/ipxe/ibft.h similarity index 86% rename from src/arch/i386/include/ipxe/ibft.h rename to src/include/ipxe/ibft.h index 8525fe81..73372122 100644 --- a/src/arch/i386/include/ipxe/ibft.h +++ b/src/include/ipxe/ibft.h @@ -43,10 +43,11 @@ FILE_LICENCE ( BSD2 ); #include #include +#include #include /** 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 */ diff --git a/src/include/ipxe/iscsi.h b/src/include/ipxe/iscsi.h index a604e1e4..e711459c 100644 --- a/src/include/ipxe/iscsi.h +++ b/src/include/ipxe/iscsi.h @@ -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 */ diff --git a/src/include/ipxe/null_sanboot.h b/src/include/ipxe/null_sanboot.h new file mode 100644 index 00000000..341a9a1d --- /dev/null +++ b/src/include/ipxe/null_sanboot.h @@ -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 */ diff --git a/src/include/ipxe/ramdisk.h b/src/include/ipxe/ramdisk.h deleted file mode 100644 index 012ac736..00000000 --- a/src/include/ipxe/ramdisk.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _IPXE_RAMDISK_H -#define _IPXE_RAMDISK_H - -/** - * @file - * - * RAM disks - * - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include -#include - -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 */ diff --git a/src/include/ipxe/sanboot.h b/src/include/ipxe/sanboot.h index d27452cb..913282eb 100644 --- a/src/include/ipxe/sanboot.h +++ b/src/include/ipxe/sanboot.h @@ -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 +#include +#include -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 + +/* Include all architecture-dependent sanboot API headers */ +#include + +/** + * 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 */ diff --git a/src/include/ipxe/scsi.h b/src/include/ipxe/scsi.h index b56ab757..b90aa3aa 100644 --- a/src/include/ipxe/scsi.h +++ b/src/include/ipxe/scsi.h @@ -2,9 +2,8 @@ #define _IPXE_SCSI_H #include -#include #include -#include +#include /** @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 */ diff --git a/src/include/ipxe/srp.h b/src/include/ipxe/srp.h index e519838a..acde7c94 100644 --- a/src/include/ipxe/srp.h +++ b/src/include/ipxe/srp.h @@ -14,6 +14,7 @@ FILE_LICENCE ( BSD2 ); #include #include #include +#include /***************************************************************************** * @@ -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 */ diff --git a/src/net/aoe.c b/src/net/aoe.c index 5e1a3b5b..fec8d33f 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -27,13 +27,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include #include #include -#include #include -#include #include +#include +#include +#include +#include +#include #include /** @file @@ -46,140 +48,302 @@ FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 ); struct net_protocol aoe_protocol __net_protocol; -/** List of all AoE sessions */ -static LIST_HEAD ( aoe_sessions ); +/****************************************************************************** + * + * AoE devices and commands + * + ****************************************************************************** + */ -static void aoe_free ( struct refcnt *refcnt ) { - struct aoe_session *aoe = - container_of ( refcnt, struct aoe_session, refcnt ); +/** List of all AoE devices */ +static LIST_HEAD ( aoe_devices ); - netdev_put ( aoe->netdev ); - free ( aoe ); +/** List of active AoE commands */ +static LIST_HEAD ( aoe_commands ); + +/** An AoE device */ +struct aoe_device { + /** Reference counter */ + struct refcnt refcnt; + + /** Network device */ + struct net_device *netdev; + /** ATA command issuing interface */ + struct interface ata; + + /** Major number */ + uint16_t major; + /** Minor number */ + uint8_t minor; + /** Target MAC address */ + uint8_t target[MAX_LL_ADDR_LEN]; + + /** Saved timeout value */ + unsigned long timeout; + + /** Configuration command interface */ + struct interface config; + /** Device is configued */ + int configured; +}; + +/** An AoE command */ +struct aoe_command { + /** Reference count */ + struct refcnt refcnt; + /** AOE device */ + struct aoe_device *aoedev; + /** List of active commands */ + struct list_head list; + + /** ATA command interface */ + struct interface ata; + + /** ATA command */ + struct ata_cmd command; + /** Command type */ + struct aoe_command_type *type; + /** Command tag */ + uint32_t tag; + + /** Retransmission timer */ + struct retry_timer timer; +}; + +/** An AoE command type */ +struct aoe_command_type { + /** + * Calculate length of AoE command IU + * + * @v aoecmd AoE command + * @ret len Length of command IU + */ + size_t ( * cmd_len ) ( struct aoe_command *aoecmd ); + /** + * Build AoE command IU + * + * @v aoecmd AoE command + * @v data Command IU + * @v len Length of command IU + */ + void ( * cmd ) ( struct aoe_command *aoecmd, void *data, size_t len ); + /** + * Handle AoE response IU + * + * @v aoecmd AoE command + * @v data Response IU + * @v len Length of response IU + * @v ll_source Link-layer source address + * @ret rc Return status code + */ + int ( * rsp ) ( struct aoe_command *aoecmd, const void *data, + size_t len, const void *ll_source ); +}; + +/** + * Get reference to AoE device + * + * @v aoedev AoE device + * @ret aoedev AoE device + */ +static inline __attribute__ (( always_inline )) struct aoe_device * +aoedev_get ( struct aoe_device *aoedev ) { + ref_get ( &aoedev->refcnt ); + return aoedev; } /** - * Mark current AoE command complete + * Drop reference to AoE device * - * @v aoe AoE session - * @v rc Return status code + * @v aoedev AoE device */ -static void aoe_done ( struct aoe_session *aoe, int rc ) { +static inline __attribute__ (( always_inline )) void +aoedev_put ( struct aoe_device *aoedev ) { + ref_put ( &aoedev->refcnt ); +} - /* Record overall command status */ - if ( aoe->command ) { - aoe->command->cb.cmd_stat = aoe->status; - aoe->command->rc = rc; - aoe->command = NULL; +/** + * Get reference to AoE command + * + * @v aoecmd AoE command + * @ret aoecmd AoE command + */ +static inline __attribute__ (( always_inline )) struct aoe_command * +aoecmd_get ( struct aoe_command *aoecmd ) { + ref_get ( &aoecmd->refcnt ); + return aoecmd; +} + +/** + * Drop reference to AoE command + * + * @v aoecmd AoE command + */ +static inline __attribute__ (( always_inline )) void +aoecmd_put ( struct aoe_command *aoecmd ) { + ref_put ( &aoecmd->refcnt ); +} + +/** + * Name AoE device + * + * @v aoedev AoE device + * @ret name AoE device name + */ +static const char * aoedev_name ( struct aoe_device *aoedev ) { + static char buf[16]; + + snprintf ( buf, sizeof ( buf ), "%s/e%d.%d", aoedev->netdev->name, + aoedev->major, aoedev->minor ); + return buf; +} + +/** + * Free AoE command + * + * @v refcnt Reference counter + */ +static void aoecmd_free ( struct refcnt *refcnt ) { + struct aoe_command *aoecmd = + container_of ( refcnt, struct aoe_command, refcnt ); + + assert ( ! timer_running ( &aoecmd->timer ) ); + assert ( list_empty ( &aoecmd->list ) ); + + aoedev_put ( aoecmd->aoedev ); + free ( aoecmd ); +} + +/** + * Close AoE command + * + * @v aoecmd AoE command + * @v rc Reason for close + */ +static void aoecmd_close ( struct aoe_command *aoecmd, int rc ) { + struct aoe_device *aoedev = aoecmd->aoedev; + + /* Stop timer */ + stop_timer ( &aoecmd->timer ); + + /* Preserve the timeout value for subsequent commands */ + aoedev->timeout = aoecmd->timer.timeout; + + /* Remove from list of commands */ + if ( ! list_empty ( &aoecmd->list ) ) { + list_del ( &aoecmd->list ); + INIT_LIST_HEAD ( &aoecmd->list ); + aoecmd_put ( aoecmd ); } - /* Stop retransmission timer */ - stop_timer ( &aoe->timer ); - - /* Mark operation as complete */ - aoe->rc = rc; + /* Shut down interfaces */ + intf_shutdown ( &aoecmd->ata, rc ); } /** - * Send AoE command + * Transmit AoE command request * - * @v aoe AoE session + * @v aoecmd AoE command * @ret rc Return status code - * - * This transmits an AoE command packet. It does not wait for a - * response. */ -static int aoe_send_command ( struct aoe_session *aoe ) { - struct ata_command *command = aoe->command; +static int aoecmd_tx ( struct aoe_command *aoecmd ) { + struct aoe_device *aoedev = aoecmd->aoedev; struct io_buffer *iobuf; struct aoehdr *aoehdr; - union aoecmd *aoecmd; - struct aoeata *aoeata; - unsigned int count; - unsigned int data_out_len; - unsigned int aoecmdlen; + size_t cmd_len; + int rc; - /* Fail immediately if we have no netdev to send on */ - if ( ! aoe->netdev ) { - aoe_done ( aoe, -ENETUNREACH ); - return -ENETUNREACH; - } + /* Sanity check */ + assert ( aoedev->netdev != NULL ); /* If we are transmitting anything that requires a response, * start the retransmission timer. Do this before attempting * to allocate the I/O buffer, in case allocation itself * fails. */ - start_timer ( &aoe->timer ); - - /* Calculate count and data_out_len for this subcommand */ - switch ( aoe->aoe_cmd_type ) { - case AOE_CMD_ATA: - count = command->cb.count.native; - if ( count > AOE_MAX_COUNT ) - count = AOE_MAX_COUNT; - data_out_len = ( command->data_out ? - ( count * ATA_SECTOR_SIZE ) : 0 ); - aoecmdlen = sizeof ( aoecmd->ata ); - break; - case AOE_CMD_CONFIG: - count = 0; - data_out_len = 0; - aoecmdlen = sizeof ( aoecmd->cfg ); - break; - default: - return -ENOTSUP; - } + start_timer ( &aoecmd->timer ); /* Create outgoing I/O buffer */ - iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) + - aoecmdlen + data_out_len ); - + cmd_len = aoecmd->type->cmd_len ( aoecmd ); + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + cmd_len ); if ( ! iobuf ) return -ENOMEM; - iob_reserve ( iobuf, ETH_HLEN ); - aoehdr = iob_put ( iobuf, sizeof ( *aoehdr ) ); - aoecmd = iob_put ( iobuf, aoecmdlen ); - memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + aoecmdlen ) ); + iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); + aoehdr = iob_put ( iobuf, cmd_len ); /* Fill AoE header */ + memset ( aoehdr, 0, sizeof ( *aoehdr ) ); aoehdr->ver_flags = AOE_VERSION; - aoehdr->major = htons ( aoe->major ); - aoehdr->minor = aoe->minor; - aoehdr->command = aoe->aoe_cmd_type; - aoehdr->tag = htonl ( ++aoe->tag ); - - /* Fill AoE payload */ - switch ( aoe->aoe_cmd_type ) { - case AOE_CMD_ATA: - /* Fill AoE command */ - aoeata = &aoecmd->ata; - linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, - __fix_ata_h__ ); - aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 )| - ( command->cb.device & ATA_DEV_SLAVE ) | - ( data_out_len ? AOE_FL_WRITE : 0 ) ); - aoeata->err_feat = command->cb.err_feat.bytes.cur; - aoeata->count = count; - aoeata->cmd_stat = command->cb.cmd_stat; - aoeata->lba.u64 = cpu_to_le64 ( command->cb.lba.native ); - if ( ! command->cb.lba48 ) - aoeata->lba.bytes[3] |= - ( command->cb.device & ATA_DEV_MASK ); - - /* Fill data payload */ - copy_from_user ( iob_put ( iobuf, data_out_len ), - command->data_out, aoe->command_offset, - data_out_len ); - break; - case AOE_CMD_CONFIG: - /* Nothing to do */ - break; - default: - assert ( 0 ); - } + aoehdr->major = htons ( aoedev->major ); + aoehdr->minor = aoedev->minor; + aoehdr->tag = htonl ( aoecmd->tag ); + aoecmd->type->cmd ( aoecmd, iobuf->data, iob_len ( iobuf ) ); /* Send packet */ - return net_tx ( iobuf, aoe->netdev, &aoe_protocol, aoe->target ); + if ( ( rc = net_tx ( iobuf, aoedev->netdev, &aoe_protocol, + aoedev->target ) ) != 0 ) { + DBGC ( aoedev, "AoE %s/%08x could not transmit: %s\n", + aoedev_name ( aoedev ), aoecmd->tag, + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Receive AoE command response + * + * @v aoecmd AoE command + * @v iobuf I/O buffer + * @v ll_source Link-layer source address + * @ret rc Return status code + */ +static int aoecmd_rx ( struct aoe_command *aoecmd, struct io_buffer *iobuf, + const void *ll_source ) { + struct aoe_device *aoedev = aoecmd->aoedev; + struct aoehdr *aoehdr = iobuf->data; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) { + DBGC ( aoedev, "AoE %s/%08x received underlength response " + "(%zd bytes)\n", aoedev_name ( aoedev ), + aoecmd->tag, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + if ( ( ntohs ( aoehdr->major ) != aoedev->major ) || + ( aoehdr->minor != aoedev->minor ) ) { + DBGC ( aoedev, "AoE %s/%08x received response for incorrect " + "device e%d.%d\n", aoedev_name ( aoedev ), aoecmd->tag, + ntohs ( aoehdr->major ), aoehdr->minor ); + rc = -EINVAL; + goto done; + } + + /* Catch command failures */ + if ( aoehdr->ver_flags & AOE_FL_ERROR ) { + DBGC ( aoedev, "AoE %s/%08x terminated in error\n", + aoedev_name ( aoedev ), aoecmd->tag ); + aoecmd_close ( aoecmd, -EIO ); + rc = -EIO; + goto done; + } + + /* Hand off to command completion handler */ + if ( ( rc = aoecmd->type->rsp ( aoecmd, iobuf->data, iob_len ( iobuf ), + ll_source ) ) != 0 ) + goto done; + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); + + /* Terminate command */ + aoecmd_close ( aoecmd, rc ); + + return rc; } /** @@ -188,94 +352,531 @@ static int aoe_send_command ( struct aoe_session *aoe ) { * @v timer AoE retry timer * @v fail Failure indicator */ -static void aoe_timer_expired ( struct retry_timer *timer, int fail ) { - struct aoe_session *aoe = - container_of ( timer, struct aoe_session, timer ); +static void aoecmd_expired ( struct retry_timer *timer, int fail ) { + struct aoe_command *aoecmd = + container_of ( timer, struct aoe_command, timer ); if ( fail ) { - aoe_done ( aoe, -ETIMEDOUT ); + aoecmd_close ( aoecmd, -ETIMEDOUT ); } else { - aoe_send_command ( aoe ); + aoecmd_tx ( aoecmd ); } } /** - * Handle AoE configuration command response + * Calculate length of AoE ATA command IU * - * @v aoe AoE session + * @v aoecmd AoE command + * @ret len Length of command IU + */ +static size_t aoecmd_ata_cmd_len ( struct aoe_command *aoecmd ) { + struct ata_cmd *command = &aoecmd->command; + + return ( sizeof ( struct aoehdr ) + sizeof ( struct aoeata ) + + command->data_out_len ); +} + +/** + * Build AoE ATA command IU + * + * @v aoecmd AoE command + * @v data Command IU + * @v len Length of command IU + */ +static void aoecmd_ata_cmd ( struct aoe_command *aoecmd, + void *data, size_t len ) { + struct aoe_device *aoedev = aoecmd->aoedev; + struct ata_cmd *command = &aoecmd->command; + struct aoehdr *aoehdr = data; + struct aoeata *aoeata = &aoehdr->payload[0].ata; + + /* Sanity check */ + linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ ); + assert ( len == ( sizeof ( *aoehdr ) + sizeof ( *aoeata ) + + command->data_out_len ) ); + + /* Build IU */ + aoehdr->command = AOE_CMD_ATA; + memset ( aoeata, 0, sizeof ( *aoeata ) ); + aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 ) | + ( command->cb.device & ATA_DEV_SLAVE ) | + ( command->data_out_len ? AOE_FL_WRITE : 0 ) ); + aoeata->err_feat = command->cb.err_feat.bytes.cur; + aoeata->count = command->cb.count.native; + aoeata->cmd_stat = command->cb.cmd_stat; + aoeata->lba.u64 = cpu_to_le64 ( command->cb.lba.native ); + if ( ! command->cb.lba48 ) + aoeata->lba.bytes[3] |= + ( command->cb.device & ATA_DEV_MASK ); + copy_from_user ( aoeata->data, command->data_out, 0, + command->data_out_len ); + + DBGC2 ( aoedev, "AoE %s/%08x ATA cmd %02x:%02x:%02x:%02x:%08llx", + aoedev_name ( aoedev ), aoecmd->tag, aoeata->aflags, + aoeata->err_feat, aoeata->count, aoeata->cmd_stat, + aoeata->lba.u64 ); + if ( command->data_out_len ) + DBGC2 ( aoedev, " out %04zx", command->data_out_len ); + if ( command->data_in_len ) + DBGC2 ( aoedev, " in %04zx", command->data_in_len ); + DBGC2 ( aoedev, "\n" ); +} + +/** + * Handle AoE ATA response IU + * + * @v aoecmd AoE command + * @v data Response IU + * @v len Length of response IU * @v ll_source Link-layer source address * @ret rc Return status code */ -static int aoe_rx_cfg ( struct aoe_session *aoe, const void *ll_source ) { +static int aoecmd_ata_rsp ( struct aoe_command *aoecmd, const void *data, + size_t len, const void *ll_source __unused ) { + struct aoe_device *aoedev = aoecmd->aoedev; + struct ata_cmd *command = &aoecmd->command; + const struct aoehdr *aoehdr = data; + const struct aoeata *aoeata = &aoehdr->payload[0].ata; + size_t data_len; - /* Record target MAC address */ - memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) ); - DBGC ( aoe, "AoE %p target MAC address %s\n", - aoe, eth_ntoa ( aoe->target ) ); + /* Sanity check */ + if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoeata ) ) ) { + DBGC ( aoedev, "AoE %s/%08x received underlength ATA response " + "(%zd bytes)\n", aoedev_name ( aoedev ), + aoecmd->tag, len ); + return -EINVAL; + } + data_len = ( len - ( sizeof ( *aoehdr ) + sizeof ( *aoeata ) ) ); + DBGC2 ( aoedev, "AoE %s/%08x ATA rsp %02x in %04zx\n", + aoedev_name ( aoedev ), aoecmd->tag, aoeata->cmd_stat, + data_len ); - /* Mark config request as complete */ - aoe_done ( aoe, 0 ); + /* Check for command failure */ + if ( aoeata->cmd_stat & ATA_STAT_ERR ) { + DBGC ( aoedev, "AoE %s/%08x status %02x\n", + aoedev_name ( aoedev ), aoecmd->tag, aoeata->cmd_stat ); + return -EIO; + } + + /* Check data-in length is sufficient. (There may be trailing + * garbage due to Ethernet minimum-frame-size padding.) + */ + if ( data_len < command->data_in_len ) { + DBGC ( aoedev, "AoE %s/%08x data-in underrun (received %zd, " + "expected %zd)\n", aoedev_name ( aoedev ), aoecmd->tag, + data_len, command->data_in_len ); + return -ERANGE; + } + + /* Copy out data payload */ + copy_to_user ( command->data_in, 0, aoeata->data, + command->data_in_len ); return 0; } +/** AoE ATA command */ +static struct aoe_command_type aoecmd_ata = { + .cmd_len = aoecmd_ata_cmd_len, + .cmd = aoecmd_ata_cmd, + .rsp = aoecmd_ata_rsp, +}; + +/** + * Calculate length of AoE configuration command IU + * + * @v aoecmd AoE command + * @ret len Length of command IU + */ +static size_t aoecmd_cfg_cmd_len ( struct aoe_command *aoecmd __unused ) { + return ( sizeof ( struct aoehdr ) + sizeof ( struct aoecfg ) ); +} + /** - * Handle AoE ATA command response + * Build AoE configuration command IU * - * @v aoe AoE session - * @v aoeata AoE ATA command - * @v len Length of AoE ATA command - * @ret rc Return status code + * @v aoecmd AoE command + * @v data Command IU + * @v len Length of command IU */ -static int aoe_rx_ata ( struct aoe_session *aoe, struct aoeata *aoeata, - size_t len ) { - struct ata_command *command = aoe->command; - unsigned int rx_data_len; - unsigned int count; - unsigned int data_len; +static void aoecmd_cfg_cmd ( struct aoe_command *aoecmd, + void *data, size_t len ) { + struct aoe_device *aoedev = aoecmd->aoedev; + struct aoehdr *aoehdr = data; + struct aoecfg *aoecfg = &aoehdr->payload[0].cfg; /* Sanity check */ - if ( len < sizeof ( *aoeata ) ) { - /* Ignore packet; allow timer to trigger retransmit */ + assert ( len == ( sizeof ( *aoehdr ) + sizeof ( *aoecfg ) ) ); + + /* Build IU */ + aoehdr->command = AOE_CMD_CONFIG; + memset ( aoecfg, 0, sizeof ( *aoecfg ) ); + + DBGC ( aoedev, "AoE %s/%08x CONFIG cmd\n", + aoedev_name ( aoedev ), aoecmd->tag ); +} + +/** + * Handle AoE configuration response IU + * + * @v aoecmd AoE command + * @v data Response IU + * @v len Length of response IU + * @v ll_source Link-layer source address + * @ret rc Return status code + */ +static int aoecmd_cfg_rsp ( struct aoe_command *aoecmd, const void *data, + size_t len, const void *ll_source ) { + struct aoe_device *aoedev = aoecmd->aoedev; + struct ll_protocol *ll_protocol = aoedev->netdev->ll_protocol; + const struct aoehdr *aoehdr = data; + const struct aoecfg *aoecfg = &aoehdr->payload[0].cfg; + + /* Sanity check */ + if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecfg ) ) ) { + DBGC ( aoedev, "AoE %s/%08x received underlength " + "configuration response (%zd bytes)\n", + aoedev_name ( aoedev ), aoecmd->tag, len ); return -EINVAL; } - rx_data_len = ( len - sizeof ( *aoeata ) ); + DBGC ( aoedev, "AoE %s/%08x CONFIG rsp buf %04x fw %04x scnt %02x\n", + aoedev_name ( aoedev ), aoecmd->tag, ntohs ( aoecfg->bufcnt ), + aoecfg->fwver, aoecfg->scnt ); - /* Calculate count and data_len for this subcommand */ - count = command->cb.count.native; - if ( count > AOE_MAX_COUNT ) - count = AOE_MAX_COUNT; - data_len = count * ATA_SECTOR_SIZE; - - /* Merge into overall ATA status */ - aoe->status |= aoeata->cmd_stat; - - /* Copy data payload */ - if ( command->data_in ) { - if ( rx_data_len > data_len ) - rx_data_len = data_len; - copy_to_user ( command->data_in, aoe->command_offset, - aoeata->data, rx_data_len ); - } - - /* Update ATA command and offset */ - aoe->command_offset += data_len; - command->cb.lba.native += count; - command->cb.count.native -= count; - - /* Check for operation complete */ - if ( ! command->cb.count.native ) { - aoe_done ( aoe, 0 ); - return 0; - } - - /* Transmit next portion of request */ - stop_timer ( &aoe->timer ); - aoe_send_command ( aoe ); + /* Record target MAC address */ + memcpy ( aoedev->target, ll_source, ll_protocol->ll_addr_len ); + DBGC ( aoedev, "AoE %s has MAC address %s\n", + aoedev_name ( aoedev ), ll_protocol->ntoa ( aoedev->target ) ); return 0; } +/** AoE configuration command */ +static struct aoe_command_type aoecmd_cfg = { + .cmd_len = aoecmd_cfg_cmd_len, + .cmd = aoecmd_cfg_cmd, + .rsp = aoecmd_cfg_rsp, +}; + +/** AoE command ATA interface operations */ +static struct interface_operation aoecmd_ata_op[] = { + INTF_OP ( intf_close, struct aoe_command *, aoecmd_close ), +}; + +/** AoE command ATA interface descriptor */ +static struct interface_descriptor aoecmd_ata_desc = + INTF_DESC ( struct aoe_command, ata, aoecmd_ata_op ); + +/** + * Identify AoE command by tag + * + * @v tag Command tag + * @ret aoecmd AoE command, or NULL + */ +static struct aoe_command * aoecmd_find_tag ( uint32_t tag ) { + struct aoe_command *aoecmd; + + list_for_each_entry ( aoecmd, &aoe_commands, list ) { + if ( aoecmd->tag == tag ) + return aoecmd; + } + return NULL; +} + +/** + * Choose an AoE command tag + * + * @ret tag New tag, or negative error + */ +static int aoecmd_new_tag ( void ) { + static uint16_t tag_idx; + unsigned int i; + + for ( i = 0 ; i < 65536 ; i++ ) { + tag_idx++; + if ( aoecmd_find_tag ( tag_idx ) == NULL ) + return ( AOE_TAG_MAGIC | tag_idx ); + } + return -EADDRINUSE; +} + +/** + * Create AoE command + * + * @v aoedev AoE device + * @v type AoE command type + * @ret aoecmd AoE command + */ +static struct aoe_command * aoecmd_create ( struct aoe_device *aoedev, + struct aoe_command_type *type ) { + struct aoe_command *aoecmd; + int tag; + + /* Allocate command tag */ + tag = aoecmd_new_tag(); + if ( tag < 0 ) + return NULL; + + /* Allocate and initialise structure */ + aoecmd = zalloc ( sizeof ( *aoecmd ) ); + if ( ! aoecmd ) + return NULL; + ref_init ( &aoecmd->refcnt, aoecmd_free ); + list_add ( &aoecmd->list, &aoe_commands ); + intf_init ( &aoecmd->ata, &aoecmd_ata_desc, &aoecmd->refcnt ); + timer_init ( &aoecmd->timer, aoecmd_expired, &aoecmd->refcnt ); + aoecmd->aoedev = aoedev_get ( aoedev ); + aoecmd->type = type; + aoecmd->tag = tag; + + /* Preserve timeout from last completed command */ + aoecmd->timer.timeout = aoedev->timeout; + + /* Return already mortalised. (Reference is held by command list.) */ + return aoecmd; +} + +/** + * Issue AoE ATA command + * + * @v aoedev AoE device + * @v parent Parent interface + * @v command ATA command + * @ret tag Command tag, or negative error + */ +static int aoedev_ata_command ( struct aoe_device *aoedev, + struct interface *parent, + struct ata_cmd *command ) { + struct aoe_command *aoecmd; + + /* Create command */ + aoecmd = aoecmd_create ( aoedev, &aoecmd_ata ); + if ( ! aoecmd ) + return -ENOMEM; + memcpy ( &aoecmd->command, command, sizeof ( aoecmd->command ) ); + + /* Attempt to send command. Allow failures to be handled by + * the retry timer. + */ + aoecmd_tx ( aoecmd ); + + /* Attach to parent interface, leave reference with command + * list, and return. + */ + intf_plug_plug ( &aoecmd->ata, parent ); + return aoecmd->tag; +} + +/** + * Issue AoE configuration command + * + * @v aoedev AoE device + * @v parent Parent interface + * @ret tag Command tag, or negative error + */ +static int aoedev_cfg_command ( struct aoe_device *aoedev, + struct interface *parent ) { + struct aoe_command *aoecmd; + + /* Create command */ + aoecmd = aoecmd_create ( aoedev, &aoecmd_cfg ); + if ( ! aoecmd ) + return -ENOMEM; + + /* Attempt to send command. Allow failures to be handled by + * the retry timer. + */ + aoecmd_tx ( aoecmd ); + + /* Attach to parent interface, leave reference with command + * list, and return. + */ + intf_plug_plug ( &aoecmd->ata, parent ); + return aoecmd->tag; +} + +/** + * Free AoE device + * + * @v refcnt Reference count + */ +static void aoedev_free ( struct refcnt *refcnt ) { + struct aoe_device *aoedev = + container_of ( refcnt, struct aoe_device, refcnt ); + + netdev_put ( aoedev->netdev ); + free ( aoedev ); +} + +/** + * Close AoE device + * + * @v aoedev AoE device + * @v rc Reason for close + */ +static void aoedev_close ( struct aoe_device *aoedev, int rc ) { + struct aoe_command *aoecmd; + struct aoe_command *tmp; + + /* Shut down interfaces */ + intf_shutdown ( &aoedev->ata, rc ); + intf_shutdown ( &aoedev->config, rc ); + + /* Shut down any active commands */ + list_for_each_entry_safe ( aoecmd, tmp, &aoe_commands, list ) { + if ( aoecmd->aoedev != aoedev ) + continue; + aoecmd_get ( aoecmd ); + aoecmd_close ( aoecmd, rc ); + aoecmd_put ( aoecmd ); + } +} + +/** + * Check AoE device flow-control window + * + * @v aoedev AoE device + * @ret len Length of window + */ +static size_t aoedev_window ( struct aoe_device *aoedev ) { + return ( aoedev->configured ? ~( ( size_t ) 0 ) : 0 ); +} + +/** + * Handle AoE device configuration completion + * + * @v aoedev AoE device + * @v rc Reason for completion + */ +static void aoedev_config_done ( struct aoe_device *aoedev, int rc ) { + + /* Shut down interface */ + intf_shutdown ( &aoedev->config, rc ); + + /* Close device on failure */ + if ( rc != 0 ) { + aoedev_close ( aoedev, rc ); + return; + } + + /* Mark device as configured */ + aoedev->configured = 1; + xfer_window_changed ( &aoedev->ata ); +} + +/** + * Describe AoE device in an ACPI table + * + * @v aoedev AoE device + * @v acpi ACPI table + * @v len Length of ACPI table + * @ret rc Return status code + */ +static int aoedev_describe ( struct aoe_device *aoedev, + struct acpi_description_header *acpi, + size_t len ) { + struct abft_table *abft = + container_of ( acpi, struct abft_table, acpi ); + + /* Sanity check */ + if ( len < sizeof ( *abft ) ) + return -ENOBUFS; + + /* Populate table */ + abft->acpi.signature = cpu_to_le32 ( ABFT_SIG ); + abft->acpi.length = cpu_to_le32 ( sizeof ( *abft ) ); + abft->acpi.revision = 1; + abft->shelf = cpu_to_le16 ( aoedev->major ); + abft->slot = aoedev->minor; + memcpy ( abft->mac, aoedev->netdev->ll_addr, sizeof ( abft->mac ) ); + + return 0; +} + +/** AoE device ATA interface operations */ +static struct interface_operation aoedev_ata_op[] = { + INTF_OP ( ata_command, struct aoe_device *, aoedev_ata_command ), + INTF_OP ( xfer_window, struct aoe_device *, aoedev_window ), + INTF_OP ( intf_close, struct aoe_device *, aoedev_close ), + INTF_OP ( acpi_describe, struct aoe_device *, aoedev_describe ), +}; + +/** AoE device ATA interface descriptor */ +static struct interface_descriptor aoedev_ata_desc = + INTF_DESC ( struct aoe_device, ata, aoedev_ata_op ); + +/** AoE device configuration interface operations */ +static struct interface_operation aoedev_config_op[] = { + INTF_OP ( intf_close, struct aoe_device *, aoedev_config_done ), +}; + +/** AoE device configuration interface descriptor */ +static struct interface_descriptor aoedev_config_desc = + INTF_DESC ( struct aoe_device, config, aoedev_config_op ); + +/** + * Open AoE device + * + * @v parent Parent interface + * @v netdev Network device + * @v major Device major number + * @v minor Device minor number + * @ret rc Return status code + */ +static int aoedev_open ( struct interface *parent, struct net_device *netdev, + unsigned int major, unsigned int minor ) { + struct aoe_device *aoedev; + int rc; + + /* Allocate and initialise structure */ + aoedev = zalloc ( sizeof ( *aoedev ) ); + if ( ! aoedev ) { + rc = -ENOMEM; + goto err_zalloc; + } + ref_init ( &aoedev->refcnt, aoedev_free ); + intf_init ( &aoedev->ata, &aoedev_ata_desc, &aoedev->refcnt ); + intf_init ( &aoedev->config, &aoedev_config_desc, &aoedev->refcnt ); + aoedev->netdev = netdev_get ( netdev ); + aoedev->major = major; + aoedev->minor = minor; + memcpy ( aoedev->target, netdev->ll_broadcast, + netdev->ll_protocol->ll_addr_len ); + + /* Initiate configuration */ + if ( ( rc = aoedev_cfg_command ( aoedev, &aoedev->config ) ) < 0 ) { + DBGC ( aoedev, "AoE %s could not initiate configuration: %s\n", + aoedev_name ( aoedev ), strerror ( rc ) ); + goto err_config; + } + + /* Attach ATA device to parent interface */ + if ( ( rc = ata_open ( parent, &aoedev->ata, ATA_DEV_MASTER, + AOE_MAX_COUNT ) ) != 0 ) { + DBGC ( aoedev, "AoE %s could not create ATA device: %s\n", + aoedev_name ( aoedev ), strerror ( rc ) ); + goto err_ata_open; + } + + /* Mortalise self and return */ + ref_put ( &aoedev->refcnt ); + return 0; + + err_ata_open: + err_config: + aoedev_close ( aoedev, rc ); + ref_put ( &aoedev->refcnt ); + err_zalloc: + return rc; +} + +/****************************************************************************** + * + * AoE network protocol + * + ****************************************************************************** + */ + /** * Process incoming AoE packets * @@ -289,52 +890,47 @@ static int aoe_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, const void *ll_source ) { struct aoehdr *aoehdr = iobuf->data; - struct aoe_session *aoe; - int rc = 0; + struct aoe_command *aoecmd; + int rc; - /* Sanity checks */ + /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) { + DBG ( "AoE received underlength packet (%zd bytes)\n", + iob_len ( iobuf ) ); rc = -EINVAL; - goto done; + goto err_sanity; } if ( ( aoehdr->ver_flags & AOE_VERSION_MASK ) != AOE_VERSION ) { + DBG ( "AoE received packet for unsupported protocol version " + "%02x\n", ( aoehdr->ver_flags & AOE_VERSION_MASK ) ); rc = -EPROTONOSUPPORT; - goto done; + goto err_sanity; } if ( ! ( aoehdr->ver_flags & AOE_FL_RESPONSE ) ) { - /* Ignore AoE requests that we happen to see */ - goto done; - } - iob_pull ( iobuf, sizeof ( *aoehdr ) ); - - /* Demultiplex amongst active AoE sessions */ - list_for_each_entry ( aoe, &aoe_sessions, list ) { - if ( ntohs ( aoehdr->major ) != aoe->major ) - continue; - if ( aoehdr->minor != aoe->minor ) - continue; - if ( ntohl ( aoehdr->tag ) != aoe->tag ) - continue; - if ( aoehdr->ver_flags & AOE_FL_ERROR ) { - aoe_done ( aoe, -EIO ); - break; - } - switch ( aoehdr->command ) { - case AOE_CMD_ATA: - rc = aoe_rx_ata ( aoe, iobuf->data, iob_len ( iobuf )); - break; - case AOE_CMD_CONFIG: - rc = aoe_rx_cfg ( aoe, ll_source ); - break; - default: - DBGC ( aoe, "AoE %p ignoring command %02x\n", - aoe, aoehdr->command ); - break; - } - break; + DBG ( "AoE received request packet\n" ); + rc = -EOPNOTSUPP; + goto err_sanity; } - done: + /* Demultiplex amongst active AoE commands */ + aoecmd = aoecmd_find_tag ( ntohl ( aoehdr->tag ) ); + if ( ! aoecmd ) { + DBG ( "AoE received packet for unused tag %08x\n", + ntohl ( aoehdr->tag ) ); + rc = -ENOENT; + goto err_demux; + } + + /* Pass received frame to command */ + aoecmd_get ( aoecmd ); + if ( ( rc = aoecmd_rx ( aoecmd, iob_disown ( iobuf ), + ll_source ) ) != 0 ) + goto err_rx; + + err_rx: + aoecmd_put ( aoecmd ); + err_demux: + err_sanity: free_iob ( iobuf ); return rc; } @@ -346,126 +942,90 @@ struct net_protocol aoe_protocol __net_protocol = { .rx = aoe_rx, }; -/** - * Issue ATA command via an open AoE session +/****************************************************************************** * - * @v ata ATA device - * @v command ATA command - * @ret rc Return status code + * AoE URIs + * + ****************************************************************************** */ -static int aoe_command ( struct ata_device *ata, - struct ata_command *command ) { - struct aoe_session *aoe = - container_of ( ata->backend, struct aoe_session, refcnt ); - aoe->command = command; - aoe->status = 0; - aoe->command_offset = 0; - aoe->aoe_cmd_type = AOE_CMD_ATA; +/** + * Parse AoE URI + * + * @v uri URI + * @ret major Major device number + * @ret minor Minor device number + * @ret rc Return status code + * + * An AoE URI has the form "aoe:e.". + */ +static int aoe_parse_uri ( struct uri *uri, unsigned int *major, + unsigned int *minor ) { + const char *ptr; + char *end; - aoe_send_command ( aoe ); + /* Check for URI with opaque portion */ + if ( ! uri->opaque ) + return -EINVAL; + ptr = uri->opaque; + + /* Check for initial 'e' */ + if ( *ptr != 'e' ) + return -EINVAL; + ptr++; + + /* Parse major device number */ + *major = strtoul ( ptr, &end, 10 ); + if ( *end != '.' ) + return -EINVAL; + ptr = ( end + 1 ); + + /* Parse minor device number */ + *minor = strtoul ( ptr, &end, 10 ); + if ( *end ) + return -EINVAL; return 0; } /** - * Issue AoE config query for AoE target discovery + * Open AoE URI * - * @v aoe AoE session + * @v parent Parent interface + * @v uri URI * @ret rc Return status code */ -static int aoe_discover ( struct aoe_session *aoe ) { +static int aoe_open ( struct interface *parent, struct uri *uri ) { + struct net_device *netdev; + unsigned int major; + unsigned int minor; int rc; - aoe->status = 0; - aoe->aoe_cmd_type = AOE_CMD_CONFIG; - aoe->command = NULL; + /* Identify network device. This is something of a hack, but + * the AoE URI scheme that has been in use for some time now + * provides no way to specify a particular device. + */ + netdev = last_opened_netdev(); + if ( ! netdev ) { + DBG ( "AoE cannot identify network device\n" ); + return -ENODEV; + } - aoe_send_command ( aoe ); + /* Parse URI */ + if ( ( rc = aoe_parse_uri ( uri, &major, &minor ) ) != 0 ) { + DBG ( "AoE cannot parse URI\n" ); + return rc; + } - aoe->rc = -EINPROGRESS; - while ( aoe->rc == -EINPROGRESS ) - step(); - rc = aoe->rc; - - return rc; -} - -static int aoe_detached_command ( struct ata_device *ata __unused, - struct ata_command *command __unused ) { - return -ENODEV; -} - -void aoe_detach ( struct ata_device *ata ) { - struct aoe_session *aoe = - container_of ( ata->backend, struct aoe_session, refcnt ); - - stop_timer ( &aoe->timer ); - ata->command = aoe_detached_command; - list_del ( &aoe->list ); - ref_put ( ata->backend ); - ata->backend = NULL; -} - -static int aoe_parse_root_path ( struct aoe_session *aoe, - const char *root_path ) { - char *ptr; - - if ( strncmp ( root_path, "aoe:", 4 ) != 0 ) - return -EINVAL; - ptr = ( ( char * ) root_path + 4 ); - - if ( *ptr++ != 'e' ) - return -EINVAL; - - aoe->major = strtoul ( ptr, &ptr, 10 ); - if ( *ptr++ != '.' ) - return -EINVAL; - - aoe->minor = strtoul ( ptr, &ptr, 10 ); - if ( *ptr ) - return -EINVAL; + /* Open AoE device */ + if ( ( rc = aoedev_open ( parent, netdev, major, minor ) ) != 0 ) + return rc; return 0; } -int aoe_attach ( struct ata_device *ata, struct net_device *netdev, - const char *root_path ) { - struct aoe_session *aoe; - int rc; - - /* Allocate and initialise structure */ - aoe = zalloc ( sizeof ( *aoe ) ); - if ( ! aoe ) - return -ENOMEM; - ref_init ( &aoe->refcnt, aoe_free ); - timer_init ( &aoe->timer, aoe_timer_expired, &aoe->refcnt ); - aoe->netdev = netdev_get ( netdev ); - memcpy ( aoe->target, netdev->ll_broadcast, sizeof ( aoe->target ) ); - aoe->tag = AOE_TAG_MAGIC; - - /* Parse root path */ - if ( ( rc = aoe_parse_root_path ( aoe, root_path ) ) != 0 ) - goto err; - - /* Attach parent interface, transfer reference to connection - * list, and return - */ - ata->backend = ref_get ( &aoe->refcnt ); - ata->command = aoe_command; - list_add ( &aoe->list, &aoe_sessions ); - - /* Send discovery packet to find the target MAC address. - * Ideally, this ought to be done asynchronously, but the - * block device interface does not yet support asynchronous - * operation. - */ - if ( ( rc = aoe_discover( aoe ) ) != 0 ) - goto err; - - return 0; - - err: - ref_put ( &aoe->refcnt ); - return rc; -} +/** AoE URI opener */ +struct uri_opener aoe_uri_opener __uri_opener = { + .scheme = "aoe", + .open = aoe_open, +}; diff --git a/src/net/infiniband/ib_srp.c b/src/net/infiniband/ib_srp.c index ef0078d4..41416a20 100644 --- a/src/net/infiniband/ib_srp.c +++ b/src/net/infiniband/ib_srp.c @@ -32,7 +32,11 @@ FILE_LICENCE ( BSD2 ); #include #include +#include +#include +#include #include +#include #include #include #include @@ -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, }; diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 80c63a6b..e19f4d82 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @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 diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 48d1fa60..0421dc43 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -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; } /**