Esempio n. 1
0
/* Create the appropriate mappings for the initramfs */
static int map_initramfs(struct syslinux_movelist **fraglist,
			 struct syslinux_memmap **mmap,
			 struct initramfs *initramfs, addr_t addr)
{
    struct initramfs *ip;
    addr_t next_addr, len, pad;

    for (ip = initramfs->next; ip->len; ip = ip->next) {
	len = ip->len;
	next_addr = addr + len;

	/* If this isn't the last entry, extend the zero-pad region
	   to enforce the alignment of the next chunk. */
	if (ip->next->len) {
	    pad = -next_addr & (ip->next->align - 1);
	    len += pad;
	    next_addr += pad;
	}

	if (ip->data_len) {
	    if (syslinux_add_movelist(fraglist, addr, (addr_t) ip->data, len))
		return -1;
	}
	if (len > ip->data_len) {
	    if (syslinux_add_memmap(mmap, addr + ip->data_len,
				    len - ip->data_len, SMT_ZERO))
		return -1;
	}
	addr = next_addr;
    }

    return 0;
}
Esempio n. 2
0
int test_attempt_movelist(struct syslinux_memmap *mmap, addr_t dst,
			  addr_t src, size_t len)
{
    struct syslinux_movelist *frags = NULL;
    struct syslinux_movelist *moves = NULL;
    int rv;

    rv = syslinux_add_movelist(&frags, dst, src, len);
    if (rv)
	goto bail;

    rv = syslinux_compute_movelist(&moves, frags, mmap);

bail:
    syslinux_free_movelist(frags);
    syslinux_free_movelist(moves);
    return rv;
}
Esempio n. 3
0
int syslinux_shuffle_boot_pm(struct syslinux_movelist *fraglist,
			     struct syslinux_memmap *memmap,
			     uint16_t bootflags,
			     struct syslinux_pm_regs *regs)
{
  int nd;
  com32sys_t ireg;
  char *regbuf;
  uint8_t handoff_code[9*5], *p;
  const uint32_t *rp;
  int i, rv;
  struct syslinux_memmap *tmap;
  addr_t regstub, stublen, safe;

  tmap = syslinux_target_memmap(fraglist, memmap);
  if (!tmap)
    return -1;

  regstub = 0x800;		/* Locate anywhere above this point */
  stublen = sizeof handoff_code;
  rv = syslinux_memmap_find(tmap, SMT_FREE, &regstub, &stublen, 1);
  syslinux_free_memmap(tmap);
  if (rv)
    return -1;

  /* Build register-setting stub */
  p = handoff_code;
  rp = (const uint32_t *)regs;
  for (i = 0; i < 8; i++) {
    *p = 0xb8 + i;		/* MOV gpr,imm32 */
    *(uint32_t *)(p+1) = *rp++;
    p += 5;
  }
  *p = 0xe9;			/* JMP */
  *(uint32_t *)(p+1) = regs->eip - regstub - sizeof handoff_code;

  /* Add register-setting stub to shuffle list */
  if (syslinux_add_movelist(&fraglist, regstub, (addr_t)handoff_code,
			    sizeof handoff_code))
    return -1;

  return syslinux_do_shuffle(fraglist, memmap, regstub, 1, bootflags);
}
Esempio n. 4
0
/*
 * Note: although there is no such thing in the spec, at least Xen makes
 * assumptions as to where in the memory space Grub would have loaded
 * certain things.  To support that, if "high" is set, then allocate this
 * at an address strictly above any previous allocations.
 *
 * As a precaution, this also pads the data with zero up to the next
 * alignment datum.
 */
