blob: 6dc1544102cd175128d3b51bda2c8b23d6797780 [file] [log] [blame]
/*
* (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