| /* |
| * (C) Copyright 2006 |
| * Mindspeed Technologies, Inc. <www.mindspeed.com> |
| * |
| * 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 |
| * |
| */ |
| |
| #include <common.h> |
| #include <config.h> |
| #include <command.h> |
| #include <asm/byteorder.h> |
| #include <asm/hardware.h> |
| |
| #if (CONFIG_COMMANDS & CFG_CMD_ELF) |
| extern int valid_elf_image (unsigned long addr); |
| extern unsigned long load_elf_image (unsigned long addr); |
| #endif |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| struct _AIF_HEADER { |
| u32 BL_DecompressCode; |
| u32 BL_SelfRelocCode; |
| u32 BL_DbgInitZeroInit; |
| u32 EntryPointOffset; |
| u32 ProgramExitInstr; |
| u32 ImageReadOnlySize; |
| u32 ImageReadWriteSize; |
| u32 ImageDebugSize; |
| u32 ImageZeroInitSize; |
| u32 ImageDebugType; |
| u32 ImageBase; |
| u32 WorkSpace; |
| u32 AddressMode; |
| u32 DataBase; |
| u32 FirstFatOffset; |
| u32 Reserved2; |
| u32 DebugInitInstr; |
| u32 ZeroInitCode[15]; |
| }; |
| |
| struct _FAT_AIF_HEADER { |
| u32 NextFatOffset; |
| u32 LoadAddress; |
| u32 Size; |
| u8 region_name[32]; |
| }; |
| |
| /** |
| * print_axf_hdr - |
| * |
| */ |
| static void print_axf_hdr(ulong addr) |
| { |
| struct _AIF_HEADER *aif_hdr = (struct _AIF_HEADER *)(addr); |
| |
| /* we assume that MSP image in memory is not less than the header size (_AIF_HEADER) */ |
| |
| printf("code_offset=0x80\n"); |
| printf("code_base=%lx\n", aif_hdr->ImageBase); |
| printf("data_offset=%lx\n", 0x80 + aif_hdr->ImageReadOnlySize); |
| printf("code_size=%lx\n", aif_hdr->ImageReadOnlySize); |
| printf("data_base=%lx\n", aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize); |
| printf("data_size=%lx\n", aif_hdr->ImageReadWriteSize); |
| printf("zeroinit_base=%lx\n", |
| aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize + aif_hdr->ImageReadWriteSize); |
| printf("prog_entry=%lx\n", aif_hdr->ImageBase + aif_hdr->EntryPointOffset); |
| |
| printf(" \n"); |
| printf("AIFHEADER:\n"); |
| printf("BL_DecompressCode=%lx\n", aif_hdr->BL_DecompressCode); |
| printf("BL_SelfRelocCode=%lx\n", aif_hdr->BL_SelfRelocCode); |
| printf("BL_DbgInitZeroInit=%lx\n", aif_hdr->BL_DbgInitZeroInit); |
| printf("EntryPointOffset=%lx\n", aif_hdr->EntryPointOffset); |
| printf("ProgramExitInstr=%lx\n", aif_hdr->ProgramExitInstr); |
| printf("ImageReadOnlySize=%lx\n", aif_hdr->ImageReadOnlySize); |
| printf("ImageReadWriteSize=%lx\n", aif_hdr->ImageReadWriteSize); |
| printf("ImageDebugSize=%lx\n", aif_hdr->ImageDebugSize); |
| printf("ImageZeroInitSize=%lx\n", aif_hdr->ImageZeroInitSize); |
| printf("ImageDebugType=%lx\n", aif_hdr->ImageDebugType); |
| printf("ImageBase=%lx\n", aif_hdr->ImageBase); |
| printf("WorkSpace=%lx\n", aif_hdr->WorkSpace); |
| printf("AddressMode=%lx\n", aif_hdr->AddressMode); |
| printf("DataBase=%lx\n", aif_hdr->DataBase); |
| printf("FirstFatOffset=%lx\n", aif_hdr->FirstFatOffset); |
| printf("Reserved2=%lx\n", aif_hdr->Reserved2); |
| printf("DebugInitInstr=%lx\n", aif_hdr->DebugInitInstr); |
| printf("ZeroInitCode[0]=%lx\n", aif_hdr->ZeroInitCode[0]); |
| } |
| |
| /** |
| * check_load_addr - |
| * |
| */ |
| static int check_load_addr(ulong addr) |
| { |
| if ((addr > MSP_BOTTOM_MEMORY_RESERVED_SIZE) && |
| #if defined(CONFIG_COMCERTO_530) |
| ((addr < ERAM_BASEADDR) || (addr >= IRAM_BASEADDR + IRAM_SIZE)) |
| #elif defined(CONFIG_COMCERTO_515) || defined(CONFIG_COMCERTO_800) |
| ((addr < ERAM_BASEADDR) || (addr >= ARAM_BASEADDR + ARAM_SIZE)) |
| #elif defined(CONFIG_COMCERTO_900) |
| ((addr < ERAM_BASEADDR) || (addr >= CRAM_BASEADDR + CRAM_SIZE)) |
| #else |
| (1) |
| #endif |
| ) |
| return -1; |
| |
| return 0; |
| } |
| |
| /** |
| * load_axf_zero - |
| * |
| */ |
| static int load_axf_zero(ulong addr) |
| { |
| struct _AIF_HEADER *aif_hdr = (struct _AIF_HEADER *)(addr); |
| u32 m_data_base; |
| u32 bytes_to_load; |
| |
| if (aif_hdr->DataBase) |
| m_data_base = aif_hdr->DataBase; |
| else |
| m_data_base = aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize; |
| |
| m_data_base = m_data_base + aif_hdr->ImageReadWriteSize; |
| bytes_to_load = aif_hdr->ImageZeroInitSize / 2; |
| |
| if (check_load_addr(m_data_base + bytes_to_load) < 0) { |
| printf("data section load address %x outside range\n", m_data_base + bytes_to_load); |
| goto err; |
| } |
| |
| memset((void *)m_data_base, 0, bytes_to_load); |
| |
| return 0; |
| |
| err: |
| return -1; |
| } |
| |
| /** |
| * load_axf_fat - |
| */ |
| static int load_axf_fat(ulong addr, ulong size) |
| { |
| struct _AIF_HEADER *aif_hdr = (struct _AIF_HEADER *)(addr); |
| struct _FAT_AIF_HEADER *fat_aif_hdr; |
| u32 fat_offset; |
| |
| fat_offset = aif_hdr->FirstFatOffset; |
| |
| while (fat_offset > 0) { |
| |
| if ((fat_offset + sizeof(struct _FAT_AIF_HEADER)) > size) { |
| printf("fat section header at %x outside image %x\n", fat_offset, size); |
| goto err; |
| } |
| |
| fat_aif_hdr = (struct _FAT_AIF_HEADER *)(addr + fat_offset); |
| |
| if ((fat_offset + sizeof(struct _FAT_AIF_HEADER) + fat_aif_hdr->Size) > size) { |
| printf("fat section size %x bigger than image size %x\n", fat_offset + sizeof(struct _FAT_AIF_HEADER) + fat_aif_hdr->Size, size); |
| goto err; |
| } |
| |
| if (check_load_addr(fat_aif_hdr->LoadAddress + fat_aif_hdr->Size) < 0) { |
| printf("fat section load address %x outside range\n", fat_aif_hdr->LoadAddress + fat_aif_hdr->Size); |
| goto err; |
| } |
| |
| memcpy((void *)fat_aif_hdr->LoadAddress, |
| (void *)(addr + fat_offset + sizeof(struct _FAT_AIF_HEADER)), fat_aif_hdr->Size); |
| |
| fat_offset = fat_aif_hdr->NextFatOffset; |
| } |
| |
| return 0; |
| |
| err: |
| return -1; |
| } |
| |
| |
| /** |
| * load_axf_data - |
| */ |
| static int load_axf_data(ulong addr, ulong size) |
| { |
| struct _AIF_HEADER *aif_hdr = (struct _AIF_HEADER *)(addr); |
| |
| if (aif_hdr->ImageReadWriteSize) { |
| |
| if ((sizeof(struct _AIF_HEADER) + aif_hdr->ImageReadOnlySize + aif_hdr->ImageReadWriteSize) > size) { |
| printf("data section size %x bigger than image size %x\n", aif_hdr->ImageReadWriteSize, size); |
| goto err; |
| } |
| |
| if (aif_hdr->DataBase == 0) { |
| if (check_load_addr(aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize + aif_hdr->ImageReadWriteSize) < 0) { |
| printf("data section load address %x outside range\n", aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize + aif_hdr->ImageReadWriteSize); |
| goto err; |
| } |
| |
| memcpy((void *)(aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize), |
| (void *)(addr + sizeof(struct _AIF_HEADER) + aif_hdr->ImageReadOnlySize), |
| aif_hdr->ImageReadWriteSize); |
| } else { |
| if (check_load_addr(aif_hdr->DataBase + aif_hdr->ImageReadWriteSize) < 0) { |
| printf("data section load address %x outside range\n", aif_hdr->DataBase + aif_hdr->ImageReadWriteSize); |
| goto err; |
| } |
| |
| memcpy((void *)aif_hdr->DataBase, |
| (void *)(addr + sizeof(struct _AIF_HEADER) + aif_hdr->ImageReadOnlySize), |
| aif_hdr->ImageReadWriteSize); |
| } |
| |
| } else { |
| printf("data section size 0\n"); |
| } |
| |
| return 0; |
| |
| err: |
| return -1; |
| } |
| |
| |
| /** |
| * load_axf_code - |
| * |
| */ |
| static int load_axf_code(ulong addr, ulong size) |
| { |
| struct _AIF_HEADER *aif_hdr = (struct _AIF_HEADER *)(addr); |
| |
| if (aif_hdr->ImageReadOnlySize) { |
| if ((sizeof(struct _AIF_HEADER) + aif_hdr->ImageReadOnlySize) > size) { |
| printf("code section size %x bigger than image size %x\n", aif_hdr->ImageReadOnlySize, size); |
| goto err; |
| } |
| |
| if (check_load_addr(aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize) < 0) { |
| printf("code section load address %x outside range\n", aif_hdr->ImageBase + aif_hdr->ImageReadOnlySize); |
| goto err; |
| } |
| |
| memcpy((void *)aif_hdr->ImageBase, (void *)(addr + sizeof(struct _AIF_HEADER)), aif_hdr->ImageReadOnlySize); |
| } else { |
| printf("code section size 0\n"); |
| } |
| |
| return 0; |
| |
| err: |
| return -1; |
| } |
| |
| /** |
| * load_axf_image - |
| * |
| */ |
| static ulong load_axf_image(ulong addr, ulong size) |
| { |
| struct _AIF_HEADER *aif_hdr = (struct _AIF_HEADER *) addr; |
| |
| printf ("## Downloading image at %08lx ...\n", addr); |
| |
| if (sizeof (struct _AIF_HEADER) > size) { |
| printf("AXF header %x bigger than image size %x\n", sizeof (struct _AIF_HEADER), size); |
| goto err; |
| } |
| |
| print_axf_hdr(addr); |
| |
| if (load_axf_code(addr, size)) { |
| printf("download code failed\n"); |
| goto err; |
| } |
| |
| if (load_axf_data(addr, size)) { |
| printf("download data failed\n"); |
| goto err; |
| } |
| |
| if (load_axf_fat(addr, size)) { |
| printf("download fat failed\n"); |
| goto err; |
| } |
| |
| if (load_axf_zero(addr)) { |
| printf("zero init failed\n"); |
| goto err; |
| } |
| |
| // printf ("## Booting image at %08lx ...\n", aif_hdr->ImageBase + aif_hdr->EntryPointOffset); |
| |
| return aif_hdr->ImageBase + aif_hdr->EntryPointOffset; |
| |
| err: |
| return (ulong)-1; |
| } |
| |
| /** |
| * strtoul_with_check - reads unsigned long hex value from string with checking |
| * |
| * ARGUMENTS: |
| * str: the string to scan value from |
| * pvalue: pointer to the value location (if be NULL just checking will be |
| * performed) |
| * label: optinal label for the typical error message (if NULL no message |
| * will be printed) |
| * |
| * RETURNS: |
| * 0 success, -1 otherwise |
| */ |
| static int strtoul_with_check(const char *str, ulong *pvalue, const char *label) |
| { |
| ulong value; |
| char *end = NULL; |
| |
| value = simple_strtoul(str, &end, 16); |
| if (str == end || *end != 0) { |
| if (label) |
| printf("Invalid %s given, please provide correct hex value.\n", label); |
| return -1; |
| } |
| |
| if (pvalue) |
| *pvalue = value; |
| |
| return 0; |
| } |
| |
| /** |
| * do_loadmsp - loads MSP image, it may be in either ELF or AXF format. If image |
| * is successfully loaded, stores MSP entry in "msp_start_addr" env. variable. |
| * |
| * ARGUMENTS: |
| * standard set of U-Boot command arguments, user must pass image address |
| * and size (all in hex). |
| * |
| * RETURNS: |
| * 0 - success, -1 otherwise |
| */ |
| static int do_loadmsp (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
| { |
| ulong addr, size; |
| char buf[32]; |
| |
| if (argc < 3) { |
| goto err; |
| } |
| |
| if (strtoul_with_check(argv[1], &addr, "image address")) |
| goto err; |
| |
| if (strtoul_with_check(argv[2], &size, "image size")) |
| goto err; |
| |
| #if (CONFIG_COMMANDS & CFG_CMD_ELF) |
| if (valid_elf_image(addr)) |
| addr = load_elf_image(addr); |
| else |
| addr = load_axf_image(addr, size); |
| #else |
| addr = load_axf_image(addr, size); |
| #endif |
| |
| if (addr == (ulong)-1) |
| goto err; |
| |
| /* store MSP start addr in environment (we will need it do_bootcomcerto) */ |
| sprintf(buf, "%lx", addr); |
| setenv("msp_start_addr", buf); |
| printf("image loaded at %lx\n", addr); |
| return 0; |
| |
| err: |
| printf("error loading image\n"); |
| return -1; |
| } |
| |
| |
| U_BOOT_CMD( |
| loadmsp, 3, 0, do_loadmsp, |
| "loadmsp - load MSP image (AXF or ELF format) and save MSP start address\n", |
| "address size\n" |
| " - 'address' (hex) points to MSP image location.\n" |
| " 'size' (hex) specifies image size.\n" |
| " See also 'bootcomcerto' command.\n" |
| ); |
| |
| |
| extern char __arm1_init_start, __arm1_init_end; |
| extern u32 _arm1_start_addr, _arm1_r0, _arm1_r1, _arm1_r2; |
| |
| /** |
| * set_arm1_init - copies ARM1 startup code to reset location and sets CSP |
| * image startup address |
| * |
| * ARGUMENTS: |
| * addr: CSP entry point |
| * r0,r1,r2: values for the corresponding ARM1 registers |
| */ |
| static void set_arm1_init(ulong addr, ulong r0, ulong r1, ulong r2) |
| { |
| _arm1_start_addr = addr; |
| _arm1_r0 = r0; |
| _arm1_r1 = r1; |
| _arm1_r2 = r2; |
| |
| printf("Copying ARM1 startup code from %08x, start address %08x\n", |
| &__arm1_init_start, addr); |
| |
| memcpy(0, &__arm1_init_start, &__arm1_init_end - &__arm1_init_start); |
| } |
| |
| #if !defined(CONFIG_COMCERTO_1000) && !defined(CONFIG_COMCERTO_100) |
| /* This code only applies to Carrier/Access platforms, where ARM0 is running |
| * the MSP and ARM1 the CSP |
| */ |
| extern image_header_t header; |
| extern void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], |
| ulong addr, ulong *len_ptr, int verify); |
| |
| #define BOOT_ETH_BASE_ADDRESS (IRAM_BASEADDR + 0x1250) |
| #define MAGIC_NUM_ADDRESS_IRAM (BOOT_ETH_BASE_ADDRESS + 0x10) |
| #define FLAGS_ADDRESS_IRAM (BOOT_ETH_BASE_ADDRESS + 0x14) |
| #define M1_ADDRESS_IRAM (BOOT_ETH_BASE_ADDRESS + 0x18) |
| #define M5_ADDRESS_IRAM (BOOT_ETH_BASE_ADDRESS + 0x24) |
| |
| #define MAGIC_NUM_IRAM 0x98765432 |
| #define ETHADDR_IRAM_MASK (1 << 0) |
| |
| static void msp_boot_eth_hdr_setup(bd_t * bd) |
| { |
| struct eth_hdr { |
| u8 hostmac[6]; |
| u8 mspmac[6]; |
| u8 padding; |
| u16 packet_type; |
| } __attribute__((packed)) *hdr; |
| |
| /* The MSP expects an ethernet header at this IRAM address */ |
| /* at boot time */ |
| |
| hdr = (struct eth_hdr *)(BOOT_ETH_BASE_ADDRESS + 1); |
| |
| hdr->hostmac[0] = 0x00; |
| hdr->hostmac[1] = 0x11; |
| hdr->hostmac[2] = 0x22; |
| hdr->hostmac[3] = 0x33; |
| hdr->hostmac[4] = 0x44; |
| hdr->hostmac[5] = 0x55; |
| |
| hdr->mspmac[0] = 0x00; |
| hdr->mspmac[1] = 0x1a; |
| hdr->mspmac[2] = 0x1b; |
| hdr->mspmac[3] = 0x1c; |
| hdr->mspmac[4] = 0x1d; |
| hdr->mspmac[5] = 0x1e; |
| |
| hdr->packet_type = 0x889b; |
| } |
| |
| static void msp_iram_flags_setup(bd_t * bd) |
| { |
| int i; |
| ulong reg; |
| char *s, *e; |
| char tmp[64]; |
| |
| /* pass miscellaneous params to the MSP via IRAM |
| Since these are not passed by all boot loaders use a magic number |
| to tell the MSP that if parameters are present */ |
| *(u32 *)MAGIC_NUM_ADDRESS_IRAM = MAGIC_NUM_IRAM; |
| |
| /* the next word is a bit-map that tells the MSP which parameters are present, |
| this allows params to be added and different versions of bootloader and MSP |
| to inter-operate */ |
| |
| /* always have ethaddr; even if user does not specify it (which is an error), we have a default */ |
| *(u32 *)FLAGS_ADDRESS_IRAM = ETHADDR_IRAM_MASK; |
| |
| /* write ethernet address */ |
| memcpy((u8 *)M1_ADDRESS_IRAM, bd->bi_enetaddr, 6); |
| |
| i = getenv_r ("eth1addr", tmp, sizeof (tmp)); |
| s = (i > 0) ? tmp : NULL; |
| for (reg = 0; reg < 6; ++reg) { |
| *(u8 *) (M5_ADDRESS_IRAM + reg) = s ? simple_strtoul (s, &e, 16) : 0; |
| if (s) |
| s = (*e) ? e + 1 : e; |
| } |
| } |
| |
| /** |
| * do_bootcomcerto - checks if MSP image was loaded, then loads CSP image which |
| * may be in either binary or ELF format and boots MSP. |
| * |
| * ARGUMENTS: |
| * standard set of U-Boot command arguments, user must pass CSP image address |
| * |
| * RETURNS: |
| * doesn't return on success |
| */ |
| static int do_bootcomcerto (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) |
| { |
| ulong csp_start_addr, msp_start_addr; |
| int csp_is_elf = 0; |
| bd_t *bd = gd->bd; |
| char *str; |
| |
| if (argc < 2) { |
| return -1; |
| } |
| |
| str = getenv("msp_start_addr"); |
| if (!str) { |
| printf("MSP start address is not set, see 'loadmsp' command\n"); |
| return -1; |
| } |
| |
| if (strtoul_with_check(str, &msp_start_addr, NULL)) { |
| printf("Invalid MSP address, aborting\n"); |
| return -1; |
| } |
| |
| if (strtoul_with_check(argv[1], &csp_start_addr, "image address")) |
| return -1; |
| |
| #if (CONFIG_COMMANDS & CFG_CMD_ELF) |
| if ((csp_is_elf = valid_elf_image(csp_start_addr)) != 0) |
| csp_start_addr = load_elf_image(csp_start_addr); |
| #endif |
| |
| /* Setup MSP boot args in IRAM */ |
| msp_boot_eth_hdr_setup(bd); |
| msp_iram_flags_setup(bd); |
| |
| if (csp_is_elf == 0) { |
| /* CSP image is in binary format, assuming Linux */ |
| |
| /* Fake header to keep do_bootm_linux() happy |
| * No ramdisk support for now |
| */ |
| header.ih_ep = htonl(msp_start_addr); /* This is actually the MSP entry point */ |
| header.ih_type = IH_TYPE_KERNEL; |
| #if 0 |
| hdr->ih_magic = htonl(IH_MAGIC); |
| hdr->ih_hcrc = htonl(0); /* FIXME calculate the checksum */ |
| hdr->ih_size = htonl(0); |
| #endif |
| |
| set_arm1_init(csp_start_addr, 0, bd->bi_arch_number, bd->bi_boot_params); |
| do_bootm_linux(cmdtp, 0, argc, argv, 0, NULL, 0); |
| } |
| else { |
| /* CSP image is in ELF, assuming VxWorks */ |
| |
| set_arm1_init(csp_start_addr, (unsigned int)getenv("bootargs"), 0, 0); |
| ((void(*)(void))msp_start_addr)(); |
| } |
| |
| printf("Unexpected return to bootloader!\n"); |
| |
| return 0; |
| } |
| |
| |
| U_BOOT_CMD( |
| bootcomcerto, 2, 0, do_bootcomcerto, |
| "bootcomcerto - load CSP image (binary or ELF format) and start Comcerto device\n", |
| "address\n" |
| " - 'address'(hex) points to CSP image location.\n" |
| " See also 'loadmsp' command, which must be run before this one.\n" |
| ); |
| #endif |