addr_t map_data(const void *data, size_t len, size_t align, int flags)
{
    addr_t start = (flags & MAP_HIGH) ? mboot_high_water_mark : 0x2000;
    addr_t pad = (flags & MAP_NOPAD) ? 0 : -len & (align - 1);
    addr_t xlen = len + pad;

    if (syslinux_memmap_find(amap, SMT_FREE, &start, &xlen, align) ||
	syslinux_add_memmap(&amap, start, len + pad, SMT_ALLOC) ||
	syslinux_add_movelist(&ml, start, (addr_t) data, len) ||
	(pad && syslinux_add_memmap(&mmap, start + len, pad, SMT_ZERO))) {
	printf("Cannot map %zu bytes\n", len + pad);
	return 0;
    }

    dprintf("Mapping 0x%08x bytes (%#x pad) at 0x%08x\n", len, pad, start);

    if (start + len + pad > mboot_high_water_mark)
	mboot_high_water_mark = start + len + pad;

    return start;
}
Esempio n. 5
0
static void do_boot(struct data_area *data, int ndata)
{
    struct syslinux_memmap *mmap;
    struct syslinux_movelist *mlist = NULL;
    addr_t endimage;
    uint8_t driveno = opt.regs.edx.b[0];
    uint8_t swapdrive = driveno & 0x80;
    int i;

    mmap = syslinux_memory_map();

    if (!mmap) {
	error("Cannot read system memory map.");
	return;
    }

    endimage = 0;
    for (i = 0; i < ndata; i++) {
	if (data[i].base + data[i].size > endimage)
	    endimage = data[i].base + data[i].size;
    }
    if (endimage > dosmax)
	goto too_big;

    for (i = 0; i < ndata; i++) {
	if (syslinux_add_movelist(&mlist, data[i].base,
				  (addr_t) data[i].data, data[i].size))
	    goto enomem;
    }

    if (opt.swap && driveno != swapdrive) {
	static const uint8_t swapstub_master[] = {
	    /* The actual swap code */
	    0x53,		/* 00: push bx */
	    0x0f, 0xb6, 0xda,	/* 01: movzx bx,dl */
	    0x2e, 0x8a, 0x57, 0x60,	/* 04: mov dl,[cs:bx+0x60] */
	    0x5b,		/* 08: pop bx */
	    0xea, 0, 0, 0, 0,	/* 09: jmp far 0:0 */
	    0x90, 0x90,		/* 0E: nop; nop */
	    /* Code to install this in the right location */
	    /* Entry with DS = CS; ES = SI = 0; CX = 256 */
	    0x26, 0x66, 0x8b, 0x7c, 0x4c,	/* 10: mov edi,[es:si+4*0x13] */
	    0x66, 0x89, 0x3e, 0x0a, 0x00,	/* 15: mov [0x0A],edi */
	    0x26, 0x8b, 0x3e, 0x13, 0x04,	/* 1A: mov di,[es:0x413] */
	    0x4f,		/* 1F: dec di */
	    0x26, 0x89, 0x3e, 0x13, 0x04,	/* 20: mov [es:0x413],di */
	    0x66, 0xc1, 0xe7, 0x16,	/* 25: shl edi,16+6 */
	    0x26, 0x66, 0x89, 0x7c, 0x4c,	/* 29: mov [es:si+4*0x13],edi */
	    0x66, 0xc1, 0xef, 0x10,	/* 2E: shr edi,16 */
	    0x8e, 0xc7,		/* 32: mov es,di */
	    0x31, 0xff,		/* 34: xor di,di */
	    0xf3, 0x66, 0xa5,	/* 36: rep movsd */
	    0xbe, 0, 0,		/* 39: mov si,0 */
	    0xbf, 0, 0,		/* 3C: mov di,0 */
	    0x8e, 0xde,		/* 3F: mov ds,si */
	    0x8e, 0xc7,		/* 41: mov es,di */
	    0x66, 0xb9, 0, 0, 0, 0,	/* 43: mov ecx,0 */
	    0x66, 0xbe, 0, 0, 0, 0,	/* 49: mov esi,0 */
	    0x66, 0xbf, 0, 0, 0, 0,	/* 4F: mov edi,0 */
	    0xea, 0, 0, 0, 0,	/* 55: jmp 0:0 */
	    /* pad out to segment boundary */
	    0x90, 0x90,		/* 5A: ... */
	    0x90, 0x90, 0x90, 0x90,	/* 5C: ... */
	};
	static uint8_t swapstub[1024];
	uint8_t *p;

	/* Note: we can't rely on either INT 13h nor the dosmax
	   vector to be correct at this stage, so we have to use an
	   installer stub to put things in the right place.
	   Round the installer location to a 1K boundary so the only
	   possible overlap is the identity mapping. */
	endimage = (endimage + 1023u) & ~1023u;

	/* Create swap stub */
	memcpy(swapstub, swapstub_master, sizeof swapstub_master);
	*(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
	*(uint16_t *) & swapstub[0x3d] = opt.regs.es;
	*(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
	*(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
	*(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
	*(uint16_t *) & swapstub[0x56] = opt.regs.ip;
	*(uint16_t *) & swapstub[0x58] = opt.regs.cs;
	p = &swapstub[sizeof swapstub_master];

	/* Mapping table; start out with identity mapping everything */
	for (i = 0; i < 256; i++)
	    p[i] = i;

	/* And the actual swap */
	p[driveno] = swapdrive;
	p[swapdrive] = driveno;

	/* Adjust registers */
	opt.regs.ds = opt.regs.cs = endimage >> 4;
	opt.regs.esi.l = opt.regs.es = 0;
	opt.regs.ecx.l = sizeof swapstub >> 2;
	opt.regs.ip = 0x10;	/* Installer offset */
	opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;

	if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
				  sizeof swapstub))
	    goto enomem;

	endimage += sizeof swapstub;
    }

    /* Tell the shuffler not to muck with this area... */
    syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);

    /* Force text mode */
    syslinux_force_text_mode();

    puts("Booting...");
    syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
    error("Chainboot failed !");
    return;

too_big:
    error("Loader file too large.");
    return;

enomem:
    error("Out of memory.");
    return;
}
Esempio n. 6
0
int boot_raw(void *ptr, size_t len, addr_t where, char **argv)
{
    struct syslinux_movelist *ml = NULL;
    struct syslinux_memmap *mmap = NULL, *amap = NULL;
    struct syslinux_pm_regs regs;
    int argc;
    addr_t argsize;
    char **argp;
    addr_t lstart, llen;
    char *stack_frame = NULL;
    addr_t stack_frame_size;
    addr_t stack_pointer;
    uint32_t *spp;
    char *sfp;
    addr_t sfa;

    memset(&regs, 0, sizeof regs);

    mmap = syslinux_memory_map();
    amap = syslinux_dup_memmap(mmap);
    if (!mmap || !amap)
	goto bail;

    dprintf("Initial memory map:\n");
    syslinux_dump_memmap(mmap);

    dprintf("Segment at 0x%08x len 0x%08x\n", where, len);

    if (syslinux_memmap_type(amap, where, len) != SMT_FREE) {
	printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
	       where, len);
	goto bail;		/* Memory region unavailable */
    }

    /* Mark this region as allocated in the available map */
    if (syslinux_add_memmap(&amap, where, len, SMT_ALLOC))
	goto bail;

    /* Data present region.  Create a move entry for it. */
    if (syslinux_add_movelist(&ml, where, (addr_t) ptr, len))
	goto bail;

    /* Create the invocation record (initial stack frame) */

    argsize = argc = 0;
    for (argp = argv; *argp; argp++) {
	dprintf("argv[%2d] = \"%s\"\n", argc, *argp);
	argc++;
	argsize += strlen(*argp) + 1;
    }

    /* We need the argument strings, argument pointers,
       argc, plus four zero-word terminators. */
    stack_frame_size = argsize + argc * sizeof(char *) + 5 * sizeof(long);
    stack_frame_size = (stack_frame_size + 15) & ~15;
    stack_frame = calloc(stack_frame_size, 1);
    if (!stack_frame)
	goto bail;

    dprintf("Right before syslinux_memmap_largest()...\n");
    syslinux_dump_memmap(amap);

    if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen))
	goto bail;		/* NO free memory?! */

    if (llen < stack_frame_size + MIN_STACK + 16)
	goto bail;		/* Insufficient memory  */

    /* Initial stack pointer address */
    stack_pointer = (lstart + llen - stack_frame_size) & ~15;

    dprintf("Stack frame at 0x%08x len 0x%08x\n",
	    stack_pointer, stack_frame_size);

    /* Create the stack frame.  sfp is the pointer in current memory for
       the next argument string, sfa is the address in its final resting place.
       spp is the pointer into the argument array in current memory. */
    spp = (uint32_t *) stack_frame;
    sfp = stack_frame + argc * sizeof(char *) + 5 * sizeof(long);
    sfa = stack_pointer + argc * sizeof(char *) + 5 * sizeof(long);

    *spp++ = argc;
    for (argp = argv; *argp; argp++) {
	int bytes = strlen(*argp) + 1;	/* Including final null */
	*spp++ = sfa;
	memcpy(sfp, *argp, bytes);
	sfp += bytes;
	sfa += bytes;
    }
    /* Zero fields are aready taken care of by calloc() */

    /* ... and we'll want to move it into the right place... */
