Home | History | Annotate | Download | only in ldlinux
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2012 Intel Corporation, author: H. Peter Anvin
      4  *
      5  *   This program is free software; you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
      8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
      9  *   (at your option) any later version; incorporated herein by reference.
     10  *
     11  * ----------------------------------------------------------------------- */
     12 
     13 /*
     14  * chainbooting - replace the current bootloader completely.  This
     15  * is BIOS-specific.
     16  */
     17 
     18 #include <fcntl.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include <stdio.h>
     22 #include <dprintf.h>
     23 
     24 #include <com32.h>
     25 #include <sys/exec.h>
     26 #include <sys/io.h>
     27 #include "core.h"
     28 #include "menu.h"
     29 #include "fs.h"
     30 #include "config.h"
     31 #include "localboot.h"
     32 #include "bios.h"
     33 
     34 #include <syslinux/boot.h>
     35 #include <syslinux/bootrm.h>
     36 #include <syslinux/movebits.h>
     37 #include <syslinux/config.h>
     38 
     39 void chainboot_file(const char *file, uint32_t type)
     40 {
     41     uint8_t keeppxe = 0;
     42     const union syslinux_derivative_info *sdi;
     43     struct syslinux_rm_regs regs;
     44     struct syslinux_movelist *fraglist = NULL;
     45     struct syslinux_memmap *mmap = NULL;
     46     struct com32_filedata fd;
     47     com32sys_t reg;
     48     char *stack;
     49     void *buf;
     50     int rv, max, size;
     51 
     52     max = 0xA0000;		/* Maximum load */
     53     buf = malloc(max);
     54     if (!buf)
     55 	goto bail;
     56 
     57     rv = open_file(file, O_RDONLY, &fd);
     58     if (rv == -1)
     59 	goto bail;
     60 
     61     reg.eax.l = max;
     62     reg.ebx.l = 0;
     63     reg.edx.w[0] = 0;
     64     reg.edi.l = (uint32_t)buf;
     65     reg.ebp.l = -1;	/* XXX: limit? */
     66     reg.esi.w[0] = rv;
     67 
     68     pm_load_high(&reg);
     69 
     70     size = reg.edi.l - (unsigned long)buf;
     71     if (size > 0xA0000 - 0x7C00) {
     72 	printf("Too large for a boostrap (need LINUX instead of KERNEL?)\n");
     73 	goto bail;
     74     }
     75 
     76     mmap = syslinux_memory_map();
     77     if (!mmap)
     78 	goto bail;
     79 
     80     sdi = syslinux_derivative_info();
     81 
     82     memset(&regs, 0, sizeof(regs));
     83     regs.ip = 0x7c00;
     84 
     85     if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX ||
     86 	sdi->c.filesystem == SYSLINUX_FS_EXTLINUX) {
     87 	if (syslinux_add_movelist(&fraglist, 0x800 - 18,
     88 				  (addr_t)sdi->r.esbx, 16))
     89 	    goto bail;
     90 
     91 	/* DS:SI points to partition info */
     92 	regs.esi.l = 0x800 - 18;
     93     }
     94 
     95     /*
     96      * For a BSS boot sector we have to transfer the
     97      * superblock.
     98      */
     99     if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX &&
    100 	type == IMAGE_TYPE_BSS && this_fs->fs_ops->copy_super(buf))
    101 	goto bail;
    102 
    103     if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
    104 	keeppxe = 0x03;		/* Chainloading + keep PXE */
    105 	stack = (char *)sdi->r.fssi;
    106 
    107 	/*
    108 	 * Set up the registers with their initial values
    109 	 */
    110 
    111 	regs.eax.l = *(uint32_t *)&stack[36];
    112 	regs.ecx.l = *(uint32_t *)&stack[32];
    113 	regs.edx.l = *(uint32_t *)&stack[28];
    114 	regs.ebx.l = *(uint32_t *)&stack[24];
    115 	regs.esp.l = sdi->rr.r.esi.w[0] + 44;
    116 	regs.ebp.l = *(uint32_t *)&stack[16];
    117 	regs.esi.l = *(uint32_t *)&stack[12];
    118 	regs.edi.l = *(uint32_t *)&stack[8];
    119 	regs.es = *(uint16_t *)&stack[4];
    120 	regs.ss = sdi->rr.r.fs;
    121 	regs.ds = *(uint16_t *)&stack[6];
    122 	regs.fs = *(uint16_t *)&stack[2];
    123 	regs.gs = *(uint16_t *)&stack[0];
    124     } else {
    125 	const uint16_t *esdi = (const uint16_t *)sdi->disk.esdi_ptr;
    126 
    127 	regs.esp.l = (uint16_t)(unsigned long)StackBuf + 44;
    128 
    129 	/*
    130 	 * DON'T DO THIS FOR PXELINUX...
    131 	 * For PXE, ES:BX -> PXENV+, and this would
    132 	 * corrupt that use.
    133 	 *
    134 	 * Restore ES:DI -> $PnP (if we were ourselves
    135 	 * called that way...)
    136 	 */
    137 	regs.edi.w[0] = esdi[0]; /* New DI */
    138 	regs.es       = esdi[2]; /* New ES */
    139 
    140 	regs.edx.l    = sdi->rr.r.edx.b[0]; /* Drive number -> DL */
    141     }
    142 
    143     if (syslinux_add_movelist(&fraglist, 0x7c00, (addr_t)buf, size))
    144 	goto bail;
    145 
    146     syslinux_shuffle_boot_rm(fraglist, mmap, keeppxe, &regs);
    147 
    148 bail:
    149     if (fraglist)
    150 	syslinux_free_movelist(fraglist);
    151     if (mmap)
    152 	syslinux_free_memmap(mmap);
    153     if (buf)
    154 	free(buf);
    155     return;
    156 }
    157