Mercurial > ecos
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 114:5ad2b71d525e | 115:6ed91473a1cd |
|---|---|
| 1 //========================================================================== | |
| 2 // | |
| 3 // load.c | |
| 4 // | |
| 5 // RedBoot file/image loader | |
| 6 // | |
| 7 //========================================================================== | |
| 8 //####COPYRIGHTBEGIN#### | |
| 9 // | |
| 10 // ------------------------------------------- | |
| 11 // The contents of this file are subject to the Red Hat eCos Public License | |
| 12 // Version 1.1 (the "License"); you may not use this file except in | |
| 13 // compliance with the License. You may obtain a copy of the License at | |
| 14 // http://www.redhat.com/ | |
| 15 // | |
| 16 // Software distributed under the License is distributed on an "AS IS" | |
| 17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the | |
| 18 // License for the specific language governing rights and limitations under | |
| 19 // the License. | |
| 20 // | |
| 21 // The Original Code is eCos - Embedded Configurable Operating System, | |
| 22 // released September 30, 1998. | |
| 23 // | |
| 24 // The Initial Developer of the Original Code is Red Hat. | |
| 25 // Portions created by Red Hat are | |
| 26 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. | |
| 27 // All Rights Reserved. | |
| 28 // ------------------------------------------- | |
| 29 // | |
| 30 //####COPYRIGHTEND#### | |
| 31 //========================================================================== | |
| 32 //#####DESCRIPTIONBEGIN#### | |
| 33 // | |
| 34 // Author(s): gthomas | |
| 35 // Contributors: gthomas | |
| 36 // Date: 2000-07-14 | |
| 37 // Purpose: | |
| 38 // Description: | |
| 39 // | |
| 40 // This code is part of RedBoot (tm). | |
| 41 // | |
| 42 //####DESCRIPTIONEND#### | |
| 43 // | |
| 44 //========================================================================== | |
| 45 | |
| 46 #include <redboot.h> | |
| 47 | |
| 48 // Exported CLI function | |
| 49 RedBoot_cmd("load", | |
| 50 "Load a file", | |
| 51 "[-raw] [-b <mem_addr>]]", | |
| 52 do_load | |
| 53 ); | |
| 54 | |
| 55 static void | |
| 56 load_elf_image(int (*getc)(void)) | |
| 57 { | |
| 58 printf("ELF images not supported\n"); | |
| 59 } | |
| 60 | |
| 61 // | |
| 62 // Scan a string of hex bytes and update the checksum | |
| 63 // | |
| 64 static long | |
| 65 _hex2(int (*getc)(void), int len, long *sum) | |
| 66 { | |
| 67 int val, byte; | |
| 68 char c1, c2; | |
| 69 | |
| 70 val = 0; | |
| 71 while (len-- > 0) { | |
| 72 c1 = (*getc)(); | |
| 73 c2 = (*getc)(); | |
| 74 if (_is_hex(c1) && _is_hex(c2)) { | |
| 75 val <<= 8; | |
| 76 byte = (_from_hex(c1)<<4) | _from_hex(c2); | |
| 77 val |= byte; | |
| 78 if (sum) { | |
| 79 *sum += byte; | |
| 80 } | |
| 81 } else { | |
| 82 return (-1); | |
| 83 } | |
| 84 } | |
| 85 return (val); | |
| 86 } | |
| 87 | |
| 88 // | |
| 89 // Process a set of S-records, loading the contents into memory. | |
| 90 // Note: if a "base" value is provided, the data will be relocated | |
| 91 // relative to that location. Of course, this can only work for | |
| 92 // the first section of the data, so if there are non-contiguous | |
| 93 // pieces of data, they will end up relocated in the same fashion. | |
| 94 // Because of this, "base" probably only makes sense for a set of | |
| 95 // data which has only one section, e.g. a ROM image. | |
| 96 // | |
| 97 #define MAX_LINE 80 | |
| 98 static void | |
| 99 load_srec_image(int (*getc)(void), unsigned long base) | |
| 100 { | |
| 101 char line[MAX_LINE]; | |
| 102 char *lp; | |
| 103 int c, len; | |
| 104 long offset = 0, count, sum, val, cksum; | |
| 105 unsigned char *addr, *base_addr; | |
| 106 char type; | |
| 107 bool first_addr = true; | |
| 108 unsigned long addr_offset = 0; | |
| 109 unsigned long highest_address = 0; | |
| 110 unsigned long lowest_address = 0xFFFFFFFF; | |
| 111 | |
| 112 while ((c = (*getc)()) > 0) { | |
| 113 lp = line; len = 0; | |
| 114 // Start of line | |
| 115 if (c != 'S') { | |
| 116 printf("Invalid S-record at offset 0x%lx, input: %c\n", | |
| 117 (unsigned long)offset, c); | |
| 118 return; | |
| 119 } | |
| 120 type = (*getc)(); | |
| 121 offset += 2; | |
| 122 sum = 0; | |
| 123 if ((count = _hex2(getc, 1, &sum)) < 0) { | |
| 124 printf("Bad S-record count at offset 0x%lx\n", (unsigned long)offset); | |
| 125 return; | |
| 126 } | |
| 127 offset += 1; | |
| 128 switch (type) { | |
| 129 case '0': | |
| 130 break; | |
| 131 case '1': | |
| 132 case '2': | |
| 133 case '3': | |
| 134 base_addr = addr = (unsigned char *)_hex2(getc, (type-'1'+2), &sum); | |
| 135 offset += (type-'1'+2); | |
| 136 if (first_addr) { | |
| 137 if (base) { | |
| 138 addr_offset = (unsigned long)base - (unsigned long)addr; | |
| 139 printf("Address offset = %lx\n", (unsigned long)addr_offset); | |
| 140 } else { | |
| 141 addr_offset = 0; | |
| 142 } | |
| 143 first_addr = false; | |
| 144 } | |
| 145 addr += addr_offset; | |
| 146 if ((unsigned long)(addr-addr_offset) < lowest_address) { | |
| 147 lowest_address = (unsigned long)(addr - addr_offset); | |
| 148 } | |
| 149 if ((addr < ram_start) || (addr > ram_end)) { | |
| 150 printf("Attempt to load S-record data to address: %p [not in RAM]\n", addr); | |
| 151 return; | |
| 152 } | |
| 153 count -= ((type-'1'+2)+1); | |
| 154 offset += count; | |
| 155 while (count-- > 0) { | |
| 156 val = _hex2(getc, 1, &sum); | |
| 157 *addr++ = val; | |
| 158 } | |
| 159 cksum = _hex2(getc, 1, 0); | |
| 160 offset += 1; | |
| 161 sum = sum & 0xFF; | |
| 162 cksum = (~cksum & 0xFF); | |
| 163 if (cksum != sum) { | |
| 164 printf("*** Warning! Checksum failure - Addr: %lx, %02lX <> %02lX\n", | |
| 165 (unsigned long)base_addr, sum, cksum); | |
| 166 } | |
| 167 if ((unsigned long)(addr-addr_offset) > highest_address) { | |
| 168 highest_address = (unsigned long)(addr - addr_offset); | |
| 169 } | |
| 170 break; | |
| 171 case '7': | |
| 172 case '8': | |
| 173 case '9': | |
| 174 addr = (unsigned char *)_hex2(getc, ('9'-type+2), &sum); | |
| 175 offset += ('9'-type+2); | |
| 176 entry_address = (unsigned long *)addr; | |
| 177 printf("Entry point: %p, address range: %p-%p\n", | |
| 178 entry_address, (void *)lowest_address, (void *)highest_address); | |
| 179 return; | |
| 180 default: | |
| 181 printf("Invalid S-record at offset 0x%lx, type: %x\n", | |
| 182 (unsigned long)offset, type); | |
| 183 return; | |
| 184 } | |
| 185 while ((c = (*getc)()) != '\n') offset++; | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 #define BUF_SIZE 256 | |
| 190 static struct { | |
| 191 int (*fun)(char *, int len, int *err); | |
| 192 unsigned char buf[BUF_SIZE]; | |
| 193 unsigned char *bufp; | |
| 194 int avail, len, err; | |
| 195 int verbose, tick; | |
| 196 } getc_info; | |
| 197 | |
| 198 static int | |
| 199 getc(void) | |
| 200 { | |
| 201 static char spin[] = "|/-\\|-"; | |
| 202 if (getc_info.avail == 0) { | |
| 203 if (getc_info.verbose) { | |
| 204 printf("%c\b", spin[getc_info.tick++]); | |
| 205 if (getc_info.tick >= sizeof(spin)) { | |
| 206 getc_info.tick = 0; | |
| 207 } | |
| 208 } | |
| 209 if (getc_info.len < BUF_SIZE) { | |
| 210 // No more data available | |
| 211 if (getc_info.verbose) printf("\n"); | |
| 212 return -1; | |
| 213 } | |
| 214 getc_info.bufp = getc_info.buf; | |
| 215 getc_info.len = (*getc_info.fun)(getc_info.bufp, BUF_SIZE, &getc_info.err); | |
| 216 if ((getc_info.avail = getc_info.len) < 0) { | |
| 217 if (getc_info.verbose) printf("\n"); | |
| 218 return -1; | |
| 219 } | |
| 220 } | |
| 221 getc_info.avail--; | |
| 222 return *getc_info.bufp++; | |
| 223 } | |
| 224 | |
| 225 static void | |
| 226 getc_init(int (*fun)(char *, int, int *), int verbose) | |
| 227 { | |
| 228 getc_info.avail = 0; | |
| 229 getc_info.len = BUF_SIZE; | |
| 230 getc_info.fun = fun; | |
| 231 getc_info.verbose = verbose; | |
| 232 getc_info.tick = 0; | |
| 233 } | |
| 234 | |
| 235 static void | |
| 236 getc_rewind(void) | |
| 237 { | |
| 238 getc_info.bufp = getc_info.buf; | |
| 239 getc_info.avail = getc_info.len; | |
| 240 } | |
| 241 | |
| 242 #define MODE_TFTP 0 | |
| 243 #define MODE_ZMODEM 1 | |
| 244 | |
| 245 void | |
| 246 do_load(int argc, char *argv[]) | |
| 247 { | |
| 248 int i, res, err; | |
| 249 int mode; | |
| 250 int verbose = 0; | |
| 251 bool raw = false; | |
| 252 bool base_addr_set = false; | |
| 253 #ifdef CYGPKG_REDBOOT_NETWORKING | |
| 254 struct sockaddr_in host; | |
| 255 #endif | |
| 256 unsigned long base = 0; | |
| 257 char type[4]; | |
| 258 char *filename = 0; | |
| 259 char *usage = "usage: load <file_name> [-r] [-v] [-h <host>] [-m {TFTP | ZMODEM}] [-b <base_address>]\n"; | |
| 260 | |
| 261 #ifdef CYGPKG_REDBOOT_NETWORKING | |
| 262 memset((char *)&host, 0, sizeof(host)); | |
| 263 host.sin_len = sizeof(host); | |
| 264 host.sin_family = AF_INET; | |
| 265 host.sin_addr = my_bootp_info.bp_siaddr; | |
| 266 host.sin_port = 0; | |
| 267 #endif | |
| 268 #ifdef CYGPKG_REDBOOT_NETWORKING | |
| 269 mode = MODE_TFTP; | |
| 270 #else | |
| 271 mode = MODE_ZMODEM; | |
| 272 #endif | |
| 273 for (i = 1; i < argc; i++) { | |
| 274 if (argv[i][0] == '-') { | |
| 275 // Option | |
| 276 switch (argv[i][1]) { | |
| 277 case 'v': | |
| 278 verbose++; | |
| 279 break; | |
| 280 case 'r': | |
| 281 raw = true; | |
| 282 break; | |
| 283 #ifdef CYGPKG_REDBOOT_NETWORKING | |
| 284 case 'h': | |
| 285 if ((i+1) >= argc) { | |
| 286 printf("Invalid option: %s\n", argv[i]); | |
| 287 printf(usage); | |
| 288 return; | |
| 289 } | |
| 290 if (!inet_aton(argv[i+1], (in_addr_t *)&host)) { | |
| 291 printf("Invalid IP address: %s\n", argv[i+1]); | |
| 292 return; | |
| 293 } | |
| 294 i++; // Skip parameter | |
| 295 break; | |
| 296 #endif | |
| 297 case 'm': | |
| 298 if ((i+1) >= argc) { | |
| 299 printf("Invalid option: %s\n", argv[i]); | |
| 300 printf(usage); | |
| 301 return; | |
| 302 } | |
| 303 if (strcmpci(argv[i+1], "zmodem") == 0) { | |
| 304 mode = MODE_ZMODEM; | |
| 305 #ifdef CYGPKG_REDBOOT_NETWORKING | |
| 306 } else if (strcmpci(argv[i+1], "tftp") == 0) { | |
| 307 mode = MODE_TFTP; | |
| 308 if (!have_net) { | |
| 309 printf("TFTP mode requires a working network\n"); | |
| 310 return; | |
| 311 } | |
| 312 #endif | |
| 313 } else { | |
| 314 printf("Invalid 'mode': %s\n", argv[i+1]); | |
| 315 return; | |
| 316 } | |
| 317 i++; // Skip parameter | |
| 318 break; | |
| 319 case 'b': | |
| 320 if ((i+1) >= argc) { | |
| 321 printf("Invalid option: %s\n", argv[i]); | |
| 322 printf(usage); | |
| 323 return; | |
| 324 } | |
| 325 if (!parse_num(argv[i+1], &base, 0, 0)) { | |
| 326 printf("Invalid offset: %s\n", argv[i+1]); | |
| 327 printf(usage); | |
| 328 return; | |
| 329 } | |
| 330 base_addr_set = true; | |
| 331 i++; // Skip parameter | |
| 332 break; | |
| 333 default: | |
| 334 printf("Invalid option: %s\n", argv[i]); | |
| 335 printf(usage); | |
| 336 return; | |
| 337 } | |
| 338 } else { | |
| 339 if (filename) { | |
| 340 printf("Only one file name allowed\n"); | |
| 341 printf(usage); | |
| 342 return; | |
| 343 } | |
| 344 filename = argv[i]; | |
| 345 } | |
| 346 } | |
| 347 if ((mode == MODE_TFTP) && !filename) { | |
| 348 printf("File name missing\n"); | |
| 349 printf(usage); | |
| 350 return; | |
| 351 } | |
| 352 #ifdef CYGPKG_REDBOOT_NETWORKING | |
| 353 if (mode == MODE_TFTP) { | |
| 354 res = tftp_stream_open(filename, &host, TFTP_OCTET, &err); | |
| 355 if (res < 0) { | |
| 356 printf("Can't load '%s': %s\n", filename, tftp_error(err)); | |
| 357 return; | |
| 358 } | |
| 359 getc_init(tftp_stream_read, verbose); | |
| 360 for (i = 0; i < sizeof(type); i++) { | |
| 361 if ((res = getc()) < 0) { | |
| 362 err = getc_info.err; | |
| 363 break; | |
| 364 } | |
| 365 type[i] = res; | |
| 366 } | |
| 367 if (res < 0) { | |
| 368 printf("Error reading header via TFTP: %s\n", tftp_error(err)); | |
| 369 tftp_stream_close(&err); | |
| 370 return; | |
| 371 } | |
| 372 } | |
| 373 #endif | |
| 374 if (mode == MODE_ZMODEM) { | |
| 375 printf("Sorry, zmodem download not yet available\n"); | |
| 376 return; | |
| 377 } | |
| 378 getc_rewind(); // Restore header to stream | |
| 379 if (raw) { | |
| 380 if (!base_addr_set) { | |
| 381 printf("Raw load requires a memory address\n"); | |
| 382 } else { | |
| 383 unsigned char *mp = (unsigned char *)base; | |
| 384 while ((res = getc()) >= 0) { | |
| 385 *mp++ = res; | |
| 386 } | |
| 387 printf("Raw file loaded %p-%p\n", (void *)base, (void *)mp); | |
| 388 } | |
| 389 } else { | |
| 390 // Treat data as some sort of executable image | |
| 391 if (strncmp(&type[1], "ELF", 3) == 0) { | |
| 392 load_elf_image(getc); | |
| 393 } else if ((type[0] == 'S') && | |
| 394 ((type[1] >= '0') && (type[1] <= '9'))) { | |
| 395 load_srec_image(getc, base); | |
| 396 } else { | |
| 397 printf("Unrecognized image type: %lx\n", *(unsigned long *)type); | |
| 398 } | |
| 399 } | |
| 400 #ifdef CYGPKG_REDBOOT_NETWORKING | |
| 401 if (mode == MODE_TFTP) { | |
| 402 tftp_stream_close(&err); | |
| 403 } | |
| 404 #endif | |
| 405 return; | |
| 406 } |
