📄 load.c
字号:
load_address_end = base + (highest_address - lowest_address);
entry_address = base + (ehdr.e_entry - lowest_address);
} else {
load_address = lowest_address;
load_address_end = highest_address;
entry_address = ehdr.e_entry;
}
redboot_getc_terminate(false);
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
diag_printf("Entry point: %p, address range: %p-%p\n",
(void*)entry_address, (void *)load_address, (void *)load_address_end);
return 1;
#else // CYGSEM_REDBOOT_ELF
diag_printf("Loading ELF images not supported\n");
return 0;
#endif // CYGSEM_REDBOOT_ELF
}
//
// Scan a string of hex bytes and update the checksum
//
static long
_hex2(int (*getc)(void), int len, long *sum)
{
int val, byte;
char c1, c2;
val = 0;
while (len-- > 0) {
c1 = (*getc)();
c2 = (*getc)();
if (_is_hex(c1) && _is_hex(c2)) {
val <<= 8;
byte = (_from_hex(c1)<<4) | _from_hex(c2);
val |= byte;
if (sum) {
*sum += byte;
}
} else {
return (-1);
}
}
return (val);
}
//
// Process a set of S-records, loading the contents into memory.
// Note: if a "base" value is provided, the data will be relocated
// relative to that location. Of course, this can only work for
// the first section of the data, so if there are non-contiguous
// pieces of data, they will end up relocated in the same fashion.
// Because of this, "base" probably only makes sense for a set of
// data which has only one section, e.g. a ROM image.
//
static unsigned long
load_srec_image(getc_t getc, unsigned long base)
{
int c;
long offset = 0, count, sum, val, cksum;
unsigned char *addr, *base_addr;
char type;
bool first_addr = true;
unsigned long addr_offset = 0;
unsigned long highest_address = 0;
unsigned long lowest_address = 0xFFFFFFFF;
while ((c = (*getc)()) > 0) {
// Start of line
if (c != 'S') {
redboot_getc_terminate(true);
diag_printf("Invalid S-record at offset %p, input: %c\n",
(void *)offset, c);
return 0;
}
type = (*getc)();
offset += 2;
sum = 0;
if ((count = _hex2(getc, 1, &sum)) < 0) {
redboot_getc_terminate(true);
diag_printf("Bad S-record count at offset %p\n", (void *)offset);
return 0;
}
offset += 1;
switch (type) {
case '0':
break;
case '1':
case '2':
case '3':
base_addr = addr = (unsigned char *)_hex2(getc, (type-'1'+2), &sum);
offset += (type-'1'+2);
if (first_addr) {
if (base) {
addr_offset = (unsigned long)base - (unsigned long)addr;
} else {
addr_offset = 0;
}
first_addr = false;
}
addr += addr_offset;
if ((unsigned long)(addr-addr_offset) < lowest_address) {
lowest_address = (unsigned long)(addr - addr_offset);
}
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
if (!valid_address(addr)) {
// Only if there is no need to stop the download before printing
// output can we ask confirmation questions.
redboot_getc_terminate(true);
diag_printf("*** Abort! Attempt to load S-record to address: %p, which is not in RAM\n",(void*)addr);
return 0;
}
#endif
count -= ((type-'1'+2)+1);
offset += count;
while (count-- > 0) {
val = _hex2(getc, 1, &sum);
*addr++ = val;
}
cksum = _hex2(getc, 1, 0);
offset += 1;
sum = sum & 0xFF;
cksum = (~cksum & 0xFF);
if (cksum != sum) {
redboot_getc_terminate(true);
diag_printf("*** Warning! Checksum failure - Addr: %lx, %02lX <> %02lX\n",
(unsigned long)base_addr, sum, cksum);
return 0;
}
if ((unsigned long)(addr-addr_offset) > highest_address) {
highest_address = (unsigned long)(addr - addr_offset);
}
break;
case '7':
case '8':
case '9':
addr = (unsigned char *)_hex2(getc, ('9'-type+2), &sum);
offset += ('9'-type+2);
// Save load base/top, entry address
if (base) {
load_address = base;
load_address_end = base + (highest_address - lowest_address);
entry_address = (unsigned long)(base + (addr - lowest_address));
} else {
load_address = lowest_address;
load_address_end = highest_address;
entry_address = (unsigned long)addr;
}
redboot_getc_terminate(false);
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
diag_printf("Entry point: %p, address range: %p-%p\n",
(void*)entry_address, (void *)load_address, (void *)load_address_end);
return load_address_end;
default:
redboot_getc_terminate(true);
diag_printf("Invalid S-record at offset 0x%lx, type: %x\n",
(unsigned long)offset, type);
return 0;
}
while ((c = (*getc)()) != '\n') offset++;
}
return 0;
}
//
// 'load' CLI command processing
// -b - specify a load [base] address
// -m - specify an I/O stream/method
// -c - Alternate serial I/O channel
#ifdef CYGBLD_BUILD_REDBOOT_WITH_ZLIB
// -d - Decompress data [packed via 'zlib']
#endif
//
void
do_load(int argc, char *argv[])
{
int res, num_options;
int i, err;
bool verbose, raw;
bool base_addr_set, mode_str_set;
char *mode_str;
#ifdef CYGPKG_REDBOOT_NETWORKING
struct sockaddr_in host;
bool hostname_set, port_set;
char *hostname;
#endif
bool decompress = false;
int chan = -1;
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
bool chan_set;
#endif
unsigned long base = 0;
unsigned long end = 0;
char type[4];
char *filename = 0;
struct option_info opts[8];
connection_info_t info;
getc_io_funcs_t *io = NULL;
struct load_io_entry *io_tab;
unsigned int port; //int because it's an OPTION_ARG_TYPE_NUM, but will be cast to short
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
bool spillover_ok = false;
#endif
#ifdef CYGPKG_REDBOOT_NETWORKING
memset((char *)&host, 0, sizeof(host));
host.sin_len = sizeof(host);
host.sin_family = AF_INET;
host.sin_addr = my_bootp_info.bp_siaddr;
host.sin_port = 0;
#endif
init_opts(&opts[0], 'v', false, OPTION_ARG_TYPE_FLG,
(void *)&verbose, 0, "verbose");
init_opts(&opts[1], 'r', false, OPTION_ARG_TYPE_FLG,
(void *)&raw, 0, "load raw data");
init_opts(&opts[2], 'b', true, OPTION_ARG_TYPE_NUM,
(void *)&base, (bool *)&base_addr_set, "load address");
init_opts(&opts[3], 'm', true, OPTION_ARG_TYPE_STR,
(void *)&mode_str, (bool *)&mode_str_set, "download mode (TFTP, xyzMODEM, or disk)");
num_options = 4;
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
init_opts(&opts[num_options], 'c', true, OPTION_ARG_TYPE_NUM,
(void *)&chan, (bool *)&chan_set, "I/O channel");
num_options++;
#endif
#ifdef CYGPKG_REDBOOT_NETWORKING
init_opts(&opts[num_options], 'h', true, OPTION_ARG_TYPE_STR,
(void *)&hostname, (bool *)&hostname_set, "host name or IP address");
num_options++;
init_opts(&opts[num_options], 'p', true, OPTION_ARG_TYPE_NUM,
(void *)&port, (bool *)&port_set, "TCP port");
num_options++;
#endif
#ifdef CYGBLD_BUILD_REDBOOT_WITH_ZLIB
init_opts(&opts[num_options], 'd', false, OPTION_ARG_TYPE_FLG,
(void *)&decompress, 0, "decompress");
num_options++;
#endif
CYG_ASSERT(num_options <= NUM_ELEMS(opts), "Too many options");
if (!scan_opts(argc, argv, 1, opts, num_options,
(void *)&filename, OPTION_ARG_TYPE_STR, "file name")) {
return;
}
#ifdef CYGPKG_REDBOOT_NETWORKING
if (hostname_set) {
ip_route_t rt;
if (!_gethostbyname(hostname, (in_addr_t *)&host)) {
diag_printf("Invalid host: %s\n", hostname);
return;
}
/* check that the host can be accessed */
if (__arp_lookup((ip_addr_t *)&host.sin_addr, &rt) < 0) {
diag_printf("Unable to reach host %s (%s)\n",
hostname, inet_ntoa((in_addr_t *)&host));
return;
}
}
if (port_set)
host.sin_port = port;
#endif
if (chan >= CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS) {
diag_printf("Invalid I/O channel: %d\n", chan);
return;
}
if (mode_str_set) {
for (io_tab = __RedBoot_LOAD_TAB__;
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
if (strncasecmp(&mode_str[0], io_tab->name, strlen(&mode_str[0])) == 0) {
io = io_tab->funcs;
break;
}
}
if (!io) {
diag_printf("Invalid 'mode': %s. Valid modes are:", mode_str);
for (io_tab = __RedBoot_LOAD_TAB__;
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
diag_printf(" %s", io_tab->name);
}
diag_printf("\n");
}
if (!io) {
return;
}
verbose &= io_tab->can_verbose;
if (io_tab->need_filename && !filename) {
diag_printf("File name required\n");
diag_printf("usage: load %s\n", usage);
return;
}
} else {
char *which;
io_tab = (struct load_io_entry *)NULL; // Default
#ifdef CYGPKG_REDBOOT_NETWORKING
#ifdef CYGSEM_REDBOOT_NET_TFTP_DOWNLOAD
which = "TFTP";
io = &tftp_io;
#elif defined(CYGSEM_REDBOOT_NET_HTTP_DOWNLOAD)
which = "HTTP";
io = &http_io;
#endif
#endif
#ifdef CYGPKG_REDBOOT_FILEIO
// Make file I/O default if mounted
if (fileio_mounted) {
which = "file";
io = &fileio_io;
}
#endif
if (!io) {
#ifdef CYGBLD_BUILD_REDBOOT_WITH_XYZMODEM
which = "Xmodem";
io = &xyzModem_io;
verbose = false;
#else
diag_printf("No default protocol!\n");
return;
#endif
}
diag_printf("Using default protocol (%s)\n", which);
}
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
if (base_addr_set && !valid_address((unsigned char *)base)) {
if (!verify_action("Specified address (%p) is not believed to be in RAM", (void*)base))
return;
spillover_ok = true;
}
#endif
if (raw && !base_addr_set) {
diag_printf("Raw load requires a memory address\n");
return;
}
info.filename = filename;
info.chan = chan;
info.mode = io_tab ? io_tab->mode : 0;
#ifdef CYGPKG_REDBOOT_NETWORKING
info.server = &host;
#endif
res = redboot_getc_init(&info, io, verbose, decompress);
if (res < 0) {
return;
}
// Stream open, process the data
if (raw) {
unsigned char *mp = (unsigned char *)base;
err = 0;
while ((res = redboot_getc()) >= 0) {
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
if (!valid_address(mp) && !spillover_ok) {
// Only if there is no need to stop the download
// before printing output can we ask confirmation
// questions.
redboot_getc_terminate(true);
diag_printf("*** Abort! RAW data spills over limit of user RAM at %p\n",(void*)mp);
err = -1;
break;
}
#endif
*mp++ = res;
}
end = (unsigned long) mp;
// Save load base/top
load_address = base;
load_address_end = end;
entry_address = base; // best guess
redboot_getc_terminate(false);
if (0 == err)
diag_printf("Raw file loaded %p-%p, assumed entry at %p\n",
(void *)base, (void *)(end - 1), (void*)base);
} else {
// Read initial header - to determine file [image] type
for (i = 0; i < sizeof(type); i++) {
if ((res = redboot_getc()) < 0) {
err = getc_info.err;
break;
}
type[i] = res;
}
if (res >= 0) {
redboot_getc_rewind(); // Restore header to stream
// Treat data as some sort of executable image
if (strncmp(&type[1], "ELF", 3) == 0) {
end = load_elf_image(redboot_getc, base);
} else if ((type[0] == 'S') &&
((type[1] >= '0') && (type[1] <= '9'))) {
end = load_srec_image(redboot_getc, base);
} else {
redboot_getc_terminate(true);
diag_printf("Unrecognized image type: 0x%lx\n", *(unsigned long *)type);
}
}
}
redboot_getc_close(); // Clean up
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -