1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
/* ----------------------------------------------------------------------- *
*
* Copyright 2012 Intel Corporation, author: H. Peter Anvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston MA 02110-1301, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* chainbooting - replace the current bootloader completely. This
* is BIOS-specific.
*/
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <dprintf.h>
#include <com32.h>
#include <sys/exec.h>
#include <sys/io.h>
#include "core.h"
#include "menu.h"
#include "fs.h"
#include "config.h"
#include "localboot.h"
#include "bios.h"
#include <syslinux/boot.h>
#include <syslinux/bootrm.h>
#include <syslinux/movebits.h>
#include <syslinux/config.h>
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(®);
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(®s, 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, ®s);
bail:
if (fraglist)
syslinux_free_movelist(fraglist);
if (mmap)
syslinux_free_memmap(mmap);
if (buf)
free(buf);
return;
}
|