123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847 |
- /*
- * Copyright (C) 2017 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- /**
- * @file
- *
- * SAN booting
- *
- */
-
- #include <stdint.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <assert.h>
- #include <ipxe/xfer.h>
- #include <ipxe/open.h>
- #include <ipxe/timer.h>
- #include <ipxe/process.h>
- #include <ipxe/iso9660.h>
- #include <ipxe/dhcp.h>
- #include <ipxe/settings.h>
- #include <ipxe/sanboot.h>
-
- /**
- * Default SAN drive number
- *
- * The drive number is a meaningful concept only in a BIOS
- * environment, where it represents the INT13 drive number (0x80 for
- * the first hard disk). We retain it in other environments to allow
- * for a simple way for iPXE commands to refer to SAN drives.
- */
- #define SAN_DEFAULT_DRIVE 0x80
-
- /**
- * Timeout for block device commands (in ticks)
- *
- * Underlying devices should ideally never become totally stuck.
- * However, if they do, then the blocking SAN APIs provide 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 SAN_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC )
-
- /**
- * Default number of times to retry commands
- *
- * We may need to retry commands. For example, the underlying
- * connection may be closed by the SAN target due to an inactivity
- * timeout, or the SAN target may return pointless "error" messages
- * such as "SCSI power-on occurred".
- */
- #define SAN_DEFAULT_RETRIES 10
-
- /**
- * Delay between reopening attempts
- *
- * Some SAN targets will always accept connections instantly and
- * report a temporary unavailability by e.g. failing the TEST UNIT
- * READY command. Avoid bombarding such targets by introducing a
- * small delay between attempts.
- */
- #define SAN_REOPEN_DELAY_SECS 5
-
- /** List of SAN devices */
- LIST_HEAD ( san_devices );
-
- /** Number of times to retry commands */
- static unsigned long san_retries = SAN_DEFAULT_RETRIES;
-
- /**
- * Find SAN device by drive number
- *
- * @v drive Drive number
- * @ret sandev SAN device, or NULL
- */
- struct san_device * sandev_find ( unsigned int drive ) {
- struct san_device *sandev;
-
- list_for_each_entry ( sandev, &san_devices, list ) {
- if ( sandev->drive == drive )
- return sandev;
- }
- return NULL;
- }
-
- /**
- * Free SAN device
- *
- * @v refcnt Reference count
- */
- static void sandev_free ( struct refcnt *refcnt ) {
- struct san_device *sandev =
- container_of ( refcnt, struct san_device, refcnt );
- unsigned int i;
-
- assert ( ! timer_running ( &sandev->timer ) );
- assert ( ! sandev->active );
- assert ( list_empty ( &sandev->opened ) );
- for ( i = 0 ; i < sandev->paths ; i++ )
- uri_put ( sandev->path[i].uri );
- free ( sandev );
- }
-
- /**
- * Close SAN device command
- *
- * @v sandev SAN device
- * @v rc Reason for close
- */
- static void sandev_command_close ( struct san_device *sandev, int rc ) {
-
- /* Stop timer */
- stop_timer ( &sandev->timer );
-
- /* Restart interface */
- intf_restart ( &sandev->command, rc );
-
- /* Record command status */
- sandev->command_rc = rc;
- }
-
- /**
- * Record SAN device capacity
- *
- * @v sandev SAN device
- * @v capacity SAN device capacity
- */
- static void sandev_command_capacity ( struct san_device *sandev,
- struct block_device_capacity *capacity ) {
-
- /* Record raw capacity information */
- memcpy ( &sandev->capacity, capacity, sizeof ( sandev->capacity ) );
- }
-
- /** SAN device command interface operations */
- static struct interface_operation sandev_command_op[] = {
- INTF_OP ( intf_close, struct san_device *, sandev_command_close ),
- INTF_OP ( block_capacity, struct san_device *,
- sandev_command_capacity ),
- };
-
- /** SAN device command interface descriptor */
- static struct interface_descriptor sandev_command_desc =
- INTF_DESC ( struct san_device, command, sandev_command_op );
-
- /**
- * Handle SAN device command timeout
- *
- * @v retry Retry timer
- */
- static void sandev_command_expired ( struct retry_timer *timer,
- int over __unused ) {
- struct san_device *sandev =
- container_of ( timer, struct san_device, timer );
-
- sandev_command_close ( sandev, -ETIMEDOUT );
- }
-
- /**
- * Open SAN path
- *
- * @v sanpath SAN path
- * @ret rc Return status code
- */
- static int sanpath_open ( struct san_path *sanpath ) {
- struct san_device *sandev = sanpath->sandev;
- int rc;
-
- /* Sanity check */
- list_check_contains_entry ( sanpath, &sandev->closed, list );
-
- /* Open interface */
- if ( ( rc = xfer_open_uri ( &sanpath->block, sanpath->uri ) ) != 0 ) {
- DBGC ( sandev, "SAN %#02x.%d could not (re)open URI: "
- "%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
- return rc;
- }
-
- /* Start process */
- process_add ( &sanpath->process );
-
- /* Mark as opened */
- list_del ( &sanpath->list );
- list_add_tail ( &sanpath->list, &sandev->opened );
-
- /* Record as in progress */
- sanpath->path_rc = -EINPROGRESS;
-
- return 0;
- }
-
- /**
- * Close SAN path
- *
- * @v sanpath SAN path
- * @v rc Reason for close
- */
- static void sanpath_close ( struct san_path *sanpath, int rc ) {
- struct san_device *sandev = sanpath->sandev;
-
- /* Record status */
- sanpath->path_rc = rc;
-
- /* Mark as closed */
- list_del ( &sanpath->list );
- list_add_tail ( &sanpath->list, &sandev->closed );
-
- /* Stop process */
- process_del ( &sanpath->process );
-
- /* Restart interfaces, avoiding potential loops */
- if ( sanpath == sandev->active ) {
- intfs_restart ( rc, &sandev->command, &sanpath->block, NULL );
- sandev->active = NULL;
- sandev_command_close ( sandev, rc );
- } else {
- intf_restart ( &sanpath->block, rc );
- }
- }
-
- /**
- * Handle closure of underlying block device interface
- *
- * @v sanpath SAN path
- * @v rc Reason for close
- */
- static void sanpath_block_close ( struct san_path *sanpath, int rc ) {
- struct san_device *sandev = sanpath->sandev;
-
- /* Any closure is an error from our point of view */
- if ( rc == 0 )
- rc = -ENOTCONN;
- DBGC ( sandev, "SAN %#02x.%d closed: %s\n",
- sandev->drive, sanpath->index, strerror ( rc ) );
-
- /* Close path */
- sanpath_close ( sanpath, rc );
- }
-
- /**
- * Check flow control window
- *
- * @v sanpath SAN path
- */
- static size_t sanpath_block_window ( struct san_path *sanpath __unused ) {
-
- /* We are never ready to receive data via this interface.
- * This prevents objects that support both block and stream
- * interfaces from attempting to send us stream data.
- */
- return 0;
- }
-
- /**
- * SAN path process
- *
- * @v sanpath SAN path
- */
- static void sanpath_step ( struct san_path *sanpath ) {
- struct san_device *sandev = sanpath->sandev;
-
- /* Ignore if we are already the active device */
- if ( sanpath == sandev->active )
- return;
-
- /* Wait until path has become available */
- if ( ! xfer_window ( &sanpath->block ) )
- return;
-
- /* Record status */
- sanpath->path_rc = 0;
-
- /* Mark as active path or close as applicable */
- if ( ! sandev->active ) {
- DBGC ( sandev, "SAN %#02x.%d is active\n",
- sandev->drive, sanpath->index );
- sandev->active = sanpath;
- } else {
- DBGC ( sandev, "SAN %#02x.%d is available\n",
- sandev->drive, sanpath->index );
- sanpath_close ( sanpath, 0 );
- }
- }
-
- /** SAN path block interface operations */
- static struct interface_operation sanpath_block_op[] = {
- INTF_OP ( intf_close, struct san_path *, sanpath_block_close ),
- INTF_OP ( xfer_window, struct san_path *, sanpath_block_window ),
- INTF_OP ( xfer_window_changed, struct san_path *, sanpath_step ),
- };
-
- /** SAN path block interface descriptor */
- static struct interface_descriptor sanpath_block_desc =
- INTF_DESC ( struct san_path, block, sanpath_block_op );
-
- /** SAN path process descriptor */
- static struct process_descriptor sanpath_process_desc =
- PROC_DESC_ONCE ( struct san_path, process, sanpath_step );
-
- /**
- * Restart SAN device interface
- *
- * @v sandev SAN device
- * @v rc Reason for restart
- */
- static void sandev_restart ( struct san_device *sandev, int rc ) {
- struct san_path *sanpath;
-
- /* Restart all block device interfaces */
- while ( ( sanpath = list_first_entry ( &sandev->opened,
- struct san_path, list ) ) ) {
- sanpath_close ( sanpath, rc );
- }
-
- /* Clear active path */
- sandev->active = NULL;
-
- /* Close any outstanding command */
- sandev_command_close ( sandev, rc );
- }
-
- /**
- * (Re)open SAN device
- *
- * @v sandev SAN device
- * @ret rc Return status code
- *
- * This function will block until the device is available.
- */
- int sandev_reopen ( struct san_device *sandev ) {
- struct san_path *sanpath;
- int rc;
-
- /* Close any outstanding command and restart interfaces */
- sandev_restart ( sandev, -ECONNRESET );
- assert ( sandev->active == NULL );
- assert ( list_empty ( &sandev->opened ) );
-
- /* Open all paths */
- while ( ( sanpath = list_first_entry ( &sandev->closed,
- struct san_path, list ) ) ) {
- if ( ( rc = sanpath_open ( sanpath ) ) != 0 )
- goto err_open;
- }
-
- /* Wait for any device to become available, or for all devices
- * to fail.
- */
- while ( sandev->active == NULL ) {
- step();
- if ( list_empty ( &sandev->opened ) ) {
- /* Get status of the first device to be
- * closed. Do this on the basis that earlier
- * errors (e.g. "invalid IQN") are probably
- * more interesting than later errors
- * (e.g. "TCP timeout").
- */
- rc = -ENODEV;
- list_for_each_entry ( sanpath, &sandev->closed, list ) {
- rc = sanpath->path_rc;
- break;
- }
- DBGC ( sandev, "SAN %#02x never became available: %s\n",
- sandev->drive, strerror ( rc ) );
- goto err_none;
- }
- }
-
- assert ( ! list_empty ( &sandev->opened ) );
- return 0;
-
- err_none:
- err_open:
- sandev_restart ( sandev, rc );
- return rc;
- }
-
- /** SAN device read/write command parameters */
- struct san_command_rw_params {
- /** SAN device read/write operation */
- int ( * block_rw ) ( struct interface *control, struct interface *data,
- uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len );
- /** Data buffer */
- userptr_t buffer;
- /** Starting LBA */
- uint64_t lba;
- /** Block count */
- unsigned int count;
- };
-
- /** SAN device command parameters */
- union san_command_params {
- /** Read/write command parameters */
- struct san_command_rw_params rw;
- };
-
- /**
- * Initiate SAN device read/write command
- *
- * @v sandev SAN device
- * @v params Command parameters
- * @ret rc Return status code
- */
- static int sandev_command_rw ( struct san_device *sandev,
- const union san_command_params *params ) {
- struct san_path *sanpath = sandev->active;
- size_t len = ( params->rw.count * sandev->capacity.blksize );
- int rc;
-
- /* Sanity check */
- assert ( sanpath != NULL );
-
- /* Initiate read/write command */
- if ( ( rc = params->rw.block_rw ( &sanpath->block, &sandev->command,
- params->rw.lba, params->rw.count,
- params->rw.buffer, len ) ) != 0 ) {
- DBGC ( sandev, "SAN %#02x.%d could not initiate read/write: "
- "%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
- return rc;
- }
-
- return 0;
- }
-
- /**
- * Initiate SAN device read capacity command
- *
- * @v sandev SAN device
- * @v params Command parameters
- * @ret rc Return status code
- */
- static int
- sandev_command_read_capacity ( struct san_device *sandev,
- const union san_command_params *params __unused){
- struct san_path *sanpath = sandev->active;
- int rc;
-
- /* Sanity check */
- assert ( sanpath != NULL );
-
- /* Initiate read capacity command */
- if ( ( rc = block_read_capacity ( &sanpath->block,
- &sandev->command ) ) != 0 ) {
- DBGC ( sandev, "SAN %#02x.%d could not initiate read capacity: "
- "%s\n", sandev->drive, sanpath->index, strerror ( rc ) );
- return rc;
- }
-
- return 0;
- }
-
- /**
- * Execute a single SAN device command and wait for completion
- *
- * @v sandev SAN device
- * @v command Command
- * @v params Command parameters (if required)
- * @ret rc Return status code
- */
- static int
- sandev_command ( struct san_device *sandev,
- int ( * command ) ( struct san_device *sandev,
- const union san_command_params *params ),
- const union san_command_params *params ) {
- unsigned int retries = 0;
- int rc;
-
- /* Sanity check */
- assert ( ! timer_running ( &sandev->timer ) );
-
- /* (Re)try command */
- do {
-
- /* Reopen block device if applicable */
- if ( sandev_needs_reopen ( sandev ) &&
- ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) {
-
- /* Delay reopening attempts */
- sleep_fixed ( SAN_REOPEN_DELAY_SECS );
-
- /* Retry opening indefinitely for multipath devices */
- if ( sandev->paths <= 1 )
- retries++;
-
- continue;
- }
-
- /* Initiate command */
- if ( ( rc = command ( sandev, params ) ) != 0 ) {
- retries++;
- continue;
- }
-
- /* Start expiry timer */
- start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT );
-
- /* Wait for command to complete */
- while ( timer_running ( &sandev->timer ) )
- step();
-
- /* Check command status */
- if ( ( rc = sandev->command_rc ) != 0 ) {
- retries++;
- continue;
- }
-
- return 0;
-
- } while ( retries <= san_retries );
-
- /* Sanity check */
- assert ( ! timer_running ( &sandev->timer ) );
-
- return rc;
- }
-
- /**
- * Reset SAN device
- *
- * @v sandev SAN device
- * @ret rc Return status code
- */
- int sandev_reset ( struct san_device *sandev ) {
- int rc;
-
- DBGC ( sandev, "SAN %#02x reset\n", sandev->drive );
-
- /* Close and reopen underlying block device */
- if ( ( rc = sandev_reopen ( sandev ) ) != 0 )
- return rc;
-
- return 0;
- }
-
- /**
- * Read from or write to SAN device
- *
- * @v sandev SAN device
- * @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
- */
- int sandev_rw ( struct san_device *sandev, 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 ) ) {
- union san_command_params params;
- unsigned int remaining;
- size_t frag_len;
- int rc;
-
- /* Initialise command parameters */
- params.rw.block_rw = block_rw;
- params.rw.buffer = buffer;
- params.rw.lba = ( lba << sandev->blksize_shift );
- params.rw.count = sandev->capacity.max_count;
- remaining = ( count << sandev->blksize_shift );
-
- /* Read/write fragments */
- while ( remaining ) {
-
- /* Determine fragment length */
- if ( params.rw.count > remaining )
- params.rw.count = remaining;
-
- /* Execute command */
- if ( ( rc = sandev_command ( sandev, sandev_command_rw,
- ¶ms ) ) != 0 )
- return rc;
-
- /* Move to next fragment */
- frag_len = ( sandev->capacity.blksize * params.rw.count );
- params.rw.buffer = userptr_add ( params.rw.buffer, frag_len );
- params.rw.lba += params.rw.count;
- remaining -= params.rw.count;
- }
-
- return 0;
- }
-
- /**
- * Configure SAN device as a CD-ROM, if applicable
- *
- * @v sandev SAN device
- * @ret rc Return status code
- *
- * Both BIOS and UEFI require SAN devices to be accessed with a block
- * size of 2048. While we could require the user to configure the
- * block size appropriately, this is non-trivial and would impose a
- * substantial learning effort on the user. Instead, we check for the
- * presence of the ISO9660 primary volume descriptor and, if found,
- * then we force a block size of 2048 and map read/write requests
- * appropriately.
- */
- static int sandev_parse_iso9660 ( struct san_device *sandev ) {
- static const struct iso9660_primary_descriptor_fixed primary_check = {
- .type = ISO9660_TYPE_PRIMARY,
- .id = ISO9660_ID,
- };
- union {
- struct iso9660_primary_descriptor primary;
- char bytes[ISO9660_BLKSIZE];
- } *scratch;
- unsigned int blksize;
- unsigned int blksize_shift;
- unsigned int lba;
- unsigned int count;
- int rc;
-
- /* Calculate required blocksize shift for potential CD-ROM access */
- blksize = sandev->capacity.blksize;
- blksize_shift = 0;
- while ( blksize < ISO9660_BLKSIZE ) {
- blksize <<= 1;
- blksize_shift++;
- }
- if ( blksize > ISO9660_BLKSIZE ) {
- /* Cannot be a CD-ROM. This is not an error. */
- rc = 0;
- goto invalid_blksize;
- }
- lba = ( ISO9660_PRIMARY_LBA << blksize_shift );
- count = ( 1 << blksize_shift );
-
- /* Allocate scratch area */
- scratch = malloc ( ISO9660_BLKSIZE );
- if ( ! scratch ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
-
- /* Read primary volume descriptor */
- if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( scratch ),
- block_read ) ) != 0 ) {
- DBGC ( sandev, "SAN %#02x could not read ISO9660 primary"
- "volume descriptor: %s\n",
- sandev->drive, strerror ( rc ) );
- goto err_rw;
- }
-
- /* Configure as CD-ROM if applicable */
- if ( memcmp ( &scratch->primary.fixed, &primary_check,
- sizeof ( primary_check ) ) == 0 ) {
- DBGC ( sandev, "SAN %#02x contains an ISO9660 filesystem; "
- "treating as CD-ROM\n", sandev->drive );
- sandev->blksize_shift = blksize_shift;
- sandev->is_cdrom = 1;
- }
-
- err_rw:
- free ( scratch );
- err_alloc:
- invalid_blksize:
- return rc;
- }
-
- /**
- * Allocate SAN device
- *
- * @v uris List of URIs
- * @v count Number of URIs
- * @v priv_size Size of private data
- * @ret sandev SAN device, or NULL
- */
- struct san_device * alloc_sandev ( struct uri **uris, unsigned int count,
- size_t priv_size ) {
- struct san_device *sandev;
- struct san_path *sanpath;
- size_t size;
- unsigned int i;
-
- /* Allocate and initialise structure */
- size = ( sizeof ( *sandev ) + ( count * sizeof ( sandev->path[0] ) ) );
- sandev = zalloc ( size + priv_size );
- if ( ! sandev )
- return NULL;
- ref_init ( &sandev->refcnt, sandev_free );
- intf_init ( &sandev->command, &sandev_command_desc, &sandev->refcnt );
- timer_init ( &sandev->timer, sandev_command_expired, &sandev->refcnt );
- sandev->priv = ( ( ( void * ) sandev ) + size );
- sandev->paths = count;
- INIT_LIST_HEAD ( &sandev->opened );
- INIT_LIST_HEAD ( &sandev->closed );
- for ( i = 0 ; i < count ; i++ ) {
- sanpath = &sandev->path[i];
- sanpath->sandev = sandev;
- sanpath->index = i;
- sanpath->uri = uri_get ( uris[i] );
- list_add_tail ( &sanpath->list, &sandev->closed );
- intf_init ( &sanpath->block, &sanpath_block_desc,
- &sandev->refcnt );
- process_init_stopped ( &sanpath->process, &sanpath_process_desc,
- &sandev->refcnt );
- sanpath->path_rc = -EINPROGRESS;
- }
-
- return sandev;
- }
-
- /**
- * Register SAN device
- *
- * @v sandev SAN device
- * @ret rc Return status code
- */
- int register_sandev ( struct san_device *sandev ) {
- int rc;
-
- /* Check that drive number is not in use */
- if ( sandev_find ( sandev->drive ) != NULL ) {
- DBGC ( sandev, "SAN %#02x is already in use\n", sandev->drive );
- rc = -EADDRINUSE;
- goto err_in_use;
- }
-
- /* Check that device is capable of being opened (i.e. that all
- * URIs are well-formed and that at least one path is
- * working).
- */
- if ( ( rc = sandev_reopen ( sandev ) ) != 0 )
- goto err_reopen;
-
- /* Read device capacity */
- if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity,
- NULL ) ) != 0 )
- goto err_capacity;
-
- /* Configure as a CD-ROM, if applicable */
- if ( ( rc = sandev_parse_iso9660 ( sandev ) ) != 0 )
- goto err_iso9660;
-
- /* Add to list of SAN devices */
- list_add_tail ( &sandev->list, &san_devices );
- DBGC ( sandev, "SAN %#02x registered\n", sandev->drive );
-
- return 0;
-
- list_del ( &sandev->list );
- err_iso9660:
- err_capacity:
- err_reopen:
- sandev_restart ( sandev, rc );
- err_in_use:
- return rc;
- }
-
- /**
- * Unregister SAN device
- *
- * @v sandev SAN device
- */
- void unregister_sandev ( struct san_device *sandev ) {
-
- /* Sanity check */
- assert ( ! timer_running ( &sandev->timer ) );
-
- /* Remove from list of SAN devices */
- list_del ( &sandev->list );
-
- /* Shut down interfaces */
- sandev_restart ( sandev, 0 );
-
- DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive );
- }
-
- /** The "san-drive" setting */
- const struct setting san_drive_setting __setting ( SETTING_SANBOOT_EXTRA,
- san-drive ) = {
- .name = "san-drive",
- .description = "SAN drive number",
- .tag = DHCP_EB_SAN_DRIVE,
- .type = &setting_type_uint8,
- };
-
- /**
- * Get default SAN drive number
- *
- * @ret drive Default drive number
- */
- unsigned int san_default_drive ( void ) {
- unsigned long drive;
-
- /* Use "san-drive" setting, if specified */
- if ( fetch_uint_setting ( NULL, &san_drive_setting, &drive ) >= 0 )
- return drive;
-
- /* Otherwise, default to booting from first hard disk */
- return SAN_DEFAULT_DRIVE;
- }
-
- /** The "san-retries" setting */
- const struct setting san_retries_setting __setting ( SETTING_SANBOOT_EXTRA,
- san-retries ) = {
- .name = "san-retries",
- .description = "SAN retry count",
- .tag = DHCP_EB_SAN_RETRY,
- .type = &setting_type_int8,
- };
-
- /**
- * Apply SAN boot settings
- *
- * @ret rc Return status code
- */
- static int sandev_apply ( void ) {
-
- /* Apply "san-retries" setting */
- if ( fetch_uint_setting ( NULL, &san_retries_setting,
- &san_retries ) < 0 ) {
- san_retries = SAN_DEFAULT_RETRIES;
- }
-
- return 0;
- }
-
- /** Settings applicator */
- struct settings_applicator sandev_applicator __settings_applicator = {
- .apply = sandev_apply,
- };
|