#include "realmode.h" struct segheader { unsigned char length; unsigned char vendortag; unsigned char reserved; unsigned char flags; unsigned long loadaddr; unsigned long imglength; unsigned long memlength; }; struct imgheader { unsigned long magic; unsigned long length; /* and flags */ union { segoff_t segoff; unsigned long location; } u; unsigned long execaddr; }; /* Keep all context about loaded image in one place */ static struct tagged_context { struct imgheader img; /* copy of header */ unsigned long linlocation; /* addr of header */ unsigned long last0, last1; /* of prev segment */ unsigned long segaddr, seglen; /* of current segment */ unsigned char segflags; unsigned char first; unsigned long curaddr; } tctx; #define TAGGED_PROGRAM_RETURNS (tctx.img.length & 0x00000100) /* bit 8 */ #define LINEAR_EXEC_ADDR (tctx.img.length & 0x80000000) /* bit 31 */ static sector_t tagged_download(unsigned char *data, unsigned int len, int eof); void xstart16 (unsigned long execaddr, segoff_t location, void *bootp); static inline os_download_t tagged_probe(unsigned char *data, unsigned int len) { struct segheader *sh; unsigned long loc; if (*((uint32_t *)data) != 0x1B031336L) { return 0; } printf("(NBI)"); /* If we don't have enough data give up */ if (len < 512) return dead_download; /* Zero all context info */ memset(&tctx, 0, sizeof(tctx)); /* Copy first 4 longwords */ memcpy(&tctx.img, data, sizeof(tctx.img)); /* Memory location where we are supposed to save it */ tctx.segaddr = tctx.linlocation = ((tctx.img.u.segoff.segment) << 4) + tctx.img.u.segoff.offset; if (!prep_segment(tctx.segaddr, tctx.segaddr + 512, tctx.segaddr + 512, 0, 512)) { return dead_download; } /* Now verify the segments we are about to load */ loc = 512; for(sh = (struct segheader *)(data + ((tctx.img.length & 0x0F) << 2) + ((tctx.img.length & 0xF0) >> 2) ); (sh->length > 0) && ((unsigned char *)sh < data + 512); sh = (struct segheader *)((unsigned char *)sh + ((sh->length & 0x0f) << 2) + ((sh->length & 0xf0) >> 2)) ) { if (!prep_segment( sh->loadaddr, sh->loadaddr + sh->imglength, sh->loadaddr + sh->imglength, loc, loc + sh->imglength)) { return dead_download; } loc = loc + sh->imglength; if (sh->flags & 0x04) break; } if (!(sh->flags & 0x04)) return dead_download; /* Grab a copy */ memcpy(phys_to_virt(tctx.segaddr), data, 512); /* Advance to first segment descriptor */ tctx.segaddr += ((tctx.img.length & 0x0F) << 2) + ((tctx.img.length & 0xF0) >> 2); /* Remember to skip the first 512 data bytes */ tctx.first = 1; return tagged_download; } static sector_t tagged_download(unsigned char *data, unsigned int len, int eof) { int i; if (tctx.first) { tctx.first = 0; if (len > 512) { len -= 512; data += 512; /* and fall through to deal with rest of block */ } else return 0; } for (;;) { if (len == 0) /* Detect truncated files */ eof = 0; while (tctx.seglen == 0) { struct segheader sh; if (tctx.segflags & 0x04) { done(1); if (LINEAR_EXEC_ADDR) { int result; /* no gateA20_unset for PM call */ result = xstart32(tctx.img.execaddr, virt_to_phys(&loaderinfo), tctx.linlocation, virt_to_phys(BOOTP_DATA_ADDR)); printf("Secondary program returned %d\n", result); if (!TAGGED_PROGRAM_RETURNS) { /* We shouldn't have returned */ result = -2; } if (result == 0) result = -2; longjmp(restart_etherboot, result); } else { gateA20_unset(); xstart16(tctx.img.execaddr, tctx.img.u.segoff, BOOTP_DATA_ADDR); longjmp(restart_etherboot, -2); } } sh = *((struct segheader *)phys_to_virt(tctx.segaddr)); tctx.seglen = sh.imglength; if ((tctx.segflags = sh.flags & 0x03) == 0) tctx.curaddr = sh.loadaddr; else if (tctx.segflags == 0x01) tctx.curaddr = tctx.last1 + sh.loadaddr; else if (tctx.segflags == 0x02) tctx.curaddr = (Address)(meminfo.memsize * 1024L + 0x100000L) - sh.loadaddr; else tctx.curaddr = tctx.last0 - sh.loadaddr; tctx.last1 = (tctx.last0 = tctx.curaddr) + sh.memlength; tctx.segflags = sh.flags; tctx.segaddr += ((sh.length & 0x0F) << 2) + ((sh.length & 0xF0) >> 2); /* Avoid lock-up */ if ( sh.length == 0 ) longjmp(restart_etherboot, -2); } if ((len <= 0) && !eof) break; i = (tctx.seglen > len) ? len : tctx.seglen; memcpy(phys_to_virt(tctx.curaddr), data, i); tctx.seglen -= i; tctx.curaddr += i; len -= i; data += i; } return 0; } void xstart16 (unsigned long execaddr, segoff_t location, void *bootp) { struct { segoff_t execaddr; segoff_t location; segoff_t bootp; } PACKED in_stack; /* AFAICT, execaddr is actually already a segment:offset */ *((unsigned long *)&in_stack.execaddr) = execaddr; in_stack.location = location; in_stack.bootp.segment = SEGMENT(bootp); in_stack.bootp.offset = OFFSET(bootp); RM_FRAGMENT(rm_xstart16, "popl %eax\n\t" /* Calculated lcall */ "pushw %cs\n\t" "call 1f\n1:\tpopw %bp\n\t" "leaw (2f-1b)(%bp), %bx\n\t" "pushw %bx\n\t" "pushl %eax\n\t" "lret\n2:\n\t" ); real_call ( rm_xstart16, &in_stack, NULL ); }