3ddff7163e9a9d4f1150ee5b8b5bd83cb755a988
[profile/ivi/syslinux.git] / com32 / chain / mangle.c
1 #include <com32.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <syslinux/config.h>
7 #include "common.h"
8 #include "chain.h"
9 #include "utility.h"
10 #include "partiter.h"
11 #include "mangle.h"
12
13 static const char cmldr_signature[8] = "cmdcons";
14
15 /* Create boot info table: needed when you want to chainload
16  * another version of ISOLINUX (or another bootlaoder that needs
17  * the -boot-info-table switch of mkisofs)
18  * (will only work when run from ISOLINUX)
19  */
20 int manglef_isolinux(struct data_area *data)
21 {
22     const union syslinux_derivative_info *sdi;
23     unsigned char *isolinux_bin;
24     uint32_t *checksum, *chkhead, *chktail;
25     uint32_t file_lba = 0;
26
27     if (!opt.isolinux)
28         return 0;
29
30     sdi = syslinux_derivative_info();
31
32     if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
33         error ("The isolinux= option is only valid when run from ISOLINUX.\n");
34         goto bail;
35     }
36
37     /* Boot info table info (integers in little endian format)
38
39        Offset Name         Size      Meaning
40        8      bi_pvd       4 bytes   LBA of primary volume descriptor
41        12     bi_file      4 bytes   LBA of boot file
42        16     bi_length    4 bytes   Boot file length in bytes
43        20     bi_csum      4 bytes   32-bit checksum
44        24     bi_reserved  40 bytes  Reserved
45
46        The 32-bit checksum is the sum of all the 32-bit words in the
47        boot file starting at byte offset 64. All linear block
48        addresses (LBAs) are given in CD sectors (normally 2048 bytes).
49
50        LBA of primary volume descriptor should already be set to 16.
51        */
52
53     isolinux_bin = (unsigned char *)data->data;
54
55     /* Get LBA address of bootfile */
56     file_lba = get_file_lba(opt.file);
57
58     if (file_lba == 0) {
59         error("Failed to find LBA offset of the boot file\n");
60         goto bail;
61     }
62     /* Set it */
63     *((uint32_t *) & isolinux_bin[12]) = file_lba;
64
65     /* Set boot file length */
66     *((uint32_t *) & isolinux_bin[16]) = data->size;
67
68     /* Calculate checksum */
69     checksum = (uint32_t *) & isolinux_bin[20];
70     chkhead = (uint32_t *) & isolinux_bin[64];
71     chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
72     *checksum = 0;
73     while (chkhead < chktail)
74         *checksum += *chkhead++;
75
76     /*
77      * Deal with possible fractional dword at the end;
78      * this *should* never happen...
79      */
80     if (data->size & 3) {
81         uint32_t xword = 0;
82         memcpy(&xword, chkhead, data->size & 3);
83         *checksum += xword;
84     }
85     return 0;
86 bail:
87     return -1;
88 }
89
90 /*
91  * GRLDR of GRUB4DOS wants the partition number in DH:
92  * -1:   whole drive (default)
93  * 0-3:  primary partitions
94  * 4-*:  logical partitions
95  */
96 int manglef_grldr(const struct part_iter *iter)
97 {
98     if (!opt.grldr)
99         return 0;
100
101     opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
102     return 0;
103 }
104
105 /*
106  * Legacy grub's stage2 chainloading
107  */
108 int manglef_grub(const struct part_iter *iter, struct data_area *data)
109 {
110     /* Layout of stage2 file (from byte 0x0 to 0x270) */
111     struct grub_stage2_patch_area {
112         /* 0x0 to 0x205 */
113         char unknown[0x206];
114         /* 0x206: compatibility version number major */
115         uint8_t compat_version_major;
116         /* 0x207: compatibility version number minor */
117         uint8_t compat_version_minor;
118
119         /* 0x208: install_partition variable */
120         struct {
121             /* 0x208: sub-partition in sub-partition part2 */
122             uint8_t part3;
123             /* 0x209: sub-partition in top-level partition */
124             uint8_t part2;
125             /* 0x20a: top-level partiton number */
126             uint8_t part1;
127             /* 0x20b: BIOS drive number (must be 0) */
128             uint8_t drive;
129         } __attribute__ ((packed)) install_partition;
130
131         /* 0x20c: deprecated (historical reason only) */
132         uint32_t saved_entryno;
133         /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
134         uint8_t stage2_id;
135         /* 0x211: force LBA */
136         uint8_t force_lba;
137         /* 0x212: version string (will probably be 0.97) */
138         char version_string[5];
139         /* 0x217: config filename */
140         char config_file[89];
141         /* 0x270: start of code (after jump from 0x200) */
142         char codestart[1];
143     } __attribute__ ((packed)) *stage2;
144
145     if (!opt.grub)
146         return 0;
147
148     if (data->size < sizeof(struct grub_stage2_patch_area)) {
149         error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
150         goto bail;
151     }
152     stage2 = data->data;
153
154     /*
155      * Check the compatibility version number to see if we loaded a real
156      * stage2 file or a stage2 file that we support.
157      */
158     if (stage2->compat_version_major != 3
159             || stage2->compat_version_minor != 2) {
160         error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
161         goto bail;
162     }
163
164     /*
165      * GRUB Legacy wants the partition number in the install_partition
166      * variable, located at offset 0x208 of stage2.
167      * When GRUB Legacy is loaded, it is located at memory address 0x8208.
168      *
169      * It looks very similar to the "boot information format" of the
170      * Multiboot specification:
171      *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
172      *
173      *   0x208 = part3: sub-partition in sub-partition part2
174      *   0x209 = part2: sub-partition in top-level partition
175      *   0x20a = part1: top-level partition number
176      *   0x20b = drive: BIOS drive number (must be 0)
177      *
178      * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
179      * another location.
180      *
181      * Partition numbers always start from zero.
182      * Unused partition bytes must be set to 0xFF.
183      *
184      * We only care about top-level partition, so we only need to change
185      * "part1" to the appropriate value:
186      *   -1:   whole drive (default) (-1 = 0xFF)
187      *   0-3:  primary partitions
188      *   4-*:  logical partitions
189      */
190     stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
191
192     /*
193      * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
194      * config filename. The filename passed via grubcfg= will overwrite
195      * the default config filename "/boot/grub/menu.lst".
196      */
197     if (opt.grubcfg) {
198         if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
199             error ("The config filename length can't exceed 88 characters.\n");
200             goto bail;
201         }
202
203         strcpy((char *)stage2->config_file, opt.grubcfg);
204     }
205
206     return 0;
207 bail:
208     return -1;
209 }
210
211 /*
212  * Adjust BPB of a BPB-compatible file
213  */
214 int mangles_bpb(const struct part_iter *iter, struct data_area *data)
215 {
216     /* BPB: hidden sectors */
217     if (opt.sethid) {
218         if (iter->start_lba < ~0u)
219             *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
220         else
221             /* won't really help much, but ... */
222             *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
223     }
224     /* BPB: legacy geometry */
225     if (opt.setgeo) {
226         if (iter->di.cbios)
227             *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.sect);
228         else {
229             if (iter->di.disk & 0x80)
230                 *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
231             else
232                 *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
233         }
234     }
235     /* BPB: drive */
236     if (opt.setdrv)
237         *(uint8_t *)((char *)data->data + opt.drvoff) = (uint8_t)
238             (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
239
240     return 0;
241 }
242
243 int manglef_bpb(const struct part_iter *iter, struct data_area *data)
244 {
245     if (!opt.filebpb)
246         return 0;
247
248     return mangles_bpb(iter, data);
249 }
250
251 int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
252 {
253     if (!opt.save)
254         return 0;
255
256     if (memcmp(org, data->data, data->size)) {
257         if (disk_write_sector(&iter->di, iter->start_lba, data->data)) {
258             error("Cannot write the updated sector.\n");
259             goto bail;
260         }
261         /* function is ready do be called again */
262         memcpy(org, data->data, data->size);
263     }
264
265     return 0;
266
267 bail:
268     return -1;
269 }
270
271 /*
272  * To boot the Recovery Console of Windows NT/2K/XP we need to write
273  * the string "cmdcons\0" to memory location 0000:7C03.
274  * Memory location 0000:7C00 contains the bootsector of the partition.
275  */
276 int mangles_cmldr(struct data_area *data)
277 {
278     if (!opt.cmldr)
279         return 0;
280
281     memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
282     return 0;
283 }
284
285 #if 0
286 /*
287  * Dell's DRMK chainloading.
288  */
289 int manglef_drmk(struct data_area *data)
290 {
291     /*
292      * DRMK entry is different than MS-DOS/PC-DOS
293      * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
294      * We only really need 4 new, usable bytes at the end.
295      */
296
297     if (!opt.drmk)
298         return 0;
299
300     uint32_t tsize = (data->size + 19) & 0xfffffff0;
301     opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;        /* Used before initialized */
302     if (!realloc(data->data, tsize)) {
303         error("Failed to realloc for DRMK.\n");
304         goto bail;
305     }
306     data->size = tsize;
307     /* ds:[bp+28] must be 0x0000003f */
308     opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
309     /* "Patch" into tail of the new space */
310     *(uint32_t *)((char*)data->data + tsize - 4) = 0x0000003f;
311
312     return 0;
313 bail:
314     return -1;
315 }
316 #endif
317
318 /* vim: set ts=8 sts=4 sw=4 noet: */