diff packages/redboot/current/src/load.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents ea3f0bd33a73
children d2c90368aeef
line wrap: on
line diff
--- a/packages/redboot/current/src/load.c
+++ b/packages/redboot/current/src/load.c
@@ -5,29 +5,38 @@
 //      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, 2001 Red Hat, Inc.                             
-// All Rights Reserved.                                                     
-// -------------------------------------------                              
-//                                                                          
-//####COPYRIGHTEND####
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// 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
+// 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
+// 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// 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
+// 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
+// License. However the source code for this file must still be made available
+// 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 file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
 //==========================================================================
 //#####DESCRIPTIONBEGIN####
 //
@@ -45,13 +54,25 @@
 
 #include <redboot.h>
 #include <xyzModem.h>
+#include <elf.h>
 #ifdef CYGPKG_REDBOOT_DISK
 #include <fs/disk.h>
 #endif
 
-
-// Buffer used by redboot_getc
-getc_info_t getc_info;
+// Buffers, data used by redboot_getc
+#define BUF_SIZE 256
+struct {
+    int (*fun)(char *, int len, int *err);
+    unsigned char  buf[BUF_SIZE];
+    unsigned char *bufp;
+    int   avail, len, err;
+    int   verbose, decompress, tick;
+#ifdef CYGPKG_COMPRESS_ZLIB
+    int (*raw_fun)(char *, int len, int *err);
+    _pipe_t load_pipe;
+    unsigned char _buffer[CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER];
+#endif
+} getc_info;
 
 static char usage[] = "[-r] [-v] "
 #ifdef CYGPKG_COMPRESS_ZLIB
@@ -73,13 +94,152 @@ RedBoot_cmd("load",
             do_load 
     );
 
+#ifdef CYGSEM_REDBOOT_ELF
+static int
+_read(int (*getc)(void), unsigned char *buf, int len)
+{
+    int total = 0;
+    int ch;
+    while (len-- > 0) {
+        ch = (*getc)();
+        if (ch < 0) {
+            // EOF or error
+            break;
+        }
+        *buf++ = ch;
+        total++;
+    }
+    return total;
+}
+#endif
+
 static unsigned long
