| /* ----------------------------------------------------------------------- * |
| * |
| * 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; |
| } |