|
|
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.