#if DEBUG
    if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size)
	!= SMT_FREE) {
	dprintf("Stack frame area not free (how did that happen?)!\n");
	goto bail;		/* Memory region unavailable */
    }
#endif

    if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC))
	goto bail;

    if (syslinux_add_movelist(&ml, stack_pointer, (addr_t) stack_frame,
			      stack_frame_size))
	goto bail;

    memset(&regs, 0, sizeof regs);
    regs.eip = where;
    regs.esp = stack_pointer;

    dprintf("Final memory map:\n");
    syslinux_dump_memmap(mmap);

    dprintf("Final available map:\n");
    syslinux_dump_memmap(amap);

    dprintf("Movelist:\n");
    syslinux_dump_movelist(ml);

    /* This should not return... */
    fputs("Booting...\n", stdout);
    syslinux_shuffle_boot_pm(ml, mmap, 0, &regs);

bail:
    if (stack_frame)
	free(stack_frame);
    syslinux_free_memmap(amap);
    syslinux_free_memmap(mmap);
    syslinux_free_movelist(ml);

    return -1;
}
Esempio n. 7
0
int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
			struct initramfs *initramfs, char *cmdline)
{
    struct linux_header hdr, *whdr;
    size_t real_mode_size, prot_mode_size;
    addr_t real_mode_base, prot_mode_base;
    addr_t irf_size;
    size_t cmdline_size, cmdline_offset;
    struct syslinux_rm_regs regs;
    struct syslinux_movelist *fraglist = NULL;
    struct syslinux_memmap *mmap = NULL;
    struct syslinux_memmap *amap = NULL;
    bool ok;
    uint32_t memlimit = 0;
    uint16_t video_mode = 0;
    const char *arg;

    cmdline_size = strlen(cmdline) + 1;

    if (kernel_size < 2 * 512)
	goto bail;

    /* Look for specific command-line arguments we care about */
    if ((arg = find_argument(cmdline, "mem=")))
	memlimit = saturate32(suffix_number(arg));

    if ((arg = find_argument(cmdline, "vga="))) {
	switch (arg[0] | 0x20) {
	case 'a':		/* "ask" */
	    video_mode = 0xfffd;
	    break;
	case 'e':		/* "ext" */
	    video_mode = 0xfffe;
	    break;
	case 'n':		/* "normal" */
	    video_mode = 0xffff;
	    break;
	case 'c':		/* "current" */
	    video_mode = 0x0f04;
	    break;
	default:
	    video_mode = strtoul(arg, NULL, 0);
	    break;
	}
    }

    /* Copy the header into private storage */
    /* Use whdr to modify the actual kernel header */
    memcpy(&hdr, kernel_buf, sizeof hdr);
    whdr = (struct linux_header *)kernel_buf;

    if (hdr.boot_flag != BOOT_MAGIC)
	goto bail;

    if (hdr.header != LINUX_MAGIC) {
	hdr.version = 0x0100;	/* Very old kernel */
	hdr.loadflags = 0;
    }

    whdr->vid_mode = video_mode;

    if (!hdr.setup_sects)
	hdr.setup_sects = 4;

    if (hdr.version < 0x0203)
	hdr.initrd_addr_max = 0x37ffffff;

    if (!memlimit && memlimit - 1 > hdr.initrd_addr_max)
	memlimit = hdr.initrd_addr_max + 1;	/* Zero for no limit */

    if (hdr.version < 0x0205 || !(hdr.loadflags & LOAD_HIGH))
	hdr.relocatable_kernel = 0;

    if (hdr.version < 0x0206)
	hdr.cmdline_max_len = 256;

    if (cmdline_size > hdr.cmdline_max_len) {
	cmdline_size = hdr.cmdline_max_len;
	cmdline[cmdline_size - 1] = '\0';
    }

    if (hdr.version < 0x0202 || !(hdr.loadflags & 0x01))
	cmdline_offset = (0x9ff0 - cmdline_size) & ~15;
    else
	cmdline_offset = 0x10000;

    real_mode_size = (hdr.setup_sects + 1) << 9;
    real_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x10000 : 0x90000;
    prot_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x100000 : 0x10000;
    prot_mode_size = kernel_size - real_mode_size;

    if (!(hdr.loadflags & LOAD_HIGH) && prot_mode_size > 512 * 1024)
	goto bail;		/* Kernel cannot be loaded low */

    if (initramfs && hdr.version < 0x0200)
	goto bail;		/* initrd/initramfs not supported */

