1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  Automatic OS bootstrap			File: cfe_autoboot.c
5    *
6    *  This module handles OS bootstrap stuff.  We use this version
7    *  to do "automatic" booting; walking down a list of possible boot
8    *  options, trying them until something good happens.
9    *
10    *  Author:  Mitch Lichtenberg (mpl@broadcom.com)
11    *
12    *********************************************************************
13    *
14    *  Copyright 2000,2001,2002,2003
15    *  Broadcom Corporation. All rights reserved.
16    *
17    *  This software is furnished under license and may be used and
18    *  copied only in accordance with the following terms and
19    *  conditions.  Subject to these conditions, you may download,
20    *  copy, install, use, modify and distribute modified or unmodified
21    *  copies of this software in source and/or binary form.  No title
22    *  or ownership is transferred hereby.
23    *
24    *  1) Any source code used, modified or distributed must reproduce
25    *     and retain this copyright notice and list of conditions
26    *     as they appear in the source file.
27    *
28    *  2) No right is granted to use any trade name, trademark, or
29    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
30    *     name may not be used to endorse or promote products derived
31    *     from this software without the prior written permission of
32    *     Broadcom Corporation.
33    *
34    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
35    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
36    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
37    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
38    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
39    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
40    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
42    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
44    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
45    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
46    *     THE POSSIBILITY OF SUCH DAMAGE.
47    ********************************************************************* */
48
49
50#include "lib_types.h"
51#include "lib_string.h"
52#include "lib_queue.h"
53#include "lib_malloc.h"
54#include "lib_printf.h"
55
56#include "cfe_iocb.h"
57#include "cfe_device.h"
58#include "cfe_console.h"
59#include "cfe_devfuncs.h"
60#include "cfe_timer.h"
61
62#include "cfe_error.h"
63
64#include "env_subr.h"
65#include "cfe.h"
66
67#include "net_ebuf.h"
68#include "net_ether.h"
69
70#include "net_api.h"
71#include "cfe_fileops.h"
72#include "cfe_bootblock.h"
73#include "bsp_config.h"
74#include "cfe_boot.h"
75
76#include "cfe_loader.h"
77
78#include "cfe_autoboot.h"
79
80/*  *********************************************************************
81    *  data
82    ********************************************************************* */
83
84queue_t cfe_autoboot_list = {&cfe_autoboot_list,&cfe_autoboot_list};
85
86extern cfe_loadargs_t cfe_loadargs;		/* from cfe_boot.c */
87extern const char *bootvar_device;
88extern const char *bootvar_file;
89extern const char *bootvar_flags;
90
91extern char *varchars;
92
93/*  *********************************************************************
94    *  macroexpand(instr,outstr)
95    *
96    *  Expand environment variables in "instr" to "outstr"
97    *
98    *  Input parameters:
99    *  	   instr - to be expanded
100    *	   outstr - expanded.
101    *
102    *  Return value:
103    *  	   nothing
104    ********************************************************************* */
105
106static void macroexpand(char *instr,char *outstr)
107{
108    char macroname[50];
109    char *m;
110
111    while (*instr) {
112	if (*instr == '$') {
113	    instr++;
114	    m = macroname;
115	    while (*instr && strchr(varchars,*instr)) {
116		*m++ = *instr++;
117		}
118	    *m = '\0';
119	    m = env_getenv(macroname);
120	    if (m) {
121		while (*m) *outstr++ = *m++;
122		}
123	    }
124	else {
125	    *outstr++ = *instr++;
126	    }
127	}
128
129    *outstr = '\0';
130}
131
132/*  *********************************************************************
133    *  cfe_tryauto_common(method,loadargs)
134    *
135    *  Common portion of autoboot
136    *
137    *  Input parameters:
138    *  	   method - details on device to boot from
139    *      filename - canonical name of file to load
140    *  	   loadargs - cfe_loadargs_t of digested load parameters.
141    *
142    *  Return value:
143    *  	   does not return if successful
144    ********************************************************************* */
145
146static int cfe_tryauto_common(cfe_autoboot_method_t *method,
147			      char *filename,
148			      cfe_loadargs_t *la)
149{
150    int res;
151
152    la->la_filename = filename;
153    la->la_options = env_getenv(bootvar_flags);
154    la->la_filesys = method->ab_filesys;
155    la->la_device = method->ab_dev;
156    la->la_flags = method->ab_flags | LOADFLG_NOISY | LOADFLG_EXECUTE;
157    la->la_address = 0;
158    la->la_maxsize = 0;
159    la->la_entrypt = 0;
160
161    /* okay, go for it. */
162
163    xprintf("Loader:%s Filesys:%s Dev:%s File:%s Options:%s\n",
164	    method->ab_loader,la->la_filesys,la->la_device,la->la_filename,la->la_options);
165
166    res = cfe_boot(method->ab_loader,&cfe_loadargs);
167
168    return res;
169
170}
171
172
173/*  *********************************************************************
174    *  cfe_tryauto_network(method)
175    *
176    *  Try to autoboot from a network device.
177    *
178    *  Input parameters:
179    *  	   method - details on device to boot from
180    *
181    *  Return value:
182    *  	   does not return unless there is an error
183    ********************************************************************* */
184
185#if CFG_NETWORK
186static int cfe_tryauto_network(cfe_autoboot_method_t *method)
187{
188    int res;
189    dhcpreply_t *reply = NULL;
190    cfe_loadargs_t *la = &cfe_loadargs;
191    char buffer[512];
192    char *x;
193
194    /*
195     * First turn off any network interface that is currently active.
196     */
197
198    net_uninit();
199
200    /*
201     * Now activate the network hardware.
202     */
203
204    res = net_init(method->ab_dev);
205    if (res < 0) {
206	printf("Could not initialize network device %s: %s\n",
207	       method->ab_dev,
208	       cfe_errortext(res));
209	return res;
210	}
211
212    /*
213     * Do a DHCP query.
214     */
215
216    res = dhcp_bootrequest(&reply);
217    if (res < 0) {
218	printf("DHCP request failed on device %s: %s\n",method->ab_dev,
219	       cfe_errortext(res));
220	net_uninit();
221	return res;
222	}
223
224    net_setparam(NET_IPADDR,reply->dr_ipaddr);
225    net_setparam(NET_NETMASK,reply->dr_netmask);
226    net_setparam(NET_GATEWAY,reply->dr_gateway);
227    net_setparam(NET_NAMESERVER,reply->dr_nameserver);
228    net_setparam(NET_DOMAIN,reply->dr_domainname);
229
230    /* Set all our environment variables. */
231    net_setnetvars();
232    dhcp_set_envvars(reply);
233
234    if (reply) dhcp_free_reply(reply);
235
236    /*
237     * Interface is now configured and ready, we can
238     * load programs now.
239     */
240
241    /*
242     * Construct the file name to load.  If the method does not
243     * specify a file name directly, get it from DHCP.
244     *
245     * For network booting, the format of the file name
246     * is 'hostname:filename'
247     *
248     * If the method's filename includes a hostname, ignore
249     * BOOT_SERVER.  Otherwise, combine BOOT_SERVER with the
250     * filename.  This way we can provide the name here
251     * but have the file come off the server specified in the
252     * DHCP message.
253     */
254
255    if (method->ab_file && strchr(method->ab_file,':')) {
256	macroexpand(method->ab_file,buffer);
257	}
258    else {
259	buffer[0] = '\0';
260	x = env_getenv("BOOT_SERVER");
261	if (x) {
262	    strcat(buffer,x);
263	    strcat(buffer,":");
264	    }
265
266	x = method->ab_file;
267	if (!x) x = env_getenv(bootvar_file);
268	if (x) {
269	    strcat(buffer,x);
270	    }
271	}
272
273    /* Okay, do it. */
274
275    cfe_tryauto_common(method,buffer,la);
276
277    /* If we get here, something bad happened. */
278
279    net_uninit();
280
281    return res;
282
283}
284
285#endif
286
287
288/*  *********************************************************************
289    *  cfe_tryauto_disk(method)
290    *
291    *  Try to autoboot from a disk device.
292    *
293    *  Input parameters:
294    *  	   method - details on device to boot from
295    *
296    *  Return value:
297    *  	   does not return unless there is an error
298    ********************************************************************* */
299static int cfe_tryauto_disk(cfe_autoboot_method_t *method)
300{
301    int res;
302    cfe_loadargs_t *la = &cfe_loadargs;
303    char buffer[512];
304    char *filename;
305
306    buffer[0] = '\0';
307
308    if (method->ab_file) {
309	macroexpand(method->ab_file,buffer);
310	filename = buffer;
311	}
312    else {
313	filename = env_getenv("BOOT_FILE");
314	if (filename) strcpy(buffer,filename);
315	}
316
317    res = cfe_tryauto_common(method,filename,la);
318
319    return res;
320}
321
322/*  *********************************************************************
323    *  cfe_tryauto(method)
324    *
325    *  Try a single autoboot method.
326    *
327    *  Input parameters:
328    *  	   method - autoboot method to try
329    *
330    *  Return value:
331    *  	   does not return if bootstrap is successful
332    *  	   else error code
333    ********************************************************************* */
334
335static int cfe_tryauto(cfe_autoboot_method_t *method)
336{
337    switch (method->ab_type) {
338#if CFG_NETWORK
339	case CFE_AUTOBOOT_NETWORK:
340	    return cfe_tryauto_network(method);
341	    break;
342#endif
343
344	case CFE_AUTOBOOT_DISK:
345	case CFE_AUTOBOOT_RAW:
346	    return cfe_tryauto_disk(method);
347	    break;
348	}
349
350    return -1;
351}
352
353/*  *********************************************************************
354    *  cfe_autoboot(dev,flags)
355    *
356    *  Try to perform an automatic system bootstrap
357    *
358    *  Input parameters:
359    *  	   dev - if not NULL, restrict bootstrap to named device
360    *  	   flags - controls behaviour of cfe_autoboot
361    *
362    *  Return value:
363    *  	   should not return if bootstrap is successful, otherwise
364    *  	   error code
365    ********************************************************************* */
366int cfe_autoboot(char *dev,int flags)
367{
368    queue_t *qb;
369    cfe_autoboot_method_t *method;
370    int res;
371    cfe_timer_t timer;
372    int forever;
373    int pollconsole;
374
375    forever = (flags & CFE_AUTOFLG_TRYFOREVER) ? 1 : 0;
376    pollconsole = (flags & CFE_AUTOFLG_POLLCONSOLE) ? 1 : 0;
377
378    do {	/* potentially forever */
379	for (qb = cfe_autoboot_list.q_next; qb != &cfe_autoboot_list; qb = qb->q_next) {
380
381	    method = (cfe_autoboot_method_t *) qb;
382
383	    /*
384	     * Skip other devices if we passed in a specific one.
385	     */
386
387	    if (dev && (strcmp(dev,method->ab_dev) != 0)) continue;
388
389	    printf("\n*** Autoboot: Trying device '%s' ", method->ab_dev);
390	    if (method->ab_file) printf("file %s ",method->ab_file);
391	    printf("(%s,%s)\n\n",method->ab_dev,method->ab_filesys,method->ab_loader);
392
393	    /*
394	     * Scan keyboard if requested.
395	     */
396	    if (pollconsole) {
397		TIMER_SET(timer,CFE_HZ);
398		while (!TIMER_EXPIRED(timer)) {
399		    POLL();
400		    if (console_status()) goto done;
401		    }
402		}
403
404	    /*
405	     * Try something.  may not return.
406	     */
407
408	    res = cfe_tryauto(method);
409	    }
410	} while (forever);
411
412    /* bail. */
413done:
414
415    printf("\n*** Autoboot failed.\n\n");
416
417    return -1;
418}
419
420
421/*  *********************************************************************
422    *  cfe_add_autoboot(type,flags,dev,loader,filesys,file)
423    *
424    *  Add an autoboot method to the table.
425    *  This is typically called in the board_finalinit one or more
426    *  times to provide a list of bootstrap methods to try for autoboot
427    *
428    *  Input parameters:
429    *  	   type - CFE_AUTOBOOT_xxx (disk,network,raw)
430    *  	   flags - loader flags (LOADFLG_xxx)
431    *  	   loader - loader string (elf, raw, srec)
432    *  	   filesys - file system string (raw, tftp, fat)
433    *  	   file - name of file to load (if null, will try to guess)
434    *
435    *  Return value:
436    *  	   0 if ok, else error code
437    ********************************************************************* */
438
439int cfe_add_autoboot(int type,int flags,char *dev,char *loader,char *filesys,char *file)
440{
441    cfe_autoboot_method_t *method;
442
443    method = (cfe_autoboot_method_t *) KMALLOC(sizeof(cfe_autoboot_method_t),0);
444
445    if (!method) return CFE_ERR_NOMEM;
446
447    method->ab_type = type;
448    method->ab_flags = flags;
449    method->ab_dev = dev;
450    method->ab_loader = loader;
451    method->ab_filesys = filesys;
452    method->ab_file = file;
453
454    q_enqueue(&cfe_autoboot_list,(queue_t *)method);
455    return 0;
456}
457