summaryrefslogtreecommitdiffstats
path: root/src/arch/e1/core/coff_loader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/e1/core/coff_loader.c')
-rw-r--r--src/arch/e1/core/coff_loader.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/arch/e1/core/coff_loader.c b/src/arch/e1/core/coff_loader.c
new file mode 100644
index 000000000..d628e5eda
--- /dev/null
+++ b/src/arch/e1/core/coff_loader.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2003 Yannis Mitsos and George Thanos
+ * {gmitsos@gthanos}@telecom.ntua.gr
+ * Released under GPL2, see the file COPYING in the top directory
+ * COFF loader is based on the source code of the ELF loader.
+ *
+ */
+#include "coff.h"
+
+#define COFF_DEBUG 0
+
+typedef struct {
+ COFF_filehdr coff32;
+ COFF_opthdr opthdr32;
+ union {
+ COFF_scnhdr scnhdr32[1];
+ unsigned char dummy[1024];
+ } p;
+ unsigned long curaddr;
+ signed int segment; /* current segment number, -1 for none */
+ unsigned int loc; /* start offset of current block */
+ unsigned int skip; /* padding to be skipped to current segment */
+ unsigned long toread; /* remaining data to be read in the segment */
+}coff_state;
+
+coff_state cstate;
+
+static sector_t coff32_download(unsigned char *data, unsigned int len, int eof);
+static inline os_download_t coff_probe(unsigned char *data, unsigned int len)
+{
+ unsigned long phdr_size;
+
+ if (len < (sizeof(cstate.coff32)+ sizeof(cstate.opthdr32))) {
+ return 0;
+ }
+ memcpy(&cstate.coff32, data, (sizeof(cstate.coff32)+sizeof(cstate.opthdr32)));
+
+ if ((cstate.coff32.f_magic != EM_E1) ||
+ (cstate.opthdr32.magic != O_MAGIC)){
+ return 0;
+ }
+ printf("(COFF");
+ printf(")... \n");
+
+ if (cstate.coff32.f_opthdr == 0){
+ printf("No optional header in COFF file, cannot find the entry point\n");
+ return dead_download;
+ }
+
+ phdr_size = cstate.coff32.f_nscns * sizeof(cstate.p.scnhdr32);
+ if (sizeof(cstate.coff32) + cstate.coff32.f_opthdr + phdr_size > len) {
+ printf("COFF header outside first block\n");
+ return dead_download;
+ }
+
+ memcpy(&cstate.p.scnhdr32, data + (sizeof(cstate.coff32) + cstate.coff32.f_opthdr), phdr_size);
+
+ /* Check for Etherboot related limitations. Memory
+ * between _text and _end is not allowed.
+ * Reasons: the Etherboot code/data area.
+ */
+ for (cstate.segment = 0; cstate.segment < cstate.coff32.f_nscns; cstate.segment++) {
+ unsigned long start, mid, end, istart, iend;
+
+ if ((cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_TEXT) &&
+ (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_DATA) &&
+ (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_BSS)){ /* Do we realy need to check the BSS section ? */
+#ifdef COFF_DEBUG
+ printf("Section <%s> in not a loadable section \n",cstate.p.scnhdr32[cstate.segment].s_name);
+#endif
+ continue;
+ }
+
+ start = cstate.p.scnhdr32[cstate.segment].s_paddr;
+ mid = start + cstate.p.scnhdr32[cstate.segment].s_size;
+ end = start + cstate.p.scnhdr32[cstate.segment].s_size;
+
+ /* Do we need the following variables ? */
+ istart = 0x8000;
+ iend = 0x8000;
+
+ if (!prep_segment(start, mid, end, istart, iend)) {
+ return dead_download;
+ }
+}
+ cstate.segment = -1;
+ cstate.loc = 0;
+ cstate.skip = 0;
+ cstate.toread = 0;
+ return coff32_download;
+}
+
+extern int mach_boot(unsigned long entry_point);
+static sector_t coff32_download(unsigned char *data, unsigned int len, int eof)
+{
+ unsigned long skip_sectors = 0;
+ unsigned int offset; /* working offset in the current data block */
+ int i;
+
+ offset = 0;
+ do {
+ if (cstate.segment != -1) {
+ if (cstate.skip) {
+ if (cstate.skip >= len - offset) {
+ cstate.skip -= len - offset;
+ break;
+ }
+ offset += cstate.skip;
+ cstate.skip = 0;
+ }
+
+ if (cstate.toread) {
+ unsigned int cplen;
+ cplen = len - offset;
+ if (cplen >= cstate.toread) {
+ cplen = cstate.toread;
+ }
+ memcpy(phys_to_virt(cstate.curaddr), data+offset, cplen);
+ cstate.curaddr += cplen;
+ cstate.toread -= cplen;
+ offset += cplen;
+ if (cstate.toread)
+ break;
+ }
+ }
+
+ /* Data left, but current segment finished - look for the next
+ * segment (in file offset order) that needs to be loaded.
+ * We can only seek forward, so select the program headers,
+ * in the correct order.
+ */
+ cstate.segment = -1;
+ for (i = 0; i < cstate.coff32.f_nscns; i++) {
+
+ if ((cstate.p.scnhdr32[i].s_flags != S_TYPE_TEXT) &&
+ (cstate.p.scnhdr32[i].s_flags != S_TYPE_DATA))
+ continue;
+ if (cstate.p.scnhdr32[i].s_size == 0)
+ continue;
+ if (cstate.p.scnhdr32[i].s_scnptr < cstate.loc + offset)
+ continue; /* can't go backwards */
+ if ((cstate.segment != -1) &&
+ (cstate.p.scnhdr32[i].s_scnptr >= cstate.p.scnhdr32[cstate.segment].s_scnptr))
+ continue; /* search minimum file offset */
+ cstate.segment = i;
+ }
+
+ if (cstate.segment == -1) {
+ /* No more segments to be loaded, so just start the
+ * kernel. This saves a lot of network bandwidth if
+ * debug info is in the kernel but not loaded. */
+ goto coff_startkernel;
+ break;
+ }
+ cstate.curaddr = cstate.p.scnhdr32[cstate.segment].s_paddr;
+ cstate.skip = cstate.p.scnhdr32[cstate.segment].s_scnptr - (cstate.loc + offset);
+ cstate.toread = cstate.p.scnhdr32[cstate.segment].s_size;
+#if COFF_DEBUG
+ printf("PHDR %d, size %#lX, curaddr %#lX\n",
+ cstate.segment, cstate.toread, cstate.curaddr);
+#endif
+ } while (offset < len);
+
+ cstate.loc += len + (cstate.skip & ~0x1ff);
+ skip_sectors = cstate.skip >> 9;
+ cstate.skip &= 0x1ff;
+
+ if (eof) {
+ unsigned long entry;
+coff_startkernel:
+ entry = cstate.opthdr32.entry;
+ done();
+ mach_boot(entry);
+ }
+ return skip_sectors;
+}