source: bootcd/isolinux/syslinux-6.03/com32/chain/mangle.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 18.6 KB
Line 
1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 *   Copyright 2010 Shao Miller
6 *   Copyright 2010-2012 Michal Soltys
7 *
8 *   Permission is hereby granted, free of charge, to any person
9 *   obtaining a copy of this software and associated documentation
10 *   files (the "Software"), to deal in the Software without
11 *   restriction, including without limitation the rights to use,
12 *   copy, modify, merge, publish, distribute, sublicense, and/or
13 *   sell copies of the Software, and to permit persons to whom
14 *   the Software is furnished to do so, subject to the following
15 *   conditions:
16 *
17 *   The above copyright notice and this permission notice shall
18 *   be included in all copies or substantial portions of the Software.
19 *
20 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 *   OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * ----------------------------------------------------------------------- */
30
31#include <com32.h>
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
35#include <stdint.h>
36#include <dprintf.h>
37#include <syslinux/config.h>
38#include "chain.h"
39#include "options.h"
40#include "utility.h"
41#include "partiter.h"
42#include "mangle.h"
43
44static const char cmldr_signature[8] = "cmdcons";
45
46/* Create boot info table: needed when you want to chainload
47 * another version of ISOLINUX (or another bootlaoder that needs
48 * the -boot-info-table switch of mkisofs)
49 * (will only work when run from ISOLINUX)
50 */
51int manglef_isolinux(struct data_area *data)
52{
53    const union syslinux_derivative_info *sdi;
54    unsigned char *isolinux_bin;
55    uint32_t *checksum, *chkhead, *chktail;
56    uint32_t file_lba = 0;
57
58    if (!(opt.file && opt.isolinux))
59        return 0;
60
61    sdi = syslinux_derivative_info();
62
63    if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
64        error("The isolinux= option is only valid when run from ISOLINUX.");
65        goto bail;
66    }
67
68    /* Boot info table info (integers in little endian format)
69
70       Offset Name         Size      Meaning
71       8      bi_pvd       4 bytes   LBA of primary volume descriptor
72       12     bi_file      4 bytes   LBA of boot file
73       16     bi_length    4 bytes   Boot file length in bytes
74       20     bi_csum      4 bytes   32-bit checksum
75       24     bi_reserved  40 bytes  Reserved
76
77       The 32-bit checksum is the sum of all the 32-bit words in the
78       boot file starting at byte offset 64. All linear block
79       addresses (LBAs) are given in CD sectors (normally 2048 bytes).
80
81       LBA of primary volume descriptor should already be set to 16.
82       */
83
84    isolinux_bin = (unsigned char *)data->data;
85
86    /* Get LBA address of bootfile */
87    file_lba = get_file_lba(opt.file);
88
89    if (file_lba == 0) {
90        error("Failed to find LBA offset of the boot file.");
91        goto bail;
92    }
93    /* Set it */
94    *((uint32_t *) & isolinux_bin[12]) = file_lba;
95
96    /* Set boot file length */
97    *((uint32_t *) & isolinux_bin[16]) = data->size;
98
99    /* Calculate checksum */
100    checksum = (uint32_t *) & isolinux_bin[20];
101    chkhead = (uint32_t *) & isolinux_bin[64];
102    chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
103    *checksum = 0;
104    while (chkhead < chktail)
105        *checksum += *chkhead++;
106
107    /*
108     * Deal with possible fractional dword at the end;
109     * this *should* never happen...
110     */
111    if (data->size & 3) {
112        uint32_t xword = 0;
113        memcpy(&xword, chkhead, data->size & 3);
114        *checksum += xword;
115    }
116    return 0;
117bail:
118    return -1;
119}
120
121/*
122 * Legacy grub's stage2 chainloading
123 */
124int manglef_grub(const struct part_iter *iter, struct data_area *data)
125{
126    /* Layout of stage2 file (from byte 0x0 to 0x270) */
127    struct grub_stage2_patch_area {
128        /* 0x0 to 0x205 */
129        char unknown[0x206];
130        /* 0x206: compatibility version number major */
131        uint8_t compat_version_major;
132        /* 0x207: compatibility version number minor */
133        uint8_t compat_version_minor;
134
135        /* 0x208: install_partition variable */
136        struct {
137            /* 0x208: sub-partition in sub-partition part2 */
138            uint8_t part3;
139            /* 0x209: sub-partition in top-level partition */
140            uint8_t part2;
141            /* 0x20a: top-level partiton number */
142            uint8_t part1;
143            /* 0x20b: BIOS drive number (must be 0) */
144            uint8_t drive;
145        } __attribute__ ((packed)) install_partition;
146
147        /* 0x20c: deprecated (historical reason only) */
148        uint32_t saved_entryno;
149        /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
150        uint8_t stage2_id;
151        /* 0x211: force LBA */
152        uint8_t force_lba;
153        /* 0x212: version string (will probably be 0.97) */
154        char version_string[5];
155        /* 0x217: config filename */
156        char config_file[89];
157        /* 0x270: start of code (after jump from 0x200) */
158        char codestart[1];
159    } __attribute__ ((packed)) *stage2;
160
161    if (!(opt.file && opt.grub))
162        return 0;
163
164    if (data->size < sizeof *stage2) {
165        error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.");
166        goto bail;
167    }
168    stage2 = data->data;
169
170    /*
171     * Check the compatibility version number to see if we loaded a real
172     * stage2 file or a stage2 file that we support.
173     */
174    if (stage2->compat_version_major != 3
175            || stage2->compat_version_minor != 2) {
176        error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.");
177        goto bail;
178    }
179
180    /*
181     * GRUB Legacy wants the partition number in the install_partition
182     * variable, located at offset 0x208 of stage2.
183     * When GRUB Legacy is loaded, it is located at memory address 0x8208.
184     *
185     * It looks very similar to the "boot information format" of the
186     * Multiboot specification:
187     *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
188     *
189     *   0x208 = part3: sub-partition in sub-partition part2
190     *   0x209 = part2: sub-partition in top-level partition
191     *   0x20a = part1: top-level partition number
192     *   0x20b = drive: BIOS drive number (must be 0)
193     *
194     * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
195     * another location.
196     *
197     * Partition numbers always start from zero.
198     * Unused partition bytes must be set to 0xFF.
199     *
200     * We only care about top-level partition, so we only need to change
201     * "part1" to the appropriate value:
202     *   -1:   whole drive (default) (-1 = 0xFF)
203     *   0-3:  primary partitions
204     *   4-*:  logical partitions
205     */
206    stage2->install_partition.part1 = iter->index - 1;
207
208    /*
209     * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
210     * config filename. The filename passed via grubcfg= will overwrite
211     * the default config filename "/boot/grub/menu.lst".
212     */
213    if (opt.grubcfg) {
214        if (strlen(opt.grubcfg) >= sizeof stage2->config_file) {
215            error("The config filename length can't exceed 88 characters.");
216            goto bail;
217        }
218
219        strcpy((char *)stage2->config_file, opt.grubcfg);
220    }
221
222    return 0;
223bail:
224    return -1;
225}
226#if 0
227/*
228 * Dell's DRMK chainloading.
229 */
230int manglef_drmk(struct data_area *data)
231{
232    /*
233     * DRMK entry is different than MS-DOS/PC-DOS
234     * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
235     * We only really need 4 new, usable bytes at the end.
236     */
237
238    if (!(opt.file && opt.drmk))
239        return 0;
240
241    uint32_t tsize = (data->size + 19) & 0xfffffff0;
242    const union syslinux_derivative_info *sdi;
243    uint64_t fs_lba;
244
245    sdi = syslinux_derivative_info();
246    /* We should lookup the Syslinux partition offset and use it */
247    fs_lba = *sdi->disk.partoffset;
248
249    /*
250     * fs_lba should be verified against the disk as some DRMK
251     * variants will check and fail if it does not match
252     */
253    dprintf("  fs_lba offset is %d\n", fs_lba);
254    /* DRMK only uses a DWORD */
255    if (fs_lba > 0xffffffff) {
256        error("LBA very large; Only using lower 32 bits; DRMK will probably fail.");
257    }
258    opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;        /* Used before initialized */
259    if (!realloc(data->data, tsize)) {
260        error("Failed to realloc for DRMK.");
261        goto bail;
262    }
263    data->size = tsize;
264    /* ds:bp is assumed by DRMK to be the boot sector */
265    /* offset 28 is the FAT HiddenSectors value */
266    opt.regs.ds = (tsize >> 4) + (opt.fseg - 2);
267    /* "Patch" into tail of the new space */
268    *(uint32_t *)((char*)data->data + tsize - 4) = fs_lba;
269
270    return 0;
271bail:
272    return -1;
273}
274#endif
275/* Adjust BPB common function */
276static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag)
277{
278    int type = bpb_detect(data->data, tag);
279    int off = drvoff_detect(type);
280
281    /* BPB: hidden sectors 64bit - exFAT only for now */
282    if (type == bpbEXF)
283            *(uint64_t *) ((char *)data->data + 0x40) = iter->abs_lba;
284    /* BPB: hidden sectors 32bit*/
285    else if (bpbV34 <= type && type <= bpbV70) {
286        if (iter->abs_lba < ~0u)
287            *(uint32_t *) ((char *)data->data + 0x1c) = iter->abs_lba;
288        else
289            /* won't really help much, but ... */
290            *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
291    /* BPB: hidden sectors 16bit*/
292    } else if (bpbV30 <= type && type <= bpbV32) {
293        if (iter->abs_lba < 0xFFFF)
294            *(uint16_t *) ((char *)data->data + 0x1c) = iter->abs_lba;
295        else
296            /* won't really help much, but ... */
297            *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
298    }
299
300    /* BPB: legacy geometry */
301    if (bpbV30 <= type && type <= bpbV70) {
302        if (iter->di.cbios)
303            *(uint32_t *)((char *)data->data + 0x18) = (iter->di.head << 16) | iter->di.spt;
304        else {
305            if (iter->di.disk & 0x80)
306                *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
307            else
308                *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
309        }
310    }
311    /* BPB: drive */
312    if (off >= 0) {
313        *(uint8_t *)((char *)data->data + off) = (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
314    }
315
316    return 0;
317}
318
319/*
320 * Adjust BPB of a BPB-compatible file
321 */
322int manglef_bpb(const struct part_iter *iter, struct data_area *data)
323{
324    if (!(opt.file && opt.filebpb))
325        return 0;
326
327    return mangle_bpb(iter, data, "file");
328}
329
330/*
331 * Adjust BPB of a sector
332 */
333int mangles_bpb(const struct part_iter *iter, struct data_area *data)
334{
335    if (!(opt.sect && opt.setbpb))
336        return 0;
337
338    return mangle_bpb(iter, data, "sect");
339}
340
341/*
342 * This function performs full BPB patching, analogously to syslinux's
343 * native BSS.
344 */
345int manglesf_bss(struct data_area *sec, struct data_area *fil)
346{
347    int type1, type2;
348    size_t cnt = 0;
349
350    if (!(opt.sect && opt.file && opt.bss))
351        return 0;
352
353    type1 = bpb_detect(fil->data, "bss/file");
354    type2 = bpb_detect(sec->data, "bss/sect");
355
356    if (!type1 || !type2) {
357        error("Couldn't determine the BPB type for option 'bss'.");
358        goto bail;
359    }
360    if (type1 != type2) {
361        error("Option 'bss' can't be used,\n"
362                "when a sector and a file have incompatible BPBs.");
363        goto bail;
364    }
365
366    /* Copy common 2.0 data */
367    memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
368
369    /* Copy 3.0+ data */
370    if (type1 <= bpbV30) {
371        cnt = 0x06;
372    } else if (type1 <= bpbV32) {
373        cnt = 0x08;
374    } else if (type1 <= bpbV34) {
375        cnt = 0x0C;
376    } else if (type1 <= bpbV40) {
377        cnt = 0x2E;
378    } else if (type1 <= bpbVNT) {
379        cnt = 0x3C;
380    } else if (type1 <= bpbV70) {
381        cnt = 0x42;
382    } else if (type1 <= bpbEXF) {
383        cnt = 0x60;
384    }
385    memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
386
387    return 0;
388bail:
389    return -1;
390}
391
392/*
393 * Save sector.
394 */
395int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
396{
397    if (!(opt.sect && opt.save))
398        return 0;
399
400    if (memcmp(org, data->data, data->size)) {
401        if (disk_write_sectors(&iter->di, iter->abs_lba, data->data, 1)) {
402            error("Cannot write the updated sector.");
403            goto bail;
404        }
405        /* function can be called again */
406        memcpy(org, data->data, data->size);
407    }
408
409    return 0;
410bail:
411    return -1;
412}
413
414/*
415 * To boot the Recovery Console of Windows NT/2K/XP we need to write
416 * the string "cmdcons\0" to memory location 0000:7C03.
417 * Memory location 0000:7C00 contains the bootsector of the partition.
418 */
419int mangles_cmldr(struct data_area *data)
420{
421    if (!(opt.sect && opt.cmldr))
422        return 0;
423
424    memcpy((char *)data->data + 3, cmldr_signature, sizeof cmldr_signature);
425    return 0;
426}
427
428/* Set common registers */
429int mangler_init(const struct part_iter *iter)
430{
431    /* Set initial registry values */
432    if (opt.file) {
433        opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.fseg;
434        opt.regs.ip = opt.fip;
435    } else {
436        opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.sseg;
437        opt.regs.ip = opt.sip;
438    }
439
440    if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
441        opt.regs.esp.l = 0x7C00;
442
443    /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
444    opt.regs.ebx.b[0] = opt.regs.edx.b[0] = iter->di.disk;
445
446    return 0;
447}
448
449/* ds:si & ds:bp */
450int mangler_handover(const struct part_iter *iter, const struct data_area *data)
451{
452    if (opt.file && opt.maps && !opt.hptr) {
453        opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
454        opt.regs.ds = opt.sseg;
455        opt.regs.eax.l = 0;
456    } else if (opt.hand) {
457        /* base is really 0x7be */
458        opt.regs.esi.l = opt.regs.ebp.l = data->base;
459        opt.regs.ds = 0;
460        if (iter->index && iter->type == typegpt)   /* must be iterated and GPT */
461            opt.regs.eax.l = 0x54504721;        /* '!GPT' */
462        else
463            opt.regs.eax.l = 0;
464    }
465
466    return 0;
467}
468
469/*
470 * GRLDR of GRUB4DOS wants the partition number in DH:
471 * -1:   whole drive (default)
472 * 0-3:  primary partitions
473 * 4-*:  logical partitions
474 */
475int mangler_grldr(const struct part_iter *iter)
476{
477    if (opt.grldr)
478        opt.regs.edx.b[1] = iter->index - 1;
479
480    return 0;
481}
482
483/*
484 * try to copy values from temporary iterator, if positions match
485 */
486static void mbrcpy(struct part_iter *diter, struct part_iter *siter)
487{
488    if (diter->dos.cebr_lba == siter->dos.cebr_lba &&
489            diter->di.disk == siter->di.disk) {
490        memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr));
491    }
492}
493
494static int fliphide(struct part_iter *iter, struct part_iter *miter)
495{
496    struct disk_dos_part_entry *dp;
497    static const uint16_t mask =
498        (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
499        (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
500    uint8_t t;
501
502    dp = (struct disk_dos_part_entry *)iter->record;
503    t = dp->ostype;
504
505    if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
506        /* It's a hideable partition type */
507        if (miter->index == iter->index || opt.hide & HIDE_REV)
508            t &= ~0x10u;        /* unhide */
509        else
510            t |= 0x10u; /* hide */
511    }
512    if (dp->ostype != t) {
513        dp->ostype = t;
514        return -1;
515    }
516    return 0;
517}
518
519/*
520 * miter - iterator we match against
521 * hide bits meaning:
522 * ..| - enable (1) / disable (0)
523 * .|. - all (1) / pri (0)
524 * |.. - unhide (1) / hide (0)
525 */
526int manglepe_hide(struct part_iter *miter)
527{
528    int wb = 0, werr = 0;
529    struct part_iter *iter = NULL;
530    int ridx;
531
532    if (!(opt.hide & HIDE_ON))
533        return 0;
534
535    if (miter->type != typedos) {
536        error("Option '[un]hide[all]' works only for legacy (DOS) partition scheme.");
537        return -1;
538    }
539
540    if (miter->index > 4 && !(opt.hide & HIDE_EXT))
541        warn("Specified partition is logical, so it can't be unhidden without 'unhideall'.");
542
543    if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
544        return -1;
545
546    while (!pi_next(iter) && !werr) {
547        ridx = iter->index0;
548        if (!(opt.hide & HIDE_EXT) && ridx > 3)
549            break;  /* skip when we're constrained to pri only */
550
551        if (iter->index != -1)
552            wb |= fliphide(iter, miter);
553
554        /*
555         * we have to update mbr and each extended partition, but only if
556         * changes (wb) were detected and there was no prior write error (werr)
557         */
558        if (ridx >= 3 && wb && !werr) {
559            mbrcpy(miter, iter);
560            werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
561            wb = 0;
562        }
563    }
564
565    if (iter->status < 0)
566        goto bail;
567
568    /* last update */
569    if (wb && !werr) {
570        mbrcpy(miter, iter);
571        werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
572    }
573    if (werr)
574        warn("Failed to write E/MBR during '[un]hide[all]'.");
575
576bail:
577    pi_del(&iter);
578    return 0;
579}
580
581static int updchs(struct part_iter *iter, int ext)
582{
583    struct disk_dos_part_entry *dp;
584    uint32_t ochs1, ochs2, lba;
585
586    dp = (struct disk_dos_part_entry *)iter->record;
587    if (!ext) {
588        /* primary or logical */
589        lba = (uint32_t)iter->abs_lba;
590    } else {
591        /* extended */
592        dp += 1;
593        lba = iter->dos.nebr_lba;
594    }
595    ochs1 = *(uint32_t *)dp->start;
596    ochs2 = *(uint32_t *)dp->end;
597
598    /*
599     * We have to be a bit more careful here in case of 0 start and/or length;
600     * start = 0 would be converted to the beginning of the disk (C/H/S =
601     * 0/0/1) or the [B]EBR, length = 0 would actually set the end CHS to be
602     * lower than the start CHS.
603     *
604     * Both are harmless in case of a hole (and in non-hole case will make
605     * partiter complain about corrupt layout if PIF_STRICT is set), but it
606     * makes everything look silly and not really correct.
607     *
608     * Thus the approach as seen below.
609     */
610
611    if (dp->start_lba || iter->index != -1) {
612        lba2chs(&dp->start, &iter->di, lba, L2C_CADD);
613    } else {
614        memset(&dp->start, 0, sizeof dp->start);
615    }
616
617    if ((dp->start_lba || iter->index != -1) && dp->length) {
618        lba2chs(&dp->end, &iter->di, lba + dp->length - 1, L2C_CADD);
619    } else {
620        memset(&dp->end, 0, sizeof dp->end);
621    }
622
623    return
624        *(uint32_t *)dp->start != ochs1 ||
625        *(uint32_t *)dp->end != ochs2;
626}
627
628/*
629 * miter - iterator we match against
630 */
631int manglepe_fixchs(struct part_iter *miter)
632{
633    int wb = 0, werr = 0;
634    struct part_iter *iter = NULL;
635    int ridx;
636
637    if (!opt.fixchs)
638        return 0;
639
640    if (miter->type != typedos) {
641        error("Option 'fixchs' works only for legacy (DOS) partition scheme.");
642        return -1;
643    }
644
645    if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
646        return -1;
647
648    while (!pi_next(iter) && !werr) {
649        ridx = iter->index0;
650
651        wb |= updchs(iter, 0);
652        if (ridx > 3)
653            wb |= updchs(iter, 1);
654
655        /*
656         * we have to update mbr and each extended partition, but only if
657         * changes (wb) were detected and there was no prior write error (werr)
658         */
659        if (ridx >= 3 && wb && !werr) {
660            mbrcpy(miter, iter);
661            werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
662            wb = 0;
663        }
664    }
665
666    if (iter->status < 0)
667        goto bail;
668
669    /* last update */
670    if (wb && !werr) {
671        mbrcpy(miter, iter);
672        werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
673    }
674    if (werr)
675        warn("Failed to write E/MBR during 'fixchs'.");
676
677bail:
678    pi_del(&iter);
679    return 0;
680}
681
682/* vim: set ts=8 sts=4 sw=4 noet: */
Note: See TracBrowser for help on using the repository browser.