Annotation of qemu/roms/ipxe/src/usr/autoboot.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (C) 2006 Michael Brown <[email protected]>.
                      3:  *
                      4:  * This program is free software; you can redistribute it and/or
                      5:  * modify it under the terms of the GNU General Public License as
                      6:  * published by the Free Software Foundation; either version 2 of the
                      7:  * License, or any later version.
                      8:  *
                      9:  * This program is distributed in the hope that it will be useful, but
                     10:  * WITHOUT ANY WARRANTY; without even the implied warranty of
                     11:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     12:  * General Public License for more details.
                     13:  *
                     14:  * You should have received a copy of the GNU General Public License
                     15:  * along with this program; if not, write to the Free Software
                     16:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     17:  */
                     18: 
                     19: FILE_LICENCE ( GPL2_OR_LATER );
                     20: 
                     21: #include <string.h>
                     22: #include <stdio.h>
                     23: #include <errno.h>
                     24: #include <ipxe/netdevice.h>
                     25: #include <ipxe/dhcp.h>
                     26: #include <ipxe/settings.h>
                     27: #include <ipxe/image.h>
                     28: #include <ipxe/sanboot.h>
                     29: #include <ipxe/uri.h>
                     30: #include <ipxe/open.h>
                     31: #include <ipxe/init.h>
                     32: #include <usr/ifmgmt.h>
                     33: #include <usr/route.h>
                     34: #include <usr/dhcpmgmt.h>
                     35: #include <usr/imgmgmt.h>
                     36: #include <usr/autoboot.h>
                     37: 
                     38: /** @file
                     39:  *
                     40:  * Automatic booting
                     41:  *
                     42:  */
                     43: 
                     44: /* Disambiguate the various error causes */
                     45: #define ENOENT_BOOT __einfo_error ( EINFO_ENOENT_BOOT )
                     46: #define EINFO_ENOENT_BOOT \
                     47:        __einfo_uniqify ( EINFO_ENOENT, 0x01, "Nothing to boot" )
                     48: 
                     49: /**
                     50:  * Perform PXE menu boot when PXE stack is not available
                     51:  */
                     52: __weak int pxe_menu_boot ( struct net_device *netdev __unused ) {
                     53:        return -ENOTSUP;
                     54: }
                     55: 
                     56: /**
                     57:  * Identify the boot network device
                     58:  *
                     59:  * @ret netdev         Boot network device
                     60:  */
                     61: static struct net_device * find_boot_netdev ( void ) {
                     62:        return NULL;
                     63: }
                     64: 
                     65: /**
                     66:  * Parse next-server and filename into a URI
                     67:  *
                     68:  * @v next_server      Next-server address
                     69:  * @v filename         Filename
                     70:  * @ret uri            URI, or NULL on failure
                     71:  */
                     72: static struct uri * parse_next_server_and_filename ( struct in_addr next_server,
                     73:                                                     const char *filename ) {
                     74:        char buf[ 23 /* "tftp://xxx.xxx.xxx.xxx/" */ + strlen ( filename )
                     75:                  + 1 /* NUL */ ];
                     76:        struct uri *uri;
                     77: 
                     78:        /* Parse filename */
                     79:        uri = parse_uri ( filename );
                     80:        if ( ! uri )
                     81:                return NULL;
                     82: 
                     83:        /* Construct a tftp:// URI for the filename, if applicable.
                     84:         * We can't just rely on the current working URI, because the
                     85:         * relative URI resolution will remove the distinction between
                     86:         * filenames with and without initial slashes, which is
                     87:         * significant for TFTP.
                     88:         */
                     89:        if ( next_server.s_addr && filename[0] && ! uri_is_absolute ( uri ) ) {
                     90:                uri_put ( uri );
                     91:                snprintf ( buf, sizeof ( buf ), "tftp://%s/%s",
                     92:                           inet_ntoa ( next_server ), filename );
                     93:                uri = parse_uri ( buf );
                     94:                if ( ! uri )
                     95:                        return NULL;
                     96:        }
                     97: 
                     98:        return uri;
                     99: }
                    100: 
                    101: /** The "keep-san" setting */
                    102: struct setting keep_san_setting __setting ( SETTING_SANBOOT_EXTRA ) = {
                    103:        .name = "keep-san",
                    104:        .description = "Preserve SAN connection",
                    105:        .tag = DHCP_EB_KEEP_SAN,
                    106:        .type = &setting_type_int8,
                    107: };
                    108: 
                    109: /** The "skip-san-boot" setting */
                    110: struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA ) = {
                    111:        .name = "skip-san-boot",
                    112:        .description = "Do not boot from SAN device",
                    113:        .tag = DHCP_EB_SKIP_SAN_BOOT,
                    114:        .type = &setting_type_int8,
                    115: };
                    116: 
                    117: /**
                    118:  * Boot from filename and root-path URIs
                    119:  *
                    120:  * @v filename         Filename
                    121:  * @v root_path                Root path
                    122:  * @ret rc             Return status code
                    123:  */
                    124: int uriboot ( struct uri *filename, struct uri *root_path ) {
                    125:        int drive;
                    126:        int rc;
                    127: 
                    128:        /* Treat empty URIs as absent */
                    129:        if ( filename && ( ! uri_has_path ( filename ) ) )
                    130:                filename = NULL;
                    131:        if ( root_path && ( ! uri_is_absolute ( root_path ) ) )
                    132:                root_path = NULL;
                    133: 
                    134:        /* If we have both a filename and a root path, ignore an
                    135:         * unsupported URI scheme in the root path, since it may
                    136:         * represent an NFS root.
                    137:         */
                    138:        if ( filename && root_path &&
                    139:             ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) {
                    140:                printf ( "Ignoring unsupported root path\n" );
                    141:                root_path = NULL;
                    142:        }
                    143: 
                    144:        /* Check that we have something to boot */
                    145:        if ( ! ( filename || root_path ) ) {
                    146:                rc = -ENOENT_BOOT;
                    147:                printf ( "Nothing to boot: %s\n", strerror ( rc ) );
                    148:                goto err_no_boot;
                    149:        }
                    150: 
                    151:        /* Hook SAN device, if applicable */
                    152:        if ( root_path ) {
                    153:                drive = san_hook ( root_path, 0 );
                    154:                if ( drive < 0 ) {
                    155:                        rc = drive;
                    156:                        printf ( "Could not open SAN device: %s\n",
                    157:                                 strerror ( rc ) );
                    158:                        goto err_san_hook;
                    159:                }
                    160:                printf ( "Registered as SAN device %#02x\n", drive );
                    161:        } else {
                    162:                drive = -ENODEV;
                    163:        }
                    164: 
                    165:        /* Describe SAN device, if applicable */
                    166:        if ( ( drive >= 0 ) && ( ( rc = san_describe ( drive ) ) != 0 ) ) {
                    167:                printf ( "Could not describe SAN device %#02x: %s\n",
                    168:                         drive, strerror ( rc ) );
                    169:                goto err_san_describe;
                    170:        }
                    171: 
                    172:        /* Allow a root-path-only boot with skip-san enabled to succeed */
                    173:        rc = 0;
                    174: 
                    175:        /* Attempt filename boot if applicable */
                    176:        if ( filename ) {
                    177:                if ( ( rc = imgdownload ( filename, NULL, NULL,
                    178:                                          register_and_boot_image ) ) != 0 ) {
                    179:                        printf ( "\nCould not chain image: %s\n",
                    180:                                 strerror ( rc ) );
                    181:                        /* Fall through to (possibly) attempt a SAN boot
                    182:                         * as a fallback.  If no SAN boot is attempted,
                    183:                         * our status will become the return status.
                    184:                         */
                    185:                } else {
                    186:                        /* Always print an extra newline, because we
                    187:                         * don't know where the NBP may have left the
                    188:                         * cursor.
                    189:                         */
                    190:                        printf ( "\n" );
                    191:                }
                    192:        }
                    193: 
                    194:        /* Attempt SAN boot if applicable */
                    195:        if ( root_path ) {
                    196:                if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) {
                    197:                        printf ( "Booting from SAN device %#02x\n", drive );
                    198:                        rc = san_boot ( drive );
                    199:                        printf ( "Boot from SAN device %#02x failed: %s\n",
                    200:                                 drive, strerror ( rc ) );
                    201:                } else {
                    202:                        printf ( "Skipping boot from SAN device %#02x\n",
                    203:                                 drive );
                    204:                        /* Avoid overwriting a possible failure status
                    205:                         * from a filename boot.
                    206:                         */
                    207:                }
                    208:        }
                    209: 
                    210:  err_san_describe:
                    211:        /* Unhook SAN device, if applicable */
                    212:        if ( drive >= 0 ) {
                    213:                if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) {
                    214:                        printf ( "Unregistering SAN device %#02x\n", drive );
                    215:                        san_unhook ( drive );
                    216:                } else {
                    217:                        printf ( "Preserving connection to SAN device %#02x\n",
                    218:                                 drive );
                    219:                }
                    220:        }
                    221:  err_san_hook:
                    222:  err_no_boot:
                    223:        return rc;
                    224: }
                    225: 
                    226: /**
                    227:  * Close all open net devices
                    228:  *
                    229:  * Called before a fresh boot attempt in order to free up memory.  We
                    230:  * don't just close the device immediately after the boot fails,
                    231:  * because there may still be TCP connections in the process of
                    232:  * closing.
                    233:  */
                    234: static void close_all_netdevs ( void ) {
                    235:        struct net_device *netdev;
                    236: 
                    237:        for_each_netdev ( netdev ) {
                    238:                ifclose ( netdev );
                    239:        }
                    240: }
                    241: 
                    242: /**
                    243:  * Fetch next-server and filename settings into a URI
                    244:  *
                    245:  * @v settings         Settings block
                    246:  * @ret uri            URI, or NULL on failure
                    247:  */
                    248: struct uri * fetch_next_server_and_filename ( struct settings *settings ) {
                    249:        struct in_addr next_server;
                    250:        char buf[256];
                    251:        char *filename;
                    252:        struct uri *uri;
                    253: 
                    254:        /* Fetch next-server setting */
                    255:        fetch_ipv4_setting ( settings, &next_server_setting, &next_server );
                    256:        if ( next_server.s_addr )
                    257:                printf ( "Next server: %s\n", inet_ntoa ( next_server ) );
                    258: 
                    259:        /* Fetch filename setting */
                    260:        fetch_string_setting ( settings, &filename_setting,
                    261:                               buf, sizeof ( buf ) );
                    262:        if ( buf[0] )
                    263:                printf ( "Filename: %s\n", buf );
                    264: 
                    265:        /* Expand filename setting */
                    266:        filename = expand_settings ( buf );
                    267:        if ( ! filename )
                    268:                return NULL;
                    269: 
                    270:        /* Parse next server and filename */
                    271:        uri = parse_next_server_and_filename ( next_server, filename );
                    272: 
                    273:        free ( filename );
                    274:        return uri;
                    275: }
                    276: 
                    277: /**
                    278:  * Fetch root-path setting into a URI
                    279:  *
                    280:  * @v settings         Settings block
                    281:  * @ret uri            URI, or NULL on failure
                    282:  */
                    283: static struct uri * fetch_root_path ( struct settings *settings ) {
                    284:        char buf[256];
                    285:        char *root_path;
                    286:        struct uri *uri;
                    287: 
                    288:        /* Fetch root-path setting */
                    289:        fetch_string_setting ( settings, &root_path_setting,
                    290:                               buf, sizeof ( buf ) );
                    291:        if ( buf[0] )
                    292:                printf ( "Root path: %s\n", buf );
                    293: 
                    294:        /* Expand filename setting */
                    295:        root_path = expand_settings ( buf );
                    296:        if ( ! root_path )
                    297:                return NULL;
                    298: 
                    299:        /* Parse root path */
                    300:        uri = parse_uri ( root_path );
                    301: 
                    302:        free ( root_path );
                    303:        return uri;
                    304: }
                    305: 
                    306: /**
                    307:  * Check whether or not we have a usable PXE menu
                    308:  *
                    309:  * @ret have_menu      A usable PXE menu is present
                    310:  */
                    311: static int have_pxe_menu ( void ) {
                    312:        struct setting vendor_class_id_setting
                    313:                = { .tag = DHCP_VENDOR_CLASS_ID };
                    314:        struct setting pxe_discovery_control_setting
                    315:                = { .tag = DHCP_PXE_DISCOVERY_CONTROL };
                    316:        struct setting pxe_boot_menu_setting
                    317:                = { .tag = DHCP_PXE_BOOT_MENU };
                    318:        char buf[256];
                    319:        unsigned int pxe_discovery_control;
                    320: 
                    321:        fetch_string_setting ( NULL, &vendor_class_id_setting,
                    322:                               buf, sizeof ( buf ) );
                    323:        pxe_discovery_control =
                    324:                fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
                    325: 
                    326:        return ( ( strcmp ( buf, "PXEClient" ) == 0 ) &&
                    327:                 setting_exists ( NULL, &pxe_boot_menu_setting ) &&
                    328:                 ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) &&
                    329:                       setting_exists ( NULL, &filename_setting ) ) ) );
                    330: }
                    331: 
                    332: /**
                    333:  * Boot from a network device
                    334:  *
                    335:  * @v netdev           Network device
                    336:  * @ret rc             Return status code
                    337:  */
                    338: int netboot ( struct net_device *netdev ) {
                    339:        struct uri *filename;
                    340:        struct uri *root_path;
                    341:        int rc;
                    342: 
                    343:        /* Close all other network devices */
                    344:        close_all_netdevs();
                    345: 
                    346:        /* Open device and display device status */
                    347:        if ( ( rc = ifopen ( netdev ) ) != 0 )
                    348:                goto err_ifopen;
                    349:        ifstat ( netdev );
                    350: 
                    351:        /* Configure device via DHCP */
                    352:        if ( ( rc = dhcp ( netdev ) ) != 0 )
                    353:                goto err_dhcp;
                    354:        route();
                    355: 
                    356:        /* Try PXE menu boot, if applicable */
                    357:        if ( have_pxe_menu() ) {
                    358:                printf ( "Booting from PXE menu\n" );
                    359:                rc = pxe_menu_boot ( netdev );
                    360:                goto err_pxe_menu_boot;
                    361:        }
                    362: 
                    363:        /* Fetch next server, filename and root path */
                    364:        filename = fetch_next_server_and_filename ( NULL );
                    365:        if ( ! filename )
                    366:                goto err_filename;
                    367:        root_path = fetch_root_path ( NULL );
                    368:        if ( ! root_path )
                    369:                goto err_root_path;
                    370: 
                    371:        /* Boot using next server, filename and root path */
                    372:        if ( ( rc = uriboot ( filename, root_path ) ) != 0 )
                    373:                goto err_uriboot;
                    374: 
                    375:  err_uriboot:
                    376:        uri_put ( root_path );
                    377:  err_root_path:
                    378:        uri_put ( filename );
                    379:  err_filename:
                    380:  err_pxe_menu_boot:
                    381:  err_dhcp:
                    382:  err_ifopen:
                    383:        return rc;
                    384: }
                    385: 
                    386: /**
                    387:  * Boot the system
                    388:  */
                    389: int autoboot ( void ) {
                    390:        struct net_device *boot_netdev;
                    391:        struct net_device *netdev;
                    392:        int rc = -ENODEV;
                    393: 
                    394:        /* If we have an identifable boot device, try that first */
                    395:        if ( ( boot_netdev = find_boot_netdev() ) )
                    396:                rc = netboot ( boot_netdev );
                    397: 
                    398:        /* If that fails, try booting from any of the other devices */
                    399:        for_each_netdev ( netdev ) {
                    400:                if ( netdev == boot_netdev )
                    401:                        continue;
                    402:                rc = netboot ( netdev );
                    403:        }
                    404: 
                    405:        printf ( "No more network devices\n" );
                    406:        return rc;
                    407: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.