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