Merge branch 'master' into symcheck2
[people/dverkamp/gpxe.git] / src / usr / autoboot.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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 #include <string.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <gpxe/netdevice.h>
23 #include <gpxe/dhcp.h>
24 #include <gpxe/image.h>
25 #include <usr/ifmgmt.h>
26 #include <usr/route.h>
27 #include <usr/dhcpmgmt.h>
28 #include <usr/imgmgmt.h>
29 #include <usr/iscsiboot.h>
30 #include <usr/autoboot.h>
31
32 /** @file
33  *
34  * Automatic booting
35  *
36  */
37
38 /**
39  * Identify the boot network device
40  *
41  * @ret netdev          Boot network device
42  */
43 static struct net_device * find_boot_netdev ( void ) {
44         return NULL;
45 }
46
47 /**
48  * Boot using filename
49  *
50  * @v filename          Boot filename
51  * @ret rc              Return status code
52  */
53 static int boot_filename ( const char *filename ) {
54         struct image *image;
55         int rc;
56
57         image = alloc_image();
58         if ( ! image ) {
59                 printf ( "Out of memory\n" );
60                 return -ENOMEM;
61         }
62         if ( ( rc = imgfetch ( image, filename, 0 ) ) != 0 ) {
63                 printf ( "Could not retrieve %s: %s\n",
64                          filename, strerror ( rc ) );
65                 image_put ( image );
66                 return rc;
67         }
68         if ( ( rc = imgload ( image ) ) != 0 ) {
69                 printf ( "Could not load %s: %s\n", image->name,
70                          strerror ( rc ) );
71                 image_put ( image );
72                 return rc;
73         }
74         if ( ( rc = imgexec ( image ) ) != 0 ) {
75                 printf ( "Could not execute %s: %s\n", image->name,
76                          strerror ( rc ) );
77                 image_put ( image );
78                 return rc;
79         }
80
81         return 0;
82 }
83
84 /**
85  * Boot using root path
86  *
87  * @v root_path         Root path
88  * @ret rc              Return status code
89  */
90 static int boot_root_path ( const char *root_path ) {
91         int rc;
92
93         /* Quick hack */
94         if ( ( rc = iscsiboot ( root_path ) ) != 0 )
95                 return rc;
96
97         return 0;
98 }
99
100 /**
101  * Boot from a network device
102  *
103  * @v netdev            Network device
104  * @ret rc              Return status code
105  */
106 static int netboot ( struct net_device *netdev ) {
107         char buf[256];
108         int rc;
109
110         /* Open device and display device status */
111         if ( ( rc = ifopen ( netdev ) ) != 0 )
112                 return rc;
113         ifstat ( netdev );
114
115         /* Configure device via DHCP */
116         if ( ( rc = dhcp ( netdev ) ) != 0 )
117                 return rc;
118         route();
119
120         /* Try to download and boot whatever we are given as a filename */
121         dhcp_snprintf ( buf, sizeof ( buf ),
122                         find_global_dhcp_option ( DHCP_BOOTFILE_NAME ) );
123         if ( buf[0] ) {
124                 printf ( "Booting from filename \"%s\"\n", buf );
125                 return boot_filename ( buf );
126         }
127         
128         /* No filename; try the root path */
129         dhcp_snprintf ( buf, sizeof ( buf ),
130                         find_global_dhcp_option ( DHCP_ROOT_PATH ) );
131         if ( buf[0] ) {
132                 printf ( "Booting from root path \"%s\"\n", buf );
133                 return boot_root_path ( buf );
134         }
135
136         printf ( "No filename or root path specified\n" );
137         return -ENOENT;
138 }
139
140 /**
141  * Close all open net devices
142  *
143  * Called before a fresh boot attempt in order to free up memory.  We
144  * don't just close the device immediately after the boot fails,
145  * because there may still be TCP connections in the process of
146  * closing.
147  */
148 static void close_all_netdevs ( void ) {
149         struct net_device *netdev;
150
151         for_each_netdev ( netdev ) {
152                 ifclose ( netdev );
153         }
154 }
155
156 /**
157  * Boot the system
158  */
159 void autoboot ( void ) {
160         struct net_device *boot_netdev;
161         struct net_device *netdev;
162
163         /* If we have an identifable boot device, try that first */
164         close_all_netdevs();
165         if ( ( boot_netdev = find_boot_netdev() ) )
166                 netboot ( boot_netdev );
167
168         /* If that fails, try booting from any of the other devices */
169         for_each_netdev ( netdev ) {
170                 if ( netdev == boot_netdev )
171                         continue;
172                 close_all_netdevs();
173                 netboot ( netdev );
174         }
175
176         printf ( "No more network devices\n" );
177 }