/************************************************************************** OS loader Author: Markus Gutschke (gutschk@math.uni-muenster.de) Date: Sep/95 Modifications: Ken Yap (for Etherboot/16) Doug Ambrisko (ELF and a.out support) Klaus Espenlaub (rewrote ELF and a.out (did it really work before?) support, added ELF Multiboot images). Someone should merge the ELF and a.out loaders, as most of the code is now identical. Maybe even NBI could be rewritten and merged into the generic loading framework. This should save quite a few bytes of code if you have selected more than one format. Ken Yap (Jan 2001) Added support for linear entry addresses in tagged images, which allows a more efficient protected mode call instead of going to real mode and back. Also means entry addresses > 1 MB can be called. Conditional on the LINEAR_EXEC_ADDR bit. Added support for Etherboot extension calls. Conditional on the TAGGED_PROGRAM_RETURNS bit. Implies LINEAR_EXEC_ADDR. Added support for non-MULTIBOOT ELF which also supports Etherboot extension calls. Conditional on the ELF_PROGRAM_RETURNS bit. **************************************************************************/ /* * This program 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. */ #include "etherboot.h" #include "memsizes.h" #ifdef KEEP_IT_REAL #warning "All download mechanisms are broken under KEEP_IT_REAL" os_download_t probe_image(unsigned char *data, unsigned int len) { return 0; } int load_block(unsigned char *data, unsigned int block, unsigned int len, int eof) { return 1; } #else /* KEEP_IT_REAL */ struct os_entry_regs os_regs; static struct ebinfo loaderinfo = { VERSION_MAJOR, VERSION_MINOR, 0 }; #define LOAD_DEBUG 0 static int prep_segment(unsigned long start, unsigned long mid, unsigned long end, unsigned long istart, unsigned long iend); static unsigned long find_segment(unsigned long size, unsigned long align); static sector_t dead_download ( unsigned char *data, unsigned int len, int eof); static void done(int do_cleanup); #if defined(IMAGE_FREEBSD) && defined(ELF_IMAGE) static void elf_freebsd_probe(void); static void elf_freebsd_fixup_segment(void); static void elf_freebsd_find_segment_end(void); static int elf_freebsd_debug_loader(unsigned int offset); static void elf_freebsd_boot(unsigned long entry); #else #define elf_freebsd_probe() do {} while(0) #define elf_freebsd_fixup_segment() do {} while(0) #define elf_freebsd_find_segment_end() do {} while(0) #define elf_freebsd_debug_loader(off) (0) #define elf_freebsd_boot(entry) do {} while(0) #endif #if defined(IMAGE_FREEBSD) && defined(AOUT_IMAGE) static void aout_freebsd_probe(void); static void aout_freebsd_boot(void); #else #define aout_freebsd_probe() do {} while(0) #define aout_freebsd_boot() do {} while(0) #endif /************************************************************************** dead_download - Restart etherboot if probe image fails **************************************************************************/ static sector_t dead_download ( unsigned char *data __unused, unsigned int len __unused, int eof __unused) { longjmp(restart_etherboot, -2); } #ifdef IMAGE_MULTIBOOT #include "../arch/i386/core/multiboot_loader.c" #else #define multiboot_probe(data, len) do {} while(0) #define multiboot_boot(entry) do {} while(0) #endif #ifdef WINCE_IMAGE #include "../arch/i386/core/wince_loader.c" #endif #ifdef AOUT_IMAGE #include "../arch/i386/core/aout_loader.c" #endif #ifdef TAGGED_IMAGE #include "../arch/i386/core/tagged_loader.c" #endif #if defined(ELF_IMAGE) || defined(ELF64_IMAGE) #include "elf_loader.c" #endif #if defined(COFF_IMAGE) #include "../arch/e1/core/coff_loader.c" #endif #ifdef IMAGE_FREEBSD #include "../arch/i386/core/freebsd_loader.c" #endif #ifdef PXE_IMAGE #include "../arch/i386/core/pxe_loader.c" #endif #ifdef RAW_IMAGE #include "../arch/armnommu/core/raw_loader.c" #endif static void done(int do_cleanup) { #ifdef SIZEINDICATOR printf("K "); #endif printf("done\n"); /* We may not want to do the cleanup: when booting a PXE * image, for example, we need to leave the network card * enabled, and it helps debugging if the serial console * remains enabled. The call the cleanup() will be triggered * when the PXE stack is shut down. */ if ( do_cleanup ) { cleanup(); /* arch_on_exit(0); */ } } static int prep_segment(unsigned long start, unsigned long mid, unsigned long end, unsigned long istart __unused, unsigned long iend __unused) { unsigned fit, i; #if LOAD_DEBUG printf ( "\nAbout to prepare segment [%lX,%lX)\n", start, end ); sleep ( 3 ); #endif if (mid > end) { printf("filesz > memsz\n"); return 0; } if ((end > virt_to_phys(_text)) && (start < virt_to_phys(_end))) { printf("segment [%lX, %lX) overlaps etherboot [%lX, %lX)\n", start, end, virt_to_phys(_text), virt_to_phys(_end) ); return 0; } if ((end > heap_ptr) && (start < heap_bot)) { printf("segment [%lX, %lX) overlaps heap [%lX, %lX)\n", start, end, heap_ptr, heap_bot ); return 0; } fit = 0; for(i = 0; i < meminfo.map_count; i++) { unsigned long long r_start, r_end; if (meminfo.map[i].type != E820_RAM) continue; r_start = meminfo.map[i].addr; r_end = r_start + meminfo.map[i].size; if ((start >= r_start) && (end <= r_end)) { fit = 1; break; } } if (!fit) { printf("\nsegment [%lX,%lX) does not fit in any memory region\n", start, end); #if LOAD_DEBUG printf("Memory regions(%d):\n", meminfo.map_count); for(i = 0; i < meminfo.map_count; i++) { unsigned long long r_start, r_end; if (meminfo.map[i].type != E820_RAM) continue; r_start = meminfo.map[i].addr; r_end = r_start + meminfo.map[i].size; printf("[%X%X, %X%X) type %d\n", (unsigned long)(r_start >> 32), (unsigned long)r_start, (unsigned long)(r_end >> 32), (unsigned long)r_end, meminfo.map[i].type); } #endif return 0; } #if LOAD_DEBUG /* Zap the whole lot. Do this so that if we're treading on * anything, it shows up now, when the debug message is * visible, rather than when we're partway through downloading * the file. * * If you see an entire screen full of exclamation marks, then * you've almost certainly written all over the display RAM. * This is likely to happen if the status of the A20 line gets * screwed up. Of course, if this happens, it's a good bet * that you've also trashed the whole of low memory, so expect * interesting things to happen... */ memset(phys_to_virt(start), '!', mid - start); #endif /* Zero the bss */ if (end > mid) { memset(phys_to_virt(mid), 0, end - mid); } return 1; } static unsigned long find_segment(unsigned long size, unsigned long align) { unsigned i; /* Verify I have a power of 2 alignment */ if (align & (align - 1)) { return ULONG_MAX; } for(i = 0; i < meminfo.map_count; i++) { unsigned long r_start, r_end; if (meminfo.map[i].type != E820_RAM) continue; if ((meminfo.map[i].addr + meminfo.map[i].size) > ULONG_MAX) { continue; } r_start = meminfo.map[i].addr; r_end = r_start + meminfo.map[i].size; /* Don't allow the segment to overlap etherboot */ if ((r_end > virt_to_phys(_text)) && (r_start < virt_to_phys(_text))) { r_end = virt_to_phys(_text); } if ((r_start > virt_to_phys(_text)) && (r_start < virt_to_phys(_end))) { r_start = virt_to_phys(_end); } /* Don't allow the segment to overlap the heap */ if ((r_end > heap_ptr) && (r_start < heap_ptr)) { r_end = heap_ptr; } if ((r_start > heap_ptr) && (r_start < heap_bot)) { r_start = heap_ptr; } r_start = (r_start + align - 1) & ~(align - 1); if ((r_end >= r_start) && ((r_end - r_start) >= size)) { return r_start; } } /* I did not find anything :( */ return ULONG_MAX; } /************************************************************************** PROBE_IMAGE - Detect image file type **************************************************************************/ os_download_t probe_image(unsigned char *data, unsigned int len) { os_download_t os_download = 0; #ifdef AOUT_IMAGE if (!os_download) os_download = aout_probe(data, len); #endif #ifdef ELF_IMAGE if (!os_download) os_download = elf32_probe(data, len); #endif #ifdef ELF64_IMAGE if (!os_download) os_download = elf64_probe(data, len); #endif #ifdef COFF_IMAGE if (!os_download) os_download = coff_probe(data, len); #endif #ifdef WINCE_IMAGE if (!os_download) os_download = wince_probe(data, len); #endif #ifdef TAGGED_IMAGE if (!os_download) os_download = tagged_probe(data, len); #endif /* PXE_IMAGE must always be last */ #ifdef PXE_IMAGE if (!os_download) os_download = pxe_probe(data, len); #endif #ifdef RAW_IMAGE if (!os_download) os_download = raw_probe(data, len); #endif return os_download; } /************************************************************************** LOAD_BLOCK - Try to load file **************************************************************************/ int load_block(unsigned char *data, unsigned int block, unsigned int len, int eof) { static os_download_t os_download; static sector_t skip_sectors; static unsigned int skip_bytes; #ifdef SIZEINDICATOR static int rlen = 0; if (block == 1) { rlen=len; printf("XXXX"); } if (!(block % 4) || eof) { int size; size = ((block-1) * rlen + len) / 1024; putchar('\b'); putchar('\b'); putchar('\b'); putchar('\b'); putchar('0' + (size/1000)%10); putchar('0' + (size/100)%10); putchar('0' + (size/10)%10); putchar('0' + (size/1)%10); } #endif if (block == 1) { skip_sectors = 0; skip_bytes = 0; os_download = probe_image(data, len); if (!os_download) { printf("error: not a valid image\n"); #if 0 printf("block: %d len: %d\n", block, len); printf("%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); #endif return 0; } } /* end of block zero processing */ /* Either len is greater or the skip is greater */ if ((skip_sectors > (len >> 9)) || ((skip_sectors == (len >> 9)) && (skip_bytes >= (len & 0x1ff)))) { /* If I don't have enough bytes borrow them from skip_sectors */ if (skip_bytes < len) { skip_sectors -= (len - skip_bytes + 511) >> 9; skip_bytes += (len - skip_bytes + 511) & ~0x1ff; } skip_bytes -= len; } else { len -= (skip_sectors << 9) + skip_bytes; data += (skip_sectors << 9) + skip_bytes; } skip_sectors = os_download(data, len, eof); skip_bytes = 0; return 1; } /* * Local variables: * c-basic-offset: 8 * End: */ #endif /* KEEP_IT_REAL */