| /* |
| * Copyright (c) 2013 Qualcomm Atheros, Inc. |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| /* The module is designed to support a BDR with the highest sequence number in the flash. |
| * If it is successfule, bootloader will choose the BDR to startup. |
| * |
| * Author Tos Xu April 22, 2009 |
| * |
| */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <flash.h> |
| #include <malloc.h> |
| #include <configs/ap93-hgw.h> |
| |
| #define TOTALFLASHSIZE CFG_FLASH_SIZE |
| #define FLASHSTARTADDRESS CFG_FLASH_BASE |
| #define FLASH_BLOCK_SIZE CFG_FLASH_SECTOR_SIZE |
| /* |
| * Boot description record definitions |
| */ |
| #define BDRWordSize 4 |
| |
| #define BDRHeaderNWords 4 |
| #define BDRHeaderNBytes (BDRHeaderNWords * BDRWordSize) |
| #define BDRHeader_OffsetMagic 0 /* bytes */ |
| #define BDRHeader_OffsetSize 4 /* bytes */ |
| #define BDRHeader_OffsetChecksum 8 /* bytes */ |
| #define BDRHeader_OffsetSequence 12 /* bytes */ |
| #define BDR_BeginMagic 0xFEEDCAFE |
| |
| #define BDRTailerNWords 4 |
| #define BDRTailerNBytes (BDRTailerNWords * BDRWordSize) |
| #define BDRTailer_OffsetMagic 4 /* bytes before end */ |
| #define BDRTailer_OffsetSize 8 /* bytes before end */ |
| #define BDR_EndMagic 0xFEEDFADE |
| |
| #define TagWordToSelf(TagWord) (((TagWord)>>24)&0xff) |
| #define TagWordToTag(TagWord) (((TagWord)>>16)&0xff) |
| #define TagWordToNWords(TagWord) ((TagWord)&0x3fff) |
| |
| #define BDRTag_STOP 1 |
| #define BDRTag_BOOTADDR 2 |
| #define BDRTag_BOOTARGS 3 |
| #define BDRTag_REQUESTNUMBER 4 |
| |
| #define BDR_SIZE 256 |
| |
| unsigned int bdr_bootaddr = 0; |
| unsigned int bdr_seq = 0; |
| char bdr_bootarg[512]; |
| |
| extern flash_info_t flash_info[]; |
| /* |
| * Boot description records can be written at begin and/or end of each |
| * 64KB block of flash (regardless of erase block size) |
| */ |
| #define BDRBlockSize 0x10000 |
| |
| #define flashaddr(x) (char *)((volatile char *)0xbf000000+(x)) |
| |
| |
| /* big endian -- extract big endian integer from byte stream |
| */ |
| static inline unsigned big_endian(unsigned char *p) |
| { |
| return ((p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]); |
| } |
| |
| /* |
| * fix endian |
| */ |
| static inline unsigned fix_endian(unsigned word) |
| { |
| return word; |
| } |
| |
| /* |
| * Big endian in the flash. |
| * 0:OK,-1:parameters error,-2: NO STOP tag. |
| */ |
| int parse_tag(int * bdrp,int size){ |
| |
| int tags = 0,tagname = 0,tagsize = 0,tagno = 0; |
| int i = 0; |
| unsigned data; |
| |
| // Reset the value to prevent the failure of parsing the bdr. |
| bdr_bootaddr = 0; |
| memset(bdr_bootarg,0,sizeof(bdr_bootarg)); |
| |
| for(i = 0;i<size;i++) |
| { |
| data = big_endian((char *)bdrp); |
| printf(" -Tag 0x%x.\n",*bdrp); |
| tagname = (data>>16)&0xff; |
| tagno = (data>>24)&0xff; |
| tagsize = (data & 0xffff) - 1; |
| |
| if((tags != tagno)||(tagsize<0)) return -1; |
| |
| switch(tagname) |
| { |
| case BDRTag_STOP: |
| if(tagsize==0) return 0; |
| else return -1; |
| |
| case BDRTag_BOOTADDR: |
| bdrp++; |
| if(tagsize==1){ |
| bdr_bootaddr = big_endian((char *)bdrp); |
| printf(" --Boot address:0x%x at sequence 0x%x.\n",bdr_bootaddr,bdr_seq); |
| bdrp++; |
| break; |
| }else return -1; |
| |
| case BDRTag_BOOTARGS: |
| bdrp++; |
| if(tagsize < 130){ |
| memcpy(bdr_bootarg,(char *)bdrp,tagsize * BDRWordSize); |
| bdrp += tagsize; |
| break; |
| }else return -1; |
| |
| case BDRTag_REQUESTNUMBER: |
| bdrp += tagsize +1; |
| break; |
| |
| default: |
| bdrp += tagsize + 1; |
| break; |
| } |
| |
| tags++; |
| } |
| |
| return -2; |
| } |
| |
| /* findBDRstart -- look for BDR at the beginning of 64KB of flash, |
| * return sequence no. |
| * Return 0 if not found (which is not a valid sequence number). |
| * |
| * This is used for searching for existing sequence number so we |
| * can be sure to have a larger one. |
| * Sequence numbers are in BDRs (Boot Description Records) which |
| * can be at the begin or end of any 64KB section of flash |
| * (regardless of the erase block size). |
| */ |
| |
| unsigned findBDRstart(int offset) |
| { |
| unsigned magic1; |
| unsigned magic2; |
| unsigned size; |
| unsigned sequence; |
| unsigned char bottom[BDRHeaderNBytes]; |
| unsigned char top[BDRTailerNBytes]; |
| unsigned topoffset; |
| unsigned bdrblock[BDR_SIZE]; |
| |
| memcpy(bottom, flashaddr(offset),sizeof(bottom)); |
| memcpy(bdrblock,flashaddr(offset),sizeof(bdrblock)); |
| magic1 = big_endian(bottom + BDRHeader_OffsetMagic); |
| |
| if (magic1 != BDR_BeginMagic) |
| return 0; |
| |
| size = BDRWordSize*big_endian( bottom + BDRHeader_OffsetSize); |
| |
| if (size <= BDRHeaderNBytes+BDRTailerNBytes) |
| return 0; |
| |
| if (size >= BDRBlockSize) |
| return 0; |
| |
| topoffset = offset + size; |
| |
| memcpy(top, flashaddr(topoffset-sizeof(top)),sizeof(top)); |
| |
| magic2 = big_endian(top + sizeof(top)-BDRTailer_OffsetMagic); |
| if (magic2 != BDR_EndMagic) |
| return 0; |
| |
| if (BDRWordSize*big_endian( |
| top+sizeof(top)-BDRTailer_OffsetSize) != size) |
| return 0; |
| |
| sequence = big_endian(bottom + BDRHeader_OffsetSequence); |
| |
| if (sequence == 0 || sequence == 0xffffffff) |
| return 0; /* invalid */ |
| |
| printf("Found starting sequence: 0x%x in offset 0x%x.\n",sequence,offset); |
| if(sequence > bdr_seq){ |
| bdr_seq = sequence; |
| parse_tag(bdrblock + BDRHeaderNWords,BDR_SIZE); |
| } |
| |
| return sequence; |
| } |
| |
| unsigned findBDRend(int offset) /* offset of begin of 64KB section */ |
| { |
| unsigned magic1; |
| unsigned magic2; |
| unsigned size; |
| unsigned sequence; |
| unsigned char bottom[BDRHeaderNBytes]; |
| unsigned char top[BDRTailerNBytes]; |
| unsigned topoffset; |
| unsigned bottomoffset; |
| unsigned bdrblock[BDR_SIZE]; |
| |
| topoffset = offset + BDRBlockSize; |
| |
| memcpy(top, flashaddr(topoffset-sizeof(top)),sizeof(top)); |
| memcpy(bdrblock, flashaddr(topoffset-sizeof(bdrblock)),sizeof(bdrblock)); |
| |
| magic2 = big_endian(top + sizeof(top)-BDRTailer_OffsetMagic); |
| |
| if (magic2 != BDR_EndMagic) |
| return 0; |
| |
| size = BDRWordSize*big_endian(top+sizeof(top)-BDRTailer_OffsetSize); |
| |
| if (size <= BDRHeaderNBytes+BDRTailerNBytes) |
| return 0; |
| |
| if (size >= BDRBlockSize) |
| return 0; |
| |
| bottomoffset = topoffset - size; |
| |
| memcpy(bottom, flashaddr(bottomoffset),sizeof(bottom)); |
| |
| magic1 = big_endian(bottom + BDRHeader_OffsetMagic); |
| |
| if (magic1 != BDR_BeginMagic) |
| return 0; |
| |
| if (BDRWordSize*big_endian(bottom + BDRHeader_OffsetSize) != size) |
| return 0; |
| |
| sequence = big_endian(bottom+BDRHeader_OffsetSequence); |
| |
| if (sequence == 0 || sequence == 0xffffffff) |
| return 0; /* invalid */ |
| |
| printf("Found end sequence: 0x%x in offset 0x%x.\n",sequence,offset); |
| if(sequence > bdr_seq){ |
| bdr_seq = sequence; |
| parse_tag(bdrblock + BDRTailerNWords,BDR_SIZE); |
| } |
| |
| return sequence; |
| } |
| |
| |
| /* return 0: no existing valid Boot Description Recorder |
| * 1: Found a valid DBR and set bootm and bootarg. |
| */ |
| unsigned findbdr(unsigned int flashaddr){ |
| int offset = 0; |
| char buf[64]; |
| |
| if(flashaddr >= FLASHSTARTADDRESS) flashaddr -= FLASHSTARTADDRESS; |
| |
| printf("findbdr flashaddr 0x%x.\n",flashaddr); |
| bdr_seq = 0; |
| bdr_bootaddr = 0xffffffff; |
| memset(bdr_bootarg,0,sizeof(bdr_bootarg)); |
| |
| for(offset =flashaddr;offset < TOTALFLASHSIZE;offset += BDRBlockSize) |
| { |
| findBDRstart(offset); |
| findBDRend(offset); |
| } |
| |
| // if bootaddr is equal to 0xffffffff or 0x0, it is not valid. |
| if(bdr_seq == 0||bdr_bootaddr==0xffffffff||bdr_bootaddr==0x0){ |
| printf("Failed to find a good BDR at seq 0x%x.\n",bdr_seq); |
| return 0; |
| } |
| |
| if(bdr_bootaddr < TOTALFLASHSIZE) bdr_bootaddr |= FLASHSTARTADDRESS; |
| sprintf(buf,"%s 0x%x","bootm",bdr_bootaddr); |
| setenv("bootcmd",buf); |
| setenv("bootargs",bdr_bootarg); |
| printf("Got a good Boot Descriptor Record.\n -Sequence:0x%x.\n",bdr_seq); |
| printf(" -Boot address: 0x%x.\n",bdr_bootaddr); |
| if(strlen(bdr_bootarg) < 512) |
| printf(" -Boot arguments: %s.\n",bdr_bootarg); |
| return 1; |
| |
| } |
| |
| |
| int do_findbdr (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
| { |
| int err = 0; |
| |
| unsigned int addr; |
| |
| if(argc < 2) |
| err = findbdr(0); |
| else{ |
| addr = simple_strtoul(argv[1], NULL, 16); |
| err = findbdr(addr); |
| } |
| |
| return err; |
| } |
| /* |
| * flashaddr is the aboslute address.(0xbf.....) |
| */ |
| static unsigned writebdr(unsigned int flashaddr,unsigned bootaddr,char * cmdline){ |
| unsigned bdrblock[BDR_SIZE]; |
| unsigned * bdrp = bdrblock; |
| unsigned flash_offset = flashaddr - FLASHSTARTADDRESS; |
| int err; |
| unsigned seq; |
| unsigned tags; |
| char * p; |
| char buffer[64]; |
| |
| //Make sure the flashaddr is located at X*1024. |
| if(flashaddr &0x3ff) return 1; |
| |
| err = findbdr(0); |
| seq = bdr_seq + 1; |
| |
| bdrp[0] = fix_endian(BDR_BeginMagic); |
| bdrp[BDR_SIZE-1] = fix_endian(BDR_EndMagic); |
| bdrp[1] = bdrp[BDR_SIZE-2] = fix_endian(BDR_SIZE); |
| bdrp[2] = 0; |
| bdrp[3] = seq; |
| |
| bdrp += 4; |
| tags = 0; |
| |
| *bdrp++ = fix_endian(tags++<<24| BDRTag_REQUESTNUMBER<<16|2); |
| *bdrp++ = fix_endian(0);//request number. |
| |
| *bdrp++ = fix_endian(tags++<<24| BDRTag_BOOTADDR <<16|2); |
| *bdrp++ = fix_endian(bootaddr);//bootaddr. |
| |
| |
| *bdrp++ = fix_endian(tags++<<24| BDRTag_BOOTARGS <<16|(1+sizeof(bdr_bootarg)/sizeof(int))); |
| memcpy(bdrp,cmdline,sizeof(bdr_bootarg)); |
| bdrp += sizeof(bdr_bootarg)/sizeof(int);//bootarg. |
| |
| *bdrp++ = fix_endian(tags++<<24| BDRTag_STOP<<16|1);//STOP tag |
| p = (char *)malloc(FLASH_BLOCK_SIZE); |
| |
| memcpy(p,(char *)(((unsigned int )flashaddr/FLASH_BLOCK_SIZE )* FLASH_BLOCK_SIZE),FLASH_BLOCK_SIZE); |
| memcpy(p + ((unsigned int )flashaddr%FLASH_BLOCK_SIZE), bdrblock,BDR_SIZE * 4); |
| |
| flash_erase(&flash_info[0],flash_offset/FLASH_BLOCK_SIZE,flash_offset/FLASH_BLOCK_SIZE); |
| err = flash_write(p,((unsigned int )flashaddr/FLASH_BLOCK_SIZE )* FLASH_BLOCK_SIZE, FLASH_BLOCK_SIZE); |
| |
| free(p); |
| |
| if(err){ |
| flash_perror(err); |
| return 1; |
| } |
| |
| if(memcmp((char *)flashaddr,bdrblock,BDR_SIZE * 4)){ |
| printf("Error when writing bdr into flash.\n"); |
| return 1; |
| } |
| |
| printf("BDR has been successfully written.\n"); |
| printf("BDR boot address: 0x%x.\n",bootaddr); |
| printf("BDR boot arg: %s.\n",cmdline); |
| |
| sprintf(buffer,"%s 0x%x","bootm",bootaddr); |
| setenv("bootcmd",buffer); |
| setenv("bootargs",cmdline); |
| |
| return 0; |
| } |
| |
| int do_writebdr (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
| { |
| int err = 0; |
| |
| unsigned int flashaddr; |
| unsigned int bootaddr; |
| char cmd[512]; |
| |
| char * s = getenv("bootargs"); |
| |
| printf("do_writebdr :: size %d bootargs = %s .\n",sizeof(s),s); |
| if(argc < 2) |
| return 1; |
| else{ |
| flashaddr = simple_strtoul(argv[1], NULL, 16); |
| if(argc == 3 ) bootaddr = simple_strtoul(argv[2], NULL, 16); |
| |
| if(flashaddr < TOTALFLASHSIZE) flashaddr |= FLASHSTARTADDRESS; |
| if(flashaddr < (FLASHSTARTADDRESS|0x80000)) return 1; |
| memset(cmd,0,sizeof(cmd)); |
| memcpy(cmd,s,sizeof(cmd)); |
| //printf("do_writebdr :: bdr_bootargs = %s size %d.\n",bdr_bootarg,sizeof(bdr_bootarg)); |
| err = writebdr(flashaddr,bootaddr,cmd); |
| } |
| |
| return err; |
| } |
| |
| U_BOOT_CMD( |
| writebdr, CFG_MAXARGS, 1, do_writebdr, |
| "writebdr- write a valid bdr in the flash based on existing sequences\n", |
| "[writebdr [arg ...]]\n write a valid bdr based on existing sequences at the designed address - \n" |
| "\tpassing arguments 'flash_offset, bootaddr'; you may assign the flash address,\n" |
| "\t'bootaddr' can be ignored or set it.\n" |
| ); |
| |
| U_BOOT_CMD( |
| findbdr, CFG_MAXARGS, 1, do_findbdr, |
| "findbdr - find a valid bdr with the highest sequence in the flash\n", |
| "[findbdr [arg ...]]\n find a valid bdr with the highest sequence in the flash from the starting address - \n" |
| "\tpassing arguments 'arg ...'; you may assign the address or not,\n" |
| "\t'arg' can be the starting address of search.\n" |
| ); |
| |
| |