    if (hdr.version >= 0x0200) {
	whdr->type_of_loader = 0x30;	/* SYSLINUX unknown module */
	if (hdr.version >= 0x0201) {
	    whdr->heap_end_ptr = cmdline_offset - 0x0200;
	    whdr->loadflags |= CAN_USE_HEAP;
	}
	if (hdr.version >= 0x0202) {
	    whdr->cmd_line_ptr = real_mode_base + cmdline_offset;
	} else {
	    whdr->old_cmd_line_magic = OLD_CMDLINE_MAGIC;
	    whdr->old_cmd_line_offset = cmdline_offset;
	    /* Be paranoid and round up to a multiple of 16 */
	    whdr->setup_move_size = (cmdline_offset + cmdline_size + 15) & ~15;
	}
    }

    /* Get the memory map */
    mmap = syslinux_memory_map();	/* Memory map for shuffle_boot */
    amap = syslinux_dup_memmap(mmap);	/* Keep track of available memory */
    if (!mmap || !amap)
	goto bail;

#if DEBUG
    dprintf("Initial memory map:\n");
    syslinux_dump_memmap(stdout, mmap);
#endif

    /* If the user has specified a memory limit, mark that as unavailable.
       Question: should we mark this off-limit in the mmap as well (meaning
       it's unavailable to the boot loader, which probably has already touched
       some of it), or just in the amap? */
    if (memlimit)
	if (syslinux_add_memmap(&amap, memlimit, -memlimit, SMT_RESERVED))
	    goto bail;

    /* Place the kernel in memory */

    /* First, find a suitable place for the protected-mode code */
    if (syslinux_memmap_type(amap, prot_mode_base, prot_mode_size)
	!= SMT_FREE) {
	const struct syslinux_memmap *mp;
	if (!hdr.relocatable_kernel)
	    goto bail;		/* Can't relocate - no hope */

	ok = false;
	for (mp = amap; mp; mp = mp->next) {
	    addr_t start, end;
	    start = mp->start;
	    end = mp->next->start;

	    if (mp->type != SMT_FREE)
		continue;

	    if (end <= prot_mode_base)
		continue;	/* Only relocate upwards */

	    if (start <= prot_mode_base)
		start = prot_mode_base;

	    start = ALIGN_UP(start, hdr.kernel_alignment);
	    if (start >= end)
		continue;

	    /* The 3* here is a total fudge factor... it's supposed to
	       account for the fact that the kernel needs to be decompressed,
	       and then followed by the BSS and BRK regions.  This doesn't,
	       however, account for the fact that the kernel is decompressed
	       into a whole other place, either. */
	    if (end - start >= 3 * prot_mode_size) {
		whdr->code32_start += start - prot_mode_base;
		prot_mode_base = start;
		ok = true;
		break;
	    }
	}

	if (!ok)
	    goto bail;
    }

    /* Real mode code */
    if (syslinux_memmap_type(amap, real_mode_base,
			     cmdline_offset + cmdline_size) != SMT_FREE) {
	const struct syslinux_memmap *mp;

	ok = false;
	for (mp = amap; mp; mp = mp->next) {
	    addr_t start, end;
	    start = mp->start;
	    end = mp->next->start;

	    if (mp->type != SMT_FREE)
		continue;

	    if (start < real_mode_base)
		start = real_mode_base;	/* Lowest address we'll use */
	    if (end > 640 * 1024)
		end = 640 * 1024;

	    start = ALIGN_UP(start, 16);
	    if (start > 0x90000 || start >= end)
		continue;

	    if (end - start >= cmdline_offset + cmdline_size) {
		real_mode_base = start;
		ok = true;
		break;
	    }
	}
    }

    if (syslinux_add_movelist(&fraglist, real_mode_base, (addr_t) kernel_buf,
			      real_mode_size))
	goto bail;
    if (syslinux_add_memmap
	(&amap, real_mode_base, cmdline_offset + cmdline_size, SMT_ALLOC))
	goto bail;

    /* Zero region between real mode code and cmdline */
    if (syslinux_add_memmap(&mmap, real_mode_base + real_mode_size,
			    cmdline_offset - real_mode_size, SMT_ZERO))
	goto bail;

    /* Command line */
    if (syslinux_add_movelist(&fraglist, real_mode_base + cmdline_offset,
			      (addr_t) cmdline, cmdline_size))
	goto bail;

    /* Protected-mode code */
    if (syslinux_add_movelist(&fraglist, prot_mode_base,
			      (addr_t) kernel_buf + real_mode_size,
			      prot_mode_size))
	goto bail;
    if (syslinux_add_memmap(&amap, prot_mode_base, prot_mode_size, SMT_ALLOC))
	goto bail;

    /* Figure out the size of the initramfs, and where to put it.
       We should put it at the highest possible address which is
       <= hdr.initrd_addr_max, which fits the entire initramfs. */

    irf_size = initramfs_size(initramfs);	/* Handles initramfs == NULL */

    if (irf_size) {
	addr_t best_addr = 0;
	struct syslinux_memmap *ml;
	const addr_t align_mask = INITRAMFS_MAX_ALIGN - 1;

	if (irf_size) {
	    for (ml = amap; ml->type != SMT_END; ml = ml->next) {
		addr_t adj_start = (ml->start + align_mask) & ~align_mask;
		addr_t adj_end = ml->next->start & ~align_mask;
		if (ml->type == SMT_FREE && adj_end - adj_start >= irf_size)
		    best_addr = (adj_end - irf_size) & ~align_mask;
	    }

	    if (!best_addr)
		goto bail;	/* Insufficient memory for initramfs */

	    whdr->ramdisk_image = best_addr;
	    whdr->ramdisk_size = irf_size;

	    if (syslinux_add_memmap(&amap, best_addr, irf_size, SMT_ALLOC))
		goto bail;

	    if (map_initramfs(&fraglist, &mmap, initramfs, best_addr))
		goto bail;
	}
    }

