|
@@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
27
|
27
|
#include <ipxe/image.h>
|
28
|
28
|
#include <ipxe/sanboot.h>
|
29
|
29
|
#include <ipxe/uri.h>
|
|
30
|
+#include <ipxe/open.h>
|
30
|
31
|
#include <ipxe/init.h>
|
31
|
32
|
#include <usr/ifmgmt.h>
|
32
|
33
|
#include <usr/route.h>
|
|
@@ -57,30 +58,21 @@ static struct net_device * find_boot_netdev ( void ) {
|
57
|
58
|
}
|
58
|
59
|
|
59
|
60
|
/**
|
60
|
|
- * Boot using next-server and filename
|
|
61
|
+ * Parse next-server and filename into a URI
|
61
|
62
|
*
|
62
|
|
- * @v filename Boot filename
|
63
|
|
- * @ret rc Return status code
|
|
63
|
+ * @v next_server Next-server address
|
|
64
|
+ * @v filename Filename
|
|
65
|
+ * @ret uri URI, or NULL on failure
|
64
|
66
|
*/
|
65
|
|
-int boot_next_server_and_filename ( struct in_addr next_server,
|
66
|
|
- const char *filename ) {
|
|
67
|
+static struct uri * parse_next_server_and_filename ( struct in_addr next_server,
|
|
68
|
+ const char *filename ) {
|
67
|
69
|
struct uri *uri;
|
68
|
|
- struct image *image;
|
69
|
|
- char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ +
|
70
|
|
- ( 3 * strlen(filename) ) /* completely URI-encoded */
|
71
|
|
- + 1 /* NUL */ ];
|
72
|
|
- int filename_is_absolute;
|
73
|
|
- int rc;
|
|
70
|
+ struct uri *tmp;
|
74
|
71
|
|
75
|
|
- /* Construct URI */
|
|
72
|
+ /* Parse filename */
|
76
|
73
|
uri = parse_uri ( filename );
|
77
|
|
- if ( ! uri ) {
|
78
|
|
- printf ( "Could not parse \"%s\"\n", filename );
|
79
|
|
- rc = -ENOMEM;
|
80
|
|
- goto err_parse_uri;
|
81
|
|
- }
|
82
|
|
- filename_is_absolute = uri_is_absolute ( uri );
|
83
|
|
- uri_put ( uri );
|
|
74
|
+ if ( ! uri )
|
|
75
|
+ return NULL;
|
84
|
76
|
|
85
|
77
|
/* Construct a tftp:// URI for the filename, if applicable.
|
86
|
78
|
* We can't just rely on the current working URI, because the
|
|
@@ -88,41 +80,17 @@ int boot_next_server_and_filename ( struct in_addr next_server,
|
88
|
80
|
* filenames with and without initial slashes, which is
|
89
|
81
|
* significant for TFTP.
|
90
|
82
|
*/
|
91
|
|
- if ( ! filename_is_absolute ) {
|
92
|
|
- snprintf ( buf, sizeof ( buf ), "tftp://%s/",
|
93
|
|
- inet_ntoa ( next_server ) );
|
94
|
|
- uri_encode ( filename, buf + strlen ( buf ),
|
95
|
|
- sizeof ( buf ) - strlen ( buf ), URI_PATH );
|
96
|
|
- filename = buf;
|
|
83
|
+ if ( ! uri_is_absolute ( uri ) ) {
|
|
84
|
+ tmp = uri;
|
|
85
|
+ tmp->scheme = "tftp";
|
|
86
|
+ tmp->host = inet_ntoa ( next_server );
|
|
87
|
+ uri = uri_dup ( tmp );
|
|
88
|
+ uri_put ( tmp );
|
|
89
|
+ if ( ! uri )
|
|
90
|
+ return NULL;
|
97
|
91
|
}
|
98
|
92
|
|
99
|
|
- /* Download and boot image */
|
100
|
|
- image = alloc_image();
|
101
|
|
- if ( ! image ) {
|
102
|
|
- printf ( "Could not allocate image\n" );
|
103
|
|
- rc = -ENOMEM;
|
104
|
|
- goto err_alloc_image;
|
105
|
|
- }
|
106
|
|
- if ( ( rc = imgfetch ( image, filename,
|
107
|
|
- register_and_autoload_image ) ) != 0 ) {
|
108
|
|
- printf ( "Could not fetch image: %s\n", strerror ( rc ) );
|
109
|
|
- goto err_imgfetch;
|
110
|
|
- }
|
111
|
|
- if ( ( rc = imgexec ( image ) ) != 0 ) {
|
112
|
|
- printf ( "Could not execute image: %s\n", strerror ( rc ) );
|
113
|
|
- goto err_imgexec;
|
114
|
|
- }
|
115
|
|
-
|
116
|
|
- /* Drop image reference */
|
117
|
|
- image_put ( image );
|
118
|
|
- return 0;
|
119
|
|
-
|
120
|
|
- err_imgexec:
|
121
|
|
- err_imgfetch:
|
122
|
|
- image_put ( image );
|
123
|
|
- err_alloc_image:
|
124
|
|
- err_parse_uri:
|
125
|
|
- return rc;
|
|
93
|
+ return uri;
|
126
|
94
|
}
|
127
|
95
|
|
128
|
96
|
/** The "keep-san" setting */
|
|
@@ -142,71 +110,99 @@ struct setting skip_san_boot_setting __setting = {
|
142
|
110
|
};
|
143
|
111
|
|
144
|
112
|
/**
|
145
|
|
- * Boot using root path
|
|
113
|
+ * Boot from filename and root-path URIs
|
146
|
114
|
*
|
|
115
|
+ * @v filename Filename
|
147
|
116
|
* @v root_path Root path
|
148
|
117
|
* @ret rc Return status code
|
149
|
118
|
*/
|
150
|
|
-int boot_root_path ( const char *root_path ) {
|
151
|
|
- struct uri *uri;
|
|
119
|
+int uriboot ( struct uri *filename, struct uri *root_path ) {
|
|
120
|
+ struct image *image;
|
152
|
121
|
int drive;
|
153
|
122
|
int rc;
|
154
|
123
|
|
155
|
|
- /* Parse URI */
|
156
|
|
- uri = parse_uri ( root_path );
|
157
|
|
- if ( ! uri ) {
|
158
|
|
- printf ( "Could not parse \"%s\"\n", root_path );
|
|
124
|
+ /* Allocate image */
|
|
125
|
+ image = alloc_image();
|
|
126
|
+ if ( ! image ) {
|
|
127
|
+ printf ( "Could not allocate image\n" );
|
159
|
128
|
rc = -ENOMEM;
|
160
|
|
- goto err_parse_uri;
|
|
129
|
+ goto err_alloc_image;
|
|
130
|
+ }
|
|
131
|
+
|
|
132
|
+ /* Treat empty URIs as absent */
|
|
133
|
+ if ( filename && ( ! filename->path ) )
|
|
134
|
+ filename = NULL;
|
|
135
|
+ if ( root_path && ( ! uri_is_absolute ( root_path ) ) )
|
|
136
|
+ root_path = NULL;
|
|
137
|
+
|
|
138
|
+ /* If we have both a filename and a root path, ignore an
|
|
139
|
+ * unsupported URI scheme in the root path, since it may
|
|
140
|
+ * represent an NFS root.
|
|
141
|
+ */
|
|
142
|
+ if ( filename && root_path &&
|
|
143
|
+ ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) {
|
|
144
|
+ printf ( "Ignoring unsupported root path\n" );
|
|
145
|
+ root_path = NULL;
|
161
|
146
|
}
|
162
|
147
|
|
163
|
|
- /* Hook SAN device */
|
164
|
|
- if ( ( drive = san_hook ( uri, 0 ) ) < 0 ) {
|
165
|
|
- rc = drive;
|
166
|
|
- printf ( "Could not open SAN device: %s\n",
|
167
|
|
- strerror ( rc ) );
|
168
|
|
- goto err_open;
|
|
148
|
+ /* Hook SAN device, if applicable */
|
|
149
|
+ if ( root_path ) {
|
|
150
|
+ drive = san_hook ( root_path, 0 );
|
|
151
|
+ if ( drive < 0 ) {
|
|
152
|
+ rc = drive;
|
|
153
|
+ printf ( "Could not open SAN device: %s\n",
|
|
154
|
+ strerror ( rc ) );
|
|
155
|
+ goto err_san_hook;
|
|
156
|
+ }
|
|
157
|
+ printf ( "Registered as SAN device %#02x\n", drive );
|
|
158
|
+ } else {
|
|
159
|
+ drive = -ENODEV;
|
169
|
160
|
}
|
170
|
|
- printf ( "Registered as SAN device %#02x\n", drive );
|
171
|
161
|
|
172
|
|
- /* Describe SAN device */
|
173
|
|
- if ( ( rc = san_describe ( drive ) ) != 0 ) {
|
|
162
|
+ /* Describe SAN device, if applicable */
|
|
163
|
+ if ( ( drive >= 0 ) && ( ( rc = san_describe ( drive ) ) != 0 ) ) {
|
174
|
164
|
printf ( "Could not describe SAN device %#02x: %s\n",
|
175
|
165
|
drive, strerror ( rc ) );
|
176
|
|
- goto err_describe;
|
|
166
|
+ goto err_san_describe;
|
177
|
167
|
}
|
178
|
168
|
|
179
|
|
- /* Boot from SAN device */
|
180
|
|
- if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) != 0 ) {
|
181
|
|
- printf ( "Skipping boot from SAN device %#02x\n", drive );
|
|
169
|
+ /* Attempt filename or SAN boot as applicable */
|
|
170
|
+ if ( filename ) {
|
|
171
|
+ if ( ( rc = imgdownload ( image, filename,
|
|
172
|
+ register_and_autoexec_image ) ) !=0){
|
|
173
|
+ printf ( "Could not chain image: %s\n",
|
|
174
|
+ strerror ( rc ) );
|
|
175
|
+ }
|
|
176
|
+ } else if ( root_path ) {
|
|
177
|
+ if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) {
|
|
178
|
+ printf ( "Booting from SAN device %#02x\n", drive );
|
|
179
|
+ rc = san_boot ( drive );
|
|
180
|
+ printf ( "Boot from SAN device %#02x failed: %s\n",
|
|
181
|
+ drive, strerror ( rc ) );
|
|
182
|
+ } else {
|
|
183
|
+ printf ( "Skipping boot from SAN device %#02x\n",
|
|
184
|
+ drive );
|
|
185
|
+ rc = 0;
|
|
186
|
+ }
|
182
|
187
|
} else {
|
183
|
|
- printf ( "Booting from SAN device %#02x\n", drive );
|
184
|
|
- rc = san_boot ( drive );
|
185
|
|
- printf ( "Boot from SAN device %#02x failed: %s\n",
|
186
|
|
- drive, strerror ( rc ) );
|
|
188
|
+ printf ( "No filename or root path specified\n" );
|
|
189
|
+ rc = -ENOENT;
|
187
|
190
|
}
|
188
|
191
|
|
189
|
|
- /* Leave drive registered, if instructed to do so */
|
190
|
|
- if ( fetch_intz_setting ( NULL, &keep_san_setting ) != 0 ) {
|
191
|
|
- printf ( "Preserving connection to SAN device %#02x\n",
|
192
|
|
- drive );
|
193
|
|
- goto err_keep_san;
|
|
192
|
+ err_san_describe:
|
|
193
|
+ /* Unhook SAN device, if applicable */
|
|
194
|
+ if ( drive >= 0 ) {
|
|
195
|
+ if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) {
|
|
196
|
+ printf ( "Unregistering SAN device %#02x\n", drive );
|
|
197
|
+ san_unhook ( drive );
|
|
198
|
+ } else {
|
|
199
|
+ printf ( "Preserving connection to SAN device %#02x\n",
|
|
200
|
+ drive );
|
|
201
|
+ }
|
194
|
202
|
}
|
195
|
|
-
|
196
|
|
- /* Unhook SAN deivce */
|
197
|
|
- printf ( "Unregistering SAN device %#02x\n", drive );
|
198
|
|
- san_unhook ( drive );
|
199
|
|
-
|
200
|
|
- /* Drop URI reference */
|
201
|
|
- uri_put ( uri );
|
202
|
|
-
|
203
|
|
- return 0;
|
204
|
|
-
|
205
|
|
- err_keep_san:
|
206
|
|
- err_describe:
|
207
|
|
- err_open:
|
208
|
|
- uri_put ( uri );
|
209
|
|
- err_parse_uri:
|
|
203
|
+ err_san_hook:
|
|
204
|
+ image_put ( image );
|
|
205
|
+ err_alloc_image:
|
210
|
206
|
return rc;
|
211
|
207
|
}
|
212
|
208
|
|
|
@@ -227,12 +223,53 @@ static void close_all_netdevs ( void ) {
|
227
|
223
|
}
|
228
|
224
|
|
229
|
225
|
/**
|
230
|
|
- * Boot from a network device
|
|
226
|
+ * Fetch next-server and filename settings into a URI
|
231
|
227
|
*
|
232
|
|
- * @v netdev Network device
|
233
|
|
- * @ret rc Return status code
|
|
228
|
+ * @v settings Settings block
|
|
229
|
+ * @ret uri URI, or NULL on failure
|
234
|
230
|
*/
|
235
|
|
-int netboot ( struct net_device *netdev ) {
|
|
231
|
+struct uri * fetch_next_server_and_filename ( struct settings *settings ) {
|
|
232
|
+ struct in_addr next_server;
|
|
233
|
+ char filename[256];
|
|
234
|
+
|
|
235
|
+ /* Fetch next-server setting */
|
|
236
|
+ fetch_ipv4_setting ( settings, &next_server_setting, &next_server );
|
|
237
|
+ if ( next_server.s_addr )
|
|
238
|
+ printf ( "Next server: %s\n", inet_ntoa ( next_server ) );
|
|
239
|
+
|
|
240
|
+ /* Fetch filename setting */
|
|
241
|
+ fetch_string_setting ( settings, &filename_setting,
|
|
242
|
+ filename, sizeof ( filename ) );
|
|
243
|
+ if ( filename[0] )
|
|
244
|
+ printf ( "Filename: %s\n", filename );
|
|
245
|
+
|
|
246
|
+ return parse_next_server_and_filename ( next_server, filename );
|
|
247
|
+}
|
|
248
|
+
|
|
249
|
+/**
|
|
250
|
+ * Fetch root-path setting into a URI
|
|
251
|
+ *
|
|
252
|
+ * @v settings Settings block
|
|
253
|
+ * @ret uri URI, or NULL on failure
|
|
254
|
+ */
|
|
255
|
+static struct uri * fetch_root_path ( struct settings *settings ) {
|
|
256
|
+ char root_path[256];
|
|
257
|
+
|
|
258
|
+ /* Fetch root-path setting */
|
|
259
|
+ fetch_string_setting ( settings, &root_path_setting,
|
|
260
|
+ root_path, sizeof ( root_path ) );
|
|
261
|
+ if ( root_path[0] )
|
|
262
|
+ printf ( "Root path: %s\n", root_path );
|
|
263
|
+
|
|
264
|
+ return parse_uri ( root_path );
|
|
265
|
+}
|
|
266
|
+
|
|
267
|
+/**
|
|
268
|
+ * Check whether or not we have a usable PXE menu
|
|
269
|
+ *
|
|
270
|
+ * @ret have_menu A usable PXE menu is present
|
|
271
|
+ */
|
|
272
|
+static int have_pxe_menu ( void ) {
|
236
|
273
|
struct setting vendor_class_id_setting
|
237
|
274
|
= { .tag = DHCP_VENDOR_CLASS_ID };
|
238
|
275
|
struct setting pxe_discovery_control_setting
|
|
@@ -240,8 +277,28 @@ int netboot ( struct net_device *netdev ) {
|
240
|
277
|
struct setting pxe_boot_menu_setting
|
241
|
278
|
= { .tag = DHCP_PXE_BOOT_MENU };
|
242
|
279
|
char buf[256];
|
243
|
|
- struct in_addr next_server;
|
244
|
280
|
unsigned int pxe_discovery_control;
|
|
281
|
+
|
|
282
|
+ fetch_string_setting ( NULL, &vendor_class_id_setting,
|
|
283
|
+ buf, sizeof ( buf ) );
|
|
284
|
+ pxe_discovery_control =
|
|
285
|
+ fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
|
|
286
|
+
|
|
287
|
+ return ( ( strcmp ( buf, "PXEClient" ) == 0 ) &&
|
|
288
|
+ setting_exists ( NULL, &pxe_boot_menu_setting ) &&
|
|
289
|
+ ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) &&
|
|
290
|
+ setting_exists ( NULL, &filename_setting ) ) ) );
|
|
291
|
+}
|
|
292
|
+
|
|
293
|
+/**
|
|
294
|
+ * Boot from a network device
|
|
295
|
+ *
|
|
296
|
+ * @v netdev Network device
|
|
297
|
+ * @ret rc Return status code
|
|
298
|
+ */
|
|
299
|
+int netboot ( struct net_device *netdev ) {
|
|
300
|
+ struct uri *filename;
|
|
301
|
+ struct uri *root_path;
|
245
|
302
|
int rc;
|
246
|
303
|
|
247
|
304
|
/* Close all other network devices */
|
|
@@ -249,44 +306,42 @@ int netboot ( struct net_device *netdev ) {
|
249
|
306
|
|
250
|
307
|
/* Open device and display device status */
|
251
|
308
|
if ( ( rc = ifopen ( netdev ) ) != 0 )
|
252
|
|
- return rc;
|
|
309
|
+ goto err_ifopen;
|
253
|
310
|
ifstat ( netdev );
|
254
|
311
|
|
255
|
312
|
/* Configure device via DHCP */
|
256
|
313
|
if ( ( rc = dhcp ( netdev ) ) != 0 )
|
257
|
|
- return rc;
|
|
314
|
+ goto err_dhcp;
|
258
|
315
|
route();
|
259
|
316
|
|
260
|
317
|
/* Try PXE menu boot, if applicable */
|
261
|
|
- fetch_string_setting ( NULL, &vendor_class_id_setting,
|
262
|
|
- buf, sizeof ( buf ) );
|
263
|
|
- pxe_discovery_control =
|
264
|
|
- fetch_uintz_setting ( NULL, &pxe_discovery_control_setting );
|
265
|
|
- if ( ( strcmp ( buf, "PXEClient" ) == 0 ) &&
|
266
|
|
- setting_exists ( NULL, &pxe_boot_menu_setting ) &&
|
267
|
|
- ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) &&
|
268
|
|
- setting_exists ( NULL, &filename_setting ) ) ) ) {
|
|
318
|
+ if ( have_pxe_menu() ) {
|
269
|
319
|
printf ( "Booting from PXE menu\n" );
|
270
|
|
- return pxe_menu_boot ( netdev );
|
|
320
|
+ rc = pxe_menu_boot ( netdev );
|
|
321
|
+ goto err_pxe_menu_boot;
|
271
|
322
|
}
|
272
|
323
|
|
273
|
|
- /* Try to download and boot whatever we are given as a filename */
|
274
|
|
- fetch_ipv4_setting ( NULL, &next_server_setting, &next_server );
|
275
|
|
- fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) );
|
276
|
|
- if ( buf[0] ) {
|
277
|
|
- printf ( "Booting from filename \"%s\"\n", buf );
|
278
|
|
- return boot_next_server_and_filename ( next_server, buf );
|
279
|
|
- }
|
280
|
|
-
|
281
|
|
- /* No filename; try the root path */
|
282
|
|
- fetch_string_setting ( NULL, &root_path_setting, buf, sizeof ( buf ) );
|
283
|
|
- if ( buf[0] ) {
|
284
|
|
- printf ( "Booting from root path \"%s\"\n", buf );
|
285
|
|
- return boot_root_path ( buf );
|
286
|
|
- }
|
287
|
|
-
|
288
|
|
- printf ( "No filename or root path specified\n" );
|
289
|
|
- return -ENOENT;
|
|
324
|
+ /* Fetch next server, filename and root path */
|
|
325
|
+ filename = fetch_next_server_and_filename ( NULL );
|
|
326
|
+ if ( ! filename )
|
|
327
|
+ goto err_filename;
|
|
328
|
+ root_path = fetch_root_path ( NULL );
|
|
329
|
+ if ( ! root_path )
|
|
330
|
+ goto err_root_path;
|
|
331
|
+
|
|
332
|
+ /* Boot using next server, filename and root path */
|
|
333
|
+ if ( ( rc = uriboot ( filename, root_path ) ) != 0 )
|
|
334
|
+ goto err_uriboot;
|
|
335
|
+
|
|
336
|
+ err_uriboot:
|
|
337
|
+ uri_put ( root_path );
|
|
338
|
+ err_root_path:
|
|
339
|
+ uri_put ( filename );
|
|
340
|
+ err_filename:
|
|
341
|
+ err_pxe_menu_boot:
|
|
342
|
+ err_dhcp:
|
|
343
|
+ err_ifopen:
|
|
344
|
+ return rc;
|
290
|
345
|
}
|
291
|
346
|
|
292
|
347
|
/**
|