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 } 157