1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  VxWorks Boot Commands			File: ui_vxboot.c
5    *
6    *  Commands useful to people booting VxWorks
7    *
8    *  Author:  Mitch Lichtenberg
9    *
10    *********************************************************************
11    *
12    *  Copyright 2000,2001,2002,2003
13    *  Broadcom Corporation. All rights reserved.
14    *
15    *  This software is furnished under license and may be used and
16    *  copied only in accordance with the following terms and
17    *  conditions.  Subject to these conditions, you may download,
18    *  copy, install, use, modify and distribute modified or unmodified
19    *  copies of this software in source and/or binary form.  No title
20    *  or ownership is transferred hereby.
21    *
22    *  1) Any source code used, modified or distributed must reproduce
23    *     and retain this copyright notice and list of conditions
24    *     as they appear in the source file.
25    *
26    *  2) No right is granted to use any trade name, trademark, or
27    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
28    *     name may not be used to endorse or promote products derived
29    *     from this software without the prior written permission of
30    *     Broadcom Corporation.
31    *
32    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44    *     THE POSSIBILITY OF SUCH DAMAGE.
45    ********************************************************************* */
46
47
48#include "cfe.h"
49
50#include "ui_command.h"
51
52#include "cfe_fileops.h"
53#include "cfe_loader.h"
54#include "cfe_bootblock.h"
55#include "cfe_boot.h"
56
57#include "env_subr.h"
58
59#include "net_ebuf.h"
60#include "net_ether.h"
61#include "net_enet.h"
62#include "net_ip.h"
63#include "net_api.h"
64
65#include "addrspace.h"
66
67typedef struct vxflash_s {
68    char *device;
69    char *filesys;
70} vxflash_t;
71
72static vxflash_t vxflash_default[] = {
73    { "flash3.vxworks", "fat" },
74    { "flash3.fatfs",   "fat" },
75    { "flash1.user",    "fat" },
76    { "flash0.os",      "fat" },
77    { NULL,NULL }
78};
79
80/* Magic address (usually in NVRAM) where to store MAC address */
81char *vxboot_mac_addr;
82char *vxboot_mac_env = "ETH0_HWADDR";
83
84/*
85 * XXX should probably go somewhere else
86 */
87
88#define BOOT_LINE_ADDRS_PPC	0x4200
89#define BOOT_LINE_ADDRS_MIPS	0x700
90
91int ui_init_vxbootcmds(void);
92static int ui_cmd_vxboot_M(ui_cmdline_t *cmd,int argc,char *argv[]);
93static int ui_cmd_vxboot_p(ui_cmdline_t *cmd,int argc,char *argv[]);
94static int ui_cmd_vxboot_c(ui_cmdline_t *cmd,int argc,char *argv[]);
95static int ui_cmd_vxboot_go(ui_cmdline_t *cmd,int argc,char *argv[]);
96
97extern cfe_loadargs_t cfe_loadargs;
98
99typedef struct vxkey_s {
100    char *name;
101    char *description;
102    int num;
103} vxkey_t;
104
105typedef struct vxval_s {
106    char *name;
107    char *val;
108} vxval_t;
109
110#define MAXVXKEYS 16
111typedef struct vxbootline_s {
112    char *bootline;		/* strdup of entire bootline */
113    vxval_t vals[MAXVXKEYS+1];	/* Stuff we parsed */
114} vxbootline_t;
115
116
117static char *vxboot_envname = "vxboot";
118
119static vxkey_t validvxkeys[] = {
120    /* These keys go in the first token of the boot line */
121    {"!bd","boot device"},
122    {"!un","unit number"},
123    {"!pn","processor number"},
124    {"!hn","host name"},
125    {"!fn","file name"},
126
127    /* These keys go on the rest of the boot line */
128    {"e","inet on ethernet (e)"},
129    {"h","host inet (h)"},
130    {"g","gateway inet (g)"},
131    {"u","user (u)"},
132    {"pw","ftp password (pw)"},
133    {"f","flags (f)"},
134    {"tn","target name (tn)"},
135    {"s","startup script (s)"},
136    {"o","other (o)"},
137    {NULL,NULL,0}};
138
139
140#define VXFS_FLASH	0
141#define VXFS_ETH	1
142
143static vxkey_t *vxboot_findkey(char *name)
144{
145    vxkey_t *key = validvxkeys;
146
147    while (key->description) {
148	if (key->name && strcmp(name,key->name) == 0) return key;
149	key++;
150	}
151
152    return NULL;
153}
154
155static vxval_t *vxboot_findval(vxbootline_t *info,char *name)
156{
157    vxval_t *val = info->vals;
158
159    while (val->name) {
160	if (strcmp(name,val->name) == 0) return val;
161	val++;
162	}
163
164    return NULL;
165}
166
167static char *vxboot_getval(vxbootline_t *info,char *name,char *def)
168{
169    vxval_t *val = vxboot_findval(info,name);
170    if (!val) return def;
171    return val->val;
172}
173
174static int vxboot_init(vxbootline_t *info,char *str)
175{
176    memset(info,0,sizeof(vxbootline_t));
177    info->bootline = lib_strdup(str);
178
179    if (!info->bootline) return -1;
180    return 0;
181}
182
183static void vxboot_free(vxbootline_t *info)
184{
185    vxval_t *val;
186
187    if (info->bootline) {
188	KFREE(info->bootline);
189	}
190
191    val = info->vals;
192    while (val->name) {
193	if (val->val) KFREE(val->val);
194	val++;
195	}
196    memset(info,0,sizeof(vxbootline_t));
197}
198
199static int vxboot_addkey(vxbootline_t *info,char *name,char *val)
200{
201    int idx;
202
203    for (idx = 0; idx < MAXVXKEYS; idx++) {
204	if (!info->vals[idx].name) break;
205	if (strcmp(info->vals[idx].name,name) == 0) break;
206	}
207    if (idx == MAXVXKEYS) return -1;	/* no more room */
208
209    info->vals[idx].name = name;
210    if (info->vals[idx].val) KFREE(info->vals[idx].val);
211    info->vals[idx].val = lib_strdup(val);
212    return 0;
213}
214
215
216static int vxboot_parse(vxbootline_t *info)
217{
218    char *x,*y;
219    char *ptr;
220    char *tok;
221
222    ptr = info->bootline;		/* start hacking it up */
223
224    tok = lib_gettoken(&ptr);		/* first token is the boot file */
225
226    if (!tok) return 0;			/* an empty booline is ok */
227
228    /*
229     * Format of the first token is as follows:
230     *
231     *	   bootdev(unit,proc)host:filename
232     */
233
234
235    x = tok;
236
237    /* find end of boot device name */
238    while (*tok && (*tok != '(')) tok++;
239    if (!*tok) goto error;
240    *tok++ = '\0';
241
242    vxboot_addkey(info,"!bd",x);		/* add boot device */
243
244    x = strchr(tok,')');
245    if (!x) goto error;		/* no close parens */
246    *x++ = '\0';
247
248    /* Okay, 'tok' now points at the stuff between the parens. */
249    if ((y = strchr(tok,','))) {
250	*y++ = '\0';
251	vxboot_addkey(info,"!un",tok);
252	vxboot_addkey(info,"!pn",y);
253	}
254    else {
255	vxboot_addkey(info,"!un",tok);
256	vxboot_addkey(info,"!pn","0");
257	}
258    tok = x;
259
260    /* 'tok' now points at the stuff just beyond the right paren */
261
262    if ((x = strchr(tok,':'))) {
263	*x++ = '\0';
264	vxboot_addkey(info,"!hn",tok);
265	vxboot_addkey(info,"!fn",x);
266	}
267    else {
268	goto error;
269	}
270
271    /* Now parse the rest of the bootline params */
272
273    while ((tok = gettoken(&ptr))) {
274	if ((x = strchr(tok,'='))) {
275	    *x++ = '\0';
276	    if (vxboot_addkey(info,tok,x) < 0) break;
277	    }
278	}
279
280    return 0;
281
282error:
283    return -1;
284}
285
286
287static void vxboot_show(vxbootline_t *info)
288{
289    vxval_t *val;
290    vxkey_t *key;
291    char buf[50];
292
293    val = info->vals;
294
295    while (val->name) {
296	key = vxboot_findkey(val->name);
297	if (key) {
298	    printf("%-20s : %s\n",key->description,val->val);
299	    }
300	else {
301	    sprintf(buf,"value '%s'",val->name);
302	    printf("%20s : %s\n",buf,val->val);
303	    }
304	val++;
305	}
306}
307
308static int vxboot_genbootline(vxbootline_t *info)
309{
310    char buffer[1000];
311    char *p;
312    char *unk = "unknown";
313    vxval_t *val;
314
315    /* Regenerate boot line */
316    if (info->bootline) KFREE(info->bootline);
317    info->bootline = NULL;
318
319    p = buffer;
320    p += sprintf(p,"%s(%s,%s)%s:%s",
321		 vxboot_getval(info,"!bd",unk),
322		 vxboot_getval(info,"!un",unk),
323		 vxboot_getval(info,"!pn",unk),
324		 vxboot_getval(info,"!hn",unk),
325		 vxboot_getval(info,"!fn",unk));
326
327    val = info->vals;
328
329    while (val->name) {
330	if ((val->name[0] != '!') && (val->val[0])) {	/* names starting with ! are special */
331	    p += sprintf(p," %s=%s",val->name,val->val);
332	    }
333	val++;
334	}
335
336    info->bootline = lib_strdup(buffer);
337
338    return 0;
339}
340
341static int vxboot_net_config(vxbootline_t *info)
342{
343    uint8_t netmask[IP_ADDR_LEN];
344    char *p, *ipstr, tmp;
345    char netcmd[128];
346    int i;
347
348    if ((p = vxboot_getval(info,"e",NULL)) == NULL) {
349        return -1;
350        }
351    if ((ipstr = strdup(p)) == NULL) {
352        return -1;
353        }
354
355    /* Extract network mask */
356    memset(netmask,0,sizeof(netmask));
357    if ((p = strchr(ipstr,':')) != NULL) {
358        *p++ = 0;
359        if (strlen(p) == 8) {
360            for (i = 0; i < IP_ADDR_LEN; i++) {
361                tmp = p[2];
362                p[2] = 0;
363                netmask[i] = lib_xtoi(p);
364                p[2] = tmp;
365                p += 2;
366                }
367            }
368        }
369
370    /* Create command line */
371    sprintf(netcmd,"ifconfig eth0 -addr=%s -mask=%d.%d.%d.%d",ipstr,
372            netmask[0],netmask[1],netmask[2],netmask[3]);
373    if ((p = vxboot_getval(info,"g",NULL)) != NULL) {
374        sprintf(&netcmd[strlen(netcmd)]," -gw=%s",p);
375        }
376
377    /* Run through standard command line parser */
378    ui_docommand(netcmd);
379
380    KFREE(ipstr);
381    return 0;
382}
383
384static int ui_cmd_vxboot_M(ui_cmdline_t *cmd,int argc,char *argv[])
385{
386    char *x;
387    uint8_t hwaddr[ENET_ADDR_LEN];
388
389    if ((x = cmd_getarg(cmd,0)) != NULL) {
390        if (strlen(x) == 17 && enet_parse_hwaddr(x,hwaddr) == 0) {
391            env_setenv(vxboot_mac_env,x,ENV_FLG_NORMAL);
392            }
393        else {
394            printf("MAC address syntax error\n");
395            return -1;
396            }
397        }
398    else if ((x = env_getenv(vxboot_mac_env)) != NULL) {
399	printf("MAC address: %s\n",x);
400        }
401    else {
402	printf("MAC address not configured\n");
403        }
404
405    return 0;
406}
407
408static int ui_cmd_vxboot_p(ui_cmdline_t *cmd,int argc,char *argv[])
409{
410    char *x;
411    vxbootline_t info;
412
413    x = env_getenv(vxboot_envname);
414    if (!x) x = "";
415
416    vxboot_init(&info,x);
417    if (vxboot_parse(&info) < 0) {
418	printf("Warning: Current boot line does not appear to be valid\n");
419	}
420    vxboot_show(&info);
421    vxboot_free(&info);
422
423    return 0;
424}
425
426static int ui_cmd_vxboot_c(ui_cmdline_t *cmd,int argc,char *argv[])
427{
428    char *x;
429    vxbootline_t info;
430    vxval_t *val;
431    int keyidx = 0;
432    int editing = 1;
433    char buffer[500];
434    char prompt[80];
435
436    x = env_getenv(vxboot_envname);
437    if (!x) x = "";
438
439    vxboot_init(&info,x);
440    if (vxboot_parse(&info) < 0) {
441	printf("Warning: Current boot line does not appear to be valid\n");
442	}
443
444    printf("Type '-' to move to previous field, or press ENTER to accept each value\n");
445
446    while (editing) {
447	if (validvxkeys[keyidx].name == NULL) break;
448        if (keyidx < 0) {
449            vxboot_free(&info);
450            return 0;
451            }
452
453	/* Emulate perculiar VxBoot behavior */
454	if (strcmp(validvxkeys[keyidx].name,"!un") == 0) {
455	    keyidx++;
456	    continue;
457	    }
458
459	sprintf(prompt,"%20s : ",validvxkeys[keyidx].description);
460
461	buffer[0] = '\0';
462	if ((val = vxboot_findval(&info,validvxkeys[keyidx].name))) {
463	    strcpy(buffer,val->val);
464	    }
465
466	/* Emulate perculiar VxBoot behavior */
467	if (strcmp(validvxkeys[keyidx].name,"!bd") == 0 &&
468            memcmp(buffer,"flash",5) != 0) {
469	    strcat(buffer,vxboot_getval(&info,"!un","0"));
470	    }
471
472	console_readline_default(prompt,buffer,sizeof(buffer));
473	if (buffer[0] && buffer[strlen(buffer)-1] == '-') {
474	    keyidx--;
475
476            /* Emulate perculiar VxBoot behavior */
477            if (keyidx >= 0 && strcmp(validvxkeys[keyidx].name,"!un") == 0) {
478                keyidx--;
479            }
480
481	    continue;
482	    }
483
484        /* Emulate perculiar VxBoot behavior */
485        if (strcmp(validvxkeys[keyidx].name,"!bd") == 0 && buffer[0]) {
486            x = &buffer[strlen(buffer)-1];
487            if (*x < '0' || *x > '9') {
488                vxboot_addkey(&info,"!un","0");
489                }
490            else {
491                vxboot_addkey(&info,"!un",x);
492                *x = 0;
493                }
494            }
495
496        vxboot_addkey(&info,validvxkeys[keyidx].name,buffer);
497        keyidx++;
498        }
499
500
501    vxboot_genbootline(&info);
502
503    env_setenv(vxboot_envname,info.bootline,ENV_FLG_NORMAL);
504
505    vxboot_free(&info);
506
507    env_save();
508
509    return 0;
510}
511
512
513static int ui_cmd_vxboot_go(ui_cmdline_t *cmd,int argc,char *argv[])
514{
515    char *x;
516    char *f;
517    char *p0, *p1;
518    vxbootline_t info;
519    char filename[256];
520    char devname[32];
521    cfe_loadargs_t *la = &cfe_loadargs;
522    vxflash_t *vxflash;
523    fileio_ctx_t *fsctx;
524    int res;
525
526    x = env_getenv(vxboot_envname);
527    if (!x) {
528	printf("VxWorks boot line has not been set.  Use the 'c' command to create a boot line\n");
529	return -1;
530	}
531
532    vxboot_init(&info,x);
533    if (vxboot_parse(&info) < 0) {
534	printf("Error: not a valid VxWorks boot line: %s.\n Use the 'c' command to edit the boot line\n",x);
535	}
536
537    /* Copy boot line to the magic place */
538    p0 = NULL;
539    if (strcmp(CPUCFG_ARCHNAME, "PPC") == 0) {
540        p0 = (char *) KERNADDR(BOOT_LINE_ADDRS_PPC);
541        strcpy(p0,x);
542        }
543    else if (strcmp(CPUCFG_ARCHNAME, "MIPS") == 0) {
544        p0 = (char *) KERNADDR(BOOT_LINE_ADDRS_MIPS);
545        strcpy(p0,x);
546        }
547    else {
548	printf("Error: unsupported architecture for VxWorks\n");
549	}
550    /* Add MAC addres to 'other' */
551    if (p0 != NULL) {
552        if ((p1 = env_getenv(vxboot_mac_env)) != NULL && strlen(p1) == 17) {
553            if (vxboot_findval(&info,"o") == NULL) {
554                strcat(p0," o=");
555                }
556            strcat(p0,";mac=");
557            strcat(p0,p1);
558            /* MAC address separator must be colon (:) */
559            p1 = &p0[strlen(p0)-17];
560            p1[2] = p1[5] = p1[8] = p1[11] = p1[14] = ':';
561            }
562	}
563    /* Also store MAC in magic place if provided */
564    if (vxboot_mac_addr != NULL) {
565        if ((p1 = env_getenv(vxboot_mac_env)) != NULL) {
566            enet_parse_hwaddr(p1,(uint8_t *)vxboot_mac_addr);
567            }
568        }
569
570    /* Load the image. */
571    la->la_filesys = NULL;
572    la->la_loader = "elf";
573    la->la_options = NULL;
574    la->la_flags = LOADFLG_NOISY | LOADFLG_EXECUTE;
575
576    x = vxboot_getval(&info,"!bd",NULL);
577    if (!x) goto error;
578    if (strcmp(x,"sbe") == 0 || strcmp(x,"bc") == 0 || memcmp(x,"et", 2) == 0) {
579	la->la_device = "eth0";
580	la->la_filesys = "tftp";
581	f = vxboot_getval(&info,"h",NULL);
582	if (!f) f = vxboot_getval(&info,"!hn","");
583	sprintf(filename,"%s:%s",f,vxboot_getval(&info,"!fn",""));
584	}
585    else if (strcmp(x,"flash") == 0) {
586        vxflash = &vxflash_default[0];
587        while (vxflash->device) {
588            la->la_device = vxflash->device;
589            la->la_filesys = vxflash->filesys;
590            if (fs_init(la->la_filesys,&fsctx,la->la_device) == CFE_OK) {
591                fs_uninit(fsctx);
592                break;
593                }
594            vxflash++;
595            }
596	sprintf(filename,"%s",vxboot_getval(&info,"!fn",""));
597	}
598    else if (memcmp(x,"flash",5) == 0) {
599        strncpy(devname,x,sizeof(devname)-1);
600        devname[sizeof(devname)-1] = 0;
601	la->la_device = devname;
602	la->la_filesys = "fat";
603	sprintf(filename,"%s",vxboot_getval(&info,"!fn",""));
604	}
605
606    if (vxboot_getval(&info,"e",NULL) != NULL) {
607        vxboot_net_config(&info);
608        }
609
610    la->la_filename = filename;
611#if CFG_ZLIB
612    if (strlen(filename) > 3 &&
613        strcmp(&filename[strlen(filename)-3], ".gz") == 0) {
614        printf("Assuming compressed image\n");
615        la->la_flags |= LOADFLG_COMPRESSED;
616	}
617#endif
618    vxboot_free(&info);
619
620    /* Call the loader. */
621    /* Run the image. */
622    res = cfe_boot(la->la_loader,la);
623
624    if (res < 0) ui_showerror(res,"Could not load image %s",filename);
625
626    /* Should not return here. */
627
628    return res;
629
630error:
631    printf("Incorrect or invalid fields in VxWorks boot line\n");
632    vxboot_free(&info);
633    return -1;
634}
635
636
637
638
639
640int ui_init_vxbootcmds(void)
641{
642    cmd_addcmd("M",
643	       ui_cmd_vxboot_M,
644	       NULL,
645	       "M [xx:xx:xx:xx:xx:xx]\n\n"
646               "Show or set Ethernet MAC address",
647	       "This command reads or writes the environment variable 'ETH0_HWADDR',\n"
648	       "and is provided for VxWorks boot loader compatibility.",
649	       "");
650
651    cmd_addcmd("p",
652	       ui_cmd_vxboot_p,
653	       NULL,
654	       "Parse and display VxWorks boot string",
655	       "This command reads the environent variable 'vxboot', if defined, and\n"
656	       "breaks out the fields for easy display.  You can change the vxworks boot\n"
657	       "string with the 'c' command.",
658	       "");
659
660    cmd_addcmd("c",
661	       ui_cmd_vxboot_c,
662	       NULL,
663	       "Change the VxWorks boot string",
664	       "This command changes the environent variable 'vxboot', prompting for\n"
665	       "the fields.  The 'vxboot' string is committed to NVRAM.",
666	       "");
667
668    cmd_addcmd("@",
669	       ui_cmd_vxboot_go,
670	       NULL,
671	       "Boot VxWorks",
672	       "This command copies the 'vxboot' string to the architecture-dependent area\n"
673	       "and transfers control to VxWorks.",
674	       "");
675
676    return 0;
677}
678
679
680