| /* a.out */ |
| struct exec { |
| unsigned long a_midmag; /* flags<<26 | mid<<16 | magic */ |
| unsigned long a_text; /* text segment size */ |
| unsigned long a_data; /* initialized data size */ |
| unsigned long a_bss; /* uninitialized data size */ |
| unsigned long a_syms; /* symbol table size */ |
| unsigned long a_entry; /* entry point */ |
| unsigned long a_trsize; /* text relocation size */ |
| unsigned long a_drsize; /* data relocation size */ |
| }; |
| |
| struct aout_state { |
| struct exec head; |
| unsigned long curaddr; |
| int segment; /* current segment number, -1 for none */ |
| unsigned long loc; /* start offset of current block */ |
| unsigned long skip; /* padding to be skipped to current segment */ |
| unsigned long toread; /* remaining data to be read in the segment */ |
| }; |
| |
| static struct aout_state astate; |
| |
| static sector_t aout_download(unsigned char *data, unsigned int len, int eof); |
| static inline os_download_t aout_probe(unsigned char *data, unsigned int len) |
| { |
| unsigned long start, mid, end, istart, iend; |
| if (len < sizeof(astate.head)) { |
| return 0; |
| } |
| memcpy(&astate.head, data, sizeof(astate.head)); |
| if ((astate.head.a_midmag & 0xffff) != 0x010BL) { |
| return 0; |
| } |
| |
| printf("(a.out"); |
| aout_freebsd_probe(); |
| printf(")... "); |
| /* Check the aout image */ |
| start = astate.head.a_entry; |
| mid = (((start + astate.head.a_text) + 4095) & ~4095) + astate.head.a_data; |
| end = ((mid + 4095) & ~4095) + astate.head.a_bss; |
| istart = 4096; |
| iend = istart + (mid - start); |
| if (!prep_segment(start, mid, end, istart, iend)) |
| return dead_download; |
| astate.segment = -1; |
| astate.loc = 0; |
| astate.skip = 0; |
| astate.toread = 0; |
| return aout_download; |
| } |
| |
| static sector_t aout_download(unsigned char *data, unsigned int len, int eof) |
| { |
| unsigned int offset; /* working offset in the current data block */ |
| |
| offset = 0; |
| |
| #ifdef AOUT_LYNX_KDI |
| astate.segment++; |
| if (astate.segment == 0) { |
| astate.curaddr = 0x100000; |
| astate.head.a_entry = astate.curaddr + 0x20; |
| } |
| memcpy(phys_to_virt(astate.curaddr), data, len); |
| astate.curaddr += len; |
| return 0; |
| #endif |
| |
| do { |
| if (astate.segment != -1) { |
| if (astate.skip) { |
| if (astate.skip >= len - offset) { |
| astate.skip -= len - offset; |
| break; |
| } |
| offset += astate.skip; |
| astate.skip = 0; |
| } |
| |
| if (astate.toread) { |
| if (astate.toread >= len - offset) { |
| memcpy(phys_to_virt(astate.curaddr), data+offset, |
| len - offset); |
| astate.curaddr += len - offset; |
| astate.toread -= len - offset; |
| break; |
| } |
| memcpy(phys_to_virt(astate.curaddr), data+offset, astate.toread); |
| offset += astate.toread; |
| astate.toread = 0; |
| } |
| } |
| |
| /* Data left, but current segment finished - look for the next |
| * segment. This is quite simple for a.out files. */ |
| astate.segment++; |
| switch (astate.segment) { |
| case 0: |
| /* read text */ |
| astate.curaddr = astate.head.a_entry; |
| astate.skip = 4096; |
| astate.toread = astate.head.a_text; |
| break; |
| case 1: |
| /* read data */ |
| /* skip and curaddr may be wrong, but I couldn't find |
| * examples where this failed. There is no reasonable |
| * documentation for a.out available. */ |
| astate.skip = ((astate.curaddr + 4095) & ~4095) - astate.curaddr; |
| astate.curaddr = (astate.curaddr + 4095) & ~4095; |
| astate.toread = astate.head.a_data; |
| break; |
| case 2: |
| /* initialize bss and start kernel */ |
| astate.curaddr = (astate.curaddr + 4095) & ~4095; |
| astate.skip = 0; |
| astate.toread = 0; |
| memset(phys_to_virt(astate.curaddr), '\0', astate.head.a_bss); |
| goto aout_startkernel; |
| default: |
| break; |
| } |
| } while (offset < len); |
| |
| astate.loc += len; |
| |
| if (eof) { |
| unsigned long entry; |
| |
| aout_startkernel: |
| entry = astate.head.a_entry; |
| done(1); |
| |
| aout_freebsd_boot(); |
| #ifdef AOUT_LYNX_KDI |
| xstart32(entry); |
| #endif |
| printf("unexpected a.out variant\n"); |
| longjmp(restart_etherboot, -2); |
| } |
| return 0; |
| } |