Mercurial > ecos
diff packages/redboot/current/src/load.c @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | |
| children | bd1a71e3e476 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/redboot/current/src/load.c @@ -0,0 +1,406 @@ +//========================================================================== +// +// load.c +// +// RedBoot file/image loader +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas +// Date: 2000-07-14 +// Purpose: +// Description: +// +// This code is part of RedBoot (tm). +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <redboot.h> + +// Exported CLI function +RedBoot_cmd("load", + "Load a file", + "[-raw] [-b <mem_addr>]]", + do_load + ); + +static void +load_elf_image(int (*getc)(void)) +{ + printf("ELF images not supported\n"); +} + +// +// 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. +// +#define MAX_LINE 80 +static void +load_srec_image(int (*getc)(void), unsigned long base) +{ + char line[MAX_LINE]; + char *lp; + int c, len; + 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) { + lp = line; len = 0; + // Start of line + if (c != 'S') { + printf("Invalid S-record at offset 0x%lx, input: %c\n", + (unsigned long)offset, c); + return; + } + type = (*getc)(); + offset += 2; + sum = 0; + if ((count = _hex2(getc, 1, &sum)) < 0) { + printf("Bad S-record count at offset 0x%lx\n", (unsigned long)offset); + return; + } + 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; + printf("Address offset = %lx\n", (unsigned long)addr_offset); + } 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); + } + if ((addr < ram_start) || (addr > ram_end)) { + printf("Attempt to load S-record data to address: %p [not in RAM]\n", addr); + return; + } + 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) { + printf("*** Warning! Checksum failure - Addr: %lx, %02lX <> %02lX\n", + (unsigned long)base_addr, sum, cksum); + } + 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); + entry_address = (unsigned long *)addr; + printf("Entry point: %p, address range: %p-%p\n", + entry_address, (void *)lowest_address, (void *)highest_address); + return; + default: + printf("Invalid S-record at offset 0x%lx, type: %x\n", + (unsigned long)offset, type); + return; + } + while ((c = (*getc)()) != '\n') offset++; + } +} + +#define BUF_SIZE 256 +static struct { + int (*fun)(char *, int len, int *err); + unsigned char buf[BUF_SIZE]; + unsigned char *bufp; + int avail, len, err; + int verbose, tick; +} getc_info; + +static int +getc(void) +{ + static char spin[] = "|/-\\|-"; + if (getc_info.avail == 0) { + if (getc_info.verbose) { + printf("%c\b", spin[getc_info.tick++]); + if (getc_info.tick >= sizeof(spin)) { + getc_info.tick = 0; + } + } + if (getc_info.len < BUF_SIZE) { + // No more data available + if (getc_info.verbose) printf("\n"); + return -1; + } + getc_info.bufp = getc_info.buf; + 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.verbose) printf("\n"); + return -1; + } + } + getc_info.avail--; + return *getc_info.bufp++; +} + +static void +getc_init(int (*fun)(char *, int, int *), int verbose) +{ + getc_info.avail = 0; + getc_info.len = BUF_SIZE; + getc_info.fun = fun; + getc_info.verbose = verbose; + getc_info.tick = 0; +} + +static void +getc_rewind(void) +{ + getc_info.bufp = getc_info.buf; + getc_info.avail = getc_info.len; +} + +#define MODE_TFTP 0 +#define MODE_ZMODEM 1 + +void +do_load(int argc, char *argv[]) +{ + int i, res, err; + int mode; + int verbose = 0; + bool raw = false; + bool base_addr_set = false; +#ifdef CYGPKG_REDBOOT_NETWORKING + struct sockaddr_in host; +#endif + unsigned long base = 0; + char type[4]; + char *filename = 0; + char *usage = "usage: load <file_name> [-r] [-v] [-h <host>] [-m {TFTP | ZMODEM}] [-b <base_address>]\n"; + +#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 +#ifdef CYGPKG_REDBOOT_NETWORKING + mode = MODE_TFTP; +#else + mode = MODE_ZMODEM; +#endif + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + // Option + switch (argv[i][1]) { + case 'v': + verbose++; + break; + case 'r': + raw = true; + break; +#ifdef CYGPKG_REDBOOT_NETWORKING + case 'h': + if ((i+1) >= argc) { + printf("Invalid option: %s\n", argv[i]); + printf(usage); + return; + } + if (!inet_aton(argv[i+1], (in_addr_t *)&host)) { + printf("Invalid IP address: %s\n", argv[i+1]); + return; + } + i++; // Skip parameter + break; +#endif + case 'm': + if ((i+1) >= argc) { + printf("Invalid option: %s\n", argv[i]); + printf(usage); + return; + } + if (strcmpci(argv[i+1], "zmodem") == 0) { + mode = MODE_ZMODEM; +#ifdef CYGPKG_REDBOOT_NETWORKING + } else if (strcmpci(argv[i+1], "tftp") == 0) { + mode = MODE_TFTP; + if (!have_net) { + printf("TFTP mode requires a working network\n"); + return; + } +#endif + } else { + printf("Invalid 'mode': %s\n", argv[i+1]); + return; + } + i++; // Skip parameter + break; + case 'b': + if ((i+1) >= argc) { + printf("Invalid option: %s\n", argv[i]); + printf(usage); + return; + } + if (!parse_num(argv[i+1], &base, 0, 0)) { + printf("Invalid offset: %s\n", argv[i+1]); + printf(usage); + return; + } + base_addr_set = true; + i++; // Skip parameter + break; + default: + printf("Invalid option: %s\n", argv[i]); + printf(usage); + return; + } + } else { + if (filename) { + printf("Only one file name allowed\n"); + printf(usage); + return; + } + filename = argv[i]; + } + } + if ((mode == MODE_TFTP) && !filename) { + printf("File name missing\n"); + printf(usage); + return; + } +#ifdef CYGPKG_REDBOOT_NETWORKING + if (mode == MODE_TFTP) { + res = tftp_stream_open(filename, &host, TFTP_OCTET, &err); + if (res < 0) { + printf("Can't load '%s': %s\n", filename, tftp_error(err)); + return; + } + getc_init(tftp_stream_read, verbose); + for (i = 0; i < sizeof(type); i++) { + if ((res = getc()) < 0) { + err = getc_info.err; + break; + } + type[i] = res; + } + if (res < 0) { + printf("Error reading header via TFTP: %s\n", tftp_error(err)); + tftp_stream_close(&err); + return; + } + } +#endif + if (mode == MODE_ZMODEM) { + printf("Sorry, zmodem download not yet available\n"); + return; + } + getc_rewind(); // Restore header to stream + if (raw) { + if (!base_addr_set) { + printf("Raw load requires a memory address\n"); + } else { + unsigned char *mp = (unsigned char *)base; + while ((res = getc()) >= 0) { + *mp++ = res; + } + printf("Raw file loaded %p-%p\n", (void *)base, (void *)mp); + } + } else { + // Treat data as some sort of executable image + if (strncmp(&type[1], "ELF", 3) == 0) { + load_elf_image(getc); + } else if ((type[0] == 'S') && + ((type[1] >= '0') && (type[1] <= '9'))) { + load_srec_image(getc, base); + } else { + printf("Unrecognized image type: %lx\n", *(unsigned long *)type); + } + } +#ifdef CYGPKG_REDBOOT_NETWORKING + if (mode == MODE_TFTP) { + tftp_stream_close(&err); + } +#endif + return; +}
