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
chainboot_file(const char * file,uint32_t type)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