com32/chain: allow service-only runs
[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  *   Copyright 2010 Shao Miller
6  *   Copyright 2010 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 "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 = 128;   /* see comments in main() */
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.\n");
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
338 static int pem_sethide(struct part_iter *miter, struct part_iter *iter)
339 {
340     struct disk_dos_part_entry *mdp, *dp;
341     static const uint16_t mask =
342         (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
343         (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
344     uint8_t t;
345
346     mdp = (struct disk_dos_part_entry *)miter->record;
347     dp = (struct disk_dos_part_entry *)iter->record;
348     t = dp->ostype;
349
350     if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
351         /* It's a hideable partition type */
352         if (miter->index == iter->index || opt.hide & 4)
353             t &= (uint8_t)(~0x10u);     /* unhide */
354         else
355             t |= 0x10u; /* hide */
356     }
357     if (dp->ostype != t) {
358         dp->ostype = t;
359         /*
360          * the type of the partition being booted has to be adjusted in
361          * the match iterator (miter) as well
362          */
363         if (miter->index == iter->index) {
364             mdp->ostype = t;
365         }
366         return -1;
367     }
368     return 0;
369 }
370
371 static int pem_setchs(const struct disk_info *di,
372                      struct disk_dos_part_entry *dp,
373                      uint32_t lba1)
374 {
375     uint32_t ochs1, ochs2;
376
377     ochs1 = *(uint32_t *)dp->start;
378     ochs2 = *(uint32_t *)dp->end;
379
380     *(uint32_t *)dp->start =
381         lba2chs(di, lba1, l2c_cadd) |
382         (*(uint32_t *)dp->start & 0xFF000000);
383
384     *(uint32_t *)dp->end =
385         lba2chs(di, lba1 + dp->length - 1, l2c_cadd) |
386         (*(uint32_t *)dp->end & 0xFF000000);
387
388     return
389         *(uint32_t *)dp->start != ochs1 ||
390         *(uint32_t *)dp->end != ochs2;
391 }
392
393 static int pentry_mangle(struct part_iter *miter)
394 {
395     int wb = 0, werr = 0;
396     struct part_iter *iter = NULL;
397     struct disk_dos_part_entry *dp;
398     int ridx;
399
400     if (miter->type != typedos) {
401         error("Partition entry mangling ('[un]hide[all]', 'mbrchs')\n"
402               "is meaningful only for legacy partition scheme.\n");
403         goto bail;
404     }
405     if (opt.hide &&
406             ((miter->index < 1 && opt.hide < 4) || /* try to hide a disk */
407              (miter->index > 4 && opt.hide == 1))) /* try to hide a part when limited to pri */
408         error("WARNING: It's impossible to hide the selected partition (or you selected a disk).\n");
409
410     if (!(iter = pi_begin(&miter->di, 1)))  /* turn on stepall */
411         goto bail;
412
413     while (!pi_next(&iter) && !werr && (opt.hide & 2 || opt.mbrchs)) {
414         ridx = iter->rawindex;
415         dp = (struct disk_dos_part_entry *)iter->record;
416
417         if (dp->ostype) {
418             if (opt.hide & 2 || (opt.hide & 1 && ridx <= 4)) {
419                 wb |= pem_sethide(miter, iter);
420             }
421             if (opt.mbrchs) {
422                 wb |= pem_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
423                 if (ridx > 4)
424                     wb |= pem_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
425             }
426         }
427
428         if (ridx >= 4 && wb && !werr) {
429             werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
430             wb = 0;
431         }
432     }
433     /* last write */
434     if (wb && !werr)
435         werr |= disk_write_sectors(&miter->di, iter->sub.dos.cebr_lba, iter->data, 1);
436
437 bail:
438     pi_del(&iter);
439     if (werr)
440         error("WARNING: failed to write E/MBR for partition\n"
441               "mangling options ('[un]hide[all]', 'mbrchs').\n");
442     return 0;
443 }
444
445 int find_dp(struct part_iter **_iter)
446 {
447     struct part_iter *iter = NULL;
448     struct disk_info diskinfo;
449     struct guid gpt_guid;
450     uint64_t fs_lba;
451     int drive, hd, partition;
452     const union syslinux_derivative_info *sdi;
453
454     sdi = syslinux_derivative_info();
455
456     if (!strncmp(opt.drivename, "mbr", 3)) {
457         if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
458             error("Unable to find requested MBR signature.\n");
459             goto bail;
460         }
461     } else if (!strncmp(opt.drivename, "guid", 4)) {
462         if (str_to_guid(opt.drivename + 5, &gpt_guid))
463             goto bail;
464         if (find_by_guid(&gpt_guid, &iter) < 0) {
465             error("Unable to find requested GPT disk or partition by guid.\n");
466             goto bail;
467         }
468     } else if (!strncmp(opt.drivename, "label", 5)) {
469         if (!opt.drivename[6]) {
470             error("No label specified.\n");
471             goto bail;
472         }
473         if (find_by_label(opt.drivename + 6, &iter) < 0) {
474             error("Unable to find requested GPT partition by label.\n");
475             goto bail;
476         }
477     } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
478                opt.drivename[1] == 'd') {
479         hd = opt.drivename[0] == 'h' ? 0x80 : 0;
480         opt.drivename += 2;
481         drive = hd | strtol(opt.drivename, NULL, 0);
482
483         if (disk_get_params(drive, &diskinfo))
484             goto bail;
485         /* this will start iteration over FDD, possibly raw */
486         if (!(iter = pi_begin(&diskinfo, 0)))
487             goto bail;
488
489     } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
490         if (!is_phys(sdi->c.filesystem)) {
491             error("When syslinux is not booted from physical disk (or its emulation),\n"
492                    "'boot' and 'fs' are meaningless.\n");
493             goto bail;
494         }
495         /* offsets match, but in case it changes in the future */
496         if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
497             drive = sdi->iso.drive_number;
498             fs_lba = *sdi->iso.partoffset;
499         } else {
500             drive = sdi->disk.drive_number;
501             fs_lba = *sdi->disk.partoffset;
502         }
503         if (disk_get_params(drive, &diskinfo))
504             goto bail;
505         /* this will start iteration over disk emulation, possibly raw */
506         if (!(iter = pi_begin(&diskinfo, 0)))
507             goto bail;
508
509         /* 'fs' => we should lookup the syslinux partition number and use it */
510         if (!strcmp(opt.drivename, "fs")) {
511             while (!pi_next(&iter)) {
512                 if (iter->start_lba == fs_lba)
513                     break;
514             }
515             /* broken part structure or other problems */
516             if (iter->status) {
517                 error("Can't find myself on the drive I booted from.\n");
518                 goto bail;
519             }
520         }
521     } else {
522         error("Unparsable drive specification.\n");
523         goto bail;
524     }
525     /* main options done - only thing left is explicit partition specification,
526      * if we're still at the disk stage with the iterator AND user supplied
527      * partition number (including disk pseudo-partition).
528      */
529     if (!iter->index && opt.partition) {
530         partition = strtol(opt.partition, NULL, 0);
531         /* search for matching part#, including disk */
532         do {
533             if (iter->index == partition)
534                 break;
535         } while (!pi_next(&iter));
536         if (iter->status) {
537             error("Requested disk / partition combination not found.\n");
538             goto bail;
539         }
540     }
541
542     if (!(iter->di.disk & 0x80) && iter->index) {
543         error("WARNING: Partitions on floppy devices may not work.\n");
544     }
545
546     *_iter = iter;
547
548     return 0;
549
550 bail:
551     pi_del(&iter);
552     return -1;
553 }
554
555 static int setup_handover(const struct part_iter *iter,
556                    struct data_area *data)
557 {
558     const struct disk_dos_part_entry *dp;
559     const struct disk_gpt_part_entry *gp;
560     struct disk_dos_part_entry *ha;
561     uint64_t lba_count;
562     uint32_t synth_size;
563     uint32_t *plen;
564
565     if (iter->type == typegpt) {
566         /* GPT handover protocol */
567         gp = (const struct disk_gpt_part_entry *)iter->record;
568         lba_count = gp->lba_last - gp->lba_first + 1;
569         synth_size = sizeof(struct disk_dos_part_entry) +
570             sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
571
572         ha = malloc(synth_size);
573         if (!ha) {
574             error("Could not build GPT hand-over record!\n");
575             goto bail;
576         }
577         *(uint32_t *)ha->start = lba2chs(&iter->di, gp->lba_first, l2c_cadd);
578         *(uint32_t *)ha->end = lba2chs(&iter->di, gp->lba_last, l2c_cadd);
579         ha->active_flag = 0x80;
580         ha->ostype = 0xED;
581         /* All bits set by default */
582         ha->start_lba = ~0u;
583         ha->length = ~0u;
584         /* If these fit the precision, pass them on */
585         if (iter->start_lba < ha->start_lba)
586             ha->start_lba = (uint32_t)iter->start_lba;
587         if (lba_count < ha->length)
588             ha->length = (uint32_t)lba_count;
589         /* Next comes the GPT partition record length */
590         plen = (uint32_t *) (ha + 1);
591         plen[0] = (uint32_t)iter->sub.gpt.pe_size;
592         /* Next comes the GPT partition record copy */
593         memcpy(plen + 1, gp, plen[0]);
594 #ifdef DEBUG
595         dprintf("GPT handover:\n");
596         disk_dos_part_dump(ha);
597         disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
598 #endif
599     } else if (iter->type == typedos || iter->type == typeraw) {
600         /* MBR / RAW handover protocol */
601         synth_size = sizeof(struct disk_dos_part_entry);
602         ha = malloc(synth_size);
603         if (!ha) {
604             error("Could not build MBR / RAW hand-over record!\n");
605             goto bail;
606         }
607         if (!iter->index) {
608             uint32_t len = ~0u;
609             if (iter->di.lbacnt < len)
610                 len = (uint32_t)iter->di.lbacnt;
611             *(uint32_t *)ha->start = lba2chs(&iter->di, 0, l2c_cadd);
612             *(uint32_t *)ha->end = lba2chs(&iter->di, len - 1, l2c_cadd);
613             ha->active_flag = 0x80;
614             ha->ostype = 0xDA;  /* "Non-FS Data", anything is good here though ... */
615             ha->start_lba = 0;
616             ha->length = len;
617         } else if (iter->type == typedos) {
618             dp = (const struct disk_dos_part_entry *)iter->record;
619
620             *(uint32_t *)ha->start = lba2chs(&iter->di, iter->start_lba, l2c_cadd);
621             *(uint32_t *)ha->end = lba2chs(&iter->di, iter->start_lba + dp->length - 1, l2c_cadd);
622             ha->active_flag = dp->active_flag;
623             ha->ostype = dp->ostype;
624             ha->start_lba = (uint32_t)iter->start_lba;  /* fine, we iterate over legacy scheme */
625             ha->length = dp->length;
626 #ifdef DEBUG
627             dprintf("MBR handover:\n");
628             disk_dos_part_dump(ha);
629         } else {
630             goto bail;
631 #endif
632         }
633 #ifdef DEBUG
634     } else {
635         /* shouldn't ever happen */
636         goto bail;
637 #endif
638     }
639
640     data->base = 0x7be;
641     data->size = synth_size;
642     data->data = (void *)ha;
643
644     return 0;
645 bail:
646     return -1;
647 }
648
649 int main(int argc, char *argv[])
650 {
651     struct part_iter *iter = NULL;
652
653     void *sbck = NULL;
654     struct data_area fdat, hdat, sdat, data[3];
655     int ndata = 0;
656
657     console_ansi_raw();
658 /*    openconsole(&dev_null_r, &dev_stdcon_w);*/
659
660     /* Prepare and set defaults */
661     memset(&fdat, 0, sizeof(fdat));
662     memset(&hdat, 0, sizeof(hdat));
663     memset(&sdat, 0, sizeof(sdat));
664     memset(&opt, 0, sizeof(opt));
665     opt.sect = true;    /* by def. load sector */
666     opt.maps = true;    /* by def. map sector */
667     opt.hand = true;    /* by def. prepare handover */
668     opt.chain = true;   /* by def. do chainload */
669     opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
670     opt.drivename = "boot";
671 #ifdef DEBUG
672     opt.warn = true;
673 #endif
674
675     /* Parse arguments */
676     if (parse_args(argc, argv))
677         goto bail;
678 #if 0
679     /* Get max fixed disk number */
680     fixed_cnt = *(uint8_t *)(0x475);
681
682     /*
683      * hmm, looks like we can't do that
684      * any better options than hardcoded 0x80 - 0xFF ?
685      */
686 #endif
687
688     /* Get disk/part iterator matching user supplied options */
689     if (find_dp(&iter))
690         goto bail;
691
692     /* Perform initial partition entry mangling */
693     if (opt.hide || opt.mbrchs)
694         pentry_mangle(iter);
695 /*      hide_unhide(iter);*/
696
697     /* Load the boot file */
698     if (opt.file) {
699         fdat.base = (opt.fseg << 4) + opt.foff;
700
701         if (loadfile(opt.file, &fdat.data, &fdat.size)) {
702             error("Couldn't read the boot file.\n");
703             goto bail;
704         }
705         if (fdat.base + fdat.size - 1 > ADDRMAX) {
706             error("The boot file is too big to load at this address.\n");
707             goto bail;
708         }
709     }
710
711     /* Load the sector */
712     if (opt.sect) {
713         sdat.size = SECTOR;
714         sdat.base = (opt.sseg << 4) + opt.soff;
715
716         if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
717             error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
718             opt.sect = false;
719         } else {
720             if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
721                 error("Couldn't read the sector.\n");
722                 goto bail;
723             }
724             if (opt.save) {
725                 if (!(sbck = malloc(SECTOR))) {
726                     error("Couldn't allocate cmp-buf for option 'save'.\n");
727                     goto bail;
728                 }
729                 memcpy(sbck, sdat.data, sdat.size);
730             }
731         }
732     }
733
734     /* Prep the handover */
735     if (opt.hand) {
736         if (setup_handover(iter, &hdat))
737             goto bail;
738         /* Verify possible conflicts */
739         if ( ( opt.file && overlap(&fdat, &hdat)) ||
740              ( opt.sect && overlap(&sdat, &hdat) && opt.maps) ) {
741             error("WARNING: Handover area won't be prepared,\n"
742                   "as it would conflict with the boot file and/or the sector.\n");
743             opt.hand = false;
744         }
745     }
746
747     /* Adjust registers */
748
749     mangler_common(iter);
750     mangler_handover(iter, &hdat);
751     mangler_grldr(iter);
752
753     /* Patching functions */
754
755     if (manglef_isolinux(&fdat))
756         goto bail;
757
758     if (manglef_grub(iter, &fdat))
759         goto bail;
760 #if 0
761     if (manglef_drmk(&fdat))
762         goto bail;
763 #endif
764     if (manglef_bpb(iter, &fdat))
765         goto bail;
766
767     if (mangles_bpb(iter, &sdat))
768         goto bail;
769
770     if (mangles_save(iter, &sdat, sbck))
771         goto bail;
772
773     if (manglesf_bss(&sdat, &fdat))
774         goto bail;
775
776     /* This *must* be after BPB saving or copying */
777     if (mangles_cmldr(&sdat))
778         goto bail;
779
780     /*
781      * Prepare boot-time mmap data. We should to it here, as manglers could
782      * potentially alter some of the data.
783      */
784
785     if (opt.file)
786         memcpy(data + ndata++, &fdat, sizeof(fdat));
787     if (opt.sect && opt.maps)
788         memcpy(data + ndata++, &sdat, sizeof(sdat));
789     if (opt.hand)
790         memcpy(data + ndata++, &hdat, sizeof(hdat));
791
792 #ifdef DEBUG
793     printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %llu, %u\n"
794            "iter->di C, H, S: %u, %u, %u\n",
795         iter->di.disk, iter->di.bps,
796         iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
797         iter->di.cyl, iter->di.head, iter->di.spt);
798     printf("iter idx: %d\n", iter->index);
799     printf("iter lba: %llu\n", iter->start_lba);
800     if (opt.hand)
801         printf("hand lba: %u\n",
802                 ((struct disk_dos_part_entry *)hdat.data)->start_lba);
803 #endif
804
805     if (opt.warn) {
806         puts("Press any key to continue booting...");
807         wait_key();
808     }
809
810     if (ndata && opt.chain) /* boot only if we actually chainload */
811         do_boot(data, ndata);
812     else
813         error("Service-only run completed, exiting.\n");
814 bail:
815     pi_del(&iter);
816     /* Free allocated areas */
817     free(fdat.data);
818     free(sdat.data);
819     free(hdat.data);
820     free(sbck);
821     return 255;
822 }
823
824 /* vim: set ts=8 sts=4 sw=4 noet: */