    /* Set up the registers on entry */
    memset(&regs, 0, sizeof regs);
    regs.es = regs.ds = regs.ss = regs.fs = regs.gs = real_mode_base >> 4;
    regs.cs = (real_mode_base >> 4) + 0x20;
    /* regs.ip = 0; */
    /* Linux is OK with sp = 0 = 64K, but perhaps other things aren't... */
    regs.esp.w[0] = min(cmdline_offset, (size_t) 0xfff0);

#if DEBUG
    dprintf("Final memory map:\n");
    syslinux_dump_memmap(stdout, mmap);

    dprintf("Final available map:\n");
    syslinux_dump_memmap(stdout, amap);

    dprintf("Initial movelist:\n");
    syslinux_dump_movelist(stdout, fraglist);
#endif

    syslinux_shuffle_boot_rm(fraglist, mmap, 0, &regs);

bail:
    syslinux_free_movelist(fraglist);
    syslinux_free_memmap(mmap);
    syslinux_free_memmap(amap);
    return -1;
}
Esempio n. 8
0
int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
			     struct syslinux_memmap *memmap,
			     uint16_t bootflags, struct syslinux_rm_regs *regs)
{
    const struct syslinux_rm_regs_alt {
	uint16_t seg[6];
	uint32_t gpr[8];
	uint32_t csip;
	bool sti;
    } *rp;
    int i, rv;
    uint8_t handoff_code[8 + 5 * 5 + 8 * 6 + 1 + 5], *p;
    uint16_t off;
    struct syslinux_memmap *tmap;
    addr_t regstub, stublen;
    /* Assign GPRs for each sreg, don't use AX and SP */
    static const uint8_t gpr_for_seg[6] =
	{ R_CX, R_DX, R_BX, R_BP, R_SI, R_DI };

    tmap = syslinux_target_memmap(fraglist, memmap);
    if (!tmap)
	return -1;

    /*
     * Search for a good place to put the real-mode register stub.
     * We prefer it as low as possible above 0x800.  KVM barfs horribly
     * if we're not aligned to a paragraph boundary, so set the alignment
     * appropriately.
     */
    regstub = 0x800;
    stublen = sizeof handoff_code;
    rv = syslinux_memmap_find_type(tmap, SMT_FREE, &regstub, &stublen, 16);

    if (rv || (regstub > 0x100000 - sizeof handoff_code)) {
	/*
	 * Uh-oh.  This isn't real-mode accessible memory.
	 * It might be possible to do something insane here like
	 * putting the stub in the IRQ vectors, or in the 0x5xx segment.
	 * This code tries the 0x510-0x7ff range and hopes for the best.
	 */
	regstub = 0x510;	/* Try the 0x5xx segment... */
	stublen = sizeof handoff_code;
	rv = syslinux_memmap_find_type(tmap, SMT_FREE, &regstub, &stublen, 16);

	if (!rv && (regstub > 0x100000 - sizeof handoff_code))
	    rv = -1;		/* No acceptable memory found */
    }

    syslinux_free_memmap(tmap);
    if (rv)
	return -1;

    /* Build register-setting stub */
    p = handoff_code;
    rp = (const struct syslinux_rm_regs_alt *)regs;

    /* Set up GPRs with segment registers - don't use AX */
    for (i = 0; i < 6; i++) {
	if (i != R_CS)
	    MOV_TO_R16(p, gpr_for_seg[i], rp->seg[i]);
    }

    /* Actual transition to real mode */
    ST32(p, 0xeac0220f);	/* MOV CR0,EAX; JMP FAR */
    off = (p - handoff_code) + 4;
    ST16(p, off);		/* Offset */
    ST16(p, regstub >> 4);	/* Segment */

    /* Load SS and ESP immediately */
    MOV_TO_SEG(p, R_SS, R_BX);
    MOV_TO_R32(p, R_SP, rp->gpr[R_SP]);

    /* Load the other segments */
    MOV_TO_SEG(p, R_ES, R_CX);
    MOV_TO_SEG(p, R_DS, R_BP);
    MOV_TO_SEG(p, R_FS, R_SI);
    MOV_TO_SEG(p, R_GS, R_DI);

    for (i = 0; i < 8; i++) {
	if (i != R_SP)
	    MOV_TO_R32(p, i, rp->gpr[i]);
    }

    ST8(p, rp->sti ? 0xfb : 0xfa);	/* STI/CLI */

    ST8(p, 0xea);		/* JMP FAR */
    ST32(p, rp->csip);

    /* Add register-setting stub to shuffle list */
    if (syslinux_add_movelist(&fraglist, regstub, (addr_t) handoff_code,
			      sizeof handoff_code))
	return -1;

    return syslinux_do_shuffle(fraglist, memmap, regstub, 0, bootflags);
}
Esempio n. 9
0
int boot_elf(void *ptr, size_t len, char **argv)
{
    char *cptr = ptr;
    Elf32_Ehdr *eh = ptr;
    Elf32_Phdr *ph;
    unsigned int i;
    struct syslinux_movelist *ml = NULL;
    struct syslinux_memmap *mmap = NULL, *amap = NULL;
    struct syslinux_pm_regs regs;
    int argc;
    addr_t argsize;
    char **argp;
    addr_t lstart, llen;
    char *stack_frame = NULL;
    addr_t stack_frame_size;
    addr_t stack_pointer;
    uint32_t *spp;
    char *sfp;
    addr_t sfa;

    memset(&regs, 0, sizeof regs);

    /*
     * Note: mmap is the memory map (containing free and zeroed regions)
     * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep
     * track ourselves which target memory ranges have already been
     * allocated.
     */

    if (len < sizeof(Elf32_Ehdr))
	goto bail;

    /* Must be ELF, 32-bit, littleendian, version 1 */
    if (memcmp(eh->e_ident, "\x7f" "ELF\1\1\1", 6))
	goto bail;

    /* Is this a worthwhile test?  In particular x86-64 normally
       would imply ELF64 support, which we could do as long as
       the addresses are 32-bit addresses, and entry is 32 bits.
       64-bit addresses would take a lot more work. */
    if (eh->e_machine != EM_386 && eh->e_machine != EM_486 &&
	eh->e_machine != EM_X86_64)
	goto bail;

    if (eh->e_version != EV_CURRENT)
	goto bail;

    if (eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len)
	goto bail;

    if (eh->e_phentsize < sizeof(Elf32_Phdr))
	goto bail;

    if (!eh->e_phnum)
	goto bail;

    if (eh->e_phoff + eh->e_phentsize * eh->e_phnum > len)
	goto bail;

    mmap = syslinux_memory_map();
    amap = syslinux_dup_memmap(mmap);
    if (!mmap || !amap)
	goto bail;

#if DEBUG
    dprintf("Initial memory map:\n");
    syslinux_dump_memmap(stdout, mmap);
#endif

    ph = (Elf32_Phdr *) (cptr + eh->e_phoff);

    for (i = 0; i < eh->e_phnum; i++) {
	if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
	    /* This loads at p_paddr, which is arguably the correct semantics.
	       The SysV spec says that SysV loads at p_vaddr (and thus Linux does,
	       too); that is, however, a major brainfuckage in the spec. */
	    addr_t addr = ph->p_paddr;
	    addr_t msize = ph->p_memsz;
	    addr_t dsize = min(msize, ph->p_filesz);

	    dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
		    addr, dsize, msize);

	    if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) {
		printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
		       addr, msize);
		goto bail;	/* Memory region unavailable */
	    }

	    /* Mark this region as allocated in the available map */
	    if (syslinux_add_memmap(&amap, addr, dsize, SMT_ALLOC))
		goto bail;

	    if (ph->p_filesz) {
		/* Data present region.  Create a move entry for it. */
		if (syslinux_add_movelist
		    (&ml, addr, (addr_t) cptr + ph->p_offset, dsize))
		    goto bail;
	    }
	    if (msize > dsize) {
		/* Zero-filled region.  Mark as a zero region in the memory map. */
		if (syslinux_add_memmap
		    (&mmap, addr + dsize, msize - dsize, SMT_ZERO))
		    goto bail;
	    }
	} else {
	    /* Ignore this program header */
	}

	ph = (Elf32_Phdr *) ((char *)ph + eh->e_phentsize);
    }

    /* Create the invocation record (initial stack frame) */

    argsize = argc = 0;
    for (argp = argv; *argp; argp++) {
	dprintf("argv[%2d] = \"%s\"\n", argc, *argp);
	argc++;
	argsize += strlen(*argp) + 1;
    }

    /* We need the argument strings, argument pointers,
       argc, plus four zero-word terminators. */
    stack_frame_size = argsize + argc * sizeof(char *) + 5 * sizeof(long);
    stack_frame_size = (stack_frame_size + 15) & ~15;
    stack_frame = calloc(stack_frame_size, 1);
    if (!stack_frame)
	goto bail;