-load_elf_image(int (*getc)(void))
+load_elf_image(getc_t getc, terminate_t terminate, unsigned long base)
 {
-    diag_printf("ELF images not supported\n");
+#ifdef CYGSEM_REDBOOT_ELF
+    Elf32_Ehdr ehdr;
+#define MAX_PHDR 8
+    Elf32_Phdr phdr[MAX_PHDR];
+    unsigned long offset = 0;
+    int phx, len, ch;
+    unsigned char *addr;
+    bool first_addr = true;
+    unsigned long addr_offset = 0;
+    unsigned long highest_address = 0;
+    unsigned long lowest_address = 0xFFFFFFFF;
+    unsigned char *SHORT_DATA = "Short data reading ELF file";
+
+    // Read the header
+    if (_read(getc, (unsigned char *)&ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
+        diag_printf("Can't read ELF header\n");
+        return 0;
+    }
+    offset += sizeof(ehdr);    
+#if 0 // DEBUG
+    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_phoff, ehdr.e_phentsize, ehdr.e_phnum,
+                ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shnum);
+#endif
+    if (ehdr.e_type != ET_EXEC) {
+        diag_printf("Only absolute ELF images supported\n");
+        return 0;
+    }
+    if (ehdr.e_phnum > MAX_PHDR) {
+        diag_printf("Too many program headers\n");
+        return 0;
+    }
+    while (offset < ehdr.e_phoff) {
+        if ((*getc)() < 0) {
+            diag_printf(SHORT_DATA);
+            return 0;
+        }
+        offset++;
+    }
+    for (phx = 0;  phx < ehdr.e_phnum;  phx++) {
+        if (_read(getc, (unsigned char *)&phdr[phx], sizeof(phdr[0])) != sizeof(phdr[0])) {
+            diag_printf("Can't read ELF program header\n");
+            return 0;
+        }
+#if 0 // DEBUG
+        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_filesz, phdr[phx].p_memsz, phdr[phx].p_flags);
+#endif
+        offset += sizeof(phdr[0]);
+    }
+    for (phx = 0;  phx < ehdr.e_phnum;  phx++) {
+        if ((phdr[phx].p_type == PT_LOAD) && (phdr[phx].p_offset != 0)) {
+            // Loadable segment
+            if (offset > phdr[phx].p_offset) {
+                diag_printf("Can't load ELF file - program headers out of order\n");
+                return 0;
+            }
+            while (offset < phdr[phx].p_offset) {
+                if ((*getc)() < 0) {
+                    diag_printf(SHORT_DATA);
+                    return 0;
+                }
+                offset++;
+            }
+            // Copy data into memory
+            addr = (unsigned char *)phdr[phx].p_paddr;
+            len = phdr[phx].p_filesz;
+            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);
+            }
+            while (len-- > 0) {
+#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
+                if ((addr < user_ram_start) || (addr > user_ram_end)) {
+                    // Only if there is no need to stop the download before printing
+                    // output can we ask confirmation questions.
+                    if (terminate) {
+                        (*terminate)(xyzModem_abort, getc);
+                        diag_printf("*** Warning! Attempt to load ELF data to address: %p\nRedBoot does not believe this is in RAM\nUse TFTP for a chance to override this.\n",(void*)addr);
+                    } else {
+                        if (!verify_action("Attempt to load ELF data to address: %p\n"
+                                           "RedBoot does not believe this is in RAM", (void*)addr))
+                            return 0;
+                    }
+                }
+#endif
+                if ((ch = (*getc)()) < 0) {
+                    diag_printf(SHORT_DATA);
+                    return 0;
+                }
+                *addr++ = ch;
+                offset++;
+                if ((unsigned long)(addr-addr_offset) > highest_address) {
+                    highest_address = (unsigned long)(addr - addr_offset);
+                }
+            }
+        }
+    }
+    // Save load base/top and entry
+    load_address = lowest_address;
+    load_address_end = highest_address;
+    entry_address = ehdr.e_entry;
+
+    if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
+    diag_printf("Entry point: %p, address range: %p-%p\n", 
+                (void*)ehdr.e_entry, (void *)lowest_address, (void *)highest_address);
+    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
 //
@@ -118,11 +278,9 @@ static long
 //
 #define MAX_LINE 80
 static unsigned long
