chain module: setbpb changes, bss & bs options, bugfixes
[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 "options.h"
10 #include "utility.h"
11 #include "partiter.h"
12 #include "mangle.h"
13
14 static const char cmldr_signature[8] = "cmdcons";
15
16 /* Create boot info table: needed when you want to chainload
17  * another version of ISOLINUX (or another bootlaoder that needs
18  * the -boot-info-table switch of mkisofs)
19  * (will only work when run from ISOLINUX)
20  */
21 int manglef_isolinux(struct data_area *data)
22 {
23     const union syslinux_derivative_info *sdi;
24     unsigned char *isolinux_bin;
25     uint32_t *checksum, *chkhead, *chktail;
26     uint32_t file_lba = 0;
27
28     if (!(opt.file && opt.isolinux))
29         return 0;
30
31     sdi = syslinux_derivative_info();
32
33     if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
34         error ("The isolinux= option is only valid when run from ISOLINUX.\n");
35         goto bail;
36     }
37
38     /* Boot info table info (integers in little endian format)
39
40        Offset Name         Size      Meaning
41        8      bi_pvd       4 bytes   LBA of primary volume descriptor
42        12     bi_file      4 bytes   LBA of boot file
43        16     bi_length    4 bytes   Boot file length in bytes
44        20     bi_csum      4 bytes   32-bit checksum
45        24     bi_reserved  40 bytes  Reserved
46
47        The 32-bit checksum is the sum of all the 32-bit words in the
48        boot file starting at byte offset 64. All linear block
49        addresses (LBAs) are given in CD sectors (normally 2048 bytes).
50
51        LBA of primary volume descriptor should already be set to 16.
52        */
53
54     isolinux_bin = (unsigned char *)data->data;
55
56     /* Get LBA address of bootfile */
57     file_lba = get_file_lba(opt.file);
58
59     if (file_lba == 0) {
60         error("Failed to find LBA offset of the boot file\n");
61         goto bail;
62     }
63     /* Set it */
64     *((uint32_t *) & isolinux_bin[12]) = file_lba;
65
66     /* Set boot file length */
67     *((uint32_t *) & isolinux_bin[16]) = data->size;
68
69     /* Calculate checksum */
70     checksum = (uint32_t *) & isolinux_bin[20];
71     chkhead = (uint32_t *) & isolinux_bin[64];
72     chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
73     *checksum = 0;
74     while (chkhead < chktail)
75         *checksum += *chkhead++;
76
77     /*
78      * Deal with possible fractional dword at the end;
79      * this *should* never happen...
80      */
81     if (data->size & 3) {
82         uint32_t xword = 0;
83         memcpy(&xword, chkhead, data->size & 3);
84         *checksum += xword;
85     }
86     return 0;
87 bail:
88     return -1;
89 }
90
91 /*
92  * Legacy grub's stage2 chainloading
93  */
94 int manglef_grub(const struct part_iter *iter, struct data_area *data)
95 {
96     /* Layout of stage2 file (from byte 0x0 to 0x270) */
97     struct grub_stage2_patch_area {
98         /* 0x0 to 0x205 */
99         char unknown[0x206];
100         /* 0x206: compatibility version number major */
101         uint8_t compat_version_major;
102         /* 0x207: compatibility version number minor */
103         uint8_t compat_version_minor;
104
105         /* 0x208: install_partition variable */
106         struct {
107             /* 0x208: sub-partition in sub-partition part2 */
108             uint8_t part3;
109             /* 0x209: sub-partition in top-level partition */
110             uint8_t part2;
111             /* 0x20a: top-level partiton number */
112             uint8_t part1;
113             /* 0x20b: BIOS drive number (must be 0) */
114             uint8_t drive;
115         } __attribute__ ((packed)) install_partition;
116
117         /* 0x20c: deprecated (historical reason only) */
118         uint32_t saved_entryno;
119         /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
120         uint8_t stage2_id;
121         /* 0x211: force LBA */
122         uint8_t force_lba;
123         /* 0x212: version string (will probably be 0.97) */
124         char version_string[5];
125         /* 0x217: config filename */
126         char config_file[89];
127         /* 0x270: start of code (after jump from 0x200) */
128         char codestart[1];
129     } __attribute__ ((packed)) *stage2;
130
131     if (!(opt.file && opt.grub))
132         return 0;
133
134     if (data->size < sizeof(struct grub_stage2_patch_area)) {
135         error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
136         goto bail;
137     }
138     stage2 = data->data;
139
140     /*
141      * Check the compatibility version number to see if we loaded a real
142      * stage2 file or a stage2 file that we support.
143      */
144     if (stage2->compat_version_major != 3
145             || stage2->compat_version_minor != 2) {
146         error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
147         goto bail;
148     }
149
150     /*
151      * GRUB Legacy wants the partition number in the install_partition
152      * variable, located at offset 0x208 of stage2.
153      * When GRUB Legacy is loaded, it is located at memory address 0x8208.
154      *
155      * It looks very similar to the "boot information format" of the
156      * Multiboot specification:
157      *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
158      *
159      *   0x208 = part3: sub-partition in sub-partition part2
160      *   0x209 = part2: sub-partition in top-level partition
161      *   0x20a = part1: top-level partition number
162      *   0x20b = drive: BIOS drive number (must be 0)
163      *
164      * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
165      * another location.
166      *
167      * Partition numbers always start from zero.
168      * Unused partition bytes must be set to 0xFF.
169      *
170      * We only care about top-level partition, so we only need to change
171      * "part1" to the appropriate value:
172      *   -1:   whole drive (default) (-1 = 0xFF)
173      *   0-3:  primary partitions
174      *   4-*:  logical partitions
175      */
176     stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
177
178     /*
179      * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
180      * config filename. The filename passed via grubcfg= will overwrite
181      * the default config filename "/boot/grub/menu.lst".
182      */
183     if (opt.grubcfg) {
184         if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
185             error ("The config filename length can't exceed 88 characters.\n");
186             goto bail;
187         }
188
189         strcpy((char *)stage2->config_file, opt.grubcfg);
190     }
191
192     return 0;
193 bail:
194     return -1;
195 }
196 #if 0
197 /*
198  * Dell's DRMK chainloading.
199  */
200 int manglef_drmk(struct data_area *data)
201 {
202     /*
203      * DRMK entry is different than MS-DOS/PC-DOS
204      * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
205      * We only really need 4 new, usable bytes at the end.
206      */
207
208     if (!(opt.file && opt.drmk))
209         return 0;
210
211     uint32_t tsize = (data->size + 19) & 0xfffffff0;
212     opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;        /* Used before initialized */
213     if (!realloc(data->data, tsize)) {
214         error("Failed to realloc for DRMK.\n");
215         goto bail;
216     }
217     data->size = tsize;
218     /* ds:[bp+28] must be 0x0000003f */
219     opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
220     /* "Patch" into tail of the new space */
221     *(uint32_t *)((char*)data->data + tsize - 4) = 0x0000003f;
222
223     return 0;
224 bail:
225     return -1;
226 }
227 #endif
228 /* Adjust BPB common function */
229 static int mangle_bpb(const struct part_iter *iter, struct data_area *data)
230 {
231     unsigned int off;
232     int type = bpb_detect(data->data);
233
234     /* BPB: hidden sectors 32bit*/
235     if (type >= bpbV34) {
236         if (iter->start_lba < ~0u)
237             *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
238         else
239             /* won't really help much, but ... */
240             *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
241     }
242     /* BPB: hidden sectors 16bit*/
243     if (bpbV30 <= type && type <= bpbV32) {
244         if (iter->start_lba < 0xFFFF)
245             *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba;
246         else
247             /* won't really help much, but ... */
248             *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
249     }
250     /* BPB: legacy geometry */
251     if (type >= bpbV30) {
252         if (iter->di.cbios)
253             *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.sect);
254         else {
255             if (iter->di.disk & 0x80)
256                 *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
257             else
258                 *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
259         }
260     }
261     /* BPB: drive */
262     if (drvoff_detect(type, &off)) {
263         *(uint8_t *)((char *)data->data + off) = (uint8_t)
264             (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
265     }
266
267     return 0;
268 }
269
270 /*
271  * Adjust BPB of a BPB-compatible file
272  */
273 int manglef_bpb(const struct part_iter *iter, struct data_area *data)
274 {
275     if (!(opt.file && opt.filebpb))
276         return 0;
277
278     return mangle_bpb(iter, data);
279 }
280
281 /*
282  * Adjust BPB of a sector
283  */
284 int mangles_bpb(const struct part_iter *iter, struct data_area *data)
285 {
286     if (!(opt.sect && opt.setbpb))
287         return 0;
288
289     return mangle_bpb(iter, data);
290 }
291
292 /*
293  * This function performs full BPB patching, analogously to syslinux's
294  * native BSS. opt.drv is prereq
295  */
296 int manglesf_bss(struct data_area *sec, struct data_area *fil)
297 {
298     int type1, type2;
299     unsigned int cnt = 0;
300
301     if (!(opt.sect && opt.file && opt.bss))
302         return 0;
303
304     type1 = bpb_detect(fil->data);
305     type2 = bpb_detect(sec->data);
306
307     if (type1 < 0 || type2 < 0) {
308         error("Option 'bss' can't determine BPB type.\n");
309         goto bail;
310     }
311     if (type1 != type2) {
312         error("Option 'bss' can't be used,\n"
313                 "when a sector and a file have incompatible BPBs.\n");
314         goto bail;
315     }
316
317     /* Copy common 2.0 data */
318     memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
319
320     /* Copy 3.0+ data */
321     if (type1 <= bpbV30) {
322         cnt = 0x06;
323     } else if (type1 <= bpbV32) {
324         cnt = 0x08;
325     } else if (type1 <= bpbV34) {
326         cnt = 0x0C;
327     } else if (type1 <= bpbV40) {
328         cnt = 0x2E;
329     } else if (type1 <= bpbVNT) {
330         cnt = 0x3C;
331     } else if (type1 <= bpbV70) {
332         cnt = 0x42;
333     }
334     memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
335
336     return 0;
337 bail:
338     return -1;
339 }
340
341 /*
342  * Save sector.
343  */
344 int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
345 {
346     if (!(opt.sect && opt.save))
347         return 0;
348
349     if (memcmp(org, data->data, data->size)) {
350         if (disk_write_sector(&iter->di, iter->start_lba, data->data)) {
351             error("Cannot write the updated sector.\n");
352             goto bail;
353         }
354         /* function can be called again */
355         memcpy(org, data->data, data->size);
356     }
357
358     return 0;
359 bail:
360     return -1;
361 }
362
363 /*
364  * To boot the Recovery Console of Windows NT/2K/XP we need to write
365  * the string "cmdcons\0" to memory location 0000:7C03.
366  * Memory location 0000:7C00 contains the bootsector of the partition.
367  */
368 int mangles_cmldr(struct data_area *data)
369 {
370     if (!(opt.sect && opt.cmldr))
371         return 0;
372
373     memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
374     return 0;
375 }
376
377 /* Set common registers */
378 int mangler_common(const struct part_iter *iter)
379 {
380     /* Set initial registry values */
381     if (opt.file) {
382         opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
383         opt.regs.ip = (uint16_t)opt.fip;
384     } else {
385         opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
386         opt.regs.ip = (uint16_t)opt.sip;
387     }
388
389     if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
390         opt.regs.esp.l = 0x7C00;
391
392     /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
393     opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
394
395     return 0;
396 }
397
398 /* ds:si & ds:bp */
399 int mangler_handover(const struct part_iter *iter, const struct data_area *data)
400 {
401     if (opt.sect && opt.file && opt.maps && !opt.hptr) {
402         opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
403         opt.regs.ds = (uint16_t)opt.sseg;
404         opt.regs.eax.l = 0;
405     } else if (opt.hand) {
406         /* base is really 0x7be */
407         opt.regs.esi.l = opt.regs.ebp.l = data->base;
408         opt.regs.ds = 0;
409         if (iter->type == typegpt)
410             opt.regs.eax.l = 0x54504721;        /* '!GPT' */
411         else
412             opt.regs.eax.l = 0;
413     }
414
415     return 0;
416 }
417
418 /*
419  * GRLDR of GRUB4DOS wants the partition number in DH:
420  * -1:   whole drive (default)
421  * 0-3:  primary partitions
422  * 4-*:  logical partitions
423  */
424 int mangler_grldr(const struct part_iter *iter)
425 {
426     if (opt.grldr)
427         opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
428
429     return 0;
430 }
431
432 /* vim: set ts=8 sts=4 sw=4 noet: */