#if DEBUG
    dprintf("Right before syslinux_memmap_largest()...\n");
    syslinux_dump_memmap(stdout, amap);
#endif

    if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen))
	goto bail;		/* NO free memory?! */

    if (llen < stack_frame_size + MIN_STACK + 16)
	goto bail;		/* Insufficient memory  */

    /* Initial stack pointer address */
    stack_pointer = (lstart + llen - stack_frame_size) & ~15;

    dprintf("Stack frame at 0x%08x len 0x%08x\n",
	    stack_pointer, stack_frame_size);

    /* Create the stack frame.  sfp is the pointer in current memory for
       the next argument string, sfa is the address in its final resting place.
       spp is the pointer into the argument array in current memory. */
    spp = (uint32_t *) stack_frame;
    sfp = stack_frame + argc * sizeof(char *) + 5 * sizeof(long);
    sfa = stack_pointer + argc * sizeof(char *) + 5 * sizeof(long);

    *spp++ = argc;
    for (argp = argv; *argp; argp++) {
	int bytes = strlen(*argp) + 1;	/* Including final null */
	*spp++ = sfa;
	memcpy(sfp, *argp, bytes);
	sfp += bytes;
	sfa += bytes;
    }
    /* Zero fields are aready taken care of by calloc() */

    /* ... and we'll want to move it into the right place... */
#if DEBUG
    if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size)
	!= SMT_FREE) {
	dprintf("Stack frame area not free (how did that happen?)!\n");
	goto bail;		/* Memory region unavailable */
    }
#endif

    if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC))
	goto bail;

    if (syslinux_add_movelist(&ml, stack_pointer, (addr_t) stack_frame,
			      stack_frame_size))
	goto bail;

    memset(&regs, 0, sizeof regs);
    regs.eip = eh->e_entry;
    regs.esp = stack_pointer;

#if DEBUG
    dprintf("Final memory map:\n");
    syslinux_dump_memmap(stdout, mmap);

    dprintf("Final available map:\n");
    syslinux_dump_memmap(stdout, amap);

    dprintf("Movelist:\n");
    syslinux_dump_movelist(stdout, ml);
#endif

    /* This should not return... */
    fputs("Booting...\n", stdout);
    syslinux_shuffle_boot_pm(ml, mmap, 0, &regs);

bail:
    if (stack_frame)
	free(stack_frame);
    syslinux_free_memmap(amap);
    syslinux_free_memmap(mmap);
    syslinux_free_movelist(ml);

    return -1;
}
Esempio n. 10
0
struct multiboot_header *map_image(void *ptr, size_t len)
{
    struct multiboot_header *mbh;
    int mbh_len;
    char *cptr = ptr;
    Elf32_Ehdr *eh = ptr;
    Elf32_Phdr *ph;
    Elf32_Shdr *sh;
    unsigned int i, mbh_offset;
    uint32_t bad_flags;

