[e16e8f2] | 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(®); |
---|
| 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(®s, 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, ®s); |
---|
| 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 | } |
---|