1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  Flash Update commands			File: ui_flash.c
5    *
6    *  The routines in this file are used for updating the
7    *  flash with new firmware.
8    *
9    *  Author:  Mitch Lichtenberg
10    *
11    *********************************************************************
12    *
13    *  Copyright 2000,2001,2002,2003,2005
14    *  Broadcom Corporation. All rights reserved.
15    *
16    *  This software is furnished under license and may be used and
17    *  copied only in accordance with the following terms and
18    *  conditions.  Subject to these conditions, you may download,
19    *  copy, install, use, modify and distribute modified or unmodified
20    *  copies of this software in source and/or binary form.  No title
21    *  or ownership is transferred hereby.
22    *
23    *  1) Any source code used, modified or distributed must reproduce
24    *     and retain this copyright notice and list of conditions
25    *     as they appear in the source file.
26    *
27    *  2) No right is granted to use any trade name, trademark, or
28    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
29    *     name may not be used to endorse or promote products derived
30    *     from this software without the prior written permission of
31    *     Broadcom Corporation.
32    *
33    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
34    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
35    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
36    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
37    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
38    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
39    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
41    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
43    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
45    *     THE POSSIBILITY OF SUCH DAMAGE.
46    ********************************************************************* */
47
48#include "cfe.h"
49
50#include "ui_command.h"
51
52#include "cfe_fileops.h"
53#include "cfe_boot.h"
54
55#include "cfe_loader.h"
56
57#include "net_ebuf.h"
58#include "net_ether.h"
59#include "net_api.h"
60
61#include "cfe_flashimage.h"
62
63#include "url.h"
64
65
66/*  *********************************************************************
67    *  Constants
68    ********************************************************************* */
69
70/*
71 * Of course, these things really belong somewhere else.
72 */
73
74#define FLASH_STAGING_BUFFER	CFG_FLASH_STAGING_BUFFER_ADDR
75#ifdef _FLASHPROG_
76#define FLASH_STAGING_BUFFER_SIZE     (1024*1024*16)
77#else
78#define FLASH_STAGING_BUFFER_SIZE CFG_FLASH_STAGING_BUFFER_SIZE
79#endif
80
81
82/*  *********************************************************************
83    *  Exerns
84    ********************************************************************* */
85
86extern int cfe_iocb_dispatch(cfe_iocb_t *iocb);
87
88int ui_init_flashcmds(void);
89static int ui_cmd_flash(ui_cmdline_t *cmd,int argc,char *argv[]);
90
91
92
93/*  *********************************************************************
94    *  ui_init_flashcmds()
95    *
96    *  Initialize the flash commands, add them to the table.
97    *
98    *  Input parameters:
99    *  	   nothing
100    *
101    *  Return value:
102    *  	   0 if ok, else error
103    ********************************************************************* */
104
105int ui_init_flashcmds(void)
106{
107    cmd_addcmd("flash",
108	       ui_cmd_flash,
109	       NULL,
110	       "Update a flash memory device",
111	       "flash [options] filename [flashdevice]\n\n"
112	       "Copies data from a source file name or device to a flash memory device.\n"
113               "The source device can be a disk file (FAT filesystem), a remote file\n"
114               "(TFTP) or a flash device.  The destination device may be a flash or eeprom.\n"
115#if (!CFG_RELOC)
116	       "If the destination device is your boot flash (usually flash0), the flash\n"
117	       "command will restart the firmware after the flash update is complete\n"
118#endif
119               "",
120               "-noerase;Don't erase flash before writing|"
121               "-offset=*;Begin programming at this offset in the flash device|"
122               "-size=*;Size of source device when programming from flash to flash|"
123	       "-noheader;Override header verification, flash binary without checking");
124
125
126    return 0;
127}
128
129/*  *********************************************************************
130    *  flash_crc32(buf,len)
131    *
132    *  Yes, this is an Ethernet CRC.  I'm lazy.
133    *
134    *  Input parameters:
135    *  	   buf - buffer to CRC
136    *  	   len - length of data
137    *
138    *  Return value:
139    *  	   CRC-32
140    ********************************************************************* */
141
142#define     CRC32_POLY        0xEDB88320UL    /* CRC-32 Poly */
143static unsigned int
144flash_crc32(const unsigned char *databuf, unsigned int  datalen)
145{
146    unsigned int idx, bit, data, crc = 0xFFFFFFFFUL;
147
148    for (idx = 0; idx < datalen; idx++) {
149	for (data = *databuf++, bit = 0; bit < 8; bit++, data >>= 1) {
150	    crc = (crc >> 1) ^ (((crc ^ data) & 1) ? CRC32_POLY : 0);
151	    }
152	}
153
154    return crc;
155}
156
157/*  *********************************************************************
158    *  flash_validate(ptr)
159    *
160    *  Validate the flash header to make sure we can program it.
161    *
162    *  Input parameters:
163    *  	   ptr - pointer to flash header
164    *      outptr - pointer to data that we should program
165    *	   outsize - size of data we should program
166    *
167    *  Return value:
168    *  	   0 if ok
169    *  	   else error occured
170    ********************************************************************* */
171
172#define GET32(x) (((uint32_t) (x[0] << 24)) | \
173                  ((uint32_t) (x[1] << 16)) | \
174                  ((uint32_t) (x[2] << 8)) |  \
175                  ((uint32_t) (x[3] << 0)))
176
177static int flash_validate(uint8_t *ptr,int insize,uint8_t **outptr,int *outsize)
178{
179    cfe_flashimage_t *hdr = (cfe_flashimage_t *) ptr;
180    uint32_t size;
181    uint32_t flags;
182    uint32_t hdrcrc;
183    uint32_t calccrc;
184
185    if (memcmp(hdr->seal,CFE_IMAGE_SEAL,sizeof(hdr->seal)) != 0) {
186	printf("Invalid header seal.  This is not a CFE flash image.\n");
187	return -1;
188	}
189
190    printf("Flash image contains CFE version %d.%d.%d for board '%s'\n",
191	   hdr->majver,hdr->minver,hdr->ecover,hdr->boardname);
192
193    size = GET32(hdr->size);
194    flags = GET32(hdr->flags);
195    hdrcrc = GET32(hdr->crc);
196    printf("Flash image is %d bytes, flags %08X, CRC %08X\n",size,flags,hdrcrc);
197
198    if (strcmp(CFG_BOARDNAME,(char *)hdr->boardname) != 0) {
199	printf("This flash image is not appropriate for board type '%s'\n",CFG_BOARDNAME);
200	return -1;
201	}
202
203    if ((size == 0) || (size > FLASH_STAGING_BUFFER_SIZE) ||
204	((size + sizeof(cfe_flashimage_t)) < insize)) {
205	printf("Flash image size is bogus!\n");
206	return -1;
207	}
208
209    calccrc = flash_crc32(ptr + sizeof(cfe_flashimage_t),size);
210
211    if (calccrc != hdrcrc) {
212	printf("CRC is incorrect. Calculated CRC is %08X\n",calccrc);
213	return -1;
214	}
215
216    *outptr = ptr + sizeof(cfe_flashimage_t);
217    *outsize = size;
218    return 0;
219}
220
221/*  *********************************************************************
222    *  ui_cmd_flash(cmd,argc,argv)
223    *
224    *  The 'flash' command lives here.  Program the boot flash,
225    *  or if a device name is specified, program the alternate
226    *  flash device.
227    *
228    *  Input parameters:
229    *  	   cmd - command table entry
230    *  	   argc,argv - parameters
231    *
232    *  Return value:
233    *  	   0 if ok
234    *  	   else error
235    ********************************************************************* */
236
237
238static int ui_cmd_flash(ui_cmdline_t *cmd,int argc,char *argv[])
239{
240    uint8_t *ptr;
241    int fh;
242    int res;
243#if (!CFG_RELOC)
244    int retlen;
245#endif
246    char *fname;
247    char *flashdev;
248    cfe_loadargs_t la;
249    int amtcopy;
250    int devtype;
251    int srcdevtype;
252    int chkheader;
253    int sfd;
254    int copysize;
255    flash_info_t flashinfo;
256    int offset = 0;
257    int noerase = 0;
258    char *x;
259    int size = 0;
260
261    /*
262     * Get the address of the staging buffer.  We can't
263     * allocate the space from the heap to store the
264     * new flash image, because the heap may not be big
265     * enough.  So, grab some unallocated memory
266     * at the 1MB line (we could also calculate
267     * something, but this will do for now).
268     * We assume the flash will be somewhere between
269     * 1KB (yeah, right) and 4MB.
270     */
271
272#if CFG_RUNFROMKSEG0
273    ptr = (uint8_t *) KERNADDR(FLASH_STAGING_BUFFER);
274#else
275    ptr = (uint8_t *) UNCADDR(FLASH_STAGING_BUFFER);
276#endif
277
278    /*
279     * Parse command line parameters
280     */
281
282    fname = cmd_getarg(cmd,0);
283
284    if (!fname) {
285	return ui_showusage(cmd);
286	}
287
288    flashdev = cmd_getarg(cmd,1);
289    if (!flashdev) flashdev = "flash0";
290
291    /*
292     * Make sure it's a flash device.
293     */
294
295    res = cfe_getdevinfo(flashdev);
296    if (res < 0) {
297	return ui_showerror(CFE_ERR_DEVNOTFOUND,flashdev);
298	}
299
300    devtype = res & CFE_DEV_MASK;
301
302    if ((res != CFE_DEV_FLASH) && (res != CFE_DEV_NVRAM)) {
303	xprintf("Device '%s' is not a flash or eeprom device.\n",flashdev);
304	return CFE_ERR_INV_PARAM;
305	}
306
307    /*
308     * We shouldn't really allow this, but there are some circumstances
309     * where you might want to bypass the header check and shoot
310     * yourself in the foot.
311     * Switch normally not supplied, so chkheader will be TRUE.
312     */
313
314    chkheader = !cmd_sw_isset(cmd,"-noheader");
315
316    /*
317     * Check for some obscure options here.
318     */
319
320    noerase = cmd_sw_isset(cmd,"-noerase");
321
322    if (cmd_sw_value(cmd,"-offset",&x)) {
323        offset = atoi(x);
324        }
325
326    if (cmd_sw_value(cmd,"-size",&x)) {
327        size = atoi(x);
328        }
329
330    /*
331     * Read the new flash image from the source device
332     */
333
334    srcdevtype = cfe_getdevinfo(fname) & CFE_DEV_MASK;
335
336    xprintf("Reading %s: ",fname);
337
338    switch (srcdevtype) {
339	case CFE_DEV_FLASH:
340	    sfd = cfe_open(fname);
341	    if (sfd < 0) {
342		return ui_showerror(sfd,"Could not open source device");
343		}
344	    memset(ptr,0xFF,FLASH_STAGING_BUFFER_SIZE);
345
346	    /*
347	     * If the flash device can be used for NVRAM,
348	     * then the max size of or flash is the
349	     * offset of the flash info.  Otherwise
350	     * it is the full staging buffer size.
351	     * XXX: if it's larger, we lose.
352	     */
353
354	    if (cfe_ioctl(sfd,IOCTL_FLASH_GETINFO,
355			  (unsigned char *) &flashinfo,
356			  sizeof(flash_info_t),
357			  &res,0) != 0) {
358		flashinfo.flash_size = FLASH_STAGING_BUFFER_SIZE;
359		}
360
361	    if (size > 0) {
362		xprintf("(size=0x%X) ",size);
363		}
364            else {
365		size = flashinfo.flash_size;
366		}
367
368	    /* Make sure we don't overrun the staging buffer */
369
370	    if (size > FLASH_STAGING_BUFFER_SIZE) {
371		size = FLASH_STAGING_BUFFER_SIZE;
372		}
373
374	    /* Read the flash device here. */
375	    res = cfe_read(sfd,PTR2HSADDR(ptr),size);
376
377	    cfe_close(sfd);
378	    if (res < 0) {
379		return ui_showerror(res,"Could not read from flash");
380		}
381	    chkheader = FALSE;		/* no header to check */
382	    /*
383	     * Search for non-0xFF byte at the end.  This will work because
384	     * flashes get erased to all FF's, we pre-fill our buffer to FF's,
385	     */
386	    while (res > 0) {
387		if (ptr[res-1] != 0xFF) break;
388		res--;
389		}
390	    break;
391
392	case CFE_DEV_SERIAL:
393	    la.la_filesys = "raw";
394	    la.la_filename = NULL;
395	    la.la_device = fname;
396	    la.la_address = (intptr_t) ptr;
397	    la.la_options = 0;
398	    la.la_maxsize = FLASH_STAGING_BUFFER_SIZE;
399	    la.la_flags =  LOADFLG_SPECADDR;
400
401	    res = cfe_load_program("srec",&la);
402
403	    if (res < 0) {
404		ui_showerror(res,"Failed.");
405		return res;
406		}
407	    break;
408
409	default:
410	    res = ui_process_url(fname, cmd, &la);
411	    if (res < 0) {
412		ui_showerror(res,"Invalid file name %s",fname);
413		return res;
414		}
415
416	    la.la_address = (intptr_t) ptr;
417	    la.la_options = 0;
418	    la.la_maxsize = FLASH_STAGING_BUFFER_SIZE;
419	    la.la_flags =  LOADFLG_SPECADDR;
420
421	    res = cfe_load_program("raw",&la);
422
423	    if (res < 0) {
424		ui_showerror(res,"Failed.");
425		return res;
426		}
427	    break;
428
429	}
430
431    xprintf("Done. %d bytes read\n",res);
432
433    copysize = res;
434
435    /*
436     * Verify the header and file's CRC.
437     */
438    if (chkheader) {
439	if (flash_validate(ptr,res,&ptr,&copysize) < 0) return -1;
440	}
441
442    if (copysize == 0) return 0;		/* 0 bytes, don't flash */
443
444    /*
445     * Open the destination flash device.
446     */
447
448    fh = cfe_open(flashdev);
449    if (fh < 0) {
450	xprintf("Could not open device '%s'\n",flashdev);
451	return CFE_ERR_DEVNOTFOUND;
452	}
453
454    if (cfe_ioctl(fh,IOCTL_FLASH_GETINFO,
455		  (unsigned char *) &flashinfo,
456		  sizeof(flash_info_t),
457		  &res,0) == 0) {
458
459	/* Make sure we have erase and program functions. */
460	if (flashinfo.flash_type != FLASH_TYPE_FLASH) {
461	    cfe_close(fh);
462	    xprintf("Programming of device '%s' not supported\n",flashdev);
463	    return CFE_ERR_UNSUPPORTED;
464	    }
465
466	/* Truncate write if source size is greater than flash size */
467	if ((copysize + offset) > flashinfo.flash_size) {
468            copysize = flashinfo.flash_size;
469	    }
470	}
471
472    /*
473     * If overwriting the boot flash, we need to use the special IOCTL
474     * that will force a reboot after writing the flash.
475     */
476
477    if (flashinfo.flash_base == 0x1FC00000 ||	/* XXX MIPS-SPECIFIC */
478        flashinfo.flash_base == 0x1B000000) {	/* XXX MIPS-SPECIFIC */
479#if CFG_RELOC
480	xprintf("\n\n** DO NOT TURN OFF YOUR MACHINE UNTIL THE FLASH UPDATE COMPLETES!! **\n\n");
481#else
482#if CFG_NETWORK
483	if (net_getparam(NET_DEVNAME)) {
484	    xprintf("Closing network.\n");
485	    net_uninit();
486	    }
487#endif
488	xprintf("Rewriting boot flash device '%s'\n",flashdev);
489	xprintf("\n\n**DO NOT TURN OFF YOUR MACHINE UNTIL IT REBOOTS!**\n\n");
490	cfe_ioctl(fh,IOCTL_FLASH_WRITE_ALL, ptr,copysize,&retlen,0);
491	/* should not return */
492	return CFE_ERR;
493#endif
494	}
495
496    /*
497     * Otherwise: it's not the flash we're using right
498     * now, so we can be more verbose about things, and
499     * more importantly, we can return to the command
500     * prompt without rebooting!
501     */
502
503    /*
504     * Erase the flash, if the device requires it.  Our new flash
505     * driver does the copy/merge/erase for us.
506     */
507
508    if (!noerase) {
509	if ((devtype == CFE_DEV_FLASH) && !(flashinfo.flash_flags & FLASH_FLAG_NOERASE)) {
510	    flash_range_t range;
511	    range.range_base = offset;
512	    range.range_length = copysize;
513	    xprintf("Erasing flash...");
514	    if (cfe_ioctl(fh,IOCTL_FLASH_ERASE_RANGE,
515			  (uint8_t *) &range,sizeof(range),NULL,0) != 0) {
516		printf("Failed to erase the flash\n");
517		cfe_close(fh);
518		return CFE_ERR_IOERR;
519		}
520	    }
521        }
522
523    /*
524     * Program the flash
525     */
526
527    xprintf("Programming...");
528
529    amtcopy = cfe_writeblk(fh,offset,PTR2HSADDR(ptr),copysize);
530
531    if (copysize == amtcopy) {
532	xprintf("done. %d bytes written\n",amtcopy);
533	res = 0;
534	}
535    else {
536	ui_showerror(amtcopy,"Failed.");
537	res = CFE_ERR_IOERR;
538	}
539
540    /*
541     * done!
542     */
543
544    cfe_close(fh);
545
546    return res;
547}
548
549