    /*
     * Search for the multiboot header...
     */
    mbh_len = 0;
    for (mbh_offset = 0; mbh_offset < MULTIBOOT_SEARCH; mbh_offset += 4) {
	mbh = (struct multiboot_header *)((char *)ptr + mbh_offset);
	if (mbh->magic != MULTIBOOT_MAGIC)
	    continue;
	if (mbh->magic + mbh->flags + mbh->checksum)
	    continue;
	if (mbh->flags & MULTIBOOT_VIDEO_MODE)
	    mbh_len = 48;
	else if (mbh->flags & MULTIBOOT_AOUT_KLUDGE)
	    mbh_len = 32;
	else
	    mbh_len = 12;

	if (mbh_offset + mbh_len > len)
	    mbh_len = 0;	/* Invalid... */
	else
	    break;		/* Found something... */
    }

    if (mbh_len) {
	bad_flags = mbh->flags & MULTIBOOT_UNSUPPORTED;
	if (bad_flags) {
	    printf("Unsupported Multiboot flags set: %#x\n", bad_flags);
	    return NULL;
	}
    }

    if (len < sizeof(Elf32_Ehdr) ||
	memcmp(eh->e_ident, "\x7f" "ELF\1\1\1", 6) ||
	(eh->e_machine != EM_386 && eh->e_machine != EM_486 &&
	 eh->e_machine != EM_X86_64) ||
	eh->e_version != EV_CURRENT ||
	eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len ||
	eh->e_phentsize < sizeof(Elf32_Phdr) ||
	!eh->e_phnum || eh->e_phoff + eh->e_phentsize * eh->e_phnum > len)
	eh = NULL;		/* No valid ELF header found */

    /* Is this a Solaris kernel? */
    if (!set.solaris && eh && kernel_is_solaris(eh))
	opt.solaris = true;

