|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.