3bd2a318fd76c362ed01b199c4fd3e1a415fdd7f
[profile/ivi/syslinux.git] / com32 / chain / chain.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5  *   Significant portions copyright (C) 2010 Shao Miller
6  *                                      [partition iteration, GPT, "fs"]
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 "common.h"
38 #include "chain.h"
39 #include "utility.h"
40 #include "options.h"
41 #include "partiter.h"
42 #include "mangle.h"
43
44 struct options opt;
45
46 static int fixed_cnt;
47
48 static int overlap(const struct data_area *a, const struct data_area *b)
49 {
50     return
51         a->base + a->size > b->base &&
52         b->base + b->size > a->base;
53 }
54
55 static int is_phys(uint8_t sdifs)
56 {
57     return
58         sdifs == SYSLINUX_FS_SYSLINUX ||
59         sdifs == SYSLINUX_FS_EXTLINUX ||
60         sdifs == SYSLINUX_FS_ISOLINUX;
61 }
62
63 /*
64  * Search for a specific drive, based on the MBR signature.
65  * Return drive and iterator at 0th position.
66  */
67 static int find_by_sig(uint32_t mbr_sig,
68                         struct part_iter **_boot_part)
69 {
70     struct part_iter *boot_part = NULL;
71     struct disk_info diskinfo;
72     int drive;
73
74     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
75         if (disk_get_params(drive, &diskinfo))
76             continue;           /* Drive doesn't exist */
77         if (!(boot_part = pi_begin(&diskinfo, 0)))
78             continue;
79         /* Check for a MBR disk */
80         if (boot_part->type != typedos) {
81             pi_del(&boot_part);
82             continue;
83         }
84         if (boot_part->sub.dos.disk_sig == mbr_sig) {
85             goto ok;
86         }
87     }
88     drive = -1;
89 ok:
90     *_boot_part = boot_part;
91     return drive;
92 }
93
94 /*
95  * Search for a specific drive/partition, based on the GPT GUID.
96  * Return drive and iterator at proper position.
97  */
98 static int find_by_guid(const struct guid *gpt_guid,
99                         struct part_iter **_boot_part)
100 {
101     struct part_iter *boot_part = NULL;
102     struct disk_info diskinfo;
103     int drive;
104
105     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
106         if (disk_get_params(drive, &diskinfo))
107             continue;           /* Drive doesn't exist */
108         if (!(boot_part = pi_begin(&diskinfo, 0)))
109             continue;
110         /* Check for a GPT disk */
111         if (boot_part->type != typegpt) {
112             pi_del(&boot_part);
113             continue;
114         }
115         /* Check for a matching GPT disk guid */
116         if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
117             goto ok;
118         }
119         /* disk guid doesn't match, maybe partition guid will */
120         while (pi_next(&boot_part)) {
121             if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
122                 goto ok;
123         }
124     }
125     drive = -1;
126 ok:
127     *_boot_part = boot_part;
128     return drive;
129 }
130
131 /*
132  * Search for a specific drive/partition, based on the GPT label.
133  * Return drive and iterator at proper position.
134  */
135 static int find_by_label(const char *label, struct part_iter **_boot_part)
136 {
137     struct part_iter *boot_part = NULL;
138     struct disk_info diskinfo;
139     int drive;
140
141     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
142         if (disk_get_params(drive, &diskinfo))
143             continue;           /* Drive doesn't exist */
144         if (!(boot_part = pi_begin(&diskinfo, 0)))
145             continue;
146         /* Check for a GPT disk */
147         if (!(boot_part->type == typegpt)) {
148             pi_del(&boot_part);
149             continue;
150         }
151         /* Check for a matching partition */
152         while (pi_next(&boot_part)) {
153             if (!strcmp(label, boot_part->sub.gpt.part_label))
154                 goto ok;
155         }
156     }
157     drive = -1;
158 ok:
159     *_boot_part = boot_part;
160     return drive;
161 }
162
163 static void do_boot(struct data_area *data, int ndata)
164 {
165     uint16_t *const bios_fbm = (uint16_t *) 0x413;
166     addr_t dosmem = (addr_t)(*bios_fbm << 10);  /* Technically a low bound */
167     struct syslinux_memmap *mmap;
168     struct syslinux_movelist *mlist = NULL;
169     addr_t endimage;
170     uint8_t driveno = opt.regs.edx.b[0];
171     uint8_t swapdrive = driveno & 0x80;
172     int i;
173
174     mmap = syslinux_memory_map();
175
176     if (!mmap) {
177         error("Cannot read system memory map\n");
178         return;
179     }
180
181     endimage = 0;
182     for (i = 0; i < ndata; i++) {
183         if (data[i].base + data[i].size > endimage)
184             endimage = data[i].base + data[i].size;
185     }
186     if (endimage > dosmem)
187         goto too_big;
188
189     for (i = 0; i < ndata; i++) {
190         if (syslinux_add_movelist(&mlist, data[i].base,
191                                   (addr_t) data[i].data, data[i].size))
192             goto enomem;
193     }
194
195     if (opt.swap && driveno != swapdrive) {
196         static const uint8_t swapstub_master[] = {
197             /* The actual swap code */
198             0x53,               /* 00: push bx */
199             0x0f, 0xb6, 0xda,   /* 01: movzx bx,dl */
200             0x2e, 0x8a, 0x57, 0x60,     /* 04: mov dl,[cs:bx+0x60] */
201             0x5b,               /* 08: pop bx */
202             0xea, 0, 0, 0, 0,   /* 09: jmp far 0:0 */
203             0x90, 0x90,         /* 0E: nop; nop */
204             /* Code to install this in the right location */
205             /* Entry with DS = CS; ES = SI = 0; CX = 256 */
206             0x26, 0x66, 0x8b, 0x7c, 0x4c,       /* 10: mov edi,[es:si+4*0x13] */
207             0x66, 0x89, 0x3e, 0x0a, 0x00,       /* 15: mov [0x0A],edi */
208             0x26, 0x8b, 0x3e, 0x13, 0x04,       /* 1A: mov di,[es:0x413] */
209             0x4f,               /* 1F: dec di */
210             0x26, 0x89, 0x3e, 0x13, 0x04,       /* 20: mov [es:0x413],di */
211             0x66, 0xc1, 0xe7, 0x16,     /* 25: shl edi,16+6 */
212             0x26, 0x66, 0x89, 0x7c, 0x4c,       /* 29: mov [es:si+4*0x13],edi */
213             0x66, 0xc1, 0xef, 0x10,     /* 2E: shr edi,16 */
214             0x8e, 0xc7,         /* 32: mov es,di */
215             0x31, 0xff,         /* 34: xor di,di */
216             0xf3, 0x66, 0xa5,   /* 36: rep movsd */
217             0xbe, 0, 0,         /* 39: mov si,0 */
218             0xbf, 0, 0,         /* 3C: mov di,0 */
219             0x8e, 0xde,         /* 3F: mov ds,si */
220             0x8e, 0xc7,         /* 41: mov es,di */
221             0x66, 0xb9, 0, 0, 0, 0,     /* 43: mov ecx,0 */
222             0x66, 0xbe, 0, 0, 0, 0,     /* 49: mov esi,0 */
223             0x66, 0xbf, 0, 0, 0, 0,     /* 4F: mov edi,0 */
224             0xea, 0, 0, 0, 0,   /* 55: jmp 0:0 */
225             /* pad out to segment boundary */
226             0x90, 0x90,         /* 5A: ... */
227             0x90, 0x90, 0x90, 0x90,     /* 5C: ... */
228         };
229         static uint8_t swapstub[1024];
230         uint8_t *p;
231
232         /* Note: we can't rely on either INT 13h nor the dosmem
233            vector to be correct at this stage, so we have to use an
234            installer stub to put things in the right place.
235            Round the installer location to a 1K boundary so the only
236            possible overlap is the identity mapping. */
237         endimage = (endimage + 1023u) & ~1023u;
238
239         /* Create swap stub */
240         memcpy(swapstub, swapstub_master, sizeof swapstub_master);
241         *(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
242         *(uint16_t *) & swapstub[0x3d] = opt.regs.es;
243         *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
244         *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
245         *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
246         *(uint16_t *) & swapstub[0x56] = opt.regs.ip;
247         *(uint16_t *) & swapstub[0x58] = opt.regs.cs;
248         p = &swapstub[sizeof swapstub_master];
249
250         /* Mapping table; start out with identity mapping everything */
251         for (i = 0; i < 256; i++)
252             p[i] = (uint8_t)i;
253
254         /* And the actual swap */
255         p[driveno] = swapdrive;
256         p[swapdrive] = driveno;
257
258         /* Adjust registers */
259         opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4);
260         opt.regs.esi.l = opt.regs.es = 0;
261         opt.regs.ecx.l = sizeof swapstub >> 2;
262         opt.regs.ip = 0x10;     /* Installer offset */
263         opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;
264
265         if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
266                                   sizeof swapstub))
267             goto enomem;
268
269         endimage += sizeof swapstub;
270     }
271
272     /* Tell the shuffler not to muck with this area... */
273     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
274
275     /* Force text mode */
276     syslinux_force_text_mode();
277
278     fputs("Booting...\n", stdout);
279     syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
280     error("Chainboot failed!\n");
281     return;
282
283 too_big:
284     error("Loader file too large\n");
285     return;
286
287 enomem:
288     error("Out of memory\n");
289     return;
290 }
291 #if 0
292 static void hide_unhide(const struct part_iter *_iter)
293 {
294     int i;
295     struct disk_dos_mbr *mbr = NULL;
296     struct disk_dos_part_entry *pt;
297     const uint16_t mask =
298         (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
299         (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
300     uint8_t t;
301     bool write_back = false;
302
303     if (_iter->type != typedos) {
304         error("Option 'hide' is only meaningful for legacy partition scheme.");
305         goto bail;
306     }
307     if (!(mbr = disk_read_sectors(&_iter->di, 0, 1))) {
308         error("WARNING: Couldn't read MBR to hide/unhide partitions.\n");
309         goto bail;
310     }
311
312     if (_iter->index < 1 || _iter->index > 4)
313         error("WARNING: option 'hide' specified with a non-primary partition.\n");
314
315     for (i = 1; i <= 4; i++) {
316         pt = mbr->table + i - 1;
317         t = pt->ostype;
318         if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
319             /* It's a hideable partition type */
320             if (i == _iter->index)
321                 t &= (uint8_t)(~0x10u); /* unhide */
322             else
323                 t |= 0x10u;     /* hide */
324         }
325         if (t != pt->ostype) {
326             write_back = true;
327             pt->ostype = t;
328         }
329     }
330     if (write_back && disk_write_verify_sector(&_iter->di, 0, mbr))
331         error("WARNING: failed to write MBR for option 'hide'\n");
332
333 bail:
334     free(mbr);
335 }
336 #endif
337 static int pem_sethide(struct disk_dos_part_entry *dp, int midx, int idx)
338 {
339     static const uint16_t mask =
340         (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
341         (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
342     uint8_t t;
343
344     t = dp->ostype;
345     if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
346         /* It's a hideable partition type */
347         if (midx == idx)
348             t &= (uint8_t)(~0x10u);     /* unhide */
349         else
350             t |= 0x10u; /* hide */
351     }
352     if (t != dp->ostype) {
353         dp->ostype = t;
354         return -1;
355     }
356     return 0;
357 }
358
359 static int pem_setchs(const struct disk_info *di,
360                      struct disk_dos_part_entry *dp,
361                      uint32_t lba1)
362 {
363     uint32_t ochs1, ochs2;
364
365     ochs1 = *(uint32_t *)dp->start;
366     ochs2 = *(uint32_t *)dp->end;
367
368     *(uint32_t *)dp->start =
369         lba2chs(di, lba1) |
370         (*(uint32_t *)dp->start & 0xFF000000);
371
372     *(uint32_t *)dp->end =
373         lba2chs(di, lba1 + dp->length - 1) |
374         (*(uint32_t *)dp->end & 0xFF000000);
375
376     return
377         *(uint32_t *)dp->start != ochs1 ||
378         *(uint32_t *)dp->end != ochs2;
379 }
380
381 static int pentry_mangle(struct part_iter *_iter)
382 {
383     int wb = 0, werr = 0;
384     uint32_t cebr_lba = 0;
385     struct part_iter *iter = NULL;
386     struct disk_dos_part_entry *dp;
387     struct disk_dos_mbr mbr;
388     int ridx;
389
390     if (_iter->type != typedos) {
391         error("Partition entry mangling ('hide[all]', 'mbrchs')\n"
392               "is meaningful only for legacy partition scheme.");
393         goto bail;
394     }
395     if ((_iter->index < 1 || _iter->index > 4) && opt.hide == 1)
396         error("WARNING: option 'hide' specified with a non-primary partition.\n");
397
398     if (!(iter = pi_begin(&_iter->di, 1)))  /* turn on stepall */
399         goto bail;
400
401     memcpy(&mbr, iter->data, sizeof(struct disk_dos_mbr));
402
403     while (pi_next(&iter) && !werr) {
404         ridx = iter->rawindex;
405         if (ridx > 4) {
406             if (opt.hide < 2 && !opt.mbrchs)
407                 break;  /* don't walk unnecessarily */
408             if (wb && !werr) {
409                 werr |= disk_write_sector(&iter->di, cebr_lba, &mbr);
410                 wb = false;
411             }
412             memcpy(&mbr, iter->data, sizeof(struct disk_dos_mbr));
413             cebr_lba = iter->sub.dos.cebr_lba;
414             dp = mbr.table;
415         } else
416             dp = mbr.table + ridx - 1;
417         if (opt.hide == 2 ||
418             (opt.hide == 1 && ridx <= 4)) {
419             wb |= pem_sethide(dp, _iter->index, iter->index);
420             if (_iter->index == iter->index) {
421                 ((struct disk_dos_part_entry *)_iter->record)->ostype =
422                     dp->ostype;
423             }
424         }
425         if (opt.mbrchs) {
426             wb |= pem_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
427             if (ridx > 4)
428                 wb |= pem_setchs(&iter->di, mbr.table + 1, iter->sub.dos.ebr_lba);
429         }
430     }
431     /* last write */
432     if (wb && !werr)
433         werr |= disk_write_sector(&_iter->di, cebr_lba, &mbr);
434
435 bail:
436     pi_del(&iter);
437     if (werr)
438         error("WARNING: failed to write E/MBR for partition\n"
439               "mangling options ('hide[all]', 'mbrchs').\n");
440     return 0;
441 }
442
443 int find_dp(struct part_iter **_iter)
444 {
445     struct part_iter *iter;
446     struct disk_info diskinfo;
447     struct guid gpt_guid;
448     uint64_t fs_lba;
449     int drive, hd, partition;
450     const union syslinux_derivative_info *sdi;
451
452     sdi = syslinux_derivative_info();
453
454     if (!strncmp(opt.drivename, "mbr", 3)) {
455         if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter)) {
456             error("Unable to find requested MBR signature.\n");
457             goto bail;
458         }
459     } else if (!strncmp(opt.drivename, "guid", 4)) {
460         if (str_to_guid(opt.drivename + 5, &gpt_guid))
461             goto bail;
462         if (find_by_guid(&gpt_guid, &iter)) {
463             error("Unable to find requested GPT disk or partition by guid.\n");
464             goto bail;
465         }
466     } else if (!strncmp(opt.drivename, "label", 5)) {
467         if (!opt.drivename[6]) {
468             error("No label specified.\n");
469             goto bail;
470         }
471         if (find_by_label(opt.drivename + 6, &iter)) {
472             error("Unable to find requested GPT partition by label.\n");
473             goto bail;
474         }
475     } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
476                opt.drivename[1] == 'd') {
477         hd = opt.drivename[0] == 'h' ? 0x80 : 0;
478         opt.drivename += 2;
479         drive = hd | strtol(opt.drivename, NULL, 0);
480
481         if (disk_get_params(drive, &diskinfo))
482             goto bail;
483         /* this will start iteration over FDD, possibly raw */
484         if (!(iter = pi_begin(&diskinfo, 0)))
485             goto bail;
486
487     } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
488         if (!is_phys(sdi->c.filesystem)) {
489             error("When syslinux is not booted from physical disk (or its emulation),\n"
490                    "'boot' and 'fs' are meaningless.\n");
491             goto bail;
492         }
493         /* offsets match, but in case it changes in the future */
494         if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
495             drive = sdi->iso.drive_number;
496             fs_lba = *sdi->iso.partoffset;
497         } else {
498             drive = sdi->disk.drive_number;
499             fs_lba = *sdi->disk.partoffset;
500         }
501         if (disk_get_params(drive, &diskinfo))
502             goto bail;
503         /* this will start iteration over disk emulation, possibly raw */
504         if (!(iter = pi_begin(&diskinfo, 0)))
505             goto bail;
506
507         /* 'fs' => we should lookup the syslinux partition number and use it */
508         if (!strcmp(opt.drivename, "fs")) {
509             while (pi_next(&iter)) {
510                 if (iter->start_lba == fs_lba)
511                     break;
512             }
513             /* broken part structure or other problems */
514             if (!iter) {
515                 error("Can't find myself on the drive I booted from.\n");
516                 goto bail;
517             }
518         }
519     } else {
520         error("Unparsable drive specification.\n");
521         goto bail;
522     }
523     /* main options done - only thing left is explicit partition specification,
524      * if we're still at the disk stage with the iterator AND user supplied
525      * partition number (including disk pseudo-partition).
526      */
527     if (!iter->index && opt.partition) {
528         partition = strtol(opt.partition, NULL, 0);
529         /* search for matching part#, including disk */
530         do {
531             if (iter->index == partition)
532                 break;
533         } while (pi_next(&iter));
534         if (!iter) {
535             error("Requested disk / partition combination not found.\n");
536             goto bail;
537         }
538     }
539
540     if (!(iter->di.disk & 0x80) && iter->index) {
541         error("WARNING: Partitions on floppy devices may not work.\n");
542     }
543
544     *_iter = iter;
545
546     return 0;
547
548 bail:
549     return -1;
550 }
551
552 static int setup_handover(const struct part_iter *iter,
553                    struct data_area *data)
554 {
555     const struct disk_dos_part_entry *dp;
556     const struct disk_gpt_part_entry *gp;
557     struct disk_dos_part_entry *ha;
558     uint64_t lba_count;
559     uint32_t synth_size;
560     uint32_t *plen;
561
562     if (iter->type == typegpt) {
563         /* GPT handover protocol */
564         gp = (const struct disk_gpt_part_entry *)iter->record;
565         lba_count = gp->lba_last - gp->lba_first + 1;
566         synth_size = sizeof(struct disk_dos_part_entry) +
567             sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
568
569         ha = malloc(synth_size);
570         if (!ha) {
571             error("Could not build GPT hand-over record!\n");
572             goto bail;
573         }
574         memset(ha, 0, synth_size);
575         *(uint32_t *)ha->start = lba2chs(&iter->di, gp->lba_first);
576         *(uint32_t *)ha->end = lba2chs(&iter->di, gp->lba_last);
577         ha->active_flag = 0x80;
578         ha->ostype = 0xED;
579         /* All bits set by default */
580         ha->start_lba = ~0u;
581         ha->length = ~0u;
582         /* If these fit the precision, pass them on */
583         if (iter->start_lba < ha->start_lba)
584             ha->start_lba = (uint32_t)iter->start_lba;
585         if (lba_count < ha->length)
586             ha->length = (uint32_t)lba_count;
587         /* Next comes the GPT partition record length */
588         plen = (uint32_t *) (ha + 1);
589         plen[0] = (uint32_t)iter->sub.gpt.pe_size;
590         /* Next comes the GPT partition record copy */
591         memcpy(plen + 1, gp, plen[0]);
592 #ifdef DEBUG
593         dprintf("GPT handover:\n");
594         disk_dos_part_dump(ha);
595         disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
596 #endif
597     } else if (iter->type == typedos) {
598         /* MBR handover protocol */
599         dp = (const struct disk_dos_part_entry *)iter->record;
600         synth_size = sizeof(struct disk_dos_part_entry);
601         ha = malloc(synth_size);
602         if (!ha) {
603             error("Could not build MBR hand-over record!\n");
604             goto bail;
605         }
606
607         *(uint32_t *)ha->start = lba2chs(&iter->di, iter->start_lba);
608         *(uint32_t *)ha->end = lba2chs(&iter->di, iter->start_lba + dp->length - 1);
609         ha->active_flag = dp->active_flag;
610         ha->ostype = dp->ostype;
611         ha->start_lba = (uint32_t)iter->start_lba;  /* fine, we iterate over legacy scheme */
612         ha->length = dp->length;
613
614 #ifdef DEBUG
615         dprintf("MBR handover:\n");
616         disk_dos_part_dump(ha);
617 #endif
618     } else {
619         /* shouldn't ever happen */
620         goto bail;
621     }
622
623     data->base = 0x7be;
624     data->size = synth_size;
625     data->data = (void *)ha;
626
627     return 0;
628 bail:
629     return -1;
630 }
631
632 int setdrv_auto(const struct part_iter *iter)
633 {
634     int a, b;
635     char *buf;
636
637     if (!(buf = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
638         error("Couldn't read a sector to detect 'setdrv' offset.\n");
639         return -1;
640     }
641
642     a = strncmp(buf + 0x36, "FAT", 3);
643     b = strncmp(buf + 0x52, "FAT", 3);
644
645     if ((!a && b && (buf[0x26] & 0xFE) == 0x28) || *((uint8_t*)buf + 0x26) == 0x80) {
646         opt.drvoff = 0x24;
647     } else if (a && !b && (buf[0x42] & 0xFE) == 0x28) {
648         opt.drvoff = 0x40;
649     } else {
650         error("WARNING: Couldn't autodetect 'setdrv' offset - turning option off.\n");
651         opt.setdrv = false;
652     }
653
654     free(buf);
655     return 0;
656
657 }
658
659 int main(int argc, char *argv[])
660 {
661     struct part_iter *iter = NULL;
662
663     void *file_area = NULL;
664     void *sect_area = NULL;
665     void *sbck_area = NULL;
666     struct disk_dos_part_entry *hand_area = NULL;
667
668     struct data_area data[3], bdata[3];
669     int ndata = 0, fidx = -1, sidx = -1, hidx = -1;
670
671     console_ansi_raw();
672 /*    openconsole(&dev_null_r, &dev_stdcon_w);*/
673
674     /* Prepare and set defaults */
675     memset(&opt, 0, sizeof(opt));
676     opt.sect = true;    /* by def load sector */
677     opt.maps = true;    /* by def map sector */
678     opt.hand = true;    /* by def prepare handover */
679     opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
680     opt.drivename = "boot";
681 #ifdef DEBUG
682     opt.warn = true;
683 #endif
684
685     /* Parse arguments */
686     if (parse_args(argc, argv))
687         goto bail;
688
689     /* Set initial registry values */
690     if (opt.file) {
691         opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
692         opt.regs.ip = (uint16_t)opt.fip;
693     } else {
694         opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
695         opt.regs.ip = (uint16_t)opt.sip;
696     }
697
698     if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
699         opt.regs.esp.l = 0x7C00;
700
701     /* Get max fixed disk number */
702     fixed_cnt = *(uint8_t *)(0x475);
703
704     /* Get disk/part iterator matching user supplied options */
705     if (find_dp(&iter))
706         goto bail;
707
708     /* Try to autodetect setdrv offest */
709     if (opt.setdrv && opt.drvoff == ~0u && setdrv_auto(iter))
710         goto bail;
711
712     /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
713     opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
714
715     /* Perform initial partition entry mangling */
716     if (opt.hide || opt.mbrchs)
717         pentry_mangle(iter);
718 /*      hide_unhide(iter);*/
719
720     /* Load the boot file */
721     if (opt.file) {
722         data[ndata].base = (opt.fseg << 4) + opt.foff;
723
724         if (loadfile(opt.file, &data[ndata].data, &data[ndata].size)) {
725             error("Couldn't read the boot file.\n");
726             goto bail;
727         }
728         file_area = (void *)data[ndata].data;
729
730         if (data[ndata].base + data[ndata].size - 1 > ADDRMAX) {
731             error("The boot file is too big to load at this address.\n");
732             goto bail;
733         }
734
735         fidx = ndata;
736         ndata++;
737     }
738
739     /* Load the sector */
740     if (opt.sect) {
741         data[ndata].size = SECTOR;
742         data[ndata].base = (opt.sseg << 4) + opt.soff;
743
744         if (opt.file && opt.maps && overlap(data + fidx, data + ndata)) {
745             error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
746         } else {
747             if (!(data[ndata].data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
748                 error("Couldn't read the sector.\n");
749                 goto bail;
750             }
751             sect_area = (void *)data[ndata].data;
752
753             if (opt.save) {
754                 if (!(sbck_area = malloc(SECTOR))) {
755                     error("Couldn't allocate cmp-buf for option 'save'.\n");
756                     goto bail;
757                 }
758                 memcpy(sbck_area, data->data, data->size);
759             }
760
761             sidx = ndata;
762             ndata++;
763         }
764     }
765
766     /* Prep the handover */
767     if (opt.hand && iter->index) {
768         if (setup_handover(iter, data + ndata))
769             goto bail;
770         hand_area = (void *)data[ndata].data;
771
772         /* Verify possible conflicts */
773         if ( ( fidx >= 0 && overlap(data + fidx, data + ndata)) ||
774              ( sidx >= 0 && opt.maps && overlap(data + sidx, data + ndata)) ) {
775             error("WARNING: Handover area won't be prepared,\n"
776                   "as it would conflict with the boot file and/or the sector.\n");
777         } else {
778             hidx = ndata;
779             ndata++;
780         }
781     }
782
783     /*
784      *  Adjust registers - ds:si & ds:bp
785      *  We do it here, as they might get further
786      *  overriden during mangling.
787      */
788
789     if (sidx >= 0 && fidx >= 0 && opt.maps && !opt.hptr) {
790         opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
791         opt.regs.ds = (uint16_t)opt.sseg;
792         opt.regs.eax.l = 0;
793     } else if (hidx >= 0) {
794         opt.regs.esi.l = opt.regs.ebp.l = data[hidx].base;
795         opt.regs.ds = 0;
796         if (iter->type == typegpt)
797             opt.regs.eax.l = 0x54504721;        /* '!GPT' */
798         else
799             opt.regs.eax.l = 0;
800     }
801
802     /* Do file related stuff */
803
804     if (fidx >= 0) {
805         if (manglef_isolinux(data + fidx))
806             goto bail;
807
808         if (manglef_grldr(iter))
809             goto bail;
810
811         if (manglef_grub(iter, data + fidx))
812             goto bail;
813 #if 0
814         if (manglef_drmk(data + fidx))
815             goto bail;
816 #endif
817         if (manglef_bpb(iter, data + fidx))
818             goto bail;
819     }
820
821     /* Do sector related stuff */
822
823     if (sidx >= 0) {
824         if (mangles_bpb(iter, data + sidx))
825             goto bail;
826
827         if (mangles_save(iter, data + sidx, sbck_area))
828             goto bail;
829
830         /* This *must* be after last BPB saving */
831         if (mangles_cmldr(data + sidx))
832             goto bail;
833     }
834
835     /* Prepare boot-time mmap data */
836
837     ndata = 0;
838     if (sidx >= 0)
839         memcpy(bdata + ndata++, data + sidx, sizeof(struct data_area));
840     if (fidx >= 0)
841         memcpy(bdata + ndata++, data + fidx, sizeof(struct data_area));
842     if (hidx >= 0)
843         memcpy(bdata + ndata++, data + hidx, sizeof(struct data_area));
844
845 #ifdef DEBUG
846     printf("iter dsk: %d\n", iter->di.disk);
847     printf("iter idx: %d\n", iter->index);
848     printf("iter lba: %llu\n", iter->start_lba);
849     if (hidx >= 0)
850         printf("hand lba: %u\n", hand_area->start_lba);
851 #endif
852
853     if (opt.warn) {
854         puts("Press any key to continue booting...");
855         wait_key();
856     }
857
858     do_boot(bdata, ndata);
859 bail:
860     pi_del(&iter);
861     /* Free allocated areas */
862     free(file_area);
863     free(sect_area);
864     free(sbck_area);
865     free(hand_area);
866     return 255;
867 }
868
869 /* vim: set ts=8 sts=4 sw=4 noet: */