    /*
     * Note: the Multiboot Specification implies that AOUT_KLUDGE should
     * have precedence over the ELF header.  However, Grub disagrees, and
     * Grub is "the reference bootloader" for the Multiboot Specification.
     * This is insane, since it makes the AOUT_KLUDGE bit functionally
     * useless, but at least Solaris apparently depends on this behavior.
     */
    if (eh && !(opt.aout && mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE))) {
	regs.eip = eh->e_entry;	/* Can be overridden further down... */

	ph = (Elf32_Phdr *) (cptr + eh->e_phoff);

	for (i = 0; i < eh->e_phnum; i++) {
	    if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
		/*
		 * This loads at p_paddr, which matches Grub.  However, if
		 * e_entry falls within the p_vaddr range of this PHDR, then
		 * adjust it to match the p_paddr range... this is how Grub
		 * behaves, so it's by definition correct (it doesn't have to
		 * make sense...)
		 */
		addr_t addr = ph->p_paddr;
		addr_t msize = ph->p_memsz;
		addr_t dsize = min(msize, ph->p_filesz);

		if (eh->e_entry >= ph->p_vaddr
		    && eh->e_entry < ph->p_vaddr + msize)
		    regs.eip = eh->e_entry + (ph->p_paddr - ph->p_vaddr);

		dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
			addr, dsize, msize);

		if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) {
		    printf
			("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
			 addr, msize);
		    return NULL;	/* Memory region unavailable */
		}

		/* Mark this region as allocated in the available map */
		if (syslinux_add_memmap(&amap, addr, msize, SMT_ALLOC)) {
		    error("Overlapping segments found in ELF header\n");
		    return NULL;
		}

		if (ph->p_filesz) {
		    /* Data present region.  Create a move entry for it. */
		    if (syslinux_add_movelist
			(&ml, addr, (addr_t) cptr + ph->p_offset, dsize)) {
			error("Failed to map PHDR data\n");
			return NULL;
		    }
		}
		if (msize > dsize) {
		    /* Zero-filled region.  Mark as a zero region in the memory map. */
		    if (syslinux_add_memmap
			(&mmap, addr + dsize, msize - dsize, SMT_ZERO)) {
			error("Failed to map PHDR zero region\n");
			return NULL;
		    }
		}
		if (addr + msize > mboot_high_water_mark)
		    mboot_high_water_mark = addr + msize;
	    } else {
		/* Ignore this program header */
	    }

	    ph = (Elf32_Phdr *) ((char *)ph + eh->e_phentsize);
	}

	/* Load the ELF symbol table */
	if (eh->e_shoff) {
	    addr_t addr, len;

	    sh = (Elf32_Shdr *) ((char *)eh + eh->e_shoff);

	    len = eh->e_shentsize * eh->e_shnum;
	    /*
	     * Align this, but don't pad -- in general this means a bunch of
	     * smaller sections gets packed into a single page.
	     */
	    addr = map_data(sh, len, 4096, MAP_HIGH | MAP_NOPAD);
	    if (!addr) {
		error("Failed to map symbol table\n");
		return NULL;
	    }

	    mbinfo.flags |= MB_INFO_ELF_SHDR;
	    mbinfo.syms.e.addr = addr;
	    mbinfo.syms.e.num = eh->e_shnum;
	    mbinfo.syms.e.size = eh->e_shentsize;
	    mbinfo.syms.e.shndx = eh->e_shstrndx;

	    for (i = 0; i < eh->e_shnum; i++) {
		addr_t align;

		if (!sh[i].sh_size)
		    continue;	/* Empty section */
		if (sh[i].sh_flags & SHF_ALLOC)
		    continue;	/* SHF_ALLOC sections should have PHDRs */

		align = sh[i].sh_addralign ? sh[i].sh_addralign : 0;
		addr = map_data((char *)ptr + sh[i].sh_offset, sh[i].sh_size,
				align, MAP_HIGH);
		if (!addr) {
		    error("Failed to map symbol section\n");
		    return NULL;
		}
		sh[i].sh_addr = addr;
	    }
	}
    } else if (mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE)) {
	/*
	 * a.out kludge thing...
	 */
	char *data_ptr;
	addr_t data_len, bss_len;
	addr_t bss_addr;

	regs.eip = mbh->entry_addr;

	data_ptr = (char *)mbh - (mbh->header_addr - mbh->load_addr);

	if (mbh->load_end_addr)
	    data_len = mbh->load_end_addr - mbh->load_addr;
	else
	    data_len = len - mbh_offset + (mbh->header_addr - mbh->load_addr);

	bss_addr = mbh->load_addr + data_len;

	if (mbh->bss_end_addr)
	    bss_len = mbh->bss_end_addr - mbh->load_end_addr;
	else
	    bss_len = 0;

	if (syslinux_memmap_type(amap, mbh->load_addr, data_len + bss_len)
	    != SMT_FREE) {
	    printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
		   mbh->load_addr, data_len + bss_len);
	    return NULL;		/* Memory region unavailable */
	}
	if (syslinux_add_memmap(&amap, mbh->load_addr,
				data_len + bss_len, SMT_ALLOC)) {
	    error("Failed to claim a.out address space!\n");
	    return NULL;
	}
	if (data_len)
	    if (syslinux_add_movelist(&ml, mbh->load_addr, (addr_t) data_ptr,
				      data_len)) {
		error("Failed to map a.out data\n");
		return NULL;
	    }
	if (bss_len)
	    if (syslinux_add_memmap
		(&mmap, bss_addr, bss_len, SMT_ZERO)) {
		error("Failed to map a.out bss\n");
		return NULL;
	    }
	if (bss_addr + bss_len > mboot_high_water_mark)
	    mboot_high_water_mark = bss_addr + bss_len;
    } else {
	error
	    ("Invalid Multiboot image: neither ELF header nor a.out kludge found\n");
	return NULL;
    }

    return mbh;
}
Esempio n. 11
0
void chainboot_file(const char *file, uint32_t type)
{
    uint8_t keeppxe = 0;
    const union syslinux_derivative_info *sdi;
    struct syslinux_rm_regs regs;
    struct syslinux_movelist *fraglist = NULL;
    struct syslinux_memmap *mmap = NULL;
    struct com32_filedata fd;
    com32sys_t reg;
    char *stack;
    void *buf;
    int rv, max, size;
    
    max = 0xA0000;		/* Maximum load */
    buf = malloc(max);
    if (!buf)
	goto bail;
    
    rv = open_file(file, O_RDONLY, &fd);
    if (rv == -1)
	goto bail;
    
    reg.eax.l = max;
    reg.ebx.l = 0;
    reg.edx.w[0] = 0;
    reg.edi.l = (uint32_t)buf;
    reg.ebp.l = -1;	/* XXX: limit? */
    reg.esi.w[0] = rv;

    pm_load_high(&reg);

    size = reg.edi.l - (unsigned long)buf;
    if (size > 0xA0000 - 0x7C00) {
	printf("Too large for a boostrap (need LINUX instead of KERNEL?)\n");
	goto bail;
    }

    mmap = syslinux_memory_map();
    if (!mmap)
	goto bail;

    sdi = syslinux_derivative_info();

    memset(&regs, 0, sizeof(regs));
    regs.ip = 0x7c00;

    if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX ||
	sdi->c.filesystem == SYSLINUX_FS_EXTLINUX) {
	if (syslinux_add_movelist(&fraglist, 0x800 - 18,
				  (addr_t)sdi->r.esbx, 16))
	    goto bail;

	/* DS:SI points to partition info */
	regs.esi.l = 0x800 - 18;
    }

    /*
     * For a BSS boot sector we have to transfer the
     * superblock.
     */
    if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX &&
	type == IMAGE_TYPE_BSS && this_fs->fs_ops->copy_super(buf))
	goto bail;

    if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
	keeppxe = 0x03;		/* Chainloading + keep PXE */
	stack = (char *)sdi->r.fssi;

	/*
	 * Set up the registers with their initial values
	 */

	regs.eax.l = *(uint32_t *)&stack[36];
	regs.ecx.l = *(uint32_t *)&stack[32];
	regs.edx.l = *(uint32_t *)&stack[28];
	regs.ebx.l = *(uint32_t *)&stack[24];
	regs.esp.l = sdi->rr.r.esi.w[0] + 44;
	regs.ebp.l = *(uint32_t *)&stack[16];
	regs.esi.l = *(uint32_t *)&stack[12];
	regs.edi.l = *(uint32_t *)&stack[8];
	regs.es = *(uint16_t *)&stack[4];
	regs.ss = sdi->rr.r.fs;
	regs.ds = *(uint16_t *)&stack[6];
	regs.fs = *(uint16_t *)&stack[2];
	regs.gs = *(uint16_t *)&stack[0];
    } else {
	const uint16_t *esdi = (const uint16_t *)sdi->disk.esdi_ptr;

	regs.esp.l = (uint16_t)(unsigned long)StackBuf + 44;

	/*
	 * DON'T DO THIS FOR PXELINUX...
	 * For PXE, ES:BX -> PXENV+, and this would
	 * corrupt that use.
	 *
	 * Restore ES:DI -> $PnP (if we were ourselves
	 * called that way...)
	 */
	regs.edi.w[0] = esdi[0]; /* New DI */
	regs.es       = esdi[2]; /* New ES */

	regs.edx.l    = sdi->rr.r.edx.b[0]; /* Drive number -> DL */
    }

    if (syslinux_add_movelist(&fraglist, 0x7c00, (addr_t)buf, size))
	goto bail;

    syslinux_shuffle_boot_rm(fraglist, mmap, keeppxe, &regs);

bail:
    if (fraglist)
	syslinux_free_movelist(fraglist);
    if (mmap)
	syslinux_free_memmap(mmap);
    if (buf)
	free(buf);
    return;
}