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

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

bootstuff

  • Property mode set to 100644
File size: 17.3 KB
RevLine 
[e16e8f2]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 *   This program is free software; you can redistribute it and/or modify
9 *   it under the terms of the GNU General Public License as published by
10 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
11 *   Boston MA 02111-1307, USA; either version 2 of the License, or
12 *   (at your option) any later version; incorporated herein by reference.
13 *
14 * ----------------------------------------------------------------------- */
15
16/*
17 * Please see doc/chain.txt for the detailed documentation.
18 */
19
20#include <com32.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <ctype.h>
24#include <string.h>
25#include <console.h>
26#include <consoles.h>
27#include <minmax.h>
28#include <stdbool.h>
29#include <dprintf.h>
30#include <errno.h>
31#include <unistd.h>
32#include <syslinux/loadfile.h>
33#include <syslinux/bootrm.h>
34#include <syslinux/config.h>
35#include <syslinux/disk.h>
36#include <syslinux/video.h>
37#include "chain.h"
38#include "utility.h"
39#include "options.h"
40#include "partiter.h"
41#include "mangle.h"
42
43static int fixed_cnt = 128;   /* see comments in main() */
44
45static int overlap(const struct data_area *a, const struct data_area *b)
46{
47    return
48        a->base + a->size > b->base &&
49        b->base + b->size > a->base;
50}
51
52static int is_phys(uint8_t sdifs)
53{
54    return
55        sdifs == SYSLINUX_FS_SYSLINUX ||
56        sdifs == SYSLINUX_FS_EXTLINUX ||
57        sdifs == SYSLINUX_FS_ISOLINUX;
58}
59
60/*
61 * Search for a specific drive, based on the MBR signature.
62 * Return drive and iterator at 0th position.
63 */
64static int find_by_sig(uint32_t mbr_sig,
65                        struct part_iter **_boot_part)
66{
67    struct part_iter *iter = NULL;
68    struct disk_info diskinfo;
69    int drive;
70
71    for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
72        if (disk_get_params(drive, &diskinfo))
73            continue;           /* Drive doesn't exist */
74        if (!(iter = pi_begin(&diskinfo, opt.piflags)))
75            continue;
76        /* Check for a matching MBR disk */
77        if (iter->type == typedos && iter->dos.disk_sig == mbr_sig)
78            goto ok;
79        pi_del(&iter);
80    }
81    drive = -1;
82ok:
83    *_boot_part = iter;
84    return drive;
85}
86
87/*
88 * Search for a specific drive/partition, based on the GPT GUID.
89 * Return drive and iterator at proper position.
90 */
91static int find_by_guid(const struct guid *gpt_guid,
92                        struct part_iter **_boot_part)
93{
94    struct part_iter *iter = NULL;
95    struct disk_info diskinfo;
96    int drive;
97
98    for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
99        if (disk_get_params(drive, &diskinfo))
100            continue;           /* Drive doesn't exist */
101        if (!(iter = pi_begin(&diskinfo, opt.piflags)))
102            continue;
103        /* Check for a matching GPT disk/partition guid */
104        if (iter->type == typegpt)
105            do {
106                if (!memcmp(&iter->gpt.part_guid, gpt_guid, sizeof *gpt_guid))
107                    goto ok;
108            } while (!pi_next(iter));
109        pi_del(&iter);
110    }
111    drive = -1;
112ok:
113    *_boot_part = iter;
114    return drive;
115}
116
117/*
118 * Search for a specific drive/partition, based on the GPT label.
119 * Return drive and iterator at proper position.
120 */
121static int find_by_label(const char *label, struct part_iter **_boot_part)
122{
123    struct part_iter *iter = NULL;
124    struct disk_info diskinfo;
125    int drive;
126
127    for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
128        if (disk_get_params(drive, &diskinfo))
129            continue;           /* Drive doesn't exist */
130        if (!(iter = pi_begin(&diskinfo, opt.piflags)))
131            continue;
132        /* Check for a matching GPT partition label */
133        if (iter->type == typegpt)
134            while (!pi_next(iter)) {
135                if (!strcmp(label, iter->gpt.part_label))
136                    goto ok;
137            }
138        pi_del(&iter);
139    }
140    drive = -1;
141ok:
142    *_boot_part = iter;
143    return drive;
144}
145
146static void do_boot(struct data_area *data, int ndata)
147{
148    struct syslinux_memmap *mmap;
149    struct syslinux_movelist *mlist = NULL;
150    addr_t endimage;
151    uint8_t driveno = opt.regs.edx.b[0];
152    uint8_t swapdrive = driveno & 0x80;
153    int i;
154
155    mmap = syslinux_memory_map();
156
157    if (!mmap) {
158        error("Cannot read system memory map.");
159        return;
160    }
161
162    endimage = 0;
163    for (i = 0; i < ndata; i++) {
164        if (data[i].base + data[i].size > endimage)
165            endimage = data[i].base + data[i].size;
166    }
167    if (endimage > dosmax)
168        goto too_big;
169
170    for (i = 0; i < ndata; i++) {
171        if (syslinux_add_movelist(&mlist, data[i].base,
172                                  (addr_t) data[i].data, data[i].size))
173            goto enomem;
174    }
175
176    if (opt.swap && driveno != swapdrive) {
177        static const uint8_t swapstub_master[] = {
178            /* The actual swap code */
179            0x53,               /* 00: push bx */
180            0x0f, 0xb6, 0xda,   /* 01: movzx bx,dl */
181            0x2e, 0x8a, 0x57, 0x60,     /* 04: mov dl,[cs:bx+0x60] */
182            0x5b,               /* 08: pop bx */
183            0xea, 0, 0, 0, 0,   /* 09: jmp far 0:0 */
184            0x90, 0x90,         /* 0E: nop; nop */
185            /* Code to install this in the right location */
186            /* Entry with DS = CS; ES = SI = 0; CX = 256 */
187            0x26, 0x66, 0x8b, 0x7c, 0x4c,       /* 10: mov edi,[es:si+4*0x13] */
188            0x66, 0x89, 0x3e, 0x0a, 0x00,       /* 15: mov [0x0A],edi */
189            0x26, 0x8b, 0x3e, 0x13, 0x04,       /* 1A: mov di,[es:0x413] */
190            0x4f,               /* 1F: dec di */
191            0x26, 0x89, 0x3e, 0x13, 0x04,       /* 20: mov [es:0x413],di */
192            0x66, 0xc1, 0xe7, 0x16,     /* 25: shl edi,16+6 */
193            0x26, 0x66, 0x89, 0x7c, 0x4c,       /* 29: mov [es:si+4*0x13],edi */
194            0x66, 0xc1, 0xef, 0x10,     /* 2E: shr edi,16 */
195            0x8e, 0xc7,         /* 32: mov es,di */
196            0x31, 0xff,         /* 34: xor di,di */
197            0xf3, 0x66, 0xa5,   /* 36: rep movsd */
198            0xbe, 0, 0,         /* 39: mov si,0 */
199            0xbf, 0, 0,         /* 3C: mov di,0 */
200            0x8e, 0xde,         /* 3F: mov ds,si */
201            0x8e, 0xc7,         /* 41: mov es,di */
202            0x66, 0xb9, 0, 0, 0, 0,     /* 43: mov ecx,0 */
203            0x66, 0xbe, 0, 0, 0, 0,     /* 49: mov esi,0 */
204            0x66, 0xbf, 0, 0, 0, 0,     /* 4F: mov edi,0 */
205            0xea, 0, 0, 0, 0,   /* 55: jmp 0:0 */
206            /* pad out to segment boundary */
207            0x90, 0x90,         /* 5A: ... */
208            0x90, 0x90, 0x90, 0x90,     /* 5C: ... */
209        };
210        static uint8_t swapstub[1024];
211        uint8_t *p;
212
213        /* Note: we can't rely on either INT 13h nor the dosmax
214           vector to be correct at this stage, so we have to use an
215           installer stub to put things in the right place.
216           Round the installer location to a 1K boundary so the only
217           possible overlap is the identity mapping. */
218        endimage = (endimage + 1023u) & ~1023u;
219
220        /* Create swap stub */
221        memcpy(swapstub, swapstub_master, sizeof swapstub_master);
222        *(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
223        *(uint16_t *) & swapstub[0x3d] = opt.regs.es;
224        *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
225        *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
226        *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
227        *(uint16_t *) & swapstub[0x56] = opt.regs.ip;
228        *(uint16_t *) & swapstub[0x58] = opt.regs.cs;
229        p = &swapstub[sizeof swapstub_master];
230
231        /* Mapping table; start out with identity mapping everything */
232        for (i = 0; i < 256; i++)
233            p[i] = i;
234
235        /* And the actual swap */
236        p[driveno] = swapdrive;
237        p[swapdrive] = driveno;
238
239        /* Adjust registers */
240        opt.regs.ds = opt.regs.cs = endimage >> 4;
241        opt.regs.esi.l = opt.regs.es = 0;
242        opt.regs.ecx.l = sizeof swapstub >> 2;
243        opt.regs.ip = 0x10;     /* Installer offset */
244        opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;
245
246        if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
247                                  sizeof swapstub))
248            goto enomem;
249
250        endimage += sizeof swapstub;
251    }
252
253    /* Tell the shuffler not to muck with this area... */
254    syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
255
256    /* Force text mode */
257    syslinux_force_text_mode();
258
259    puts("Booting...");
260    syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
261    error("Chainboot failed !");
262    return;
263
264too_big:
265    error("Loader file too large.");
266    return;
267
268enomem:
269    error("Out of memory.");
270    return;
271}
272
273int find_dp(struct part_iter **_iter)
274{
275    struct part_iter *iter = NULL;
276    struct disk_info diskinfo;
277    struct guid gpt_guid;
278    uint64_t fs_lba;
279    int drive, hd, partition;
280    const union syslinux_derivative_info *sdi;
281
282    sdi = syslinux_derivative_info();
283
284    if (!strncmp(opt.drivename, "mbr", 3)) {
285        if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
286            error("Unable to find requested MBR signature.");
287            goto bail;
288        }
289    } else if (!strncmp(opt.drivename, "guid", 4)) {
290        if (str_to_guid(opt.drivename + 5, &gpt_guid))
291            goto bail;
292        if (find_by_guid(&gpt_guid, &iter) < 0) {
293            error("Unable to find requested GPT disk or partition by guid.");
294            goto bail;
295        }
296    } else if (!strncmp(opt.drivename, "label", 5)) {
297        if (!opt.drivename[6]) {
298            error("No label specified.");
299            goto bail;
300        }
301        if (find_by_label(opt.drivename + 6, &iter) < 0) {
302            error("Unable to find requested GPT partition by label.");
303            goto bail;
304        }
305    } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
306               opt.drivename[1] == 'd') {
307        hd = opt.drivename[0] == 'h' ? 0x80 : 0;
308        opt.drivename += 2;
309        drive = hd | strtol(opt.drivename, NULL, 0);
310
311        if (disk_get_params(drive, &diskinfo))
312            goto bail;
313        /* this will start iteration over FDD, possibly raw */
314        if (!(iter = pi_begin(&diskinfo, opt.piflags)))
315            goto bail;
316
317    } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
318        if (!is_phys(sdi->c.filesystem)) {
319            error("When syslinux is not booted from physical disk (or its emulation),\n"
320                   "'boot' and 'fs' are meaningless.");
321            goto bail;
322        }
323        /* offsets match, but in case it changes in the future */
324        if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
325            drive = sdi->iso.drive_number;
326            fs_lba = *sdi->iso.partoffset;
327        } else {
328            drive = sdi->disk.drive_number;
329            fs_lba = *sdi->disk.partoffset;
330        }
331        if (disk_get_params(drive, &diskinfo))
332            goto bail;
333        /* this will start iteration over disk emulation, possibly raw */
334        if (!(iter = pi_begin(&diskinfo, opt.piflags)))
335            goto bail;
336
337        /* 'fs' => we should lookup the syslinux partition number and use it */
338        if (!strcmp(opt.drivename, "fs")) {
339            do {
340                if (iter->abs_lba == fs_lba)
341                    break;
342            } while (!pi_next(iter));
343            /* broken part structure or other problems */
344            if (iter->status) {
345                error("Unable to find partition with syslinux (fs).");
346                goto bail;
347            }
348        }
349    } else {
350        error("Unparsable drive specification.");
351        goto bail;
352    }
353    /* main options done - only thing left is explicit partition specification,
354     * if we're still at the disk stage with the iterator AND user supplied
355     * partition number (including disk pseudo-partition).
356     */
357    if (!iter->index && opt.partition) {
358        partition = strtol(opt.partition, NULL, 0);
359        /* search for matching part#, including disk */
360        do {
361            if (iter->index == partition)
362                break;
363        } while (!pi_next(iter));
364        if (iter->status) {
365            error("Unable to find requested disk / partition combination.");
366            goto bail;
367        }
368    }
369
370    if (!(iter->di.disk & 0x80) && iter->index) {
371        warn("Partitions on floppy devices may not work.");
372    }
373
374    *_iter = iter;
375
376    return 0;
377
378bail:
379    pi_del(&iter);
380    return -1;
381}
382
383static int setup_handover(const struct part_iter *iter,
384                   struct data_area *data)
385{
386    struct disk_dos_part_entry *ha;
387    uint32_t synth_size = sizeof *ha;
388
389    /*
390     * we have to cover both non-iterated but otherwise properly detected
391     * gpt/dos schemes as well as raw disks; checking index for 0 covers both
392     */
393    if (iter->index == 0) {
394        uint32_t len;
395        /* RAW handover protocol */
396        ha = malloc(synth_size);
397        if (!ha) {
398            critm();
399            goto bail;
400        }
401        len = ~0u;
402        if (iter->length < len)
403            len = iter->length;
404        lba2chs(&ha->start, &iter->di, 0, L2C_CADD);
405        lba2chs(&ha->end, &iter->di, len - 1, L2C_CADD);
406        ha->active_flag = 0x80;
407        ha->ostype = 0xDA;      /* "Non-FS Data", anything is good here though ... */
408        ha->start_lba = 0;
409        ha->length = len;
410    } else if (iter->type == typegpt) {
411        uint32_t *plen;
412        /* GPT handover protocol */
413        synth_size += sizeof *plen + iter->gpt.pe_size;
414        ha = malloc(synth_size);
415        if (!ha) {
416            critm();
417            goto bail;
418        }
419        lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
420        lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
421        ha->active_flag = 0x80;
422        ha->ostype = 0xED;
423        /* All bits set by default */
424        ha->start_lba = ~0u;
425        ha->length = ~0u;
426        /* If these fit the precision, pass them on */
427        if (iter->abs_lba < ha->start_lba)
428            ha->start_lba = iter->abs_lba;
429        if (iter->length < ha->length)
430            ha->length = iter->length;
431        /* Next comes the GPT partition record length */
432        plen = (uint32_t *)(ha + 1);
433        plen[0] = iter->gpt.pe_size;
434        /* Next comes the GPT partition record copy */
435        memcpy(plen + 1, iter->record, plen[0]);
436#ifdef DEBUG
437        dprintf("GPT handover:\n");
438        disk_dos_part_dump(ha);
439        disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
440#endif
441    /* the only possible case left is dos scheme */
442    } else if (iter->type == typedos) {
443        /* MBR handover protocol */
444        ha = malloc(synth_size);
445        if (!ha) {
446            critm();
447            goto bail;
448        }
449        memcpy(ha, iter->record, synth_size);
450        /* make sure these match bios imaginations and are ebr agnostic */
451        lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
452        lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
453        ha->start_lba = iter->abs_lba;
454        ha->length = iter->length;
455
456#ifdef DEBUG
457        dprintf("MBR handover:\n");
458        disk_dos_part_dump(ha);
459#endif
460    } else {
461        /* shouldn't ever happen */
462        goto bail;
463    }
464
465    data->base = 0x7be;
466    data->size = synth_size;
467    data->data = (void *)ha;
468
469    return 0;
470bail:
471    return -1;
472}
473
474int main(int argc, char *argv[])
475{
476    struct part_iter *iter = NULL;
477    void *sbck = NULL;
478    struct data_area fdat, hdat, sdat, data[3];
479    int ndata = 0;
480
481    console_ansi_raw();
482
483    memset(&fdat, 0, sizeof fdat);
484    memset(&hdat, 0, sizeof hdat);
485    memset(&sdat, 0, sizeof sdat);
486
487    opt_set_defs();
488    if (opt_parse_args(argc, argv))
489        goto bail;
490
491#if 0
492    /* Get max fixed disk number */
493    fixed_cnt = *(uint8_t *)(0x475);
494
495    /*
496     * hmm, looks like we can't do that -
497     * some bioses/vms just set it to 1
498     * and go on living happily
499     * any better options than hardcoded 0x80 - 0xFF ?
500     */
501#endif
502
503    /* Get disk/part iterator matching user supplied options */
504    if (find_dp(&iter))
505        goto bail;
506
507    /* Perform initial partition entry mangling */
508    if (manglepe_fixchs(iter))
509        goto bail;
510    if (manglepe_hide(iter))
511        goto bail;
512
513    /* Load the boot file */
514    if (opt.file) {
515        fdat.base = (opt.fseg << 4) + opt.foff;
516
517        if (loadfile(opt.file, &fdat.data, &fdat.size)) {
518            error("Couldn't read the boot file.");
519            goto bail;
520        }
521        if (fdat.base + fdat.size > dosmax) {
522            error("The boot file is too big to load at this address.");
523            goto bail;
524        }
525    }
526
527    /* Load the sector */
528    if (opt.sect) {
529        sdat.base = (opt.sseg << 4) + opt.soff;
530        sdat.size = iter->di.bps;
531
532        if (sdat.base + sdat.size > dosmax) {
533            error("The sector cannot be loaded at such high address.");
534            goto bail;
535        }
536        if (!(sdat.data = disk_read_sectors(&iter->di, iter->abs_lba, 1))) {
537            error("Couldn't read the sector.");
538            goto bail;
539        }
540        if (opt.save) {
541            if (!(sbck = malloc(sdat.size))) {
542                critm();
543                goto bail;
544            }
545            memcpy(sbck, sdat.data, sdat.size);
546        }
547        if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
548            warn("The sector won't be mmapped, as it would conflict with the boot file.");
549            opt.maps = false;
550        }
551    }
552
553    /* Prep the handover */
554    if (opt.hand) {
555        if (setup_handover(iter, &hdat))
556            goto bail;
557        /* Verify possible conflicts */
558        if ( ( opt.file && overlap(&fdat, &hdat)) ||
559             ( opt.maps && overlap(&sdat, &hdat)) ) {
560            warn("Handover area won't be prepared,\n"
561                  "as it would conflict with the boot file and/or the sector.");
562            opt.hand = false;
563        }
564    }
565
566    /* Adjust registers */
567
568    mangler_init(iter);
569    mangler_handover(iter, &hdat);
570    mangler_grldr(iter);
571
572    /* Patching functions */
573
574    if (manglef_isolinux(&fdat))
575        goto bail;
576
577    if (manglef_grub(iter, &fdat))
578        goto bail;
579#if 0
580    if (manglef_drmk(&fdat))
581        goto bail;
582#endif
583    if (manglef_bpb(iter, &fdat))
584        goto bail;
585
586    if (mangles_bpb(iter, &sdat))
587        goto bail;
588
589    if (mangles_save(iter, &sdat, sbck))
590        goto bail;
591
592    if (manglesf_bss(&sdat, &fdat))
593        goto bail;
594
595    /* This *must* be after BPB saving or copying */
596    if (mangles_cmldr(&sdat))
597        goto bail;
598
599    /*
600     * Prepare boot-time mmap data. We should to it here, as manglers could
601     * potentially alter some of the data.
602     */
603
604    if (opt.file)
605        memcpy(data + ndata++, &fdat, sizeof fdat);
606    if (opt.maps)
607        memcpy(data + ndata++, &sdat, sizeof sdat);
608    if (opt.hand)
609        memcpy(data + ndata++, &hdat, sizeof hdat);
610
611#ifdef DEBUG
612    dprintf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
613           "iter->di C, H, S: %u, %u, %u\n",
614        iter->di.disk, iter->di.bps,
615        iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
616        iter->di.cyl, iter->di.head, iter->di.spt);
617    dprintf("iter idx: %d\n", iter->index);
618    dprintf("iter lba: %"PRIu64"\n", iter->abs_lba);
619    if (opt.hand)
620        dprintf("hand lba: %u\n",
621                ((struct disk_dos_part_entry *)hdat.data)->start_lba);
622#endif
623
624    if (opt.warn) {
625        puts("Press any key to continue booting...");
626        wait_key();
627    }
628
629    if (ndata && !opt.brkchain) /* boot only if we actually chainload */
630        do_boot(data, ndata);
631    else
632        puts("Service-only run completed, exiting.");
633bail:
634    pi_del(&iter);
635    /* Free allocated areas */
636    free(fdat.data);
637    free(sdat.data);
638    free(hdat.data);
639    free(sbck);
640    return 255;
641}
642
643/* vim: set ts=8 sts=4 sw=4 noet: */
Note: See TracBrowser for help on using the repository browser.