//==========================================================================
|
//==========================================================================
|
//
|
//
|
// load.c
|
// load.c
|
//
|
//
|
// RedBoot file/image loader
|
// RedBoot file/image loader
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 2002 Gary Thomas
|
// Copyright (C) 2002 Gary Thomas
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// eCos 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
|
// the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 or (at your option) any later version.
|
// Software Foundation; either version 2 or (at your option) any later version.
|
//
|
//
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// As a special exception, if other files instantiate templates or use macros
|
// or inline functions from this file, or you compile this file and link it
|
// or inline functions from this file, or you compile this file and link it
|
// with other works to produce a work based on this file, this file does not
|
// with other works to produce a work based on this file, this file does not
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// by itself cause the resulting work to be covered by the GNU General Public
|
// License. However the source code for this file must still be made available
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// in accordance with section (3) of the GNU General Public License.
|
//
|
//
|
// This exception does not invalidate any other reasons why a work based on
|
// This exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): gthomas
|
// Author(s): gthomas
|
// Contributors: gthomas, tsmith
|
// Contributors: gthomas, tsmith
|
// Date: 2000-07-14
|
// Date: 2000-07-14
|
// Purpose:
|
// Purpose:
|
// Description:
|
// Description:
|
//
|
//
|
// This code is part of RedBoot (tm).
|
// This code is part of RedBoot (tm).
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
|
|
#include <redboot.h>
|
#include <redboot.h>
|
#include <xyzModem.h>
|
#include <xyzModem.h>
|
#include <elf.h>
|
#include <elf.h>
|
#ifdef CYGPKG_REDBOOT_DISK
|
#ifdef CYGPKG_REDBOOT_DISK
|
#include <fs/disk.h>
|
#include <fs/disk.h>
|
#endif
|
#endif
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#include <net/tftp_support.h>
|
#include <net/tftp_support.h>
|
#ifdef CYGSEM_REDBOOT_NET_HTTP_DOWNLOAD
|
#ifdef CYGSEM_REDBOOT_NET_HTTP_DOWNLOAD
|
#include <net/http.h>
|
#include <net/http.h>
|
#endif
|
#endif
|
#endif
|
#endif
|
|
|
static char usage[] = "[-r] [-v] "
|
static char usage[] = "[-r] [-v] "
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
"[-d] "
|
"[-d] "
|
#endif
|
#endif
|
"[-h <host>] [-m <varies>] "
|
"[-h <host>] [-m <varies>] "
|
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
|
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
|
"[-c <channel_number>] "
|
"[-c <channel_number>] "
|
#endif
|
#endif
|
"\n [-b <base_address>] <file_name>";
|
"\n [-b <base_address>] <file_name>";
|
|
|
// Exported CLI function
|
// Exported CLI function
|
RedBoot_cmd("load",
|
RedBoot_cmd("load",
|
"Load a file",
|
"Load a file",
|
usage,
|
usage,
|
do_load
|
do_load
|
);
|
);
|
|
|
//
|
//
|
// Stream I/O support
|
// Stream I/O support
|
//
|
//
|
|
|
// Table describing the various I/O methods
|
// Table describing the various I/O methods
|
CYG_HAL_TABLE_BEGIN( __RedBoot_LOAD_TAB__, RedBoot_load );
|
CYG_HAL_TABLE_BEGIN( __RedBoot_LOAD_TAB__, RedBoot_load );
|
CYG_HAL_TABLE_END( __RedBoot_LOAD_TAB_END__, RedBoot_load );
|
CYG_HAL_TABLE_END( __RedBoot_LOAD_TAB_END__, RedBoot_load );
|
extern struct load_io_entry __RedBoot_LOAD_TAB__[], __RedBoot_LOAD_TAB_END__;
|
extern struct load_io_entry __RedBoot_LOAD_TAB__[], __RedBoot_LOAD_TAB_END__;
|
|
|
// Buffers, data used by redboot_getc
|
// Buffers, data used by redboot_getc
|
#define BUF_SIZE 256
|
#define BUF_SIZE 256
|
struct {
|
struct {
|
getc_io_funcs_t *io;
|
getc_io_funcs_t *io;
|
int (*fun)(char *, int len, int *err);
|
int (*fun)(char *, int len, int *err);
|
unsigned char buf[BUF_SIZE];
|
unsigned char buf[BUF_SIZE];
|
unsigned char *bufp;
|
unsigned char *bufp;
|
int avail, len, err;
|
int avail, len, err;
|
int verbose, decompress, tick;
|
int verbose, decompress, tick;
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
int (*raw_fun)(char *, int len, int *err);
|
int (*raw_fun)(char *, int len, int *err);
|
_pipe_t load_pipe;
|
_pipe_t load_pipe;
|
unsigned char _buffer[CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER];
|
unsigned char _buffer[CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER];
|
#endif
|
#endif
|
} getc_info;
|
} getc_info;
|
|
|
typedef int (*getc_t)(void);
|
typedef int (*getc_t)(void);
|
|
|
//
|
//
|
// Read the next data byte from the stream.
|
// Read the next data byte from the stream.
|
// Returns:
|
// Returns:
|
// >= 0 - actual data
|
// >= 0 - actual data
|
// -1 - error or EOF, status in getc_info.err
|
// -1 - error or EOF, status in getc_info.err
|
//
|
//
|
static int
|
static int
|
redboot_getc(void)
|
redboot_getc(void)
|
{
|
{
|
static char spin[] = "|/-\\|-";
|
static char spin[] = "|/-\\|-";
|
if (getc_info.avail < 0) {
|
if (getc_info.avail < 0) {
|
return -1;
|
return -1;
|
}
|
}
|
if (getc_info.avail == 0) {
|
if (getc_info.avail == 0) {
|
if (getc_info.verbose) {
|
if (getc_info.verbose) {
|
diag_printf("%c\b", spin[getc_info.tick++]);
|
diag_printf("%c\b", spin[getc_info.tick++]);
|
if (getc_info.tick >= sizeof(spin)) {
|
if (getc_info.tick >= sizeof(spin)) {
|
getc_info.tick = 0;
|
getc_info.tick = 0;
|
}
|
}
|
}
|
}
|
if (getc_info.len < BUF_SIZE) {
|
if (getc_info.len < BUF_SIZE) {
|
// No more data available
|
// No more data available
|
if (getc_info.verbose) diag_printf("\n");
|
if (getc_info.verbose) diag_printf("\n");
|
return -1;
|
return -1;
|
}
|
}
|
getc_info.bufp = getc_info.buf;
|
getc_info.bufp = getc_info.buf;
|
getc_info.len = (*getc_info.fun)(getc_info.bufp, BUF_SIZE, &getc_info.err);
|
getc_info.len = (*getc_info.fun)(getc_info.bufp, BUF_SIZE, &getc_info.err);
|
if ((getc_info.avail = getc_info.len) <= 0) {
|
if ((getc_info.avail = getc_info.len) <= 0) {
|
if (getc_info.verbose) diag_printf("\n");
|
if (getc_info.verbose) diag_printf("\n");
|
return -1;
|
return -1;
|
}
|
}
|
}
|
}
|
getc_info.avail--;
|
getc_info.avail--;
|
return *getc_info.bufp++;
|
return *getc_info.bufp++;
|
}
|
}
|
|
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
//
|
//
|
// Called to fetch a new chunk of data and decompress it
|
// Called to fetch a new chunk of data and decompress it
|
//
|
//
|
static int
|
static int
|
_decompress_stream(char *buf, int len, int *err)
|
_decompress_stream(char *buf, int len, int *err)
|
{
|
{
|
_pipe_t* p = &getc_info.load_pipe;
|
_pipe_t* p = &getc_info.load_pipe;
|
int res, total;
|
int res, total;
|
|
|
total = 0;
|
total = 0;
|
while (len > 0) {
|
while (len > 0) {
|
if (p->in_avail == 0) {
|
if (p->in_avail == 0) {
|
p->in_buf = &getc_info._buffer[0];
|
p->in_buf = &getc_info._buffer[0];
|
res = (*getc_info.raw_fun)(p->in_buf, CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER,
|
res = (*getc_info.raw_fun)(p->in_buf, CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER,
|
&getc_info.err);
|
&getc_info.err);
|
if ((p->in_avail = res) <= 0) {
|
if ((p->in_avail = res) <= 0) {
|
// No more data
|
// No more data
|
return total;
|
return total;
|
}
|
}
|
}
|
}
|
p->out_buf = buf;
|
p->out_buf = buf;
|
p->out_size = 0;
|
p->out_size = 0;
|
p->out_max = len;
|
p->out_max = len;
|
res = (*_dc_inflate)(p);
|
res = (*_dc_inflate)(p);
|
if (res != 0) {
|
if (res != 0) {
|
*err = res;
|
*err = res;
|
return total;
|
return total;
|
}
|
}
|
len -= p->out_size;
|
len -= p->out_size;
|
buf += p->out_size;
|
buf += p->out_size;
|
total += p->out_size;
|
total += p->out_size;
|
}
|
}
|
return total;
|
return total;
|
}
|
}
|
#endif
|
#endif
|
|
|
static int
|
static int
|
redboot_getc_init(connection_info_t *info, getc_io_funcs_t *funcs,
|
redboot_getc_init(connection_info_t *info, getc_io_funcs_t *funcs,
|
int verbose, int decompress)
|
int verbose, int decompress)
|
{
|
{
|
int res;
|
int res;
|
|
|
res = (funcs->open)(info, &getc_info.err);
|
res = (funcs->open)(info, &getc_info.err);
|
if (res < 0) {
|
if (res < 0) {
|
diag_printf("Can't load '%s': %s\n", info->filename, (funcs->error)(getc_info.err));
|
diag_printf("Can't load '%s': %s\n", info->filename, (funcs->error)(getc_info.err));
|
return res;
|
return res;
|
}
|
}
|
getc_info.io = funcs;
|
getc_info.io = funcs;
|
getc_info.fun = funcs->read;
|
getc_info.fun = funcs->read;
|
getc_info.avail = 0;
|
getc_info.avail = 0;
|
getc_info.len = BUF_SIZE;
|
getc_info.len = BUF_SIZE;
|
getc_info.verbose = verbose;
|
getc_info.verbose = verbose;
|
getc_info.decompress = decompress;
|
getc_info.decompress = decompress;
|
getc_info.tick = 0;
|
getc_info.tick = 0;
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
if (decompress) {
|
if (decompress) {
|
_pipe_t* p = &getc_info.load_pipe;
|
_pipe_t* p = &getc_info.load_pipe;
|
p->out_buf = &getc_info.buf[0];
|
p->out_buf = &getc_info.buf[0];
|
p->out_size = 0;
|
p->out_size = 0;
|
p->in_avail = 0;
|
p->in_avail = 0;
|
getc_info.raw_fun = getc_info.fun;
|
getc_info.raw_fun = getc_info.fun;
|
getc_info.fun = _decompress_stream;
|
getc_info.fun = _decompress_stream;
|
getc_info.err = (*_dc_init)(p);
|
getc_info.err = (*_dc_init)(p);
|
if (0 != getc_info.err && p->msg) {
|
if (0 != getc_info.err && p->msg) {
|
diag_printf("open decompression error: %s\n", p->msg);
|
diag_printf("open decompression error: %s\n", p->msg);
|
}
|
}
|
}
|
}
|
#endif
|
#endif
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static void
|
static void
|
redboot_getc_rewind(void)
|
redboot_getc_rewind(void)
|
{
|
{
|
getc_info.bufp = getc_info.buf;
|
getc_info.bufp = getc_info.buf;
|
getc_info.avail = getc_info.len;
|
getc_info.avail = getc_info.len;
|
}
|
}
|
|
|
static void
|
static void
|
redboot_getc_terminate(bool abort)
|
redboot_getc_terminate(bool abort)
|
{
|
{
|
if (getc_info.io->terminate) {
|
if (getc_info.io->terminate) {
|
(getc_info.io->terminate)(abort, redboot_getc);
|
(getc_info.io->terminate)(abort, redboot_getc);
|
}
|
}
|
}
|
}
|
|
|
static void
|
static void
|
redboot_getc_close(void)
|
redboot_getc_close(void)
|
{
|
{
|
(getc_info.io->close)(&getc_info.err);
|
(getc_info.io->close)(&getc_info.err);
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
if (getc_info.decompress) {
|
if (getc_info.decompress) {
|
_pipe_t* p = &getc_info.load_pipe;
|
_pipe_t* p = &getc_info.load_pipe;
|
int err = getc_info.err;
|
int err = getc_info.err;
|
if (0 != err && p->msg) {
|
if (0 != err && p->msg) {
|
diag_printf("decompression error: %s\n", p->msg);
|
diag_printf("decompression error: %s\n", p->msg);
|
}
|
}
|
err = (*_dc_close)(p, getc_info.err);
|
err = (*_dc_close)(p, getc_info.err);
|
}
|
}
|
#endif
|
#endif
|
}
|
}
|
|
|
#ifdef CYGSEM_REDBOOT_ELF
|
#ifdef CYGSEM_REDBOOT_ELF
|
//
|
//
|
// Support function - used to read bytes into a buffer
|
// Support function - used to read bytes into a buffer
|
// Returns the number of bytes read (stops short on errors)
|
// Returns the number of bytes read (stops short on errors)
|
//
|
//
|
static int
|
static int
|
_read(int (*getc)(void), unsigned char *buf, int len)
|
_read(int (*getc)(void), unsigned char *buf, int len)
|
{
|
{
|
int total = 0;
|
int total = 0;
|
int ch;
|
int ch;
|
while (len-- > 0) {
|
while (len-- > 0) {
|
ch = (*getc)();
|
ch = (*getc)();
|
if (ch < 0) {
|
if (ch < 0) {
|
// EOF or error
|
// EOF or error
|
break;
|
break;
|
}
|
}
|
*buf++ = ch;
|
*buf++ = ch;
|
total++;
|
total++;
|
}
|
}
|
return total;
|
return total;
|
}
|
}
|
#endif
|
#endif
|
|
|
//
|
//
|
// Load an ELF [binary] image
|
// Load an ELF [binary] image
|
//
|
//
|
static unsigned long
|
static unsigned long
|
load_elf_image(getc_t getc, unsigned long base)
|
load_elf_image(getc_t getc, unsigned long base)
|
{
|
{
|
#ifdef CYGSEM_REDBOOT_ELF
|
#ifdef CYGSEM_REDBOOT_ELF
|
Elf32_Ehdr ehdr;
|
Elf32_Ehdr ehdr;
|
#define MAX_PHDR 8
|
#define MAX_PHDR 8
|
Elf32_Phdr phdr[MAX_PHDR];
|
Elf32_Phdr phdr[MAX_PHDR];
|
unsigned long offset = 0;
|
unsigned long offset = 0;
|
int phx, len, ch;
|
int phx, len, ch;
|
unsigned char *addr;
|
unsigned char *addr;
|
unsigned long addr_offset = 0;
|
unsigned long addr_offset = 0;
|
unsigned long highest_address = 0;
|
unsigned long highest_address = 0;
|
unsigned long lowest_address = 0xFFFFFFFF;
|
unsigned long lowest_address = 0xFFFFFFFF;
|
unsigned char *SHORT_DATA = "Short data reading ELF file";
|
unsigned char *SHORT_DATA = "Short data reading ELF file";
|
|
|
// Read the header
|
// Read the header
|
if (_read(getc, (unsigned char *)&ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
|
if (_read(getc, (unsigned char *)&ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
|
diag_printf("Can't read ELF header\n");
|
diag_printf("Can't read ELF header\n");
|
return 0;
|
return 0;
|
}
|
}
|
offset += sizeof(ehdr);
|
offset += sizeof(ehdr);
|
#if 0 // DEBUG
|
#if 0 // DEBUG
|
diag_printf("Type: %d, Machine: %d, Version: %d, Entry: %p, PHoff: %p/%d/%d, SHoff: %p/%d/%d\n",
|
diag_printf("Type: %d, Machine: %d, Version: %d, Entry: %p, PHoff: %p/%d/%d, SHoff: %p/%d/%d\n",
|
ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_entry,
|
ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_entry,
|
ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_phnum,
|
ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_phnum,
|
ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shnum);
|
ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shnum);
|
#endif
|
#endif
|
if (ehdr.e_type != ET_EXEC) {
|
if (ehdr.e_type != ET_EXEC) {
|
diag_printf("Only absolute ELF images supported\n");
|
diag_printf("Only absolute ELF images supported\n");
|
return 0;
|
return 0;
|
}
|
}
|
if (ehdr.e_phnum > MAX_PHDR) {
|
if (ehdr.e_phnum > MAX_PHDR) {
|
diag_printf("Too many program headers\n");
|
diag_printf("Too many program headers\n");
|
return 0;
|
return 0;
|
}
|
}
|
while (offset < ehdr.e_phoff) {
|
while (offset < ehdr.e_phoff) {
|
if ((*getc)() < 0) {
|
if ((*getc)() < 0) {
|
diag_printf(SHORT_DATA);
|
diag_printf(SHORT_DATA);
|
return 0;
|
return 0;
|
}
|
}
|
offset++;
|
offset++;
|
}
|
}
|
for (phx = 0; phx < ehdr.e_phnum; phx++) {
|
for (phx = 0; phx < ehdr.e_phnum; phx++) {
|
if (_read(getc, (unsigned char *)&phdr[phx], sizeof(phdr[0])) != sizeof(phdr[0])) {
|
if (_read(getc, (unsigned char *)&phdr[phx], sizeof(phdr[0])) != sizeof(phdr[0])) {
|
diag_printf("Can't read ELF program header\n");
|
diag_printf("Can't read ELF program header\n");
|
return 0;
|
return 0;
|
}
|
}
|
#if 0 // DEBUG
|
#if 0 // DEBUG
|
diag_printf("Program header: type: %d, off: %p, va: %p, pa: %p, len: %d/%d, flags: %d\n",
|
diag_printf("Program header: type: %d, off: %p, va: %p, pa: %p, len: %d/%d, flags: %d\n",
|
phdr[phx].p_type, phdr[phx].p_offset, phdr[phx].p_vaddr, phdr[phx].p_paddr,
|
phdr[phx].p_type, phdr[phx].p_offset, phdr[phx].p_vaddr, phdr[phx].p_paddr,
|
phdr[phx].p_filesz, phdr[phx].p_memsz, phdr[phx].p_flags);
|
phdr[phx].p_filesz, phdr[phx].p_memsz, phdr[phx].p_flags);
|
#endif
|
#endif
|
offset += sizeof(phdr[0]);
|
offset += sizeof(phdr[0]);
|
}
|
}
|
if (base) {
|
if (base) {
|
// Set address offset based on lowest address in file.
|
// Set address offset based on lowest address in file.
|
addr_offset = 0xFFFFFFFF;
|
addr_offset = 0xFFFFFFFF;
|
for (phx = 0; phx < ehdr.e_phnum; phx++) {
|
for (phx = 0; phx < ehdr.e_phnum; phx++) {
|
if (phdr[phx].p_vaddr < addr_offset) {
|
if (phdr[phx].p_vaddr < addr_offset) {
|
addr_offset = phdr[phx].p_vaddr;
|
addr_offset = phdr[phx].p_vaddr;
|
}
|
}
|
}
|
}
|
addr_offset = (unsigned long)base - addr_offset;
|
addr_offset = (unsigned long)base - addr_offset;
|
} else {
|
} else {
|
addr_offset = 0;
|
addr_offset = 0;
|
}
|
}
|
for (phx = 0; phx < ehdr.e_phnum; phx++) {
|
for (phx = 0; phx < ehdr.e_phnum; phx++) {
|
if (phdr[phx].p_type == PT_LOAD) {
|
if (phdr[phx].p_type == PT_LOAD) {
|
// Loadable segment
|
// Loadable segment
|
addr = (unsigned char *)phdr[phx].p_vaddr;
|
addr = (unsigned char *)phdr[phx].p_vaddr;
|
len = phdr[phx].p_filesz;
|
len = phdr[phx].p_filesz;
|
if ((unsigned long)addr < lowest_address) {
|
if ((unsigned long)addr < lowest_address) {
|
lowest_address = (unsigned long)addr;
|
lowest_address = (unsigned long)addr;
|
}
|
}
|
addr += addr_offset;
|
addr += addr_offset;
|
if (offset > phdr[phx].p_offset) {
|
if (offset > phdr[phx].p_offset) {
|
if ((phdr[phx].p_offset + len) < offset) {
|
if ((phdr[phx].p_offset + len) < offset) {
|
diag_printf("Can't load ELF file - program headers out of order\n");
|
diag_printf("Can't load ELF file - program headers out of order\n");
|
return 0;
|
return 0;
|
}
|
}
|
addr += offset - phdr[phx].p_offset;
|
addr += offset - phdr[phx].p_offset;
|
} else {
|
} else {
|
while (offset < phdr[phx].p_offset) {
|
while (offset < phdr[phx].p_offset) {
|
if ((*getc)() < 0) {
|
if ((*getc)() < 0) {
|
diag_printf(SHORT_DATA);
|
diag_printf(SHORT_DATA);
|
return 0;
|
return 0;
|
}
|
}
|
offset++;
|
offset++;
|
}
|
}
|
}
|
}
|
// Copy data into memory
|
// Copy data into memory
|
while (len-- > 0) {
|
while (len-- > 0) {
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
if ((addr < user_ram_start) || (addr > user_ram_end)) {
|
if ((addr < user_ram_start) || (addr > user_ram_end)) {
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("*** Abort! Attempt to load ELF data to address: %p which is not in RAM\n", (void*)addr);
|
diag_printf("*** Abort! Attempt to load ELF data to address: %p which is not in RAM\n", (void*)addr);
|
return 0;
|
return 0;
|
}
|
}
|
#endif
|
#endif
|
if ((ch = (*getc)()) < 0) {
|
if ((ch = (*getc)()) < 0) {
|
diag_printf(SHORT_DATA);
|
diag_printf(SHORT_DATA);
|
return 0;
|
return 0;
|
}
|
}
|
*addr++ = ch;
|
*addr++ = ch;
|
offset++;
|
offset++;
|
if ((unsigned long)(addr-addr_offset) > highest_address) {
|
if ((unsigned long)(addr-addr_offset) > highest_address) {
|
highest_address = (unsigned long)(addr - addr_offset);
|
highest_address = (unsigned long)(addr - addr_offset);
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
// Save load base/top and entry
|
// Save load base/top and entry
|
if (base) {
|
if (base) {
|
load_address = base;
|
load_address = base;
|
load_address_end = base + (highest_address - lowest_address);
|
load_address_end = base + (highest_address - lowest_address);
|
entry_address = base + (ehdr.e_entry - lowest_address);
|
entry_address = base + (ehdr.e_entry - lowest_address);
|
} else {
|
} else {
|
load_address = lowest_address;
|
load_address = lowest_address;
|
load_address_end = highest_address;
|
load_address_end = highest_address;
|
entry_address = ehdr.e_entry;
|
entry_address = ehdr.e_entry;
|
}
|
}
|
|
|
redboot_getc_terminate(false);
|
redboot_getc_terminate(false);
|
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
|
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
|
diag_printf("Entry point: %p, address range: %p-%p\n",
|
diag_printf("Entry point: %p, address range: %p-%p\n",
|
(void*)entry_address, (void *)load_address, (void *)load_address_end);
|
(void*)entry_address, (void *)load_address, (void *)load_address_end);
|
return 1;
|
return 1;
|
#else // CYGSEM_REDBOOT_ELF
|
#else // CYGSEM_REDBOOT_ELF
|
diag_printf("Loading ELF images not supported\n");
|
diag_printf("Loading ELF images not supported\n");
|
return 0;
|
return 0;
|
#endif // CYGSEM_REDBOOT_ELF
|
#endif // CYGSEM_REDBOOT_ELF
|
}
|
}
|
|
|
|
|
//
|
//
|
// Scan a string of hex bytes and update the checksum
|
// Scan a string of hex bytes and update the checksum
|
//
|
//
|
static long
|
static long
|
_hex2(int (*getc)(void), int len, long *sum)
|
_hex2(int (*getc)(void), int len, long *sum)
|
{
|
{
|
int val, byte;
|
int val, byte;
|
char c1, c2;
|
char c1, c2;
|
|
|
val = 0;
|
val = 0;
|
while (len-- > 0) {
|
while (len-- > 0) {
|
c1 = (*getc)();
|
c1 = (*getc)();
|
c2 = (*getc)();
|
c2 = (*getc)();
|
if (_is_hex(c1) && _is_hex(c2)) {
|
if (_is_hex(c1) && _is_hex(c2)) {
|
val <<= 8;
|
val <<= 8;
|
byte = (_from_hex(c1)<<4) | _from_hex(c2);
|
byte = (_from_hex(c1)<<4) | _from_hex(c2);
|
val |= byte;
|
val |= byte;
|
if (sum) {
|
if (sum) {
|
*sum += byte;
|
*sum += byte;
|
}
|
}
|
} else {
|
} else {
|
return (-1);
|
return (-1);
|
}
|
}
|
}
|
}
|
return (val);
|
return (val);
|
}
|
}
|
|
|
//
|
//
|
// Process a set of S-records, loading the contents into memory.
|
// Process a set of S-records, loading the contents into memory.
|
// Note: if a "base" value is provided, the data will be relocated
|
// Note: if a "base" value is provided, the data will be relocated
|
// relative to that location. Of course, this can only work for
|
// relative to that location. Of course, this can only work for
|
// the first section of the data, so if there are non-contiguous
|
// the first section of the data, so if there are non-contiguous
|
// pieces of data, they will end up relocated in the same fashion.
|
// pieces of data, they will end up relocated in the same fashion.
|
// Because of this, "base" probably only makes sense for a set of
|
// Because of this, "base" probably only makes sense for a set of
|
// data which has only one section, e.g. a ROM image.
|
// data which has only one section, e.g. a ROM image.
|
//
|
//
|
static unsigned long
|
static unsigned long
|
load_srec_image(getc_t getc, unsigned long base)
|
load_srec_image(getc_t getc, unsigned long base)
|
{
|
{
|
int c;
|
int c;
|
long offset = 0, count, sum, val, cksum;
|
long offset = 0, count, sum, val, cksum;
|
unsigned char *addr, *base_addr;
|
unsigned char *addr, *base_addr;
|
char type;
|
char type;
|
bool first_addr = true;
|
bool first_addr = true;
|
unsigned long addr_offset = 0;
|
unsigned long addr_offset = 0;
|
unsigned long highest_address = 0;
|
unsigned long highest_address = 0;
|
unsigned long lowest_address = 0xFFFFFFFF;
|
unsigned long lowest_address = 0xFFFFFFFF;
|
|
|
while ((c = (*getc)()) > 0) {
|
while ((c = (*getc)()) > 0) {
|
// Start of line
|
// Start of line
|
if (c != 'S') {
|
if (c != 'S') {
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("Invalid S-record at offset %p, input: %c\n",
|
diag_printf("Invalid S-record at offset %p, input: %c\n",
|
(void *)offset, c);
|
(void *)offset, c);
|
return 0;
|
return 0;
|
}
|
}
|
type = (*getc)();
|
type = (*getc)();
|
offset += 2;
|
offset += 2;
|
sum = 0;
|
sum = 0;
|
if ((count = _hex2(getc, 1, &sum)) < 0) {
|
if ((count = _hex2(getc, 1, &sum)) < 0) {
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("Bad S-record count at offset %p\n", (void *)offset);
|
diag_printf("Bad S-record count at offset %p\n", (void *)offset);
|
return 0;
|
return 0;
|
}
|
}
|
offset += 1;
|
offset += 1;
|
switch (type) {
|
switch (type) {
|
case '0':
|
case '0':
|
break;
|
break;
|
case '1':
|
case '1':
|
case '2':
|
case '2':
|
case '3':
|
case '3':
|
base_addr = addr = (unsigned char *)_hex2(getc, (type-'1'+2), &sum);
|
base_addr = addr = (unsigned char *)_hex2(getc, (type-'1'+2), &sum);
|
offset += (type-'1'+2);
|
offset += (type-'1'+2);
|
if (first_addr) {
|
if (first_addr) {
|
if (base) {
|
if (base) {
|
addr_offset = (unsigned long)base - (unsigned long)addr;
|
addr_offset = (unsigned long)base - (unsigned long)addr;
|
} else {
|
} else {
|
addr_offset = 0;
|
addr_offset = 0;
|
}
|
}
|
first_addr = false;
|
first_addr = false;
|
}
|
}
|
addr += addr_offset;
|
addr += addr_offset;
|
if ((unsigned long)(addr-addr_offset) < lowest_address) {
|
if ((unsigned long)(addr-addr_offset) < lowest_address) {
|
lowest_address = (unsigned long)(addr - addr_offset);
|
lowest_address = (unsigned long)(addr - addr_offset);
|
}
|
}
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
if ((addr < user_ram_start) || (addr > user_ram_end)) {
|
if ((addr < user_ram_start) || (addr > user_ram_end)) {
|
// Only if there is no need to stop the download before printing
|
// Only if there is no need to stop the download before printing
|
// output can we ask confirmation questions.
|
// output can we ask confirmation questions.
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("*** Abort! Attempt to load S-record to address: %p, which is not in RAM\n",(void*)addr);
|
diag_printf("*** Abort! Attempt to load S-record to address: %p, which is not in RAM\n",(void*)addr);
|
return 0;
|
return 0;
|
}
|
}
|
#endif
|
#endif
|
count -= ((type-'1'+2)+1);
|
count -= ((type-'1'+2)+1);
|
offset += count;
|
offset += count;
|
while (count-- > 0) {
|
while (count-- > 0) {
|
val = _hex2(getc, 1, &sum);
|
val = _hex2(getc, 1, &sum);
|
*addr++ = val;
|
*addr++ = val;
|
}
|
}
|
cksum = _hex2(getc, 1, 0);
|
cksum = _hex2(getc, 1, 0);
|
offset += 1;
|
offset += 1;
|
sum = sum & 0xFF;
|
sum = sum & 0xFF;
|
cksum = (~cksum & 0xFF);
|
cksum = (~cksum & 0xFF);
|
if (cksum != sum) {
|
if (cksum != sum) {
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("*** Warning! Checksum failure - Addr: %lx, %02lX <> %02lX\n",
|
diag_printf("*** Warning! Checksum failure - Addr: %lx, %02lX <> %02lX\n",
|
(unsigned long)base_addr, sum, cksum);
|
(unsigned long)base_addr, sum, cksum);
|
return 0;
|
return 0;
|
}
|
}
|
if ((unsigned long)(addr-addr_offset) > highest_address) {
|
if ((unsigned long)(addr-addr_offset) > highest_address) {
|
highest_address = (unsigned long)(addr - addr_offset);
|
highest_address = (unsigned long)(addr - addr_offset);
|
}
|
}
|
break;
|
break;
|
case '7':
|
case '7':
|
case '8':
|
case '8':
|
case '9':
|
case '9':
|
addr = (unsigned char *)_hex2(getc, ('9'-type+2), &sum);
|
addr = (unsigned char *)_hex2(getc, ('9'-type+2), &sum);
|
offset += ('9'-type+2);
|
offset += ('9'-type+2);
|
// Save load base/top, entry address
|
// Save load base/top, entry address
|
if (base) {
|
if (base) {
|
load_address = base;
|
load_address = base;
|
load_address_end = base + (highest_address - lowest_address);
|
load_address_end = base + (highest_address - lowest_address);
|
entry_address = (unsigned long)(base + (addr - lowest_address));
|
entry_address = (unsigned long)(base + (addr - lowest_address));
|
} else {
|
} else {
|
load_address = lowest_address;
|
load_address = lowest_address;
|
load_address_end = highest_address;
|
load_address_end = highest_address;
|
entry_address = (unsigned long)addr;
|
entry_address = (unsigned long)addr;
|
}
|
}
|
redboot_getc_terminate(false);
|
redboot_getc_terminate(false);
|
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
|
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
|
diag_printf("Entry point: %p, address range: %p-%p\n",
|
diag_printf("Entry point: %p, address range: %p-%p\n",
|
(void*)entry_address, (void *)load_address, (void *)load_address_end);
|
(void*)entry_address, (void *)load_address, (void *)load_address_end);
|
|
|
return load_address_end;
|
return load_address_end;
|
default:
|
default:
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("Invalid S-record at offset 0x%lx, type: %x\n",
|
diag_printf("Invalid S-record at offset 0x%lx, type: %x\n",
|
(unsigned long)offset, type);
|
(unsigned long)offset, type);
|
return 0;
|
return 0;
|
}
|
}
|
while ((c = (*getc)()) != '\n') offset++;
|
while ((c = (*getc)()) != '\n') offset++;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
//
|
//
|
// 'load' CLI command processing
|
// 'load' CLI command processing
|
// -b - specify a load [base] address
|
// -b - specify a load [base] address
|
// -m - specify an I/O stream/method
|
// -m - specify an I/O stream/method
|
// -c - Alternate serial I/O channel
|
// -c - Alternate serial I/O channel
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
// -d - Decompress data [packed via 'zlib']
|
// -d - Decompress data [packed via 'zlib']
|
#endif
|
#endif
|
//
|
//
|
void
|
void
|
do_load(int argc, char *argv[])
|
do_load(int argc, char *argv[])
|
{
|
{
|
int res, num_options;
|
int res, num_options;
|
int i, err;
|
int i, err;
|
bool verbose, raw;
|
bool verbose, raw;
|
bool base_addr_set, mode_str_set;
|
bool base_addr_set, mode_str_set;
|
char *mode_str;
|
char *mode_str;
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
struct sockaddr_in host;
|
struct sockaddr_in host;
|
bool hostname_set;
|
bool hostname_set;
|
char *hostname;
|
char *hostname;
|
#endif
|
#endif
|
bool decompress = false;
|
bool decompress = false;
|
int chan = -1;
|
int chan = -1;
|
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
|
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
|
bool chan_set;
|
bool chan_set;
|
#endif
|
#endif
|
unsigned long base = 0;
|
unsigned long base = 0;
|
unsigned long end = 0;
|
unsigned long end = 0;
|
char type[4];
|
char type[4];
|
char *filename = 0;
|
char *filename = 0;
|
struct option_info opts[7];
|
struct option_info opts[7];
|
connection_info_t info;
|
connection_info_t info;
|
getc_io_funcs_t *io;
|
getc_io_funcs_t *io;
|
struct load_io_entry *io_tab;
|
struct load_io_entry *io_tab;
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
bool spillover_ok = false;
|
bool spillover_ok = false;
|
#endif
|
#endif
|
|
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
memset((char *)&host, 0, sizeof(host));
|
memset((char *)&host, 0, sizeof(host));
|
host.sin_len = sizeof(host);
|
host.sin_len = sizeof(host);
|
host.sin_family = AF_INET;
|
host.sin_family = AF_INET;
|
host.sin_addr = my_bootp_info.bp_siaddr;
|
host.sin_addr = my_bootp_info.bp_siaddr;
|
host.sin_port = 0;
|
host.sin_port = 0;
|
#endif
|
#endif
|
|
|
init_opts(&opts[0], 'v', false, OPTION_ARG_TYPE_FLG,
|
init_opts(&opts[0], 'v', false, OPTION_ARG_TYPE_FLG,
|
(void **)&verbose, 0, "verbose");
|
(void **)&verbose, 0, "verbose");
|
init_opts(&opts[1], 'r', false, OPTION_ARG_TYPE_FLG,
|
init_opts(&opts[1], 'r', false, OPTION_ARG_TYPE_FLG,
|
(void **)&raw, 0, "load raw data");
|
(void **)&raw, 0, "load raw data");
|
init_opts(&opts[2], 'b', true, OPTION_ARG_TYPE_NUM,
|
init_opts(&opts[2], 'b', true, OPTION_ARG_TYPE_NUM,
|
(void **)&base, (bool *)&base_addr_set, "load address");
|
(void **)&base, (bool *)&base_addr_set, "load address");
|
init_opts(&opts[3], 'm', true, OPTION_ARG_TYPE_STR,
|
init_opts(&opts[3], 'm', true, OPTION_ARG_TYPE_STR,
|
(void **)&mode_str, (bool *)&mode_str_set, "download mode (TFTP, xyzMODEM, or disk)");
|
(void **)&mode_str, (bool *)&mode_str_set, "download mode (TFTP, xyzMODEM, or disk)");
|
num_options = 4;
|
num_options = 4;
|
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
|
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
|
init_opts(&opts[num_options], 'c', true, OPTION_ARG_TYPE_NUM,
|
init_opts(&opts[num_options], 'c', true, OPTION_ARG_TYPE_NUM,
|
(void **)&chan, (bool *)&chan_set, "I/O channel");
|
(void **)&chan, (bool *)&chan_set, "I/O channel");
|
num_options++;
|
num_options++;
|
#endif
|
#endif
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
init_opts(&opts[num_options], 'h', true, OPTION_ARG_TYPE_STR,
|
init_opts(&opts[num_options], 'h', true, OPTION_ARG_TYPE_STR,
|
(void **)&hostname, (bool *)&hostname_set, "host name or IP address");
|
(void **)&hostname, (bool *)&hostname_set, "host name or IP address");
|
num_options++;
|
num_options++;
|
#endif
|
#endif
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
#ifdef CYGPKG_COMPRESS_ZLIB
|
init_opts(&opts[num_options], 'd', false, OPTION_ARG_TYPE_FLG,
|
init_opts(&opts[num_options], 'd', false, OPTION_ARG_TYPE_FLG,
|
(void **)&decompress, 0, "decompress");
|
(void **)&decompress, 0, "decompress");
|
num_options++;
|
num_options++;
|
#endif
|
#endif
|
|
|
if (!scan_opts(argc, argv, 1, opts, num_options,
|
if (!scan_opts(argc, argv, 1, opts, num_options,
|
(void *)&filename, OPTION_ARG_TYPE_STR, "file name")) {
|
(void *)&filename, OPTION_ARG_TYPE_STR, "file name")) {
|
return;
|
return;
|
}
|
}
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
if (hostname_set) {
|
if (hostname_set) {
|
ip_route_t rt;
|
ip_route_t rt;
|
if (!_gethostbyname(hostname, (in_addr_t *)&host)) {
|
if (!_gethostbyname(hostname, (in_addr_t *)&host)) {
|
diag_printf("Invalid host: %s\n", hostname);
|
diag_printf("Invalid host: %s\n", hostname);
|
return;
|
return;
|
}
|
}
|
/* check that the host can be accessed */
|
/* check that the host can be accessed */
|
if (__arp_lookup((ip_addr_t *)&host.sin_addr, &rt) < 0) {
|
if (__arp_lookup((ip_addr_t *)&host.sin_addr, &rt) < 0) {
|
diag_printf("Unable to reach host %s (%s)\n",
|
diag_printf("Unable to reach host %s (%s)\n",
|
hostname, inet_ntoa((in_addr_t *)&host));
|
hostname, inet_ntoa((in_addr_t *)&host));
|
return;
|
return;
|
}
|
}
|
}
|
}
|
#endif
|
#endif
|
if (chan >= CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS) {
|
if (chan >= CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS) {
|
diag_printf("Invalid I/O channel: %d\n", chan);
|
diag_printf("Invalid I/O channel: %d\n", chan);
|
return;
|
return;
|
}
|
}
|
if (mode_str_set) {
|
if (mode_str_set) {
|
io = (getc_io_funcs_t *)NULL;
|
io = (getc_io_funcs_t *)NULL;
|
for (io_tab = __RedBoot_LOAD_TAB__;
|
for (io_tab = __RedBoot_LOAD_TAB__;
|
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
|
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
|
if (strncasecmp(&mode_str[0], io_tab->name, strlen(&mode_str[0])) == 0) {
|
if (strncasecmp(&mode_str[0], io_tab->name, strlen(&mode_str[0])) == 0) {
|
io = io_tab->funcs;
|
io = io_tab->funcs;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (!io) {
|
if (!io) {
|
diag_printf("Invalid 'mode': %s. Valid modes are:", mode_str);
|
diag_printf("Invalid 'mode': %s. Valid modes are:", mode_str);
|
for (io_tab = __RedBoot_LOAD_TAB__;
|
for (io_tab = __RedBoot_LOAD_TAB__;
|
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
|
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
|
diag_printf(" %s", io_tab->name);
|
diag_printf(" %s", io_tab->name);
|
}
|
}
|
diag_printf("\n");
|
diag_printf("\n");
|
}
|
}
|
if (!io) {
|
if (!io) {
|
return;
|
return;
|
}
|
}
|
verbose &= io_tab->can_verbose;
|
verbose &= io_tab->can_verbose;
|
if (io_tab->need_filename && !filename) {
|
if (io_tab->need_filename && !filename) {
|
diag_printf("File name required\n");
|
diag_printf("File name required\n");
|
diag_printf("usage: load %s\n", usage);
|
diag_printf("usage: load %s\n", usage);
|
return;
|
return;
|
}
|
}
|
} else {
|
} else {
|
io_tab = (struct load_io_entry *)NULL; // Default
|
io_tab = (struct load_io_entry *)NULL; // Default
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
io = &tftp_io;
|
io = &tftp_io;
|
#else
|
#else
|
io = &xyzModem_io;
|
io = &xyzModem_io;
|
verbose = false;
|
verbose = false;
|
#endif
|
#endif
|
}
|
}
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
if (base_addr_set &&
|
if (base_addr_set &&
|
((base < (unsigned long)user_ram_start) ||
|
((base < (unsigned long)user_ram_start) ||
|
(base > (unsigned long)user_ram_end))) {
|
(base > (unsigned long)user_ram_end))) {
|
if (!verify_action("Specified address (%p) is not believed to be in RAM", (void*)base))
|
if (!verify_action("Specified address (%p) is not believed to be in RAM", (void*)base))
|
return;
|
return;
|
spillover_ok = true;
|
spillover_ok = true;
|
}
|
}
|
#endif
|
#endif
|
if (raw && !base_addr_set) {
|
if (raw && !base_addr_set) {
|
diag_printf("Raw load requires a memory address\n");
|
diag_printf("Raw load requires a memory address\n");
|
return;
|
return;
|
}
|
}
|
info.filename = filename;
|
info.filename = filename;
|
info.chan = chan;
|
info.chan = chan;
|
info.mode = io_tab ? io_tab->mode : 0;
|
info.mode = io_tab ? io_tab->mode : 0;
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
#ifdef CYGPKG_REDBOOT_NETWORKING
|
info.server = &host;
|
info.server = &host;
|
#endif
|
#endif
|
res = redboot_getc_init(&info, io, verbose, decompress);
|
res = redboot_getc_init(&info, io, verbose, decompress);
|
if (res < 0) {
|
if (res < 0) {
|
return;
|
return;
|
}
|
}
|
|
|
// Stream open, process the data
|
// Stream open, process the data
|
if (raw) {
|
if (raw) {
|
unsigned char *mp = (unsigned char *)base;
|
unsigned char *mp = (unsigned char *)base;
|
err = 0;
|
err = 0;
|
while ((res = redboot_getc()) >= 0) {
|
while ((res = redboot_getc()) >= 0) {
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
|
if (mp >= user_ram_end && !spillover_ok) {
|
if (mp >= user_ram_end && !spillover_ok) {
|
// Only if there is no need to stop the download
|
// Only if there is no need to stop the download
|
// before printing output can we ask confirmation
|
// before printing output can we ask confirmation
|
// questions.
|
// questions.
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("*** Abort! RAW data spills over limit of user RAM at %p\n",(void*)mp);
|
diag_printf("*** Abort! RAW data spills over limit of user RAM at %p\n",(void*)mp);
|
err = -1;
|
err = -1;
|
break;
|
break;
|
}
|
}
|
#endif
|
#endif
|
*mp++ = res;
|
*mp++ = res;
|
}
|
}
|
end = (unsigned long) mp;
|
end = (unsigned long) mp;
|
|
|
// Save load base/top
|
// Save load base/top
|
load_address = base;
|
load_address = base;
|
load_address_end = end;
|
load_address_end = end;
|
entry_address = base; // best guess
|
entry_address = base; // best guess
|
|
|
redboot_getc_terminate(false);
|
redboot_getc_terminate(false);
|
if (0 == err)
|
if (0 == err)
|
diag_printf("Raw file loaded %p-%p, assumed entry at %p\n",
|
diag_printf("Raw file loaded %p-%p, assumed entry at %p\n",
|
(void *)base, (void *)(end - 1), (void*)base);
|
(void *)base, (void *)(end - 1), (void*)base);
|
} else {
|
} else {
|
// Read initial header - to determine file [image] type
|
// Read initial header - to determine file [image] type
|
for (i = 0; i < sizeof(type); i++) {
|
for (i = 0; i < sizeof(type); i++) {
|
if ((res = redboot_getc()) < 0) {
|
if ((res = redboot_getc()) < 0) {
|
err = getc_info.err;
|
err = getc_info.err;
|
break;
|
break;
|
}
|
}
|
type[i] = res;
|
type[i] = res;
|
}
|
}
|
if (res >= 0) {
|
if (res >= 0) {
|
redboot_getc_rewind(); // Restore header to stream
|
redboot_getc_rewind(); // Restore header to stream
|
// Treat data as some sort of executable image
|
// Treat data as some sort of executable image
|
if (strncmp(&type[1], "ELF", 3) == 0) {
|
if (strncmp(&type[1], "ELF", 3) == 0) {
|
end = load_elf_image(redboot_getc, base);
|
end = load_elf_image(redboot_getc, base);
|
} else if ((type[0] == 'S') &&
|
} else if ((type[0] == 'S') &&
|
((type[1] >= '0') && (type[1] <= '9'))) {
|
((type[1] >= '0') && (type[1] <= '9'))) {
|
end = load_srec_image(redboot_getc, base);
|
end = load_srec_image(redboot_getc, base);
|
} else {
|
} else {
|
redboot_getc_terminate(true);
|
redboot_getc_terminate(true);
|
diag_printf("Unrecognized image type: 0x%lx\n", *(unsigned long *)type);
|
diag_printf("Unrecognized image type: 0x%lx\n", *(unsigned long *)type);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
redboot_getc_close(); // Clean up
|
redboot_getc_close(); // Clean up
|
return;
|
return;
|
}
|
}
|
|
|