Merge remote-tracking branch 'origin/master' into chaindev
[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     const union syslinux_derivative_info *sdi;
213     uint64_t fs_lba;
214
215     sdi = syslinux_derivative_info();
216     /* We should lookup the Syslinux partition offset and use it */
217     fs_lba = *sdi->disk.partoffset;
218
219     /*
220      * fs_lba should be verified against the disk as some DRMK
221      * variants will check and fail if it does not match
222      */
223     dprintf("  fs_lba offset is %d\n", fs_lba);
224     /* DRMK only uses a DWORD */
225     if (fs_lba > 0xffffffff) {
226         error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
227     }
228     opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;        /* Used before initialized */
229     if (!realloc(data->data, tsize)) {
230         error("Failed to realloc for DRMK.\n");
231         goto bail;
232     }
233     data->size = tsize;
234     /* ds:bp is assumed by DRMK to be the boot sector */
235     /* offset 28 is the FAT HiddenSectors value */
236     opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
237     /* "Patch" into tail of the new space */
238     *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba;
239
240     return 0;
241 bail:
242     return -1;
243 }
244 #endif
245 /* Adjust BPB common function */
246 static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag)
247 {
248     unsigned int off;
249     int type = bpb_detect(data->data, tag);
250
251     /* BPB: hidden sectors 32bit*/
252     if (type >= bpbV34) {
253         if (iter->start_lba < ~0u)
254             *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
255         else
256             /* won't really help much, but ... */
257             *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
258     }
259     /* BPB: hidden sectors 16bit*/
260     if (bpbV30 <= type && type <= bpbV32) {
261         if (iter->start_lba < 0xFFFF)
262             *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba;
263         else
264             /* won't really help much, but ... */
265             *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
266     }
267     /* BPB: legacy geometry */
268     if (type >= bpbV30) {
269         if (iter->di.cbios)
270             *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt);
271         else {
272             if (iter->di.disk & 0x80)
273                 *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
274             else
275                 *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
276         }
277     }
278     /* BPB: drive */
279     if (drvoff_detect(type, &off)) {
280         *(uint8_t *)((char *)data->data + off) = (uint8_t)
281             (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
282     }
283
284     return 0;
285 }
286
287 /*
288  * Adjust BPB of a BPB-compatible file
289  */
290 int manglef_bpb(const struct part_iter *iter, struct data_area *data)
291 {
292     if (!(opt.file && opt.filebpb))
293         return 0;
294
295     return mangle_bpb(iter, data, "file");
296 }
297
298 /*
299  * Adjust BPB of a sector
300  */
301 int mangles_bpb(const struct part_iter *iter, struct data_area *data)
302 {
303     if (!(opt.sect && opt.setbpb))
304         return 0;
305
306     return mangle_bpb(iter, data, "sect");
307 }
308
309 /*
310  * This function performs full BPB patching, analogously to syslinux's
311  * native BSS.
312  */
313 int manglesf_bss(struct data_area *sec, struct data_area *fil)
314 {
315     int type1, type2;
316     unsigned int cnt = 0;
317
318     if (!(opt.sect && opt.file && opt.bss))
319         return 0;
320
321     type1 = bpb_detect(fil->data, "bss/file");
322     type2 = bpb_detect(sec->data, "bss/sect");
323
324     if (!type1 || !type2) {
325         error("Couldn't determine the BPB type for option 'bss'.\n");
326         goto bail;
327     }
328     if (type1 != type2) {
329         error("Option 'bss' can't be used,\n"
330                 "when a sector and a file have incompatible BPBs.\n");
331         goto bail;
332     }
333
334     /* Copy common 2.0 data */
335     memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
336
337     /* Copy 3.0+ data */
338     if (type1 <= bpbV30) {
339         cnt = 0x06;
340     } else if (type1 <= bpbV32) {
341         cnt = 0x08;
342     } else if (type1 <= bpbV34) {
343         cnt = 0x0C;
344     } else if (type1 <= bpbV40) {
345         cnt = 0x2E;
346     } else if (type1 <= bpbVNT) {
347         cnt = 0x3C;
348     } else if (type1 <= bpbV70) {
349         cnt = 0x42;
350     }
351     memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
352
353     return 0;
354 bail:
355     return -1;
356 }
357
358 /*
359  * Save sector.
360  */
361 int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
362 {
363     if (!(opt.sect && opt.save))
364         return 0;
365
366     if (memcmp(org, data->data, data->size)) {
367         if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) {
368             error("Cannot write the updated sector.\n");
369             goto bail;
370         }
371         /* function can be called again */
372         memcpy(org, data->data, data->size);
373     }
374
375     return 0;
376 bail:
377     return -1;
378 }
379
380 /*
381  * To boot the Recovery Console of Windows NT/2K/XP we need to write
382  * the string "cmdcons\0" to memory location 0000:7C03.
383  * Memory location 0000:7C00 contains the bootsector of the partition.
384  */
385 int mangles_cmldr(struct data_area *data)
386 {
387     if (!(opt.sect && opt.cmldr))
388         return 0;
389
390     memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
391     return 0;
392 }
393
394 /* Set common registers */
395 int mangler_init(const struct part_iter *iter)
396 {
397     /* Set initial registry values */
398     if (opt.file) {
399         opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
400         opt.regs.ip = (uint16_t)opt.fip;
401     } else {
402         opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
403         opt.regs.ip = (uint16_t)opt.sip;
404     }
405
406     if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
407         opt.regs.esp.l = 0x7C00;
408
409     /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
410     opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
411
412     return 0;
413 }
414
415 /* ds:si & ds:bp */
416 int mangler_handover(const struct part_iter *iter, const struct data_area *data)
417 {
418     if (opt.file && opt.maps && !opt.hptr) {
419         opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
420         opt.regs.ds = (uint16_t)opt.sseg;
421         opt.regs.eax.l = 0;
422     } else if (opt.hand) {
423         /* base is really 0x7be */
424         opt.regs.esi.l = opt.regs.ebp.l = data->base;
425         opt.regs.ds = 0;
426         if (iter->index && iter->type == typegpt)   /* must be iterated and GPT */
427             opt.regs.eax.l = 0x54504721;        /* '!GPT' */
428         else
429             opt.regs.eax.l = 0;
430     }
431
432     return 0;
433 }
434
435 /*
436  * GRLDR of GRUB4DOS wants the partition number in DH:
437  * -1:   whole drive (default)
438  * 0-3:  primary partitions
439  * 4-*:  logical partitions
440  */
441 int mangler_grldr(const struct part_iter *iter)
442 {
443     if (opt.grldr)
444         opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
445
446     return 0;
447 }
448
449 /*
450  * try to copy values from temporary iterator, if positions match
451  */
452 static void push_embr(struct part_iter *diter, struct part_iter *siter)
453 {
454     if (diter->sub.dos.cebr_lba == siter->sub.dos.cebr_lba &&
455             diter->di.disk == siter->di.disk) {
456         memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr));
457     }
458 }
459
460 static int mpe_sethide(struct part_iter *iter, struct part_iter *miter)
461 {
462     struct disk_dos_part_entry *dp;
463     static const uint16_t mask =
464         (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
465         (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
466     uint8_t t;
467
468     dp = (struct disk_dos_part_entry *)iter->record;
469     t = dp->ostype;
470
471     if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
472         /* It's a hideable partition type */
473         if (miter->index == iter->index || opt.hide & 4)
474             t &= (uint8_t)(~0x10u);     /* unhide */
475         else
476             t |= 0x10u; /* hide */
477     }
478     if (dp->ostype != t) {
479         dp->ostype = t;
480         return -1;
481     }
482     return 0;
483 }
484
485 /*
486  * miter - iterator we match against
487  * hide bits meaning:
488  * ..| - enable (1) / disable (0)
489  * .|. - all (1) / pri (0)
490  * |.. - unhide (1) / hide (0)
491  */
492 int manglepe_hide(struct part_iter *miter)
493 {
494     int wb = 0, werr = 0;
495     struct part_iter *iter = NULL;
496     struct disk_dos_part_entry *dp;
497     int ridx;
498
499     if (!opt.hide)
500         return 0;
501
502     if (miter->type != typedos) {
503         error("Options '*hide*' is meaningful only for legacy partition scheme.\n");
504         return -1;
505     }
506
507     if (miter->index < 1)
508         error("WARNING: It's impossible to unhide a disk.\n");
509
510     if (miter->index > 4 && !(opt.hide & 2))
511         error("WARNING: your partition is beyond mbr, so it can't be unhidden without '*hideall'.\n");
512
513     if (!(iter = pi_begin(&miter->di, 1)))  /* turn stepall on */
514         return -1;
515
516     while (!pi_next(&iter) && !werr) {
517         ridx = iter->rawindex;
518         if (!(opt.hide & 2) && ridx > 4)
519             break;  /* skip when we're constrained to pri only */
520
521         dp = (struct disk_dos_part_entry *)iter->record;
522         if (dp->ostype)
523             wb |= mpe_sethide(iter, miter);
524
525         if (ridx >= 4 && wb && !werr) {
526             push_embr(miter, iter);
527             werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
528             wb = 0;
529         }
530     }
531
532     if (iter->status > PI_DONE)
533         goto bail;
534
535     /* last write */
536     if (wb && !werr) {
537         push_embr(miter, iter);
538         werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
539     }
540     if (werr)
541         error("WARNING: failed to write E/MBR during '*hide*'\n");
542
543 bail:
544     pi_del(&iter);
545     return 0;
546 }
547
548 static int mpe_setchs(const struct disk_info *di,
549                      struct disk_dos_part_entry *dp,
550                      uint32_t lba1)
551 {
552     uint32_t ochs1, ochs2;
553
554     ochs1 = *(uint32_t *)dp->start;
555     ochs2 = *(uint32_t *)dp->end;
556
557     lba2chs(&dp->start, di, lba1, l2c_cadd);
558     lba2chs(&dp->end, di, lba1 + dp->length - 1, l2c_cadd);
559
560     return
561         *(uint32_t *)dp->start != ochs1 ||
562         *(uint32_t *)dp->end != ochs2;
563 }
564
565 /*
566  * miter - iterator we match against
567  */
568 int manglepe_fixchs(struct part_iter *miter)
569 {
570     int wb = 0, werr = 0;
571     struct part_iter *iter = NULL;
572     struct disk_dos_part_entry *dp;
573     int ridx;
574
575     if (!opt.fixchs)
576         return 0;
577
578     if (miter->type != typedos) {
579         error("Options 'fixchs' is meaningful only for legacy partition scheme.\n");
580         return -1;
581     }
582
583     if (!(iter = pi_begin(&miter->di, 1)))  /* turn stepall on */
584         return -1;
585
586     while (!pi_next(&iter) && !werr) {
587         ridx = iter->rawindex;
588         dp = (struct disk_dos_part_entry *)iter->record;
589
590         wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
591         if (ridx > 4)
592                 wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
593
594         if (ridx >= 4 && wb && !werr) {
595             push_embr(miter, iter);
596             werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
597             wb = 0;
598         }
599     }
600
601     if (iter->status > PI_DONE)
602         goto bail;
603
604     /* last write */
605     if (wb && !werr) {
606         push_embr(miter, iter);
607         werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
608     }
609     if (werr)
610         error("WARNING: failed to write E/MBR during 'fixchs'\n");
611
612 bail:
613     pi_del(&iter);
614     return 0;
615 }
616
617 /* vim: set ts=8 sts=4 sw=4 noet: */