-load_srec_image(int (*getc)(void), void (*terminate)(int method,int (*getc)(void)), unsigned long base)
+load_srec_image(getc_t getc, terminate_t terminate, unsigned long base)
 {
-    char line[MAX_LINE];
-    char *lp;
-    int  c, len;
+    int  c;
     long offset = 0, count, sum, val, cksum;
     unsigned char *addr, *base_addr;
     char type;
@@ -132,7 +290,6 @@ load_srec_image(int (*getc)(void), void 
     unsigned long lowest_address = 0xFFFFFFFF;
 
     while ((c = (*getc)()) > 0) {
-        lp = line;  len = 0;
         // Start of line
         if (c != 'S') {
 	    if (terminate) (*terminate)(xyzModem_abort, getc);
@@ -208,6 +365,7 @@ load_srec_image(int (*getc)(void), void 
         case '9':
             addr = (unsigned char *)_hex2(getc, ('9'-type+2), &sum);
             offset += ('9'-type+2);
+            // Save entry address
             entry_address = (unsigned long)addr;
 	    if (terminate) (*terminate)(xyzModem_close, getc);
             if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
@@ -230,7 +388,9 @@ load_srec_image(int (*getc)(void), void 
     return 0;
 }
 
-
+//
+// Stream I/O support
+//
 int
 redboot_getc(void)
 {
@@ -261,14 +421,66 @@ redboot_getc(void)
     return *getc_info.bufp++;
 }
 
+#ifdef CYGPKG_COMPRESS_ZLIB
+// Called to fetch a new chunk of data and decompress it
+int 
+_decompress_stream(char *buf, int len, int *err)
+{
+    _pipe_t* p = &getc_info.load_pipe;
+    int res, total;
+
+    total = 0;
+    while (len > 0) {
+        if (p->in_avail == 0) {
+            p->in_buf = &getc_info._buffer[0];
+            res = (*getc_info.raw_fun)(p->in_buf, CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER, 
+                                       &getc_info.err);
+            if ((p->in_avail = res) <= 0) {
+                // No more data
+                return total;
+            }
+        }
+        p->out_buf = buf;
+        p->out_size = 0;
+        p->out_max = len;
+        res = (*_dc_inflate)(p);
+        if (res != 0) {
+            *err = res;
+            return total;
+        }        
+        len -= p->out_size;
+        buf += p->out_size;
+        total += p->out_size;
+    }
+    return total;
+}
+#endif
+
 void
-redboot_getc_init(int (*fun)(char *, int, int *), int verbose)
+redboot_getc_init(int (*fun)(char *, int, int *), 
+                  int verbose, int decompress)
 {
     getc_info.avail = 0;
     getc_info.len = BUF_SIZE;
     getc_info.fun = fun;
     getc_info.verbose = verbose;
+    getc_info.decompress = decompress;
     getc_info.tick = 0;
+#ifdef CYGPKG_COMPRESS_ZLIB
+    if (decompress) {
+        _pipe_t* p = &getc_info.load_pipe;
+        int err;
+        p->out_buf = &getc_info.buf[0];
+        p->out_size = 0;
+        p->in_avail = 0;
+        getc_info.fun = _decompress_stream;
+        getc_info.raw_fun = fun;
+        err = (*_dc_init)(p);
+        if (0 != err && p->msg) {
+            diag_printf("open decompression error: %s\n", p->msg);
+        }
+    }
+#endif
 }
 
 void
@@ -278,6 +490,21 @@ redboot_getc_rewind(void)
     getc_info.avail = getc_info.len;
 }
 
+void
+redboot_getc_close(void)
+{
+#ifdef CYGPKG_COMPRESS_ZLIB
+    if (getc_info.decompress) {
+        _pipe_t* p = &getc_info.load_pipe;
+        int err = getc_info.err;
+        if (0 != err && p->msg) {
+            diag_printf("decompression error: %s\n", p->msg);
+        }
+        err = (*_dc_close)(p, getc_info.err);
+    }
+#endif
+}
+
 #define MODE_TFTP   0
 #define MODE_XMODEM xyzModem_xmodem  // 1
 #define MODE_YMODEM xyzModem_ymodem  // 2
@@ -297,9 +524,7 @@ do_load(int argc, char *argv[])
     bool hostname_set;
     char *hostname;
 #endif
-#ifdef CYGPKG_COMPRESS_ZLIB
-    bool decompress;
-#endif
+    bool decompress = false;
     int chan = -1;
 #if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
     bool chan_set;
@@ -309,6 +534,7 @@ do_load(int argc, char *argv[])
     char type[4];
     char *filename = 0;
     struct option_info opts[7];
+    terminate_t terminate = NULL;
 
 #ifdef CYGPKG_REDBOOT_NETWORKING
     memset((char *)&host, 0, sizeof(host));
@@ -348,8 +574,8 @@ do_load(int argc, char *argv[])
     num_options++;
 #endif
 
-    if (!scan_opts(argc, argv, 1, opts, num_options, (void *)&filename, OPTION_ARG_TYPE_STR, "file name"))
-    {
+    if (!scan_opts(argc, argv, 1, opts, num_options, 
+                   (void *)&filename, OPTION_ARG_TYPE_STR, "file name")) {
         return;
     }
 #ifdef CYGPKG_REDBOOT_NETWORKING
@@ -443,17 +669,18 @@ do_load(int argc, char *argv[])
             diag_printf("Can't load '%s': %s\n", filename, tftp_error(err));
             return;
         }
-        redboot_getc_init(tftp_stream_read, verbose);
+        redboot_getc_init(tftp_stream_read, verbose, decompress);
     }
 #endif
 #ifdef CYGPKG_REDBOOT_DISK
     else if (mode == MODE_DISK) {
         res = disk_stream_open(filename, &err);
         if (res < 0) {
+
             diag_printf("Can't load '%s': %s\n", filename, disk_error(err));
             return;
         }
-        redboot_getc_init(disk_stream_read, verbose);
+        redboot_getc_init(disk_stream_read, verbose, decompress);
     }
 #endif
     else {
@@ -463,59 +690,49 @@ do_load(int argc, char *argv[])
             return;
         }
         // Suppress verbosity when using xyz modem download
-        redboot_getc_init(xyzModem_stream_read, 0 && verbose);
+        redboot_getc_init(xyzModem_stream_read, 0 && verbose, decompress);
+
+        terminate = xyzModem_stream_terminate;
     }
-    if (raw) {
-#ifdef CYGPKG_COMPRESS_ZLIB
-        if (decompress) {
-            _pipe_t load_pipe;
-            _pipe_t* p = &load_pipe;
-            unsigned char _buffer[CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER];
 
-            p->out_buf = (unsigned char*) base;
-            p->out_size = 0;
-            p->in_buf = _buffer;
-                
-            err = (*_dc_init)(p);
-
-            while (0 == err) {
-                p->in_avail = 0;
-                for (i = 0; i < CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER; i++) {
-                    res = redboot_getc();
-                    if (res < 0) break;
-                    p->in_buf[p->in_avail++] = res;
+    if (raw) {
+        bool continue_load = false;
+        unsigned char *mp = (unsigned char *)base;
+        err = 0;
+        while ((res = redboot_getc()) >= 0) {
+#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
+            if (!continue_load && mp >= user_ram_end) {
+                // Only if there is no need to stop the download
+                // before printing output can we ask confirmation
+                // questions.
+                if (terminate) {
+                    (*terminate)(xyzModem_abort, redboot_getc);
+                    diag_printf("*** Warning! RAW data spills over limit of user RAM at %p\nRedBoot does not believe this is in RAM\nUse TFTP for a chance to override this.\n",(void*)mp);
+                    err = -1;
+                    break;
+                } else {
+                    if (!verify_action("RAW data spills over limit of user RAM at %p\n"
+                                       "Continuing may cause a hang or crash", (void*)mp)) {
+                        err = -1;
+                        break;
+                    }
+                    // Don't ask again
+                    continue_load = true;
                 }
-                if (0 == p->in_avail) break;
-
-                err = (*_dc_inflate)(p);
             }
-
-            // Free used resources, do final translation of
-            // error value.
-            err = (*_dc_close)(p, err);
-
-            if (0 != err && p->msg) {
-                diag_printf("decompression error: %s\n", p->msg);
-            }
-
-            end = (unsigned long) base + p->out_size;
-        } else // dangling block
 #endif
-        {
-            unsigned char *mp = (unsigned char *)base;
-            while ((res = redboot_getc()) >= 0) {
-                *mp++ = res;
-            }
-            err = 0;
-            end = (unsigned long) mp;
+            *mp++ = res;
         }
+        end = (unsigned long) mp;
 
         // Save load base/top
         load_address = base;
         load_address_end = end;
+        entry_address = base;           // best guess
 
         if (0 == err)
-            diag_printf("Raw file loaded %p-%p\n", (void *)base, (void *)end);
+            diag_printf("Raw file loaded %p-%p, assumed entry at %p\n", 
+                        (void *)base, (void *)end, (void*)base);
     } else {
         // Read initial header - to determine file [image] type
         for (i = 0;  i < sizeof(type);  i++) {
@@ -529,19 +746,10 @@ do_load(int argc, char *argv[])
             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);
+                end = load_elf_image(redboot_getc, terminate, base);
             } else if ((type[0] == 'S') &&
                        ((type[1] >= '0') && (type[1] <= '9'))) {
-		switch (mode) {
-		  case MODE_XMODEM:
-		  case MODE_YMODEM:
-		  case MODE_ZMODEM:
-                    end = load_srec_image(redboot_getc, xyzModem_stream_terminate, base);
-		    break;
-		  default:
-                    end = load_srec_image(redboot_getc, NULL, base);
-		    break;
-		}
+		end = load_srec_image(redboot_getc, terminate, base);
             } else {
                 diag_printf("Unrecognized image type: 0x%lx\n", *(unsigned long *)type);
             }
@@ -563,5 +771,6 @@ do_load(int argc, char *argv[])
         xyzModem_stream_close(&err);
 	break;
     }
+    redboot_getc_close();  // Clean up
     return;
 }