Separate out bus-scanning and device-probing logic.
[people/xl0/gpxe.git] / src / core / main.c
1 /**************************************************************************
2 Etherboot -  Network Bootstrap Program
3
4 Literature dealing with the network protocols:
5         ARP - RFC826
6         RARP - RFC903
7         UDP - RFC768
8         BOOTP - RFC951, RFC2132 (vendor extensions)
9         DHCP - RFC2131, RFC2132 (options)
10         TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
11         RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
12         NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
13         IGMP - RFC1112
14
15 **************************************************************************/
16
17 /* #define MDEBUG */
18
19 #include "etherboot.h"
20 #include "dev.h"
21 #include "nic.h"
22 #include "disk.h"
23 #include "http.h"
24 #include "timer.h"
25 #include "cpu.h"
26 #include "console.h"
27 #include "init.h"
28 #include <stdarg.h>
29
30 #ifdef CONFIG_FILO
31 #include <lib.h>
32 #endif
33
34 jmp_buf restart_etherboot;
35 int     url_port;               
36
37 char as_main_program = 1;
38
39 #ifdef  IMAGE_FREEBSD
40 int freebsd_howto = 0;
41 char freebsd_kernel_env[FREEBSD_KERNEL_ENV_SIZE];
42 #endif
43
44 #if 0
45
46 static inline unsigned long ask_boot(unsigned *index)
47 {
48         unsigned long order = DEFAULT_BOOT_ORDER;
49         *index = DEFAULT_BOOT_INDEX;
50 #ifdef LINUXBIOS
51         order = get_boot_order(order, index);
52 #endif
53 #if defined(ASK_BOOT)
54 #if ASK_BOOT >= 0
55         while(1) {
56                 int c = 0;
57                 printf(ASK_PROMPT);
58 #if ASK_BOOT > 0
59                 {
60                         unsigned long time;
61                         for ( time = currticks() + ASK_BOOT*TICKS_PER_SEC;
62                               !c && !iskey(); ) {
63                                 if (currticks() > time) c = ANS_DEFAULT;
64                         }
65                 }
66 #endif /* ASK_BOOT > 0 */
67                 if ( !c ) c = getchar();
68                 if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
69                 if ((c >= ' ') && (c <= '~')) putchar(c);
70                 putchar('\n');
71
72                 switch(c) {
73                 default:
74                         /* Nothing useful try again */
75                         continue;
76                 case ANS_QUIT:
77                         order = BOOT_NOTHING;
78                         *index = 0;
79                         break;
80                 case ANS_DEFAULT:
81                         /* Preserve the default boot order */
82                         break;
83                 case ANS_NETWORK:
84                         order = (BOOT_NIC     << (0*BOOT_BITS)) | 
85                                 (BOOT_NOTHING << (1*BOOT_BITS));
86                         *index = 0;
87                         break;
88                 case ANS_DISK:
89                         order = (BOOT_DISK    << (0*BOOT_BITS)) | 
90                                 (BOOT_NOTHING << (1*BOOT_BITS));
91                         *index = 0;
92                         break;
93                 case ANS_FLOPPY:
94                         order = (BOOT_FLOPPY  << (0*BOOT_BITS)) | 
95                                 (BOOT_NOTHING << (1*BOOT_BITS));
96                         *index = 0;
97                         break;
98                 }
99                 break;
100         }
101         putchar('\n');
102 #endif /* ASK_BOOT >= 0 */
103 #endif /* defined(ASK_BOOT) */
104         return order;
105 }
106
107 static inline void try_floppy_first(void)
108 {
109 #if (TRY_FLOPPY_FIRST > 0)
110         int i;
111         printf("Trying floppy");
112         disk_init();
113         for (i = TRY_FLOPPY_FIRST; i-- > 0; ) {
114                 putchar('.');
115                 if (pcbios_disk_read(0, 0, 0, 0, ((char *)FLOPPY_BOOT_LOCATION)) != 0x8000) {
116                         printf("using floppy\n");
117                         exit(0);
118                 }
119         }
120         printf("no floppy\n");
121 #endif /* TRY_FLOPPY_FIRST */   
122 }
123
124 static struct class_operations {
125         struct dev *dev;
126         int (*probe)(struct dev *dev);
127         int (*load_configuration)(struct dev *dev);
128         int (*load)(struct dev *dev);
129 }
130 operations[] = {
131         { &nic.dev,  eth_probe,  eth_load_configuration,  eth_load  },
132         { &disk.dev, disk_probe, disk_load_configuration, disk_load },
133         { &disk.dev, disk_probe, disk_load_configuration, disk_load },
134 };
135
136 #endif
137
138
139
140 static int main_loop(int state);
141 static int exit_ok;
142 static int exit_status;
143 static int initialized;
144
145
146 /* Global instance of the current boot device */
147 DEV_BUS(struct bus_device, dev_bus);
148 struct dev dev = {
149         .bus = &dev_bus,
150 };
151
152 /**************************************************************************
153  * initialise() - perform any C-level initialisation
154  *
155  * This does not include initialising the NIC, but it does include
156  * e.g. getting the memory map, relocating to high memory,
157  * initialising the console, etc.
158  **************************************************************************
159  */
160 void initialise ( void ) {
161         /* Zero the BSS */
162         memset ( _bss, 0, _ebss - _bss );
163
164         /* Call all registered initialisation functions */
165         call_init_fns ();
166 }
167
168 /**************************************************************************
169 MAIN - Kick off routine
170 **************************************************************************/
171 int main ( void ) {
172
173         /* Print out configuration */
174         print_config();
175
176         /*
177          * Trivial main loop: we need to think about how we want to
178          * prompt the user etc.
179          *
180          */
181         for ( ; ; disable ( &dev ), call_reset_fns() ) {
182
183                 /* Get next boot device */
184                 if ( ! find_boot_device ( &dev ) ) {
185                         /* Reached end of device list */
186                         printf ( "No more boot devices\n" );
187                         continue;
188                 }
189
190                 /* Probe boot device */
191                 if ( ! probe ( &dev ) ) {
192                         /* Device found on bus, but probe failed */
193                         printf ( "Probe failed on %s, trying next device\n",
194                                  dev.name );
195                         continue;
196                 }
197                 
198                 /* Print device info */
199                 print_info ( &dev );
200
201                 /* Load configuration (e.g. DHCP) */
202                 if ( ! load_configuration ( &dev ) ) {
203                         /* DHCP failed */
204                         printf ( "Could not configure device %s\n", dev.name );
205                         continue;
206                 }
207
208                 /* Load image */
209                 if ( ! load ( &dev ) )
210                         /* Load failed */
211                         printf ( "Could not boot from device %s\n", dev.name );
212                         continue;
213         }
214
215         /* Call registered per-object exit functions */
216         call_exit_fns ();
217
218         return exit_status;
219 }
220
221 void exit(int status)
222 {
223         while(!exit_ok)
224                 ;
225         exit_status = status;
226         longjmp(restart_etherboot, 255);
227 }
228
229
230 #if 0
231
232 static int main_loop(int state)
233 {
234         /* Splitting main into 2 pieces makes the semantics of 
235          * which variables are preserved across a longjmp clean
236          * and predictable.
237          */
238         static unsigned long order;
239         static unsigned boot_index;
240         static struct dev * dev = 0;
241         static struct class_operations *ops;
242         static int type;
243         static int i;
244
245         if (!initialized) {
246                 initialized = 1;
247                 if (dev && (state >= 1) && (state <= 2)) {
248                         dev->how_probe = PROBE_AWAKE;
249                         dev->how_probe = ops->probe(dev);
250                         if (dev->how_probe == PROBE_FAILED) {
251                                 state = -1;
252                         }
253                 }
254         }
255         switch(state) {
256         case 0:
257         {
258                 static int firsttime = 1;
259                 /* First time through */
260                 if (firsttime) {
261                         cleanup();
262                         firsttime = 0;
263                 } 
264 #ifdef  EXIT_IF_NO_OFFER
265                 else {
266                         cleanup();
267                         exit(0);
268                 }
269 #endif
270                 i = -1;
271                 state = 4;
272                 dev = 0;
273
274                 /* We just called setjmp ... */
275                 order = ask_boot(&boot_index);
276                 try_floppy_first();
277                 break;
278         }
279         case 4:
280                 cleanup();
281                 call_reset_fns();
282                 /* Find a dev entry to probe with */
283                 if (!dev) {
284                         int boot;
285                         int failsafe;
286
287                         /* Advance to the next device type */
288                         i++;
289                         boot = (order >> (i * BOOT_BITS)) & BOOT_MASK;
290                         type = boot & BOOT_TYPE_MASK;
291                         failsafe = (boot & BOOT_FAILSAFE) != 0;
292                         if (i >= MAX_BOOT_ENTRIES) {
293                                 type = BOOT_NOTHING;
294                         }
295                         if ((i == 0) && (type == BOOT_NOTHING)) {
296                                 /* Return to caller */
297                                 exit(0);
298                         }
299                         if (type >= BOOT_NOTHING) {
300                                 interruptible_sleep(2);
301                                 state = 0;
302                                 break;
303                         }
304                         ops = &operations[type];
305                         dev = ops->dev;
306                         dev->how_probe = PROBE_FIRST;
307                         dev->type = type;
308                         dev->failsafe = failsafe;
309                         dev->type_index = 0;
310                 } else {
311                         /* Advance to the next device of the same type */
312                         dev->how_probe = PROBE_NEXT;
313                 }
314                 state = 3;
315                 break;
316         case 3:
317                 state = -1;
318                 /* Removed the following line because it was causing
319                  * heap.o to be dragged in unnecessarily.  It's also
320                  * slightly puzzling: by resetting heap_base, doesn't
321                  * this mean that we permanently leak memory?
322                  */
323                 /* heap_base = allot(0); */
324                 dev->how_probe = ops->probe(dev);
325                 if (dev->how_probe == PROBE_FAILED) {
326                         dev = 0;
327                         state = 4;
328                 } else if (boot_index && (i == 0) && (boot_index != (unsigned)dev->type_index)) {
329                         printf("Wrong index\n");
330                         state = 4;
331                 }
332                 else {
333                         state = 2;
334                 }
335                 break;
336         case 2:
337                 state = -1;
338                 if (ops->load_configuration(dev) >= 0) {
339                         state = 1;
340                 }
341                 break;
342         case 1:
343                 /* Any return from load is a failure */
344                 ops->load(dev);
345                 state = -1;
346                 break;
347         case 256:
348                 state = 0;
349                 break;
350         case -3:
351                 i = MAX_BOOT_ENTRIES;
352                 type = BOOT_NOTHING;
353                 /* fall through */
354         default:
355                 printf("<abort>\n");
356                 state = 4;
357                 /* At the end goto state 0 */
358                 if ((type >= BOOT_NOTHING) || (i >= MAX_BOOT_ENTRIES)) {
359                         state = 0;
360                 }
361                 break;
362         }
363         return state;
364 }
365
366
367 #endif
368
369
370 /**************************************************************************
371 LOADKERNEL - Try to load kernel image
372 **************************************************************************/
373 struct proto {
374         char *name;
375         int (*load)(const char *name,
376                 int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
377 };
378 static const struct proto protos[] = {
379 #ifdef DOWNLOAD_PROTO_TFTM
380         { "x-tftm", url_tftm },
381 #endif
382 #ifdef DOWNLOAD_PROTO_SLAM
383         { "x-slam", url_slam },
384 #endif
385 #ifdef DOWNLOAD_PROTO_NFS
386         { "nfs", nfs },
387 #endif
388 #ifdef DOWNLOAD_PROTO_DISK
389         { "file", url_file },
390 #endif
391 #ifdef DOWNLOAD_PROTO_TFTP
392         { "tftp", tftp },
393 #endif
394 #ifdef DOWNLOAD_PROTO_HTTP
395         { "http", http },
396 #endif
397 };
398
399 int loadkernel(const char *fname)
400 {
401         static const struct proto * const last_proto = 
402                 &protos[sizeof(protos)/sizeof(protos[0])];
403         const struct proto *proto;
404         in_addr ip;
405         int len;
406         const char *name;
407 #ifdef  DNS_RESOLVER
408         const char *resolvt;
409 #endif
410         ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
411         name = fname;
412         url_port = -1;
413         len = 0;
414         while(fname[len] && fname[len] != ':') {
415                 len++;
416         }
417         for(proto = &protos[0]; proto < last_proto; proto++) {
418                 if (memcmp(name, proto->name, len) == 0) {
419                         break;
420                 }
421         }
422         if ((proto < last_proto) && (memcmp(fname + len, "://", 3) == 0)) {
423                 name += len + 3;
424                 if (name[0] != '/') {
425 #ifdef DNS_RESOLVER
426                         resolvt = dns_resolver ( name );
427                         if ( NULL != resolvt ) {
428                                 //printf ("Resolved host name [%s] to [%s]\n",
429                                 //      name, resolvt );
430                                 inet_aton(resolvt, &ip);
431                                 while ( ( '/' != name[0] ) && ( 0 != name[0]))
432                                         ++name;
433                         } else
434 #endif  /* DNS_RESOLVER */
435                         name += inet_aton(name, &ip);
436                         if (name[0] == ':') {
437                                 name++;
438                                 url_port = strtoul(name, &name, 10);
439                         }
440                 }
441                 if (name[0] == '/') {
442                         arptable[ARP_SERVER].ipaddr.s_addr = ip.s_addr;
443                         printf( "Loading %s ", fname );
444                         return proto->load(name + 1, load_block);
445                 }
446         }
447         printf("Loading %@:%s ", arptable[ARP_SERVER].ipaddr, fname);
448 #ifdef  DEFAULT_PROTO_NFS
449         return nfs(fname, load_block);
450 #else
451         return tftp(fname, load_block);
452 #endif
453 }
454
455
456 /**************************************************************************
457 CLEANUP - shut down networking and console so that the OS may be called 
458 **************************************************************************/
459 void cleanup(void)
460 {
461 #ifdef  DOWNLOAD_PROTO_NFS
462         nfs_umountall(ARP_SERVER);
463 #endif
464         /* Stop receiving packets */
465         eth_disable();
466         disk_disable();
467         initialized = 0;
468 }
469
470 /*
471  * Local variables:
472  *  c-basic-offset: 8
473  * End:
474  */