com32/chain: Add unhide and unhideall options
[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.");
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.");
403         goto bail;
404     }
405     if ((miter->index < 1 || miter->index > 4) && opt.hide & 1)
406         error("WARNING: option '[un]hide' specified with a non-primary partition.\n");
407
408     if (!(iter = pi_begin(&miter->di, 1)))  /* turn on stepall */
409         goto bail;
410
411     while (!pi_next(&iter) && !werr && (opt.hide & 2 || opt.mbrchs)) {
412         ridx = iter->rawindex;
413         dp = (struct disk_dos_part_entry *)iter->record;
414
415         if (dp->ostype) {
416             if (opt.hide & 2 || (opt.hide & 1 && ridx <= 4)) {
417                 wb |= pem_sethide(miter, iter);
418             }
419             if (opt.mbrchs) {
420                 wb |= pem_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
421                 if (ridx > 4)
422                     wb |= pem_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
423             }
424         }
425
426         if (ridx >= 4 && wb && !werr) {
427             werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
428             wb = 0;
429         }
430     }
431     /* last write */
432     if (wb && !werr)
433         werr |= disk_write_sectors(&miter->di, iter->sub.dos.cebr_lba, iter->data, 1);
434
435 bail:
436     pi_del(&iter);
437     if (werr)
438         error("WARNING: failed to write E/MBR for partition\n"
439               "mangling options ('[un]hide[all]', 'mbrchs').\n");
440     return 0;
441 }
442
443 int find_dp(struct part_iter **_iter)
444 {
445     struct part_iter *iter = NULL;
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) < 0) {
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) < 0) {
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) < 0) {
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->status) {
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->status) {
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     pi_del(&iter);
550     return -1;
551 }
552
553 static int setup_handover(const struct part_iter *iter,
554                    struct data_area *data)
555 {
556     const struct disk_dos_part_entry *dp;
557     const struct disk_gpt_part_entry *gp;
558     struct disk_dos_part_entry *ha;
559     uint64_t lba_count;
560     uint32_t synth_size;
561     uint32_t *plen;
562
563     if (iter->type == typegpt) {
564         /* GPT handover protocol */
565         gp = (const struct disk_gpt_part_entry *)iter->record;
566         lba_count = gp->lba_last - gp->lba_first + 1;
567         synth_size = sizeof(struct disk_dos_part_entry) +
568             sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
569
570         ha = malloc(synth_size);
571         if (!ha) {
572             error("Could not build GPT hand-over record!\n");
573             goto bail;
574         }
575         *(uint32_t *)ha->start = lba2chs(&iter->di, gp->lba_first, l2c_cadd);
576         *(uint32_t *)ha->end = lba2chs(&iter->di, gp->lba_last, l2c_cadd);
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 || iter->type == typeraw) {
598         /* MBR / RAW handover protocol */
599         synth_size = sizeof(struct disk_dos_part_entry);
600         ha = malloc(synth_size);
601         if (!ha) {
602             error("Could not build MBR / RAW hand-over record!\n");
603             goto bail;
604         }
605         if (!iter->index) {
606             *(uint32_t *)ha->start = lba2chs(&iter->di, 0, l2c_cadd);
607             *(uint32_t *)ha->end = lba2chs(&iter->di, 2879, l2c_cadd);
608             ha->active_flag = 0x80;
609             ha->ostype = 0xDA;
610             ha->start_lba = 0;
611             ha->length = 2880;
612         } else if (iter->type == typedos) {
613             dp = (const struct disk_dos_part_entry *)iter->record;
614
615             *(uint32_t *)ha->start = lba2chs(&iter->di, iter->start_lba, l2c_cadd);
616             *(uint32_t *)ha->end = lba2chs(&iter->di, iter->start_lba + dp->length - 1, l2c_cadd);
617             ha->active_flag = dp->active_flag;
618             ha->ostype = dp->ostype;
619             ha->start_lba = (uint32_t)iter->start_lba;  /* fine, we iterate over legacy scheme */
620             ha->length = dp->length;
621 #ifdef DEBUG
622             dprintf("MBR handover:\n");
623             disk_dos_part_dump(ha);
624         } else {
625             goto bail;
626 #endif
627         }
628 #ifdef DEBUG
629     } else {
630         /* shouldn't ever happen */
631         goto bail;
632 #endif
633     }
634
635     data->base = 0x7be;
636     data->size = synth_size;
637     data->data = (void *)ha;
638
639     return 0;
640 bail:
641     return -1;
642 }
643
644 int main(int argc, char *argv[])
645 {
646     struct part_iter *iter = NULL;
647
648     void *sbck = NULL;
649     struct data_area fdat, hdat, sdat, data[3];
650     int ndata = 0;
651
652     console_ansi_raw();
653 /*    openconsole(&dev_null_r, &dev_stdcon_w);*/
654
655     /* Prepare and set defaults */
656     memset(&fdat, 0, sizeof(fdat));
657     memset(&hdat, 0, sizeof(hdat));
658     memset(&sdat, 0, sizeof(sdat));
659     memset(&opt, 0, sizeof(opt));
660     opt.sect = true;    /* by def load sector */
661     opt.maps = true;    /* by def map sector */
662     opt.hand = true;    /* by def prepare handover */
663     opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
664     opt.drivename = "boot";
665 #ifdef DEBUG
666     opt.warn = true;
667 #endif
668
669     /* Parse arguments */
670     if (parse_args(argc, argv))
671         goto bail;
672 #if 0
673     /* Get max fixed disk number */
674     fixed_cnt = *(uint8_t *)(0x475);
675
676     /*
677      * hmm, looks like we can't do that
678      * any better options than hardcoded 0x80 - 0xFF ?
679      */
680 #endif
681
682     /* Get disk/part iterator matching user supplied options */
683     if (find_dp(&iter))
684         goto bail;
685
686     /* Perform initial partition entry mangling */
687     if (opt.hide || opt.mbrchs)
688         pentry_mangle(iter);
689 /*      hide_unhide(iter);*/
690
691     /* Load the boot file */
692     if (opt.file) {
693         fdat.base = (opt.fseg << 4) + opt.foff;
694
695         if (loadfile(opt.file, &fdat.data, &fdat.size)) {
696             error("Couldn't read the boot file.\n");
697             goto bail;
698         }
699         if (fdat.base + fdat.size - 1 > ADDRMAX) {
700             error("The boot file is too big to load at this address.\n");
701             goto bail;
702         }
703     }
704
705     /* Load the sector */
706     if (opt.sect) {
707         sdat.size = SECTOR;
708         sdat.base = (opt.sseg << 4) + opt.soff;
709
710         if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
711             error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
712             opt.sect = false;
713         } else {
714             if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
715                 error("Couldn't read the sector.\n");
716                 goto bail;
717             }
718             if (opt.save) {
719                 if (!(sbck = malloc(SECTOR))) {
720                     error("Couldn't allocate cmp-buf for option 'save'.\n");
721                     goto bail;
722                 }
723                 memcpy(sbck, sdat.data, sdat.size);
724             }
725         }
726     }
727
728     /* Prep the handover */
729     if (opt.hand) {
730         if (setup_handover(iter, &hdat))
731             goto bail;
732         /* Verify possible conflicts */
733         if ( ( opt.file && overlap(&fdat, &hdat)) ||
734              ( opt.sect && overlap(&sdat, &hdat) && opt.maps) ) {
735             error("WARNING: Handover area won't be prepared,\n"
736                   "as it would conflict with the boot file and/or the sector.\n");
737             opt.hand = false;
738         }
739     }
740
741     /* Adjust registers */
742
743     mangler_common(iter);
744     mangler_handover(iter, &hdat);
745     mangler_grldr(iter);
746
747     /* Patching functions */
748
749     if (manglef_isolinux(&fdat))
750         goto bail;
751
752     if (manglef_grub(iter, &fdat))
753         goto bail;
754 #if 0
755     if (manglef_drmk(&fdat))
756         goto bail;
757 #endif
758     if (manglef_bpb(iter, &fdat))
759         goto bail;
760
761     if (mangles_bpb(iter, &sdat))
762         goto bail;
763
764     if (mangles_save(iter, &sdat, sbck))
765         goto bail;
766
767     if (manglesf_bss(&sdat, &fdat))
768         goto bail;
769
770     /* This *must* be after BPB saving or copying */
771     if (mangles_cmldr(&sdat))
772         goto bail;
773
774     /*
775      * Prepare boot-time mmap data We should to it here, as manglers could
776      * potentially alter some of the data.
777      */
778
779     if (opt.file)
780         memcpy(data + ndata++, &fdat, sizeof(fdat));
781     if (opt.sect && opt.maps)
782         memcpy(data + ndata++, &sdat, sizeof(sdat));
783     if (opt.hand)
784         memcpy(data + ndata++, &hdat, sizeof(hdat));
785
786 #ifdef DEBUG
787     printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %llu, %u\n"
788            "iter->di C, H, S: %u, %u, %u\n",
789         iter->di.disk, iter->di.bps,
790         iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
791         iter->di.cyl, iter->di.head, iter->di.spt);
792     printf("iter idx: %d\n", iter->index);
793     printf("iter lba: %llu\n", iter->start_lba);
794     if (opt.hand)
795         printf("hand lba: %u\n",
796                 ((struct disk_dos_part_entry *)hdat.data)->start_lba);
797 #endif
798
799     if (opt.warn) {
800         puts("Press any key to continue booting...");
801         wait_key();
802     }
803
804     do_boot(data, ndata);
805 bail:
806     pi_del(&iter);
807     /* Free allocated areas */
808     free(fdat.data);
809     free(sdat.data);
810     free(hdat.data);
811     free(sbck);
812     return 255;
813 }
814
815 /* vim: set ts=8 sts=4 sw=4 noet: */