+++ /dev/null
-/*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/**
- * @file
- *
- * El Torito bootable ISO image format
- *
- */
-
-#include <stdint.h>
-#include <errno.h>
-#include <assert.h>
-#include <realmode.h>
-#include <bootsector.h>
-#include <int13.h>
-#include <ipxe/uaccess.h>
-#include <ipxe/image.h>
-#include <ipxe/segment.h>
-#include <ipxe/ramdisk.h>
-#include <ipxe/init.h>
-
-#define ISO9660_BLKSIZE 2048
-#define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE )
-
-/** An El Torito Boot Record Volume Descriptor */
-struct eltorito_vol_desc {
- /** Boot record indicator; must be 0 */
- uint8_t record_indicator;
- /** ISO-9660 identifier; must be "CD001" */
- uint8_t iso9660_id[5];
- /** Version, must be 1 */
- uint8_t version;
- /** Boot system indicator; must be "EL TORITO SPECIFICATION" */
- uint8_t system_indicator[32];
- /** Unused */
- uint8_t unused[32];
- /** Boot catalog sector */
- uint32_t sector;
-} __attribute__ (( packed ));
-
-/** An El Torito Boot Catalog Validation Entry */
-struct eltorito_validation_entry {
- /** Header ID; must be 1 */
- uint8_t header_id;
- /** Platform ID
- *
- * 0 = 80x86
- * 1 = PowerPC
- * 2 = Mac
- */
- uint8_t platform_id;
- /** Reserved */
- uint16_t reserved;
- /** ID string */
- uint8_t id_string[24];
- /** Checksum word */
- uint16_t checksum;
- /** Signature; must be 0xaa55 */
- uint16_t signature;
-} __attribute__ (( packed ));
-
-/** A bootable entry in the El Torito Boot Catalog */
-struct eltorito_boot_entry {
- /** Boot indicator
- *
- * Must be @c ELTORITO_BOOTABLE for a bootable ISO image
- */
- uint8_t indicator;
- /** Media type
- *
- */
- uint8_t media_type;
- /** Load segment */
- uint16_t load_segment;
- /** System type */
- uint8_t filesystem;
- /** Unused */
- uint8_t reserved_a;
- /** Sector count */
- uint16_t length;
- /** Starting sector */
- uint32_t start;
- /** Unused */
- uint8_t reserved_b[20];
-} __attribute__ (( packed ));
-
-/** Boot indicator for a bootable ISO image */
-#define ELTORITO_BOOTABLE 0x88
-
-/** El Torito media types */
-enum eltorito_media_type {
- /** No emulation */
- ELTORITO_NO_EMULATION = 0,
-};
-
-struct image_type eltorito_image_type __image_type ( PROBE_NORMAL );
-
-/**
- * Calculate 16-bit word checksum
- *
- * @v data Data to checksum
- * @v len Length (in bytes, must be even)
- * @ret sum Checksum
- */
-static unsigned int word_checksum ( void *data, size_t len ) {
- uint16_t *words;
- uint16_t sum = 0;
-
- for ( words = data ; len ; words++, len -= 2 ) {
- sum += *words;
- }
- return sum;
-}
-
-/**
- * Execute El Torito image
- *
- * @v image El Torito image
- * @ret rc Return status code
- */
-static int eltorito_exec ( struct image *image ) {
- struct ramdisk ramdisk;
- struct int13_drive int13_drive;
- unsigned int load_segment = image->priv.ul;
- unsigned int load_offset = ( load_segment ? 0 : 0x7c00 );
- int rc;
-
- memset ( &ramdisk, 0, sizeof ( ramdisk ) );
- init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE );
-
- memset ( &int13_drive, 0, sizeof ( int13_drive ) );
- int13_drive.blockdev = &ramdisk.blockdev;
- register_int13_drive ( &int13_drive );
-
- if ( ( rc = call_bootsector ( load_segment, load_offset,
- int13_drive.drive ) ) != 0 ) {
- DBGC ( image, "ElTorito %p boot failed: %s\n",
- image, strerror ( rc ) );
- goto err;
- }
-
- rc = -ECANCELED; /* -EIMPOSSIBLE */
- err:
- unregister_int13_drive ( &int13_drive );
- return rc;
-}
-
-/**
- * Read and verify El Torito Boot Record Volume Descriptor
- *
- * @v image El Torito file
- * @ret catalog_offset Offset of Boot Catalog
- * @ret rc Return status code
- */
-static int eltorito_read_voldesc ( struct image *image,
- unsigned long *catalog_offset ) {
- static const struct eltorito_vol_desc vol_desc_signature = {
- .record_indicator = 0,
- .iso9660_id = "CD001",
- .version = 1,
- .system_indicator = "EL TORITO SPECIFICATION",
- };
- struct eltorito_vol_desc vol_desc;
-
- /* Sanity check */
- if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
- DBGC ( image, "ElTorito %p too short\n", image );
- return -ENOEXEC;
- }
-
- /* Read and verify Boot Record Volume Descriptor */
- copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
- sizeof ( vol_desc ) );
- if ( memcmp ( &vol_desc, &vol_desc_signature,
- offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
- DBGC ( image, "ElTorito %p invalid Boot Record Volume "
- "Descriptor\n", image );
- return -ENOEXEC;
- }
- *catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );
-
- DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
- image, *catalog_offset );
-
- return 0;
-}
-
-/**
- * Read and verify El Torito Boot Catalog
- *
- * @v image El Torito file
- * @v catalog_offset Offset of Boot Catalog
- * @ret boot_entry El Torito boot entry
- * @ret rc Return status code
- */
-static int eltorito_read_catalog ( struct image *image,
- unsigned long catalog_offset,
- struct eltorito_boot_entry *boot_entry ) {
- struct eltorito_validation_entry validation_entry;
-
- /* Sanity check */
- if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
- DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
- image, catalog_offset );
- return -ENOEXEC;
- }
-
- /* Read and verify the Validation Entry of the Boot Catalog */
- copy_from_user ( &validation_entry, image->data, catalog_offset,
- sizeof ( validation_entry ) );
- if ( word_checksum ( &validation_entry,
- sizeof ( validation_entry ) ) != 0 ) {
- DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
- image );
- return -ENOEXEC;
- }
-
- /* Read and verify the Initial/Default entry */
- copy_from_user ( boot_entry, image->data,
- ( catalog_offset + sizeof ( validation_entry ) ),
- sizeof ( *boot_entry ) );
- if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
- DBGC ( image, "ElTorito %p not bootable\n", image );
- return -ENOEXEC;
- }
- if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
- DBGC ( image, "ElTorito %p cannot support media type %d\n",
- image, boot_entry->media_type );
- return -ENOTSUP;
- }
-
- DBGC ( image, "ElTorito %p media type %d segment %04x\n",
- image, boot_entry->media_type, boot_entry->load_segment );
-
- return 0;
-}
-
-/**
- * Load El Torito virtual disk image into memory
- *
- * @v image El Torito file
- * @v boot_entry El Torito boot entry
- * @ret rc Return status code
- */
-static int eltorito_load_disk ( struct image *image,
- struct eltorito_boot_entry *boot_entry ) {
- unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
- unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
- unsigned int load_segment;
- userptr_t buffer;
- int rc;
-
- /* Sanity check */
- if ( image->len < ( start + length ) ) {
- DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
- image );
- return -ENOEXEC;
- }
- DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
- image, start, length );
-
- /* Calculate load address */
- load_segment = boot_entry->load_segment;
- buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );
-
- /* Verify and prepare segment */
- if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
- DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
- image, strerror ( rc ) );
- return rc;
- }
-
- /* Copy image to segment */
- memcpy_user ( buffer, 0, image->data, start, length );
-
- return 0;
-}
-
-/**
- * Load El Torito image into memory
- *
- * @v image El Torito file
- * @ret rc Return status code
- */
-static int eltorito_load ( struct image *image ) {
- struct eltorito_boot_entry boot_entry;
- unsigned long bootcat_offset;
- int rc;
-
- /* Read Boot Record Volume Descriptor, if present */
- if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 )
- return rc;
-
- /* This is an El Torito image, valid or otherwise */
- if ( ! image->type )
- image->type = &eltorito_image_type;
-
- /* Read Boot Catalog */
- if ( ( rc = eltorito_read_catalog ( image, bootcat_offset,
- &boot_entry ) ) != 0 )
- return rc;
-
- /* Load Virtual Disk image */
- if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 )
- return rc;
-
- /* Record load segment in image private data field */
- image->priv.ul = boot_entry.load_segment;
-
- return 0;
-}
-
-/** El Torito image type */
-struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
- .name = "El Torito",
- .load = eltorito_load,
- .exec = eltorito_exec,
-};
+++ /dev/null
-#ifndef ELTORITO_PLATFORM
-#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86
-#endif /* ELTORITO_PLATFORM */
--- /dev/null
+#ifndef _BITS_SANBOOT_H
+#define _BITS_SANBOOT_H
+
+/** @file
+ *
+ * i386-specific sanboot API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/bios_sanboot.h>
+
+#endif /* _BITS_SANBOOT_H */
#include <ipxe/list.h>
#include <realmode.h>
-struct block_device;
-
/**
* @defgroup int13ops INT 13 operation codes
* @{
#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
/** 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 */
uint64_t sectors;
/** Bytes per sector */
uint16_t sector_size;
-
} __attribute__ (( packed ));
/**
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 */
+++ /dev/null
-#ifndef _IPXE_ABFT_H
-#define _IPXE_ABFT_H
-
-/** @file
- *
- * AoE boot firmware table
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <ipxe/acpi.h>
-#include <ipxe/if_ether.h>
-
-/** AoE boot firmware table signature */
-#define ABFT_SIG "aBFT"
-
-/**
- * AoE Boot Firmware Table (aBFT)
- */
-struct abft_table {
- /** ACPI header */
- struct acpi_description_header acpi;
- /** AoE shelf */
- uint16_t shelf;
- /** AoE slot */
- uint8_t slot;
- /** Reserved */
- uint8_t reserved_a;
- /** MAC address */
- uint8_t mac[ETH_ALEN];
-} __attribute__ (( packed ));
-
-extern void abft_fill_data ( struct aoe_session *aoe );
-
-#endif /* _IPXE_ABFT_H */
--- /dev/null
+#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 */
+++ /dev/null
-#ifndef _IPXE_SBFT_H
-#define _IPXE_SBFT_H
-
-/*
- * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-FILE_LICENCE ( BSD2 );
-
-/** @file
- *
- * SRP boot firmware table
- *
- * The working draft specification for the SRP boot firmware table can
- * be found at
- *
- * http://ipxe.org/wiki/srp/sbft
- *
- */
-
-#include <stdint.h>
-#include <ipxe/acpi.h>
-#include <ipxe/scsi.h>
-#include <ipxe/srp.h>
-#include <ipxe/ib_srp.h>
-
-/** SRP Boot Firmware Table signature */
-#define SBFT_SIG "sBFT"
-
-/** An offset from the start of the sBFT */
-typedef uint16_t sbft_off_t;
-
-/**
- * SRP Boot Firmware Table
- */
-struct sbft_table {
- /** ACPI header */
- struct acpi_description_header acpi;
- /** Offset to SCSI subtable */
- sbft_off_t scsi_offset;
- /** Offset to SRP subtable */
- sbft_off_t srp_offset;
- /** Offset to IB subtable, if present */
- sbft_off_t ib_offset;
- /** Reserved */
- uint8_t reserved[6];
-} __attribute__ (( packed ));
-
-/**
- * sBFT SCSI subtable
- */
-struct sbft_scsi_subtable {
- /** LUN */
- struct scsi_lun lun;
-} __attribute__ (( packed ));
-
-/**
- * sBFT SRP subtable
- */
-struct sbft_srp_subtable {
- /** Initiator and target ports */
- struct srp_port_ids port_ids;
-} __attribute__ (( packed ));
-
-/**
- * sBFT IB subtable
- */
-struct sbft_ib_subtable {
- /** Source GID */
- struct ib_gid sgid;
- /** Destination GID */
- struct ib_gid dgid;
- /** Service ID */
- struct ib_gid_half service_id;
- /** Partition key */
- uint16_t pkey;
- /** Reserved */
- uint8_t reserved[6];
-} __attribute__ (( packed ));
-
-/**
- * An sBFT created by iPXE
- */
-struct ipxe_sbft {
- /** The table header */
- struct sbft_table table;
- /** The SCSI subtable */
- struct sbft_scsi_subtable scsi;
- /** The SRP subtable */
- struct sbft_srp_subtable srp;
- /** The IB subtable */
- struct sbft_ib_subtable ib;
-} __attribute__ (( packed, aligned ( 16 ) ));
-
-struct srp_device;
-
-extern int sbft_fill_data ( struct srp_device *srp );
-
-#endif /* _IPXE_SBFT_H */
+++ /dev/null
-/*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <realmode.h>
-#include <ipxe/aoe.h>
-#include <ipxe/netdevice.h>
-#include <ipxe/abft.h>
-
-/** @file
- *
- * AoE Boot Firmware Table
- *
- */
-
-#define abftab __use_data16 ( abftab )
-/** The aBFT used by iPXE */
-struct abft_table __data16 ( abftab ) __attribute__ (( aligned ( 16 ) )) = {
- /* ACPI header */
- .acpi = {
- .signature = ABFT_SIG,
- .length = sizeof ( abftab ),
- .revision = 1,
- .oem_id = "FENSYS",
- .oem_table_id = "iPXE",
- },
-};
-
-/**
- * Fill in all variable portions of aBFT
- *
- * @v aoe AoE session
- */
-void abft_fill_data ( struct aoe_session *aoe ) {
-
- /* Fill in boot parameters */
- abftab.shelf = aoe->major;
- abftab.slot = aoe->minor;
- memcpy ( abftab.mac, aoe->netdev->ll_addr, sizeof ( abftab.mac ) );
-
- /* Update checksum */
- acpi_fix_checksum ( &abftab.acpi );
-
- DBG ( "AoE boot firmware table:\n" );
- DBG_HD ( &abftab, sizeof ( abftab ) );
-}
+++ /dev/null
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <ipxe/aoe.h>
-#include <ipxe/ata.h>
-#include <ipxe/netdevice.h>
-#include <ipxe/sanboot.h>
-#include <ipxe/abft.h>
-#include <int13.h>
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-static int aoeboot ( const char *root_path ) {
- struct ata_device *ata;
- struct int13_drive *drive;
- int rc;
-
- ata = zalloc ( sizeof ( *ata ) );
- if ( ! ata ) {
- rc = -ENOMEM;
- goto err_alloc_ata;
- }
- drive = zalloc ( sizeof ( *drive ) );
- if ( ! drive ) {
- rc = -ENOMEM;
- goto err_alloc_drive;
- }
-
- /* FIXME: ugly, ugly hack */
- struct net_device *netdev = last_opened_netdev();
-
- if ( ( rc = aoe_attach ( ata, netdev, root_path ) ) != 0 ) {
- printf ( "Could not attach AoE device: %s\n",
- strerror ( rc ) );
- goto err_attach;
- }
- if ( ( rc = init_atadev ( ata ) ) != 0 ) {
- printf ( "Could not initialise AoE device: %s\n",
- strerror ( rc ) );
- goto err_init;
- }
-
- /* FIXME: ugly, ugly hack */
- struct aoe_session *aoe =
- container_of ( ata->backend, struct aoe_session, refcnt );
- abft_fill_data ( aoe );
-
- drive->blockdev = &ata->blockdev;
-
- register_int13_drive ( drive );
- printf ( "Registered as BIOS drive %#02x\n", drive->drive );
- printf ( "Booting from BIOS drive %#02x\n", drive->drive );
- rc = int13_boot ( drive->drive );
- printf ( "Boot failed\n" );
-
- /* Leave drive registered, if instructed to do so */
- if ( keep_san() )
- return rc;
-
- printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
- unregister_int13_drive ( drive );
-
- err_init:
- aoe_detach ( ata );
- err_attach:
- free ( drive );
- err_alloc_drive:
- free ( ata );
- err_alloc_ata:
- return rc;
-}
-
-struct sanboot_protocol aoe_sanboot_protocol __sanboot_protocol = {
- .prefix = "aoe:",
- .boot = aoeboot,
-};
+++ /dev/null
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <ipxe/sanboot.h>
-#include <int13.h>
-#include <ipxe/srp.h>
-#include <ipxe/sbft.h>
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-static int ib_srpboot ( const char *root_path ) {
- struct scsi_device *scsi;
- struct int13_drive *drive;
- int rc;
-
- scsi = zalloc ( sizeof ( *scsi ) );
- if ( ! scsi ) {
- rc = -ENOMEM;
- goto err_alloc_scsi;
- }
- drive = zalloc ( sizeof ( *drive ) );
- if ( ! drive ) {
- rc = -ENOMEM;
- goto err_alloc_drive;
- }
-
- if ( ( rc = srp_attach ( scsi, root_path ) ) != 0 ) {
- printf ( "Could not attach IB_SRP device: %s\n",
- strerror ( rc ) );
- goto err_attach;
- }
- if ( ( rc = init_scsidev ( scsi ) ) != 0 ) {
- printf ( "Could not initialise IB_SRP device: %s\n",
- strerror ( rc ) );
- goto err_init;
- }
-
- drive->blockdev = &scsi->blockdev;
-
- /* FIXME: ugly, ugly hack */
- struct srp_device *srp =
- container_of ( scsi->backend, struct srp_device, refcnt );
- sbft_fill_data ( srp );
-
- register_int13_drive ( drive );
- printf ( "Registered as BIOS drive %#02x\n", drive->drive );
- printf ( "Booting from BIOS drive %#02x\n", drive->drive );
- rc = int13_boot ( drive->drive );
- printf ( "Boot failed\n" );
-
- /* Leave drive registered, if instructed to do so */
- if ( keep_san() )
- return rc;
-
- printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
- unregister_int13_drive ( drive );
-
- err_init:
- srp_detach ( scsi );
- err_attach:
- free ( drive );
- err_alloc_drive:
- free ( scsi );
- err_alloc_scsi:
- return rc;
-}
-
-struct sanboot_protocol ib_srp_sanboot_protocol __sanboot_protocol = {
- .prefix = "ib_srp:",
- .boot = ib_srpboot,
-};
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
+#include <stdlib.h>
#include <limits.h>
#include <byteswap.h>
#include <errno.h>
#include <ipxe/list.h>
#include <ipxe/blockdev.h>
#include <ipxe/io.h>
+#include <ipxe/open.h>
+#include <ipxe/uri.h>
+#include <ipxe/process.h>
+#include <ipxe/xfer.h>
+#include <ipxe/retry.h>
+#include <ipxe/timer.h>
+#include <ipxe/acpi.h>
+#include <ipxe/sanboot.h>
#include <realmode.h>
#include <bios.h>
#include <biosint.h>
*
*/
+/**
+ * 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 )
extern void int13_wrapper ( void );
/** List of registered emulated drives */
-static LIST_HEAD ( drives );
+static LIST_HEAD ( int13s );
/**
* Number of BIOS 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 */
/**
* 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;
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;
}
/**
* 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
* @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
* @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
* @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 );
/**
* 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;
/**
* 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;
/**
* 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;
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;
}
/**
* 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 ) );
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;
}
* 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()).
/**
* 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;
+
+ /* 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;
+ }
+ }
- /* 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;
+ /* 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;
- 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 );
+ /* 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 ) {
+static void int13_unhook ( unsigned int drive ) {
+ struct int13_drive *int13;
+
+ /* 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 ( &drive->list );
+ list_del ( &int13->list );
- /* Should adjust BIOS drive count, but it's difficult to do so
- * reliably.
+ /* 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 );
}
/**
*
* 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"
/* 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;
}
/* 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 );
+++ /dev/null
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <ipxe/iscsi.h>
-#include <ipxe/netdevice.h>
-#include <ipxe/ibft.h>
-#include <ipxe/sanboot.h>
-#include <int13.h>
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-static int iscsiboot ( const char *root_path ) {
- struct scsi_device *scsi;
- struct int13_drive *drive;
- int rc;
-
- scsi = zalloc ( sizeof ( *scsi ) );
- if ( ! scsi ) {
- rc = -ENOMEM;
- goto err_alloc_scsi;
- }
- drive = zalloc ( sizeof ( *drive ) );
- if ( ! drive ) {
- rc = -ENOMEM;
- goto err_alloc_drive;
- }
-
- if ( ( rc = iscsi_attach ( scsi, root_path ) ) != 0 ) {
- printf ( "Could not attach iSCSI device: %s\n",
- strerror ( rc ) );
- goto err_attach;
- }
- if ( ( rc = init_scsidev ( scsi ) ) != 0 ) {
- printf ( "Could not initialise iSCSI device: %s\n",
- strerror ( rc ) );
- goto err_init;
- }
-
- drive->blockdev = &scsi->blockdev;
-
- /* FIXME: ugly, ugly hack */
- struct net_device *netdev = last_opened_netdev();
- struct iscsi_session *iscsi =
- container_of ( scsi->backend, struct iscsi_session, refcnt );
- ibft_fill_data ( netdev, iscsi );
-
- register_int13_drive ( drive );
- printf ( "Registered as BIOS drive %#02x\n", drive->drive );
- printf ( "Booting from BIOS drive %#02x\n", drive->drive );
- rc = int13_boot ( drive->drive );
- printf ( "Boot failed\n" );
-
- /* Leave drive registered, if instructed to do so */
- if ( keep_san() )
- return rc;
-
- printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
- unregister_int13_drive ( drive );
-
- err_init:
- iscsi_detach ( scsi );
- err_attach:
- free ( drive );
- err_alloc_drive:
- free ( scsi );
- err_alloc_scsi:
- return rc;
-}
-
-struct sanboot_protocol iscsi_sanboot_protocol __sanboot_protocol = {
- .prefix = "iscsi:",
- .boot = iscsiboot,
-};
+++ /dev/null
-#include <stdint.h>
-#include <stdio.h>
-#include <ipxe/settings.h>
-#include <ipxe/dhcp.h>
-#include <ipxe/init.h>
-#include <ipxe/sanboot.h>
-#include <usr/autoboot.h>
-
-struct setting keep_san_setting __setting = {
- .name = "keep-san",
- .description = "Preserve SAN connection",
- .tag = DHCP_EB_KEEP_SAN,
- .type = &setting_type_int8,
-};
-
-int keep_san ( void ) {
- int keep_san;
-
- keep_san = fetch_intz_setting ( NULL, &keep_san_setting );
- if ( ! keep_san )
- return 0;
-
- printf ( "Preserving connection to SAN disk\n" );
- shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES;
- return 1;
-}
+++ /dev/null
-/*
- * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-FILE_LICENCE ( BSD2 );
-
-/** @file
- *
- * SRP boot firmware table
- *
- */
-
-#include <assert.h>
-#include <realmode.h>
-#include <ipxe/srp.h>
-#include <ipxe/ib_srp.h>
-#include <ipxe/acpi.h>
-#include <ipxe/sbft.h>
-
-#define sbftab __use_data16 ( sbftab )
-/** The sBFT used by iPXE */
-struct ipxe_sbft __data16 ( sbftab ) = {
- /* Table header */
- .table = {
- /* ACPI header */
- .acpi = {
- .signature = SBFT_SIG,
- .length = sizeof ( sbftab ),
- .revision = 1,
- .oem_id = "FENSYS",
- .oem_table_id = "iPXE",
- },
- .scsi_offset = offsetof ( typeof ( sbftab ), scsi ),
- .srp_offset = offsetof ( typeof ( sbftab ), srp ),
- .ib_offset = offsetof ( typeof ( sbftab ), ib ),
- },
-};
-
-/**
- * Fill in all variable portions of sBFT
- *
- * @v srp SRP device
- * @ret rc Return status code
- */
-int sbft_fill_data ( struct srp_device *srp ) {
- struct sbft_scsi_subtable *sbft_scsi = &sbftab.scsi;
- struct sbft_srp_subtable *sbft_srp = &sbftab.srp;
- struct sbft_ib_subtable *sbft_ib = &sbftab.ib;
- struct ib_srp_parameters *ib_params;
- struct segoff rm_sbftab = {
- .segment = rm_ds,
- .offset = __from_data16 ( &sbftab ),
- };
-
- /* Fill in the SCSI subtable */
- memcpy ( &sbft_scsi->lun, &srp->lun, sizeof ( sbft_scsi->lun ) );
-
- /* Fill in the SRP subtable */
- memcpy ( &sbft_srp->port_ids, &srp->port_ids,
- sizeof ( sbft_srp->port_ids ) );
-
- /* Fill in the IB subtable */
- assert ( srp->transport == &ib_srp_transport );
- ib_params = ib_srp_params ( srp );
- memcpy ( &sbft_ib->sgid, &ib_params->sgid, sizeof ( sbft_ib->sgid ) );
- memcpy ( &sbft_ib->dgid, &ib_params->dgid, sizeof ( sbft_ib->dgid ) );
- memcpy ( &sbft_ib->service_id, &ib_params->service_id,
- sizeof ( sbft_ib->service_id ) );
- sbft_ib->pkey = ib_params->pkey;
-
- /* Update checksum */
- acpi_fix_checksum ( &sbftab.table.acpi );
-
- DBGC ( &sbftab, "SRP Boot Firmware Table at %04x:%04x:\n",
- rm_sbftab.segment, rm_sbftab.offset );
- DBGC_HDA ( &sbftab, rm_sbftab, &sbftab, sizeof ( sbftab ) );
-
- return 0;
-}
--- /dev/null
+#ifndef _BITS_SANBOOT_H
+#define _BITS_SANBOOT_H
+
+/** @file
+ *
+ * x86_64-specific sanboot API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#endif /* _BITS_SANBOOT_H */
*
*/
#ifdef SANBOOT_PROTO_ISCSI
-REQUIRE_OBJECT ( iscsiboot );
+REQUIRE_OBJECT ( iscsi );
#endif
/*
* Drag in Ethernet-specific protocols
*/
#ifdef SANBOOT_PROTO_AOE
-REQUIRE_OBJECT ( aoeboot );
+REQUIRE_OBJECT ( aoe );
#endif
* Drag in Infiniband-specific protocols
*/
#ifdef SANBOOT_PROTO_IB_SRP
-REQUIRE_OBJECT ( ib_srpboot );
+REQUIRE_OBJECT ( ib_srp );
#endif
#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 */
#define UMALLOC_LINUX
#define NAP_LINUX
#define SMBIOS_LINUX
+#define SANBOOT_NULL
#define DRIVERS_LINUX
#define NAP_PCBIOS
#define UMALLOC_MEMTOP
#define SMBIOS_PCBIOS
+#define SANBOOT_PCBIOS
#define IMAGE_ELF /* ELF image support */
#define IMAGE_MULTIBOOT /* MultiBoot image support */
--- /dev/null
+#ifndef CONFIG_SANBOOT_H
+#define CONFIG_SANBOOT_H
+
+/** @file
+ *
+ * sanboot API configuration
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <config/defaults.h>
+
+#include <config/local/sanboot.h>
+
+#endif /* CONFIG_SANBOOT_H */
FILE_LICENCE ( GPL2_OR_LATER );
+#include <errno.h>
#include <ipxe/acpi.h>
+#include <ipxe/interface.h>
/** @file
*
*
*/
+/******************************************************************************
+ *
+ * Utility functions
+ *
+ ******************************************************************************
+ */
+
/**
* Fix up ACPI table checksum
*
}
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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <ipxe/interface.h>
+#include <ipxe/blockdev.h>
+
+/** @file
+ *
+ * Block devices
+ *
+ */
+
+/**
+ * Read from block device
+ *
+ * @v control Control interface
+ * @v data Data interface
+ * @v lba Starting logical block address
+ * @v count Number of logical blocks
+ * @v buffer Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int block_read ( struct interface *control, struct interface *data,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ struct interface *dest;
+ block_read_TYPE ( void * ) *op =
+ intf_get_dest_op ( control, block_read, &dest );
+ void *object = intf_object ( dest );
+ int rc;
+
+ if ( op ) {
+ rc = op ( object, data, lba, count, buffer, len );
+ } else {
+ /* Default is to fail to issue the command */
+ rc = -EOPNOTSUPP;
+ }
+
+ intf_put ( dest );
+ return rc;
+}
+
+/**
+ * Write to block device
+ *
+ * @v control Control interface
+ * @v data Data interface
+ * @v lba Starting logical block address
+ * @v count Number of logical blocks
+ * @v buffer Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+int block_write ( struct interface *control, struct interface *data,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ struct interface *dest;
+ block_write_TYPE ( void * ) *op =
+ intf_get_dest_op ( control, block_write, &dest );
+ void *object = intf_object ( dest );
+ int rc;
+
+ if ( op ) {
+ rc = op ( object, data, lba, count, buffer, len );
+ } else {
+ /* Default is to fail to issue the command */
+ rc = -EOPNOTSUPP;
+ }
+
+ intf_put ( dest );
+ return rc;
+}
+
+/**
+ * Read block device capacity
+ *
+ * @v control Control interface
+ * @v data Data interface
+ * @ret rc Return status code
+ */
+int block_read_capacity ( struct interface *control, struct interface *data ) {
+ struct interface *dest;
+ block_read_capacity_TYPE ( void * ) *op =
+ intf_get_dest_op ( control, block_read_capacity, &dest );
+ void *object = intf_object ( dest );
+ int rc;
+
+ if ( op ) {
+ rc = op ( object, data );
+ } else {
+ /* Default is to fail to issue the command */
+ rc = -EOPNOTSUPP;
+ }
+
+ intf_put ( dest );
+ return rc;
+}
+
+/**
+ * Report block device capacity
+ *
+ * @v intf Interface
+ * @v capacity Block device capacity
+ */
+void block_capacity ( struct interface *intf,
+ struct block_device_capacity *capacity ) {
+ struct interface *dest;
+ block_capacity_TYPE ( void * ) *op =
+ intf_get_dest_op ( intf, block_capacity, &dest );
+ void *object = intf_object ( dest );
+
+ if ( op ) {
+ op ( object, capacity );
+ } else {
+ /* Default is to do nothing */
+ }
+
+ intf_put ( dest );
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <ipxe/sanboot.h>
+
+static int null_san_hook ( struct uri *uri __unused,
+ unsigned int drive __unused ) {
+ return -EOPNOTSUPP;
+}
+
+static void null_san_unhook ( unsigned int drive __unused ) {
+ /* Do nothing */
+}
+
+static int null_san_boot ( unsigned int drive __unused ) {
+ return -EOPNOTSUPP;
+}
+
+static int null_san_describe ( unsigned int drive __unused ) {
+ return -EOPNOTSUPP;
+}
+
+PROVIDE_SANBOOT ( null, san_hook, null_san_hook );
+PROVIDE_SANBOOT ( null, san_unhook, null_san_unhook );
+PROVIDE_SANBOOT ( null, san_boot, null_san_boot );
+PROVIDE_SANBOOT ( null, san_describe, null_san_describe );
FILE_LICENCE ( GPL2_OR_LATER );
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
+#include <ipxe/list.h>
+#include <ipxe/interface.h>
#include <ipxe/blockdev.h>
-#include <ipxe/process.h>
#include <ipxe/ata.h>
/** @file
*
*/
-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;
+
+ if ( op ) {
+ tag = op ( object, data, command );
+ } else {
+ /* Default is to fail to issue the command */
+ tag = -EOPNOTSUPP;
+ }
+
+ intf_put ( dest );
+ return tag;
+}
+
+/******************************************************************************
+ *
+ * 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 );
+};
+
+/**
+ * Get reference to ATA device
+ *
+ * @v atadev ATA device
+ * @ret atadev ATA device
+ */
+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;
+}
- 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 );
+/**
+ * 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;
- /* Flag command as in-progress */
- command->rc = -EINPROGRESS;
+ for ( i = 0 ; i < ( sizeof ( identity->model ) / 2 ) ; i++ )
+ buf.words[i] = bswap_16 ( identity->model[i] );
- /* 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;
+ 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;
}
- /* 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;
+ /* 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 0;
+ /* 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 );
+
/**
- * Read block from ATA device
+ * Create ATA command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
+ * @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 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;
+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 = block;
+ command.cb.lba.native = lba;
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 = ( 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 = ( ata->lba48 ? ATA_CMD_READ_EXT : ATA_CMD_READ );
- command.data_in = buffer;
- return ata_command ( ata, &command );
+ 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;
}
/**
- * Write block to ATA device
+ * Issue ATA block read
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
+ * @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 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 );
+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 );
}
/**
- * Identify ATA device
+ * Issue ATA block write
*
- * @v blockdev Block device
+ * @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 ata_identify ( struct block_device *blockdev ) {
- struct ata_device *ata = block_to_ata ( blockdev );
- struct ata_command command;
- struct ata_identity identity;
- int rc;
+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 );
+}
- /* 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 );
- } else {
- blockdev->blocks = le32_to_cpu ( identity.lba_sectors );
+/**
+ * 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 );
}
- return 0;
}
-static struct block_device_operations ata_operations = {
- .read = ata_read,
- .write = ata_write
+/** 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 );
+
/**
- * Initialise ATA device
+ * Open ATA device
*
- * @v ata 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
- *
- * 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.
*/
-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 );
+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;
}
#include <string.h>
#include <errno.h>
#include <byteswap.h>
-#include <realmode.h>
#include <ipxe/pci.h>
#include <ipxe/acpi.h>
#include <ipxe/in.h>
*
*/
-#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;
};
/**
* @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 );
}
* @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 );
}
/**
* @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;
* @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;
}
* @v string String field
* @ret data String content (or "<empty>")
*/
-static const char * ibft_string ( struct ibft_string_block *strings,
+static const char * ibft_string ( struct ibft_strings *strings,
struct ibft_string *string ) {
return ( string->offset ?
( ( ( char * ) strings->table ) + string->offset ) : NULL );
* @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 ) );
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;
}
* @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;
* @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;
* @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;
* @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;
}
/**
- * 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;
}
+++ /dev/null
-/*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <ipxe/blockdev.h>
-#include <ipxe/ramdisk.h>
-
-/**
- * @file
- *
- * RAM disks
- *
- */
-
-static inline __attribute__ (( always_inline )) struct ramdisk *
-block_to_ramdisk ( struct block_device *blockdev ) {
- return container_of ( blockdev, struct ramdisk, blockdev );
-}
-
-/**
- * Read block
- *
- * @v blockdev Block device
- * @v block Block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
- */
-static int ramdisk_read ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
- unsigned long offset = ( block * blockdev->blksize );
- unsigned long length = ( count * blockdev->blksize );
-
- DBGC ( ramdisk, "RAMDISK %p reading [%lx,%lx)\n",
- ramdisk, offset, length );
-
- memcpy_user ( buffer, 0, ramdisk->data, offset, length );
- return 0;
-}
-
-/**
- * Write block
- *
- * @v blockdev Block device
- * @v block Block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
- */
-static int ramdisk_write ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
- unsigned long offset = ( block * blockdev->blksize );
- unsigned long length = ( count * blockdev->blksize );
-
- DBGC ( ramdisk, "RAMDISK %p writing [%lx,%lx)\n",
- ramdisk, offset, length );
-
- memcpy_user ( ramdisk->data, offset, buffer, 0, length );
- return 0;
-}
-
-static struct block_device_operations ramdisk_operations = {
- .read = ramdisk_read,
- .write = ramdisk_write
-};
-
-int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
- unsigned int blksize ) {
-
- if ( ! blksize )
- blksize = 512;
-
- ramdisk->data = data;
- ramdisk->blockdev.op = &ramdisk_operations;
- ramdisk->blockdev.blksize = blksize;
- ramdisk->blockdev.blocks = ( len / blksize );
-
- return 0;
-}
#include <string.h>
#include <byteswap.h>
#include <errno.h>
+#include <ipxe/list.h>
#include <ipxe/blockdev.h>
-#include <ipxe/process.h>
#include <ipxe/scsi.h>
/** @file
*
*/
-/** Maximum number of dummy "read capacity (10)" operations
+/** Maximum number of command retries */
+#define SCSICMD_MAX_RETRIES 10
+
+/******************************************************************************
+ *
+ * Utility functions
*
- * These are issued at connection setup to draw out various useless
- * power-on messages.
+ ******************************************************************************
*/
-#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
+ * Parse SCSI LUN
*
- * @v scsi SCSI device
- * @v command SCSI command
+ * @v lun_string LUN string representation
+ * @v lun LUN to fill in
* @ret rc Return status code
*/
-int scsi_detached_command ( struct scsi_device *scsi __unused,
- struct scsi_command *command __unused ) {
- return -ENODEV;
+int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) {
+ char *p;
+ int i;
+
+ memset ( lun, 0, sizeof ( *lun ) );
+ if ( lun_string ) {
+ p = ( char * ) lun_string;
+ for ( i = 0 ; i < 4 ; i++ ) {
+ lun->u16[i] = htons ( strtoul ( p, &p, 16 ) );
+ if ( *p == '\0' )
+ break;
+ if ( *p != '-' )
+ return -EINVAL;
+ p++;
+ }
+ if ( *p )
+ return -EINVAL;
+ }
+
+ return 0;
}
+/******************************************************************************
+ *
+ * Interface methods
+ *
+ ******************************************************************************
+ */
+
/**
* Issue SCSI command
*
- * @v scsi SCSI device
+ * @v control SCSI control interface
+ * @v data SCSI data interface
* @v command SCSI command
- * @ret rc Return status code
+ * @ret tag Command tag, or negative error
*/
-static int scsi_command ( struct scsi_device *scsi,
- struct scsi_command *command ) {
- int rc;
+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;
+ }
- DBGC2 ( scsi, "SCSI %p " SCSI_CDB_FORMAT "\n",
- scsi, SCSI_CDB_DATA ( command->cdb ) );
+ intf_put ( dest );
+ return tap;
+}
- /* Clear sense response code before issuing command */
- command->sense_response = 0;
+/**
+ * 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 */
+ }
- /* Flag command as in-progress */
- command->rc = -EINPROGRESS;
+ intf_put ( dest );
+}
- /* 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;
- }
+/******************************************************************************
+ *
+ * SCSI devices and commands
+ *
+ ******************************************************************************
+ */
- /* 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;
- }
+/** A SCSI device */
+struct scsi_device {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** Block control interface */
+ struct interface block;
+ /** SCSI control interface */
+ struct interface scsi;
- /* 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;
- }
+ /** SCSI LUN */
+ struct scsi_lun lun;
- return 0;
+ /** 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;
}
/**
- * Read block from SCSI device using READ (10)
+ * Drop reference to SCSI device
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v scsidev SCSI device
*/
-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;
+static inline __attribute__ (( always_inline )) void
+scsidev_put ( struct scsi_device *scsidev ) {
+ ref_put ( &scsidev->refcnt );
+}
- /* 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 );
+/**
+ * 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;
}
/**
- * Read block from SCSI device using READ (16)
+ * Drop reference to SCSI command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v scsicmd SCSI command
*/
-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;
+static inline __attribute__ (( always_inline )) void
+scsicmd_put ( struct scsi_command *scsicmd ) {
+ ref_put ( &scsicmd->refcnt );
+}
- /* 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 );
+/**
+ * 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;
}
/**
- * Write block to SCSI device using WRITE (10)
+ * Free SCSI command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v refcnt Reference count
*/
-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;
+static void scsicmd_free ( struct refcnt *refcnt ) {
+ struct scsi_command *scsicmd =
+ container_of ( refcnt, struct scsi_command, refcnt );
- /* 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 );
+ /* Remove from list of commands */
+ list_del ( &scsicmd->list );
+ scsidev_put ( scsicmd->scsidev );
+
+ /* Free command */
+ free ( scsicmd );
}
/**
- * Write block to SCSI device using WRITE (16)
+ * Close SCSI command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v scsicmd SCSI command
+ * @v rc Reason for close
*/
-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;
+static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
- /* 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 );
+ 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 );
}
/**
- * Read capacity of SCSI device via READ CAPACITY (10)
+ * Construct and issue SCSI command
*
- * @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;
+static int scsicmd_command ( struct scsi_command *scsicmd ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
+ struct scsi_cmd command;
+ int tag;
int rc;
- /* Issue READ CAPACITY (10) */
+ /* Construct command */
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 )
+ 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;
+ }
- /* Fill in block device fields */
- blockdev->blksize = be32_to_cpu ( capacity.blksize );
- blockdev->blocks = ( be32_to_cpu ( capacity.lba ) + 1 );
+ /* 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;
}
/**
- * Read capacity of SCSI device via READ CAPACITY (16)
+ * Handle SCSI command completion
*
- * @v blockdev Block device
- * @ret rc Return status code
+ * @v scsicmd SCSI command
+ * @v rc Reason for close
*/
-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;
+static void scsicmd_done ( struct scsi_command *scsicmd, int rc ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
- /* 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 );
+ /* Restart SCSI interface */
+ intf_restart ( &scsicmd->scsi, rc );
- if ( ( rc = scsi_command ( scsi, &command ) ) != 0 )
- return 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;
+ }
- /* Fill in block device fields */
- blockdev->blksize = be32_to_cpu ( capacity.blksize );
- blockdev->blocks = ( be64_to_cpu ( capacity.lba ) + 1 );
- return 0;
+ /* If we didn't (successfully) reissue the command, hand over
+ * to the command completion handler.
+ */
+ scsicmd->type->done ( scsicmd, rc );
}
-static struct block_device_operations scsi_operations_16 = {
- .read = scsi_read_16,
- .write = scsi_write_16,
-};
+/**
+ * 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 );
+ }
+}
-static struct block_device_operations scsi_operations_10 = {
- .read = scsi_read_10,
- .write = scsi_write_10,
+/**
+ * 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,
};
/**
- * Initialise SCSI device
+ * Construct SCSI WRITE command
*
- * @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.
+ * @v scsicmd SCSI command
+ * @v command SCSI command IU
*/
-int init_scsidev ( struct scsi_device *scsi ) {
- unsigned int i;
- int rc;
+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;
+}
- /* 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 ) );
+/** 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 );
}
+}
- /* 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;
+/**
+ * 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;
}
- /* 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;
+ /* 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 ),
+};
- 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 );
+/** 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;
}
/**
- * Parse SCSI LUN
+ * Issue SCSI block read
*
- * @v lun_string LUN string representation
- * @v lun LUN to fill in
+ * @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
+
*/
-int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) {
- char *p;
- int i;
+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 );
+}
- memset ( lun, 0, sizeof ( *lun ) );
- if ( lun_string ) {
- p = ( char * ) lun_string;
- for ( i = 0 ; i < 4 ; i++ ) {
- lun->u16[i] = htons ( strtoul ( p, &p, 16 ) );
- if ( *p == '\0' )
- break;
- if ( *p != '-' )
- return -EINVAL;
- p++;
- }
- if ( *p )
- return -EINVAL;
+/**
+ * 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;
}
#include <ipxe/scsi.h>
#include <ipxe/xfer.h>
#include <ipxe/features.h>
-#include <ipxe/ib_srp.h>
#include <ipxe/srp.h>
/**
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
+
+/** 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;
+};
+
+/**
+ * Get reference to SRP device
+ *
+ * @v srpdev SRP device
+ * @ret srpdev SRP device
+ */
+static inline __attribute__ (( always_inline )) struct srp_device *
+srpdev_get ( struct srp_device *srpdev ) {
+ ref_get ( &srpdev->refcnt );
+ return srpdev;
+}
-static void srp_login ( struct srp_device *srp );
-static void srp_cmd ( struct srp_device *srp );
+/**
+ * Drop reference to SRP device
+ *
+ * @v srpdev SRP device
+ */
+static inline __attribute__ (( always_inline )) void
+srpdev_put ( struct srp_device *srpdev ) {
+ ref_put ( &srpdev->refcnt );
+}
+
+/**
+ * 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;
+}
/**
- * Mark SRP SCSI command as complete
+ * Drop reference to SRP command
*
- * @v srp SRP device
- * @v rc Status code
+ * @v srpcmd SRP command
*/
-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 )) void
+srpcmd_put ( struct srp_command *srpcmd ) {
+ ref_put ( &srpcmd->refcnt );
}
/**
- * Handle SRP session failure
+ * Free SRP command
*
- * @v srp SRP device
- * @v rc Reason for failure
+ * @v refcnt Reference count
*/
-static void srp_fail ( struct srp_device *srp, int rc ) {
+static void srpcmd_free ( struct refcnt *refcnt ) {
+ struct srp_command *srpcmd =
+ container_of ( refcnt, struct srp_command, refcnt );
- /* Close underlying socket */
- intf_restart ( &srp->socket, rc );
+ assert ( list_empty ( &srpcmd->list ) );
- /* Clear session state */
- srp->state = 0;
+ srpdev_put ( srpcmd->srpdev );
+ free ( srpcmd );
+}
- /* If we have reached the retry limit, report the failure */
- if ( srp->retry_count >= SRP_MAX_RETRIES ) {
- srp_scsi_done ( srp, rc );
- return;
+/**
+ * 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 ) {
- struct io_buffer *iobuf;
- struct srp_login_req *login_req;
- int rc;
+static void srpdev_close ( struct srp_device *srpdev, int rc ) {
+ struct srp_command *srpcmd;
+ struct srp_command *tmp;
- assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) );
+ if ( rc != 0 ) {
+ DBGC ( srpdev, "SRP %p closed: %s\n",
+ srpdev, strerror ( rc ) );
+ }
- /* 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;
+ /* 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 );
}
- 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;
+/**
+ * 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;
+
+ /* Allocate I/O buffer */
+ 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;
}
-
- /* 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 ) );
- }
+ 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;
}
- 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 ) );
+
+ /* Hold command reference for remainder of function */
+ srpcmd_get ( srpcmd );
+
+ /* 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);
}
- srp->command->status = rsp->status;
+ sense = srp_rsp_sense_data ( rsp );
+ if ( sense )
+ memcpy ( &response.sense, sense, sizeof ( response.sense ) );
- /* Mark SCSI command as complete */
- srp_scsi_done ( srp, 0 );
+ /* Report SCSI response */
+ scsi_response ( &srpcmd->scsi, &response );
- rc = 0;
- out:
- free_iob ( iobuf );
- return rc;
+ /* Close SCSI command */
+ srpcmd_close ( srpcmd, 0 );
+
+ /* Drop temporary command reference */
+ srpcmd_put ( srpcmd );
+
+ return 0;
}
/**
- * Handle SRP unrecognised response
+ * Receive SRP unrecognised response IU
*
- * @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_unrecognised ( struct srp_device *srp,
- struct io_buffer *iobuf ) {
- struct srp_common *common = iobuf->data;
+static int srp_unrecognised ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_common *common = 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 );
+ 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 );
- free_iob ( iobuf );
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;
+}
+
/**
- * Receive data from underlying socket
+ * Receive data from SRP 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:
}
/* 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 ),
-};
+/**
+ * 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 );
+}
-/** SRP data transfer interface descriptor */
-static struct interface_descriptor srp_xfer_desc =
- INTF_DESC ( struct srp_device, socket, srp_xfer_operations );
+/**
+ * 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 ) ));
/**
- * Issue SCSI command via SRP
+ * 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 */
+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;
+
+ /* Sanity check */
+ if ( len < sizeof ( *sbft ) )
+ return -ENOBUFS;
+
+ /* 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 ) );
+
+ /* 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;
}
return 0;
}
+/** 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 );
+
/**
- * Attach SRP device
+ * Open SRP device
*
- * @v scsi SCSI device
- * @v root_path Root path
+ * @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
*/
-int srp_attach ( struct scsi_device *scsi, const char *root_path ) {
- struct srp_transport_type *transport;
- struct srp_device *srp;
+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;
- /* Hard-code an IB SRP back-end for now */
- transport = &ib_srp_transport;
-
/* Allocate and initialise structure */
- srp = zalloc ( sizeof ( *srp ) + transport->priv_len );
- if ( ! srp ) {
+ srpdev = zalloc ( sizeof ( *srpdev ) );
+ if ( ! srpdev ) {
rc = -ENOMEM;
- goto err_alloc;
+ goto err_zalloc;
}
- 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 );
-
- /* 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;
+ 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;
}
- /* Attach parent interface, mortalise self, and return */
- scsi->backend = ref_get ( &srp->refcnt );
- scsi->command = srp_command;
- ref_put ( &srp->refcnt );
+ /* Mortalise self and return */
+ ref_put ( &srpdev->refcnt );
return 0;
- err_parse_root_path:
- ref_put ( &srp->refcnt );
- err_alloc:
+ err_scsi_open:
+ err_login:
+ srpdev_close ( srpdev, rc );
+ ref_put ( &srpdev->refcnt );
+ err_zalloc:
return rc;
}
-
-/**
- * Detach SRP device
- *
- * @v scsi SCSI device
- */
-void srp_detach ( struct scsi_device *scsi ) {
- struct srp_device *srp =
- container_of ( scsi->backend, struct srp_device, refcnt );
-
- /* Close socket */
- intf_shutdown ( &srp->socket, 0 );
- scsi->command = scsi_detached_command;
- ref_put ( scsi->backend );
- scsi->backend = NULL;
-}
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
+#include <ipxe/interface.h>
/**
* An ACPI description header
*/
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 */
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 */
#include <ipxe/if_ether.h>
#include <ipxe/retry.h>
#include <ipxe/ata.h>
+#include <ipxe/acpi.h>
/** An AoE config command */
struct aoecfg {
- /** AoE Queue depth */
+ /** AoE queue depth */
uint16_t bufcnt;
/** ATA target firmware version */
uint16_t fwver;
/** 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 */
#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 */
#define _IPXE_ATA_H
#include <stdint.h>
-#include <ipxe/blockdev.h>
#include <ipxe/uaccess.h>
-#include <ipxe/refcnt.h>
+#include <ipxe/interface.h>
/** @file
*
uint8_t device;
/** Command/status register */
uint8_t cmd_stat;
- /** LBA48 addressing flag */
+ /** Use LBA48 extended addressing */
int lba48;
};
/** "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
* 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 */
/** 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 */
FILE_LICENCE ( GPL2_OR_LATER );
+#include <stdint.h>
#include <ipxe/uaccess.h>
+#include <ipxe/interface.h>
-struct block_device;
-
-/** Block device operations */
-struct block_device_operations {
- /**
- * Read block
- *
- * @v blockdev Block device
- * @v block Block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
- */
- int ( * read ) ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer );
- /**
- * Write block
- *
- * @v blockdev Block device
- * @v block Block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
- */
- int ( * write ) ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer );
-};
-
-/** A block device */
-struct block_device {
- /** Block device operations */
- struct block_device_operations *op;
- /** Block size */
- size_t blksize;
+/** Block device capacity */
+struct block_device_capacity {
/** Total number of blocks */
uint64_t blocks;
+ /** Block size */
+ size_t blksize;
+ /** Maximum number of blocks per single transfer */
+ unsigned int max_count;
};
+extern int block_read ( struct interface *control, struct interface *data,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len );
+#define block_read_TYPE( object_type ) \
+ typeof ( int ( object_type, struct interface *data, \
+ uint64_t lba, unsigned int count, \
+ userptr_t buffer, size_t len ) )
+
+extern int block_write ( struct interface *control, struct interface *data,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len );
+#define block_write_TYPE( object_type ) \
+ typeof ( int ( object_type, struct interface *data, \
+ uint64_t lba, unsigned int count, \
+ userptr_t buffer, size_t len ) )
+
+extern int block_read_capacity ( struct interface *control,
+ struct interface *data );
+#define block_read_capacity_TYPE( object_type ) \
+ typeof ( int ( object_type, struct interface *data ) )
+
+extern void block_capacity ( struct interface *intf,
+ struct block_device_capacity *capacity );
+#define block_capacity_TYPE( object_type ) \
+ typeof ( void ( object_type, \
+ struct block_device_capacity *capacity ) )
+
+
#endif /* _IPXE_BLOCKDEV_H */
#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 )
#include <ipxe/srp.h>
/** SRP initiator port identifier for Infiniband */
-struct ib_srp_initiator_port_id {
- /** Identifier extension */
- struct ib_gid_half id_ext;
- /** IB channel adapter GUID */
- struct ib_gid_half hca_guid;
-} __attribute__ (( packed ));
+union ib_srp_initiator_port_id {
+ /** SRP version of port identifier */
+ union srp_port_id srp;
+ /** Infiniband version of port identifier */
+ struct {
+ /** Identifier extension */
+ struct ib_gid_half id_ext;
+ /** IB channel adapter GUID */
+ struct ib_gid_half hca_guid;
+ } __attribute__ (( packed )) ib;
+};
/** SRP target port identifier for Infiniband */
-struct ib_srp_target_port_id {
- /** Identifier extension */
- struct ib_gid_half id_ext;
- /** I/O controller GUID */
- struct ib_gid_half ioc_guid;
-} __attribute__ (( packed ));
-
-/**
- * Get Infiniband-specific initiator port ID
- *
- * @v port_ids SRP port IDs
- * @ret initiator_port_id Infiniband-specific initiator port ID
- */
-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 );
-}
+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 target port ID
- *
- * @v port_ids SRP port IDs
- * @ret target_port_id Infiniband-specific target port ID
+ * sBFT Infiniband subtable
*/
-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 */
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 */
#include <stdint.h>
#include <ipxe/acpi.h>
+#include <ipxe/scsi.h>
#include <ipxe/in.h>
/** iSCSI Boot Firmware Table signature */
-#define IBFT_SIG "iBFT"
+#define IBFT_SIG ACPI_SIGNATURE ( 'i', 'B', 'F', 'T' )
/** An offset from the start of the iBFT */
typedef uint16_t ibft_off_t;
/** 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 ));
/** 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.
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 */
/** 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
*
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 */
/** 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
/** Reference counter */
struct refcnt refcnt;
+ /** SCSI command-issuing interface */
+ struct interface control;
+ /** SCSI command interface */
+ struct interface data;
/** Transport-layer socket */
struct interface socket;
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
*
* 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;
/** 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
* 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.
/** 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 */
/** 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 */
--- /dev/null
+#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 */
+++ /dev/null
-#ifndef _IPXE_RAMDISK_H
-#define _IPXE_RAMDISK_H
-
-/**
- * @file
- *
- * RAM disks
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <ipxe/uaccess.h>
-#include <ipxe/blockdev.h>
-
-struct ramdisk {
- struct block_device blockdev;
- userptr_t data;
-};
-
-extern int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
- unsigned int blksize );
-
-#endif /* _IPXE_RAMDISK_H */
#ifndef _IPXE_SANBOOT_H
#define _IPXE_SANBOOT_H
+/** @file
+ *
+ * iPXE sanboot API
+ *
+ * The sanboot API provides methods for hooking, unhooking,
+ * describing, and booting from SAN devices.
+ *
+ * The standard methods (readl()/writel() etc.) do not strictly check
+ * the type of the address parameter; this is because traditional
+ * usage does not necessarily provide the correct pointer type. For
+ * example, code written for ISA devices at fixed I/O addresses (such
+ * as the keyboard controller) tend to use plain integer constants for
+ * the address parameter.
+ */
+
FILE_LICENCE ( GPL2_OR_LATER );
-#include <ipxe/tables.h>
+#include <ipxe/api.h>
+#include <config/sanboot.h>
+
+struct uri;
+
+/**
+ * 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 )
+
+/**
+ * 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 )
+
+/**
+ * Provide a static inline sanboot API implementation
+ *
+ * @v _prefix Subsystem prefix
+ * @v _api_func API function
+ */
+#define PROVIDE_SANBOOT_INLINE( _subsys, _api_func ) \
+ PROVIDE_SINGLE_API_INLINE ( SANBOOT_PREFIX_ ## _subsys, _api_func )
+
+/* Include all architecture-independent sanboot API headers */
+#include <ipxe/null_sanboot.h>
+
+/* Include all architecture-dependent sanboot API headers */
+#include <bits/sanboot.h>
-struct sanboot_protocol {
- const char *prefix;
- int ( * boot ) ( const char *root_path );
-};
+/**
+ * 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 );
-#define SANBOOT_PROTOCOLS \
- __table ( struct sanboot_protocol, "sanboot_protocols" )
+/**
+ * Unhook SAN device
+ *
+ * @v drive Drive number
+ */
+void san_unhook ( unsigned int drive );
-#define __sanboot_protocol __table_entry ( SANBOOT_PROTOCOLS, 01 )
+/**
+ * Attempt to boot from a SAN device
+ *
+ * @v drive Drive number
+ * @ret rc Return status code
+ */
+int san_boot ( unsigned int drive );
-extern int keep_san ( void );
+/**
+ * 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 */
#define _IPXE_SCSI_H
#include <stdint.h>
-#include <ipxe/blockdev.h>
#include <ipxe/uaccess.h>
-#include <ipxe/refcnt.h>
+#include <ipxe/interface.h>
/** @file
*
FILE_LICENCE ( GPL2_OR_LATER );
+/** Maximum block for READ/WRITE (10) commands */
+#define SCSI_MAX_BLOCK_10 0xffffffffULL
+
/**
* @defgroup scsiops SCSI operation codes
* @{
/** @} */
-/** 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) */
* Must be zero if @c data_in is NULL
*/
size_t data_in_len;
- /** SCSI status code */
- uint8_t status;
- /** SCSI sense response code */
- uint8_t sense_response;
- /** Command status code */
- int rc;
};
-/** 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 ));
+/** 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 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;
+/** A SCSI response information unit */
+struct scsi_rsp {
+ /** SCSI status code */
+ uint8_t status;
+ /** Data overrun (or negative underrun) */
+ ssize_t overrun;
+ /** Autosense data (if any) */
+ struct scsi_sns sense;
};
-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 */
#include <ipxe/iobuf.h>
#include <ipxe/xfer.h>
#include <ipxe/scsi.h>
+#include <ipxe/acpi.h>
/*****************************************************************************
*
*/
/** An SRP information unit tag */
-struct srp_tag {
+union srp_tag {
+ uint8_t bytes[8];
uint32_t dwords[2];
} __attribute__ (( packed ));
+/** SRP tag magic marker */
+#define SRP_TAG_MAGIC 0x69505845
+
/** An SRP port ID */
-struct srp_port_id {
+union srp_port_id {
uint8_t bytes[16];
-} __attribute__ (( packed ));
-
-/** 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;
+ uint32_t dwords[4];
} __attribute__ (( packed ));
/** SRP information unit common fields */
/** Reserved */
uint8_t reserved0[7];
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
} __attribute__ (( packed ));
/*****************************************************************************
/** 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 */
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 */
/** 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 */
*/
uint32_t reason;
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
/** Reserved */
uint8_t reserved1[8];
/** Supported buffer formats
/** Reserved */
uint8_t reserved0[7];
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP initiator logout request */
*/
uint32_t reason;
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP target logout request */
/** Reserved */
uint8_t reserved0[6];
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
/** Reserved */
uint8_t reserved1[4];
/** Logical unit number */
/** 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 ));
/** 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 */
/** Request limit delta */
uint32_t request_limit_delta;
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
/** Reserved */
uint8_t reserved1[2];
/** Valid fields
* @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 );
}
/**
* @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 );
}
* @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 );
}
* @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 );
}
/** 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 */
/** Reserved */
uint8_t reserved0[7];
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP credit response */
/** 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 */
/** Reserved */
uint8_t reserved0[7];
/** Tag */
- struct srp_tag tag;
+ union srp_tag tag;
} __attribute__ (( packed ));
/** Type of an SRP asynchronous event response */
/*****************************************************************************
*
- * Information units
- *
- *****************************************************************************
- */
-
-/** Maximum length of any initiator-to-target IU that we will send
+ * SRP boot firmware table
*
- * 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
-
-/*****************************************************************************
+ * The working draft specification for the SRP boot firmware table can
+ * be found at
*
- * SRP device
+ * http://ipxe.org/wiki/srp/sbft
*
*****************************************************************************
*/
-struct srp_device;
+/** SRP Boot Firmware Table signature */
+#define SBFT_SIG ACPI_SIGNATURE ( 's', 'B', 'F', 'T' )
-/** 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 offset from the start of the sBFT */
+typedef uint16_t sbft_off_t;
-/** An SRP device */
-struct srp_device {
- /** Reference count */
- struct refcnt refcnt;
+/**
+ * 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 ));
- /** Initiator and target port IDs */
- struct srp_port_ids port_ids;
- /** Logical unit number */
+/**
+ * sBFT SCSI subtable
+ */
+struct sbft_scsi_subtable {
+ /** LUN */
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];
-};
+} __attribute__ (( packed ));
/**
- * Get SRP transport private data
- *
- * @v srp SRP device
- * @ret priv SRP transport private data
+ * sBFT SRP subtable
*/
-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,
-};
+struct sbft_srp_subtable {
+ /** Initiator port identifier */
+ union srp_port_id initiator;
+ /** Target port identifier */
+ union srp_port_id target;
+} __attribute__ (( packed ));
-/** Maximum number of SRP retry attempts */
-#define SRP_MAX_RETRIES 3
+/*****************************************************************************
+ *
+ * SRP devices
+ *
+ *****************************************************************************
+ */
-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 */
#include <byteswap.h>
#include <ipxe/list.h>
#include <ipxe/if_ether.h>
-#include <ipxe/ethernet.h>
#include <ipxe/iobuf.h>
#include <ipxe/uaccess.h>
-#include <ipxe/ata.h>
#include <ipxe/netdevice.h>
-#include <ipxe/process.h>
#include <ipxe/features.h>
+#include <ipxe/interface.h>
+#include <ipxe/xfer.h>
+#include <ipxe/uri.h>
+#include <ipxe/open.h>
+#include <ipxe/ata.h>
#include <ipxe/aoe.h>
/** @file
struct net_protocol aoe_protocol __net_protocol;
-/** List of all AoE sessions */
-static LIST_HEAD ( aoe_sessions );
+/******************************************************************************
+ *
+ * AoE devices and commands
+ *
+ ******************************************************************************
+ */
+
+/** List of all AoE devices */
+static LIST_HEAD ( aoe_devices );
+
+/** 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];
-static void aoe_free ( struct refcnt *refcnt ) {
- struct aoe_session *aoe =
- container_of ( refcnt, struct aoe_session, refcnt );
+ /** 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 );
+};
- netdev_put ( aoe->netdev );
- free ( aoe );
+/**
+ * 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;
+}
- /* Stop retransmission timer */
- stop_timer ( &aoe->timer );
+/**
+ * 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 );
+}
- /* Mark operation as complete */
- aoe->rc = rc;
+/**
+ * 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;
}
/**
- * Send AoE command
+ * Free AoE command
*
- * @v aoe AoE session
- * @ret rc Return status code
+ * @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
*
- * This transmits an AoE command packet. It does not wait for a
- * response.
+ * @v aoecmd AoE command
+ * @v rc Reason for close
*/
-static int aoe_send_command ( struct aoe_session *aoe ) {
- struct ata_command *command = aoe->command;
+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 );
+ }
+
+ /* Shut down interfaces */
+ intf_shutdown ( &aoecmd->ata, rc );
+}
+
+/**
+ * Transmit AoE command request
+ *
+ * @v aoecmd AoE command
+ * @ret rc Return status code
+ */
+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;
-
- /* Fail immediately if we have no netdev to send on */
- if ( ! aoe->netdev ) {
- aoe_done ( aoe, -ENETUNREACH );
- return -ENETUNREACH;
- }
+ size_t cmd_len;
+ int rc;
+
+ /* 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;
}
/**
* @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 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 aoe AoE session
+ * @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 );
+
+ /* 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;
+ }
- /* Mark config request as complete */
- aoe_done ( aoe, 0 );
+ /* 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 ) );
+}
+
+/**
+ * Build AoE configuration command IU
+ *
+ * @v aoecmd AoE command
+ * @v data Command IU
+ * @v len Length of command IU
+ */
+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 */
+ 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 ATA command response
+ * Handle AoE configuration response IU
*
- * @v aoe AoE session
- * @v aoeata AoE ATA command
- * @v len Length of AoE ATA command
+ * @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_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 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 ( *aoeata ) ) {
- /* Ignore packet; allow timer to trigger retransmit */
+ 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 ) );
-
- /* 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 );
+ 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 );
+
+ /* 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;
- /* Update ATA command and offset */
- aoe->command_offset += data_len;
- command->cb.lba.native += count;
- command->cb.count.native -= count;
+ /* 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 ) );
- /* Check for operation complete */
- if ( ! command->cb.count.native ) {
- aoe_done ( aoe, 0 );
- return 0;
+ 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;
}
- /* Transmit next portion of request */
- stop_timer ( &aoe->timer );
- aoe_send_command ( aoe );
+ /* 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
*
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;
+ DBG ( "AoE received request packet\n" );
+ rc = -EOPNOTSUPP;
+ goto err_sanity;
}
- 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;
+ /* 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;
}
- done:
+ /* 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;
}
.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;
-
- aoe_send_command ( aoe );
-
- return 0;
-}
/**
- * Issue AoE config query for AoE target discovery
+ * Parse AoE URI
*
- * @v aoe AoE session
+ * @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<major>.<minor>".
*/
-static int aoe_discover ( struct aoe_session *aoe ) {
- int rc;
-
- aoe->status = 0;
- aoe->aoe_cmd_type = AOE_CMD_CONFIG;
- aoe->command = NULL;
-
- aoe_send_command ( aoe );
-
- 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 );
+static int aoe_parse_uri ( struct uri *uri, unsigned int *major,
+ unsigned int *minor ) {
+ const char *ptr;
+ char *end;
- 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 )
+ /* Check for URI with opaque portion */
+ if ( ! uri->opaque )
return -EINVAL;
- ptr = ( ( char * ) root_path + 4 );
+ ptr = uri->opaque;
- if ( *ptr++ != 'e' )
+ /* Check for initial 'e' */
+ if ( *ptr != 'e' )
return -EINVAL;
+ ptr++;
- aoe->major = strtoul ( ptr, &ptr, 10 );
- if ( *ptr++ != '.' )
+ /* Parse major device number */
+ *major = strtoul ( ptr, &end, 10 );
+ if ( *end != '.' )
return -EINVAL;
+ ptr = ( end + 1 );
- aoe->minor = strtoul ( ptr, &ptr, 10 );
- if ( *ptr )
+ /* Parse minor device number */
+ *minor = strtoul ( ptr, &end, 10 );
+ if ( *end )
return -EINVAL;
return 0;
}
-int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
- const char *root_path ) {
- struct aoe_session *aoe;
+/**
+ * Open AoE URI
+ *
+ * @v parent Parent interface
+ * @v uri URI
+ * @ret rc Return status code
+ */
+static int aoe_open ( struct interface *parent, struct uri *uri ) {
+ struct net_device *netdev;
+ unsigned int major;
+ unsigned int minor;
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
+ /* 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.
*/
- 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;
+ netdev = last_opened_netdev();
+ if ( ! netdev ) {
+ DBG ( "AoE cannot identify network device\n" );
+ return -ENODEV;
+ }
- return 0;
+ /* Parse URI */
+ if ( ( rc = aoe_parse_uri ( uri, &major, &minor ) ) != 0 ) {
+ DBG ( "AoE cannot parse URI\n" );
+ return rc;
+ }
- err:
- ref_put ( &aoe->refcnt );
- return rc;
+ /* Open AoE device */
+ if ( ( rc = aoedev_open ( parent, netdev, major, minor ) ) != 0 )
+ return rc;
+
+ return 0;
}
+
+/** AoE URI opener */
+struct uri_opener aoe_uri_opener __uri_opener = {
+ .scheme = "aoe",
+ .open = aoe_open,
+};
#include <stdlib.h>
#include <errno.h>
+#include <ipxe/interface.h>
+#include <ipxe/uri.h>
+#include <ipxe/open.h>
#include <ipxe/base16.h>
+#include <ipxe/acpi.h>
#include <ipxe/srp.h>
#include <ipxe/infiniband.h>
#include <ipxe/ib_cmrc.h>
#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,
/** 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;
};
/**
decoded_size = base16_decode ( rp_comp, bytes );
if ( decoded_size < 0 )
return decoded_size;
- assert ( decoded_size == size );
return 0;
}
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
*
/* 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 ) );
}
*/
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 ) );
}
*/
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 ) );
}
*/
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 ) );
}
if ( ( pkey = ib_srp_parse_integer ( rp_comp, IB_PKEY_DEFAULT ) ) < 0 )
return pkey;
- rp->ib->pkey = pkey;
+ rp->pkey = pkey;
return 0;
}
*/
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 ) );
}
*/
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 );
}
/**
*/
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 ) );
}
*/
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 ) );
}
/** 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 },
/**
* 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;
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;
}
}
/* 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;
}
}
}
/**
- * 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,
};
#include <ipxe/vsprintf.h>
#include <ipxe/socket.h>
#include <ipxe/iobuf.h>
+#include <ipxe/uri.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/scsi.h>
#include <ipxe/features.h>
#include <ipxe/base16.h>
#include <ipxe/base64.h>
+#include <ipxe/ibft.h>
#include <ipxe/iscsi.h>
/** @file
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
*
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
*
iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_REQUIRED;
/* Assign fresh initiator task tag */
- iscsi->itt++;
+ iscsi_new_itt ( iscsi );
/* Initiate login */
iscsi_start_login ( iscsi );
*
* @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 );
}
/****************************************************************************
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 );
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;
}
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;
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 );
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 |
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 );
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
*
response->status_class, response->status_detail );
rc = iscsi_status_to_rc ( response->status_class,
response->status_detail );
- iscsi->instant_rc = rc;
return rc;
}
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;
}
* 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 );
}
/**
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 );
/* 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 );
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;
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 );
}
}
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;
}
done:
/* Free I/O buffer */
free_iob ( iobuf );
- 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 );
+ /* Destroy session on error */
+ if ( rc != 0 )
+ iscsi_close ( 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 );
- }
+ return rc;
}
/**
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 */
*/
/**
- * 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;
+ if ( ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) ==
+ ISCSI_STATUS_FULL_FEATURE_PHASE ) &&
+ ( iscsi->command == NULL ) ) {
+ /* We cannot handle concurrent commands */
+ return 1;
+ } else {
+ return 0;
+ }
+}
- /* Record SCSI command */
- iscsi->command = command;
+/**
+ * Issue iSCSI SCSI command
+ *
+ * @v iscsi iSCSI session
+ * @v parent Parent interface
+ * @v command SCSI command
+ * @ret tag Command tag, or negative error
+ */
+static int iscsi_scsi_command ( struct iscsi_session *iscsi,
+ struct interface *parent,
+ struct scsi_cmd *command ) {
- /* Issue command or open connection as appropriate */
- if ( iscsi->status ) {
- iscsi_start_command ( iscsi );
- } else {
- if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
- iscsi->command = NULL;
- return rc;
- }
+ /* 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;
}
- return 0;
+ /* 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 );
+
/**
- * Shut down iSCSI interface
+ * Close iSCSI command
*
- * @v scsi SCSI device
+ * @v iscsi iSCSI session
+ * @v rc Reason for close
*/
-void iscsi_detach ( struct scsi_device *scsi ) {
- struct iscsi_session *iscsi =
- container_of ( scsi->backend, struct iscsi_session, refcnt );
+static void iscsi_command_close ( struct iscsi_session *iscsi, int rc ) {
- iscsi_close_connection ( iscsi, 0 );
- process_del ( &iscsi->process );
- scsi->command = scsi_detached_command;
- ref_put ( scsi->backend );
- scsi->backend = NULL;
+ /* 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
/** 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,
}
/**
- * 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
#include <ipxe/image.h>
#include <ipxe/sanboot.h>
#include <ipxe/uri.h>
+#include <ipxe/init.h>
#include <usr/ifmgmt.h>
#include <usr/route.h>
#include <usr/dhcpmgmt.h>
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
*
* @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;
}
/**