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