chain: Remove type-assurance macro for declarations
[profile/ivi/syslinux.git] / com32 / modules / 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]
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  * chain.c
18  *
19  * Chainload a hard disk (currently rather braindead.)
20  *
21  * Usage: chain hd<disk#> [<partition>] [options]
22  *        chain fd<disk#> [options]
23  *        chain mbr:<id> [<partition>] [options]
24  *        chain boot [<partition>] [options]
25  *
26  * ... e.g. "chain hd0 1" will boot the first partition on the first hard
27  * disk.
28  *
29  *
30  * The mbr: syntax means search all the hard disks until one with a
31  * specific MBR serial number (bytes 440-443) is found.
32  *
33  * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
34  *
35  * Options:
36  *
37  * file=<loader>:
38  *      loads the file <loader> **from the SYSLINUX filesystem**
39  *      instead of loading the boot sector.
40  *
41  * seg=<segment>:
42  *      loads at and jumps to <seg>:0000 instead of 0000:7C00.
43  *
44  * isolinux=<loader>:
45  *      chainload another version/build of the ISOLINUX bootloader and patch
46  *      the loader with appropriate parameters in memory.
47  *      This avoids the need for the -eltorito-alt-boot parameter of mkisofs,
48  *      when you want more than one ISOLINUX per CD/DVD.
49  *
50  * ntldr=<loader>:
51  *      equivalent to seg=0x2000 file=<loader> sethidden,
52  *      used with WinNT's loaders
53  *
54  * cmldr=<loader>:
55  *      used with Recovery Console of Windows NT/2K/XP.
56  *      same as ntldr=<loader> & "cmdcons\0" written to
57  *      the system name field in the bootsector
58  *
59  * freedos=<loader>:
60  *      equivalent to seg=0x60 file=<loader> sethidden,
61  *      used with FreeDOS kernel.sys.
62  *
63  * msdos=<loader>
64  * pcdos=<loader>
65  *      equivalent to seg=0x70 file=<loader> sethidden,
66  *      used with DOS' io.sys.
67  *
68  * grub=<loader>:
69  *      same as seg=0x800 file=<loader> & jumping to seg 0x820,
70  *      used with GRUB stage2 files.
71  *
72  * swap:
73  *      if the disk is not fd0/hd0, install a BIOS stub which swaps
74  *      the drive numbers.
75  *
76  * hide:
77  *      change type of primary partitions with IDs 01, 04, 06, 07,
78  *      0b, 0c, or 0e to 1x, except for the selected partition, which
79  *      is converted the other way.
80  *
81  * sethidden:
82  *      update the "hidden sectors" (partition offset) field in a
83  *      FAT/NTFS boot sector.
84  */
85
86 #define DEBUG 0                 /* 1 to enable */
87
88 #include <com32.h>
89 #include <stdlib.h>
90 #include <stdio.h>
91 #include <ctype.h>
92 #include <string.h>
93 #include <console.h>
94 #include <minmax.h>
95 #include <stdbool.h>
96 #include <syslinux/loadfile.h>
97 #include <syslinux/bootrm.h>
98 #include <syslinux/config.h>
99 #include <syslinux/video.h>
100
101 #define SECTOR 512              /* bytes/sector */
102
103 static struct options {
104     const char *loadfile;
105     uint16_t keeppxe;
106     uint16_t seg;
107     bool isolinux;
108     bool cmldr;
109     bool swap;
110     bool hide;
111     bool sethidden;
112     bool grub;
113 } opt;
114
115 struct data_area {
116     void *data;
117     addr_t base;
118     addr_t size;
119 };
120
121 static inline void error(const char *msg)
122 {
123     fputs(msg, stderr);
124 }
125
126 /*
127  * Call int 13h, but with retry on failure.  Especially floppies need this.
128  */
129 static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
130 {
131     int retry = 6;              /* Number of retries */
132     com32sys_t tmpregs;
133
134     if (!outreg)
135         outreg = &tmpregs;
136
137     while (retry--) {
138         __intcall(0x13, inreg, outreg);
139         if (!(outreg->eflags.l & EFLAGS_CF))
140             return 0;           /* CF=0, OK */
141     }
142
143     return -1;                  /* Error */
144 }
145
146 /*
147  * Query disk parameters and EBIOS availability for a particular disk.
148  */
149 struct diskinfo {
150     int disk;
151     int ebios;                  /* EBIOS supported on this disk */
152     int cbios;                  /* CHS geometry is valid */
153     int head;
154     int sect;
155 } disk_info;
156
157 static int get_disk_params(int disk)
158 {
159     static com32sys_t getparm, parm, getebios, ebios;
160
161     disk_info.disk = disk;
162     disk_info.ebios = disk_info.cbios = 0;
163
164     /* Get EBIOS support */
165     getebios.eax.w[0] = 0x4100;
166     getebios.ebx.w[0] = 0x55aa;
167     getebios.edx.b[0] = disk;
168     getebios.eflags.b[0] = 0x3; /* CF set */
169
170     __intcall(0x13, &getebios, &ebios);
171
172     if (!(ebios.eflags.l & EFLAGS_CF) &&
173         ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) {
174         disk_info.ebios = 1;
175     }
176
177     /* Get disk parameters -- really only useful for
178        hard disks, but if we have a partitioned floppy
179        it's actually our best chance... */
180     getparm.eax.b[1] = 0x08;
181     getparm.edx.b[0] = disk;
182
183     __intcall(0x13, &getparm, &parm);
184
185     if (parm.eflags.l & EFLAGS_CF)
186         return disk_info.ebios ? 0 : -1;
187
188     disk_info.head = parm.edx.b[1] + 1;
189     disk_info.sect = parm.ecx.b[0] & 0x3f;
190     if (disk_info.sect == 0) {
191         disk_info.sect = 1;
192     } else {
193         disk_info.cbios = 1;    /* Valid geometry */
194     }
195
196     return 0;
197 }
198
199 /*
200  * Get a disk block and return a malloc'd buffer.
201  * Uses the disk number and information from disk_info.
202  */
203 struct ebios_dapa {
204     uint16_t len;
205     uint16_t count;
206     uint16_t off;
207     uint16_t seg;
208     uint64_t lba;
209 };
210
211 /* Read count sectors from drive, starting at lba.  Return a new buffer */
212 static void *read_sectors(uint64_t lba, uint8_t count)
213 {
214     com32sys_t inreg;
215     struct ebios_dapa *dapa = __com32.cs_bounce;
216     void *buf = (char *)__com32.cs_bounce + SECTOR;
217     void *data;
218
219     if (!count)
220         /* Silly */
221         return NULL;
222
223     memset(&inreg, 0, sizeof inreg);
224
225     if (disk_info.ebios) {
226         dapa->len = sizeof(*dapa);
227         dapa->count = count;
228         dapa->off = OFFS(buf);
229         dapa->seg = SEG(buf);
230         dapa->lba = lba;
231
232         inreg.esi.w[0] = OFFS(dapa);
233         inreg.ds = SEG(dapa);
234         inreg.edx.b[0] = disk_info.disk;
235         inreg.eax.b[1] = 0x42;  /* Extended read */
236     } else {
237         unsigned int c, h, s, t;
238
239         if (!disk_info.cbios) {
240             /* We failed to get the geometry */
241
242             if (lba)
243                 return NULL;    /* Can only read MBR */
244
245             s = 1;
246             h = 0;
247             c = 0;
248         } else {
249             s = (lba % disk_info.sect) + 1;
250             t = lba / disk_info.sect;   /* Track = head*cyl */
251             h = t % disk_info.head;
252             c = t / disk_info.head;
253         }
254
255         if (s > 63 || h > 256 || c > 1023)
256             return NULL;
257
258         inreg.eax.b[0] = count;
259         inreg.eax.b[1] = 0x02;  /* Read */
260         inreg.ecx.b[1] = c & 0xff;
261         inreg.ecx.b[0] = s + (c >> 6);
262         inreg.edx.b[1] = h;
263         inreg.edx.b[0] = disk_info.disk;
264         inreg.ebx.w[0] = OFFS(buf);
265         inreg.es = SEG(buf);
266     }
267
268     if (int13_retry(&inreg, NULL))
269         return NULL;
270
271     data = malloc(count * SECTOR);
272     if (data)
273         memcpy(data, buf, count * SECTOR);
274     return data;
275 }
276
277 static int write_sector(unsigned int lba, const void *data)
278 {
279     com32sys_t inreg;
280     struct ebios_dapa *dapa = __com32.cs_bounce;
281     void *buf = (char *)__com32.cs_bounce + SECTOR;
282
283     memcpy(buf, data, SECTOR);
284     memset(&inreg, 0, sizeof inreg);
285
286     if (disk_info.ebios) {
287         dapa->len = sizeof(*dapa);
288         dapa->count = 1;        /* 1 sector */
289         dapa->off = OFFS(buf);
290         dapa->seg = SEG(buf);
291         dapa->lba = lba;
292
293         inreg.esi.w[0] = OFFS(dapa);
294         inreg.ds = SEG(dapa);
295         inreg.edx.b[0] = disk_info.disk;
296         inreg.eax.w[0] = 0x4300;        /* Extended write */
297     } else {
298         unsigned int c, h, s, t;
299
300         if (!disk_info.cbios) {
301             /* We failed to get the geometry */
302
303             if (lba)
304                 return -1;      /* Can only write MBR */
305
306             s = 1;
307             h = 0;
308             c = 0;
309         } else {
310             s = (lba % disk_info.sect) + 1;
311             t = lba / disk_info.sect;   /* Track = head*cyl */
312             h = t % disk_info.head;
313             c = t / disk_info.head;
314         }
315
316         if (s > 63 || h > 256 || c > 1023)
317             return -1;
318
319         inreg.eax.w[0] = 0x0301;        /* Write one sector */
320         inreg.ecx.b[1] = c & 0xff;
321         inreg.ecx.b[0] = s + (c >> 6);
322         inreg.edx.b[1] = h;
323         inreg.edx.b[0] = disk_info.disk;
324         inreg.ebx.w[0] = OFFS(buf);
325         inreg.es = SEG(buf);
326     }
327
328     if (int13_retry(&inreg, NULL))
329         return -1;
330
331     return 0;                   /* ok */
332 }
333
334 static int write_verify_sector(unsigned int lba, const void *buf)
335 {
336     char *rb;
337     int rv;
338
339     rv = write_sector(lba, buf);
340     if (rv)
341         return rv;              /* Write failure */
342     rb = read_sectors(lba, 1);
343     if (!rb)
344         return -1;              /* Readback failure */
345     rv = memcmp(buf, rb, SECTOR);
346     free(rb);
347     return rv ? -1 : 0;
348 }
349
350 /*
351  * CHS (cylinder, head, sector) value extraction macros.
352  * Taken from WinVBlock.  Does not expand to an lvalue
353 */
354 #define     chs_head(chs) chs[0]
355 #define   chs_sector(chs) (chs[1] & 0x3F)
356 #define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
357 #define  chs_cyl_low(chs) ((uint16_t)chs[2])
358 #define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
359 typedef uint8_t chs[3];
360
361 /* A DOS partition table entry */
362 struct part_entry {
363     uint8_t active_flag;        /* 0x80 if "active" */
364     chs start;
365     uint8_t ostype;
366     chs end;
367     uint32_t start_lba;
368     uint32_t length;
369 } __attribute__ ((packed));
370
371 #if DEBUG
372 static void mbr_part_dump(const struct part_entry *part)
373 {
374     printf("-------------------------------\n"
375            "Partition status _____ : 0x%.2x\n"
376            "Partition CHS start\n"
377            "  Cylinder ___________ : 0x%.4x\n"
378            "  Head _______________ : 0x%.2x\n"
379            "  Sector _____________ : 0x%.2x\n"
380            "Partition type _______ : 0x%.2x\n"
381            "Partition CHS end\n"
382            "  Cylinder ___________ : 0x%.4x\n"
383            "  Head _______________ : 0x%.2x\n"
384            "  Sector _____________ : 0x%.2x\n"
385            "Partition LBA start __ : 0x%.16x\n"
386            "Partition LBA count __ : 0x%.16x\n",
387            part->active_flag,
388            chs_cylinder(part->start),
389            chs_head(part->start),
390            chs_sector(part->start),
391            part->ostype,
392            chs_cylinder(part->end),
393            chs_head(part->end),
394            chs_sector(part->end), part->start_lba, part->length);
395 }
396 #endif
397
398 /* A DOS MBR */
399 struct mbr {
400     char code[440];
401     uint32_t disk_sig;
402     char pad[2];
403     struct part_entry table[4];
404     uint16_t sig;
405 } __attribute__ ((packed));
406 static const uint16_t mbr_sig_magic = 0xAA55;
407
408 /* Search for a specific drive, based on the MBR signature; bytes
409    440-443. */
410 static int find_disk(uint32_t mbr_sig)
411 {
412     int drive;
413     bool is_me;
414     struct mbr *mbr;
415
416     for (drive = 0x80; drive <= 0xff; drive++) {
417         if (get_disk_params(drive))
418             continue;           /* Drive doesn't exist */
419         if (!(mbr = read_sectors(0, 1)))
420             continue;           /* Cannot read sector */
421         is_me = (mbr->disk_sig == mbr_sig);
422         free(mbr);
423         if (is_me)
424             return drive;
425     }
426     return -1;
427 }
428
429 /* Forward declaration */
430 struct disk_part_iter;
431
432 /* Partition-/scheme-specific routine returning the next partition */
433 typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
434                                                        part);
435
436 /* Contains details for a partition under examination */
437 struct disk_part_iter {
438     /* The block holding the table we are part of */
439     char *block;
440     /* The LBA for the beginning of data */
441     uint64_t lba_data;
442     /* The partition number, as determined by our heuristic */
443     int index;
444     /* The DOS partition record to pass, if applicable */
445     const struct part_entry *record;
446     /* Function returning the next available partition */
447     disk_part_iter_func next;
448     /* Partition-/scheme-specific details */
449     union {
450         /* MBR specifics */
451         int mbr_index;
452         /* EBR specifics */
453         struct {
454             /* The first extended partition's start LBA */
455             uint64_t lba_extended;
456             /* Any applicable parent, or NULL */
457             struct disk_part_iter *parent;
458             /* The parent extended partition index */
459             int parent_index;
460         } ebr;
461         /* GPT specifics */
462         struct {
463             /* Real (not effective) index in the partition table */
464             int index;
465             /* Count of entries in GPT */
466             int parts;
467             /* Partition record size */
468             uint32_t size;
469         } gpt;
470     } private;
471 };
472
473 static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
474 {
475     const struct part_entry *ebr_table;
476     const struct part_entry *parent_table =
477         ((const struct mbr *)part->private.ebr.parent->block)->table;
478     static const struct part_entry phony = {.start_lba = 0 };
479     uint64_t ebr_lba;
480
481     /* Don't look for a "next EBR" the first time around */
482     if (part->private.ebr.parent_index >= 0)
483         /* Look at the linked list */
484         ebr_table = ((const struct mbr *)part->block)->table + 1;
485     /* Do we need to look for an extended partition? */
486     if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
487         /* Start looking for an extended partition in the MBR */
488         while (++part->private.ebr.parent_index < 4) {
489             uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
490
491             if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
492                 break;
493         }
494         if (part->private.ebr.parent_index == 4)
495             /* No extended partitions found */
496             goto out_finished;
497         part->private.ebr.lba_extended =
498             parent_table[part->private.ebr.parent_index].start_lba;
499         ebr_table = &phony;
500     }
501     /* Load next EBR */
502     ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
503     free(part->block);
504     part->block = read_sectors(ebr_lba, 1);
505     if (!part->block) {
506         error("Could not load EBR!\n");
507         goto err_ebr;
508     }
509     ebr_table = ((const struct mbr *)part->block)->table;
510 #if DEBUG
511     mbr_part_dump(ebr_table);
512 #endif
513     /*
514      * Sanity check entry: must not extend outside the
515      * extended partition.  This is necessary since some OSes
516      * put crap in some entries.
517      */
518     {
519         const struct mbr *mbr =
520             (const struct mbr *)part->private.ebr.parent->block;
521         const struct part_entry *extended =
522             mbr->table + part->private.ebr.parent_index;
523
524         if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
525             error("Insane logical partition!\n");
526             goto err_insane;
527         }
528     }
529     /* Success */
530     part->lba_data = ebr_table[0].start_lba + ebr_lba;
531     part->index++;
532     part->record = ebr_table;
533     return part;
534
535 err_insane:
536
537     free(part->block);
538     part->block = NULL;
539 err_ebr:
540
541 out_finished:
542     free(part->private.ebr.parent->block);
543     free(part->private.ebr.parent);
544     free(part->block);
545     free(part);
546     return NULL;
547 }
548
549 static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
550 {
551     struct disk_part_iter *ebr_part;
552     /* Look at the partition table */
553     struct part_entry *table = ((struct mbr *)part->block)->table;
554
555     /* Look for data partitions */
556     while (++part->private.mbr_index < 4) {
557         uint8_t type = table[part->private.mbr_index].ostype;
558
559         if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
560             /* Skip empty or extended partitions */
561             continue;
562         if (!table[part->private.mbr_index].length)
563             /* Empty */
564             continue;
565         break;
566     }
567     /* If we're currently the last partition, it's time for EBR processing */
568     if (part->private.mbr_index == 4) {
569         /* Allocate another iterator for extended partitions */
570         ebr_part = malloc(sizeof(*ebr_part));
571         if (!ebr_part) {
572             error("Could not allocate extended partition iterator!\n");
573             goto err_alloc;
574         }
575         /* Setup EBR iterator parameters */
576         ebr_part->block = NULL;
577         ebr_part->index = 4;
578         ebr_part->record = NULL;
579         ebr_part->next = next_ebr_part;
580         ebr_part->private.ebr.parent = part;
581         /* Trigger an initial EBR load */
582         ebr_part->private.ebr.parent_index = -1;
583         /* The EBR iterator is responsible for freeing us */
584         return next_ebr_part(ebr_part);
585     }
586 #if DEBUG
587     mbr_part_dump(table + part->private.mbr_index);
588 #endif
589     /* Update parameters to reflect this new partition.  Re-use iterator */
590     part->lba_data = table[part->private.mbr_index].start_lba;
591     part->index++;
592     part->record = table + part->private.mbr_index;
593     return part;
594
595     free(ebr_part);
596 err_alloc:
597
598     free(part->block);
599     free(part);
600     return NULL;
601 }
602
603 /*
604  * GUID
605  * Be careful with endianness, you must adjust it yourself
606  * iff you are directly using the fourth data chunk
607  */
608 struct guid {
609     uint32_t data1;
610     uint16_t data2;
611     uint16_t data3;
612     uint64_t data4;
613 } __attribute__ ((packed));
614
615 #if DEBUG
616 /*
617  * Fill a buffer with a textual GUID representation.
618  * The buffer must be >= char[37] and will be populated
619  * with an ASCII NUL C string terminator.
620  * Example: 11111111-2222-3333-4444-444444444444
621  * Endian:  LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
622  */
623 static void guid_to_str(char *buf, const struct guid *id)
624 {
625     /*
626      * This walk-map effectively reverses the little-endian
627      * portions of the GUID in the output text
628      */
629     static const char le_walk_map[] = {
630         3, -1, -1, -1, 0,
631         5, -1, 0,
632         3, -1, 0,
633         2, 1, 0,
634         1, 1, 1, 1, 1, 1
635     };
636     unsigned int i = 0;
637     const char *walker = (const char *)id;
638
639     while (i < sizeof(le_walk_map)) {
640         walker += le_walk_map[i];
641         if (!le_walk_map[i])
642             *buf = '-';
643         else {
644             *buf = ((*walker & 0xF0) >> 4) + '0';
645             if (*buf > '9')
646                 *buf += 'A' - '9' - 1;
647             buf++;
648             *buf = (*walker & 0x0F) + '0';
649             if (*buf > '9')
650                 *buf += 'A' - '9' - 1;
651         }
652         buf++;
653         i++;
654     }
655     *buf = 0;
656 }
657 #endif
658
659 /* A GPT partition */
660 struct gpt_part {
661     struct guid type;
662     struct guid uid;
663     uint64_t lba_first;
664     uint64_t lba_last;
665     uint64_t attribs;
666     char name[72];
667 } __attribute__ ((packed));
668
669 #if DEBUG
670 static void gpt_part_dump(const struct gpt_part *gpt_part)
671 {
672     unsigned int i;
673     char guid_text[37];
674
675     printf("----------------------------------\n"
676            "GPT part. LBA first __ : 0x%.16llx\n"
677            "GPT part. LBA last ___ : 0x%.16llx\n"
678            "GPT part. attribs ____ : 0x%.16llx\n"
679            "GPT part. name _______ : '",
680            gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
681     for (i = 0; i < sizeof(gpt_part->name); i++) {
682         if (gpt_part->name[i])
683             printf("%c", gpt_part->name[i]);
684     }
685     puts("'");
686     guid_to_str(guid_text, &gpt_part->type);
687     printf("GPT part. type GUID __ : {%s}\n", guid_text);
688     guid_to_str(guid_text, &gpt_part->uid);
689     printf("GPT part. unique ID __ : {%s}\n", guid_text);
690 }
691 #endif
692
693 /* A GPT header */
694 struct gpt {
695     char sig[8];
696     union {
697         struct {
698             uint16_t minor;
699             uint16_t major;
700         } fields __attribute__ ((packed));
701         uint32_t uint32;
702         char raw[4];
703     } rev __attribute__ ((packed));
704     uint32_t hdr_size;
705     uint32_t chksum;
706     char reserved1[4];
707     uint64_t lba_cur;
708     uint64_t lba_alt;
709     uint64_t lba_first_usable;
710     uint64_t lba_last_usable;
711     struct guid disk_guid;
712     uint64_t lba_table;
713     uint32_t part_count;
714     uint32_t part_size;
715     uint32_t table_chksum;
716     char reserved2[1];
717 } __attribute__ ((packed));
718 static const char gpt_sig_magic[] = "EFI PART";
719
720 #if DEBUG
721 static void gpt_dump(const struct gpt *gpt)
722 {
723     char guid_text[37];
724
725     printf("GPT sig ______________ : '%8.8s'\n"
726            "GPT major revision ___ : 0x%.4x\n"
727            "GPT minor revision ___ : 0x%.4x\n"
728            "GPT header size ______ : 0x%.8x\n"
729            "GPT header checksum __ : 0x%.8x\n"
730            "GPT reserved _________ : '%4.4s'\n"
731            "GPT LBA current ______ : 0x%.16llx\n"
732            "GPT LBA alternative __ : 0x%.16llx\n"
733            "GPT LBA first usable _ : 0x%.16llx\n"
734            "GPT LBA last usable __ : 0x%.16llx\n"
735            "GPT LBA part. table __ : 0x%.16llx\n"
736            "GPT partition count __ : 0x%.8x\n"
737            "GPT partition size ___ : 0x%.8x\n"
738            "GPT part. table chksum : 0x%.8x\n",
739            gpt->sig,
740            gpt->rev.fields.major,
741            gpt->rev.fields.minor,
742            gpt->hdr_size,
743            gpt->chksum,
744            gpt->reserved1,
745            gpt->lba_cur,
746            gpt->lba_alt,
747            gpt->lba_first_usable,
748            gpt->lba_last_usable,
749            gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
750     guid_to_str(guid_text, &gpt->disk_guid);
751     printf("GPT disk GUID ________ : {%s}\n", guid_text);
752 }
753 #endif
754
755 static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
756 {
757     const struct gpt_part *gpt_part = NULL;
758
759     while (++part->private.gpt.index < part->private.gpt.parts) {
760         gpt_part =
761             (const struct gpt_part *)(part->block +
762                                       (part->private.gpt.index *
763                                        part->private.gpt.size));
764         if (!gpt_part->lba_first)
765             continue;
766         break;
767     }
768     /* Were we the last partition? */
769     if (part->private.gpt.index == part->private.gpt.parts) {
770         goto err_last;
771     }
772     part->lba_data = gpt_part->lba_first;
773     /* Update our index */
774     part->index++;
775 #if DEBUG
776     gpt_part_dump(gpt_part);
777 #endif
778     /* In a GPT scheme, we re-use the iterator */
779     return part;
780
781 err_last:
782     free(part->block);
783     free(part);
784
785     return NULL;
786 }
787
788 static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
789 {
790     const struct gpt *gpt_candidate;
791
792     /*
793      * Ignore any passed partition iterator.  The caller should
794      * have passed NULL.  Allocate a new partition iterator
795      */
796     part = malloc(sizeof(*part));
797     if (!part) {
798         error("Count not allocate partition iterator!\n");
799         goto err_alloc_iter;
800     }
801     /* Read MBR */
802     part->block = read_sectors(0, 2);
803     if (!part->block) {
804         error("Could not read two sectors!\n");
805         goto err_read_mbr;
806     }
807     /* Check for an MBR */
808     if (((struct mbr *)part->block)->sig != mbr_sig_magic) {
809         error("No MBR magic!\n");
810         goto err_mbr;
811     }
812     /* Establish a pseudo-partition for the MBR (index 0) */
813     part->index = 0;
814     part->record = NULL;
815     part->private.mbr_index = -1;
816     part->next = next_mbr_part;
817     /* Check for a GPT disk */
818     gpt_candidate = (const struct gpt *)(part->block + SECTOR);
819     if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
820         /* LBA for partition table */
821         uint64_t lba_table;
822
823         /* It looks like one */
824         /* TODO: Check checksum.  Possibly try alternative GPT */
825 #if DEBUG
826         puts("Looks like a GPT disk.");
827         gpt_dump(gpt_candidate);
828 #endif
829         /* TODO: Check table checksum (maybe) */
830         /* Note relevant GPT details */
831         part->next = next_gpt_part;
832         part->private.gpt.index = -1;
833         part->private.gpt.parts = gpt_candidate->part_count;
834         part->private.gpt.size = gpt_candidate->part_size;
835         lba_table = gpt_candidate->lba_table;
836         gpt_candidate = NULL;
837         /* Load the partition table */
838         free(part->block);
839         part->block =
840             read_sectors(lba_table,
841                          ((part->private.gpt.size * part->private.gpt.parts) +
842                           SECTOR - 1) / SECTOR);
843         if (!part->block) {
844             error("Could not read GPT partition list!\n");
845             goto err_gpt_table;
846         }
847     }
848     /* Return the pseudo-partition's next partition, which is real */
849     return part->next(part);
850
851 err_gpt_table:
852
853 err_mbr:
854
855     free(part->block);
856     part->block = NULL;
857 err_read_mbr:
858
859     free(part);
860 err_alloc_iter:
861
862     return NULL;
863 }
864
865 static void do_boot(struct data_area *data, int ndata,
866                     struct syslinux_rm_regs *regs)
867 {
868     uint16_t *const bios_fbm = (uint16_t *) 0x413;
869     addr_t dosmem = *bios_fbm << 10;    /* Technically a low bound */
870     struct syslinux_memmap *mmap;
871     struct syslinux_movelist *mlist = NULL;
872     addr_t endimage;
873     uint8_t driveno = regs->edx.b[0];
874     uint8_t swapdrive = driveno & 0x80;
875     int i;
876
877     mmap = syslinux_memory_map();
878
879     if (!mmap) {
880         error("Cannot read system memory map\n");
881         return;
882     }
883
884     endimage = 0;
885     for (i = 0; i < ndata; i++) {
886         if (data[i].base + data[i].size > endimage)
887             endimage = data[i].base + data[i].size;
888     }
889     if (endimage > dosmem)
890         goto too_big;
891
892     for (i = 0; i < ndata; i++) {
893         if (syslinux_add_movelist(&mlist, data[i].base,
894                                   (addr_t) data[i].data, data[i].size))
895             goto enomem;
896     }
897
898     if (opt.swap && driveno != swapdrive) {
899         static const uint8_t swapstub_master[] = {
900             /* The actual swap code */
901             0x53,               /* 00: push bx */
902             0x0f, 0xb6, 0xda,   /* 01: movzx bx,dl */
903             0x2e, 0x8a, 0x57, 0x60,     /* 04: mov dl,[cs:bx+0x60] */
904             0x5b,               /* 08: pop bx */
905             0xea, 0, 0, 0, 0,   /* 09: jmp far 0:0 */
906             0x90, 0x90,         /* 0E: nop; nop */
907             /* Code to install this in the right location */
908             /* Entry with DS = CS; ES = SI = 0; CX = 256 */
909             0x26, 0x66, 0x8b, 0x7c, 0x4c,       /* 10: mov edi,[es:si+4*0x13] */
910             0x66, 0x89, 0x3e, 0x0a, 0x00,       /* 15: mov [0x0A],edi */
911             0x26, 0x8b, 0x3e, 0x13, 0x04,       /* 1A: mov di,[es:0x413] */
912             0x4f,               /* 1F: dec di */
913             0x26, 0x89, 0x3e, 0x13, 0x04,       /* 20: mov [es:0x413],di */
914             0x66, 0xc1, 0xe7, 0x16,     /* 25: shl edi,16+6 */
915             0x26, 0x66, 0x89, 0x7c, 0x4c,       /* 29: mov [es:si+4*0x13],edi */
916             0x66, 0xc1, 0xef, 0x10,     /* 2E: shr edi,16 */
917             0x8e, 0xc7,         /* 32: mov es,di */
918             0x31, 0xff,         /* 34: xor di,di */
919             0xf3, 0x66, 0xa5,   /* 36: rep movsd */
920             0xbe, 0, 0,         /* 39: mov si,0 */
921             0xbf, 0, 0,         /* 3C: mov di,0 */
922             0x8e, 0xde,         /* 3F: mov ds,si */
923             0x8e, 0xc7,         /* 41: mov es,di */
924             0x66, 0xb9, 0, 0, 0, 0,     /* 43: mov ecx,0 */
925             0x66, 0xbe, 0, 0, 0, 0,     /* 49: mov esi,0 */
926             0x66, 0xbf, 0, 0, 0, 0,     /* 4F: mov edi,0 */
927             0xea, 0, 0, 0, 0,   /* 55: jmp 0:0 */
928             /* pad out to segment boundary */
929             0x90, 0x90,         /* 5A: ... */
930             0x90, 0x90, 0x90, 0x90,     /* 5C: ... */
931         };
932         static uint8_t swapstub[1024];
933         uint8_t *p;
934
935         /* Note: we can't rely on either INT 13h nor the dosmem
936            vector to be correct at this stage, so we have to use an
937            installer stub to put things in the right place.
938            Round the installer location to a 1K boundary so the only
939            possible overlap is the identity mapping. */
940         endimage = (endimage + 1023) & ~1023;
941
942         /* Create swap stub */
943         memcpy(swapstub, swapstub_master, sizeof swapstub_master);
944         *(uint16_t *) & swapstub[0x3a] = regs->ds;
945         *(uint16_t *) & swapstub[0x3d] = regs->es;
946         *(uint32_t *) & swapstub[0x45] = regs->ecx.l;
947         *(uint32_t *) & swapstub[0x4b] = regs->esi.l;
948         *(uint32_t *) & swapstub[0x51] = regs->edi.l;
949         *(uint16_t *) & swapstub[0x56] = regs->ip;
950         *(uint16_t *) & swapstub[0x58] = regs->cs;
951         p = &swapstub[sizeof swapstub_master];
952
953         /* Mapping table; start out with identity mapping everything */
954         for (i = 0; i < 256; i++)
955             p[i] = i;
956
957         /* And the actual swap */
958         p[driveno] = swapdrive;
959         p[swapdrive] = driveno;
960
961         /* Adjust registers */
962         regs->ds = regs->cs = endimage >> 4;
963         regs->es = regs->esi.l = 0;
964         regs->ecx.l = sizeof swapstub >> 2;
965         regs->ip = 0x10;        /* Installer offset */
966         regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
967
968         if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
969                                   sizeof swapstub))
970             goto enomem;
971
972         endimage += sizeof swapstub;
973     }
974
975     /* Tell the shuffler not to muck with this area... */
976     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
977
978     /* Force text mode */
979     syslinux_force_text_mode();
980
981     fputs("Booting...\n", stdout);
982     syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
983     error("Chainboot failed!\n");
984     return;
985
986 too_big:
987     error("Loader file too large\n");
988     return;
989
990 enomem:
991     error("Out of memory\n");
992     return;
993 }
994
995 static int hide_unhide(struct mbr *mbr, int part)
996 {
997     int i;
998     struct part_entry *pt;
999     const uint16_t mask =
1000         (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
1001                                                                                <<
1002                                                                                0x0c)
1003         | (1 << 0x0e);
1004     uint8_t t;
1005     bool write_back = false;
1006
1007     for (i = 1; i <= 4; i++) {
1008         pt = mbr->table + i - 1;
1009         t = pt->ostype;
1010         if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
1011             /* It's a hideable partition type */
1012             if (i == part)
1013                 t &= ~0x10;     /* unhide */
1014             else
1015                 t |= 0x10;      /* hide */
1016         }
1017         if (t != pt->ostype) {
1018             write_back = true;
1019             pt->ostype = t;
1020         }
1021     }
1022
1023     if (write_back)
1024         return write_verify_sector(0, mbr);
1025
1026     return 0;                   /* ok */
1027 }
1028
1029 static uint32_t get_file_lba(const char *filename)
1030 {
1031     com32sys_t inregs;
1032     uint32_t lba;
1033
1034     /* Start with clean registers */
1035     memset(&inregs, 0, sizeof(com32sys_t));
1036
1037     /* Put the filename in the bounce buffer */
1038     strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1039
1040     /* Call comapi_open() which returns a structure pointer in SI
1041      * to a structure whose first member happens to be the LBA.
1042      */
1043     inregs.eax.w[0] = 0x0006;
1044     inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1045     inregs.es = SEG(__com32.cs_bounce);
1046     __com32.cs_intcall(0x22, &inregs, &inregs);
1047
1048     if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
1049         return 0;               /* Filename not found */
1050     }
1051
1052     /* Since the first member is the LBA, we simply cast */
1053     lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
1054
1055     /* Clean the registers for the next call */
1056     memset(&inregs, 0, sizeof(com32sys_t));
1057
1058     /* Put the filename in the bounce buffer */
1059     strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
1060
1061     /* Call comapi_close() to free the structure */
1062     inregs.eax.w[0] = 0x0008;
1063     inregs.esi.w[0] = OFFS(__com32.cs_bounce);
1064     inregs.es = SEG(__com32.cs_bounce);
1065     __com32.cs_intcall(0x22, &inregs, &inregs);
1066
1067     return lba;
1068 }
1069
1070 static void usage(void)
1071 {
1072     error("Usage:   chain.c32 hd<disk#> [<partition>] [options]\n"
1073           "         chain.c32 fd<disk#> [options]\n"
1074           "         chain.c32 mbr:<id> [<partition>] [options]\n"
1075           "         chain.c32 boot [<partition>] [options]\n"
1076           "Options: file=<loader>      load file, instead of boot sector\n"
1077           "         isolinux=<loader>  load another version of ISOLINUX\n"
1078           "         ntldr=<loader>     load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n"
1079           "         cmldr=<loader>     load Recovery Console of Windows NT/2K/XP\n"
1080           "         freedos=<loader>   load FreeDOS kernel.sys\n"
1081           "         msdos=<loader>     load MS-DOS io.sys\n"
1082           "         pcdos=<loader>     load PC-DOS ibmbio.com\n"
1083           "         grub=<loader>      load GRUB stage2\n"
1084           "         seg=<segment>      jump to <seg>:0000 instead of 0000:7C00\n"
1085           "         swap               swap drive numbers, if bootdisk is not fd0/hd0\n"
1086           "         hide               hide primary partitions, except selected partition\n"
1087           "         sethidden          set the FAT/NTFS hidden sectors field\n");
1088 }
1089
1090 int main(int argc, char *argv[])
1091 {
1092     struct mbr *mbr = NULL;
1093     char *p;
1094     struct disk_part_iter *cur_part = NULL;
1095     struct syslinux_rm_regs regs;
1096     char *drivename, *partition;
1097     int hd, drive, whichpart;
1098     int i;
1099     uint32_t file_lba = 0;
1100     unsigned char *isolinux_bin;
1101     uint32_t *checksum, *chkhead, *chktail;
1102     struct data_area data[3];
1103     int ndata = 0;
1104     addr_t load_base;
1105     static const char cmldr_signature[8] = "cmdcons";
1106
1107     openconsole(&dev_null_r, &dev_stdcon_w);
1108
1109     drivename = "boot";
1110     partition = NULL;
1111
1112     /* Prepare the register set */
1113     memset(&regs, 0, sizeof regs);
1114
1115     for (i = 1; i < argc; i++) {
1116         if (!strncmp(argv[i], "file=", 5)) {
1117             opt.loadfile = argv[i] + 5;
1118         } else if (!strncmp(argv[i], "seg=", 4)) {
1119             uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
1120             if (segval < 0x50 || segval > 0x9f000) {
1121                 error("Invalid segment\n");
1122                 goto bail;
1123             }
1124             opt.seg = segval;
1125         } else if (!strncmp(argv[i], "isolinux=", 9)) {
1126             opt.loadfile = argv[i] + 9;
1127             opt.isolinux = true;
1128         } else if (!strncmp(argv[i], "ntldr=", 6)) {
1129             opt.seg = 0x2000;   /* NTLDR wants this address */
1130             opt.loadfile = argv[i] + 6;
1131             opt.sethidden = true;
1132         } else if (!strncmp(argv[i], "cmldr=", 6)) {
1133             opt.seg = 0x2000;   /* CMLDR wants this address */
1134             opt.loadfile = argv[i] + 6;
1135             opt.cmldr = true;
1136             opt.sethidden = true;
1137         } else if (!strncmp(argv[i], "freedos=", 8)) {
1138             opt.seg = 0x60;     /* FREEDOS wants this address */
1139             opt.loadfile = argv[i] + 8;
1140             opt.sethidden = true;
1141         } else if (!strncmp(argv[i], "msdos=", 6) ||
1142                    !strncmp(argv[i], "pcdos=", 6)) {
1143             opt.seg = 0x70;     /* MS-DOS 2.0+ wants this address */
1144             opt.loadfile = argv[i] + 6;
1145             opt.sethidden = true;
1146         } else if (!strncmp(argv[i], "grub=", 5)) {
1147             opt.seg = 0x800;    /* stage2 wants this address */
1148             opt.loadfile = argv[i] + 5;
1149             opt.grub = true;
1150         } else if (!strcmp(argv[i], "swap")) {
1151             opt.swap = true;
1152         } else if (!strcmp(argv[i], "noswap")) {
1153             opt.swap = false;
1154         } else if (!strcmp(argv[i], "hide")) {
1155             opt.hide = true;
1156         } else if (!strcmp(argv[i], "nohide")) {
1157             opt.hide = false;
1158         } else if (!strcmp(argv[i], "keeppxe")) {
1159             opt.keeppxe = 3;
1160         } else if (!strcmp(argv[i], "sethidden")) {
1161             opt.sethidden = true;
1162         } else if (!strcmp(argv[i], "nosethidden")) {
1163             opt.sethidden = false;
1164         } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
1165                     && argv[i][1] == 'd')
1166                    || !strncmp(argv[i], "mbr:", 4)
1167                    || !strncmp(argv[i], "mbr=", 4)
1168                    || !strcmp(argv[i], "boot")
1169                    || !strncmp(argv[i], "boot,", 5)) {
1170             drivename = argv[i];
1171             p = strchr(drivename, ',');
1172             if (p) {
1173                 *p = '\0';
1174                 partition = p + 1;
1175             } else if (argv[i + 1] && argv[i + 1][0] >= '0'
1176                        && argv[i + 1][0] <= '9') {
1177                 partition = argv[++i];
1178             }
1179         } else {
1180             usage();
1181             goto bail;
1182         }
1183     }
1184
1185     if (opt.seg) {
1186         regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
1187     } else {
1188         regs.ip = regs.esp.l = 0x7c00;
1189     }
1190
1191     hd = 0;
1192     if (!strncmp(drivename, "mbr", 3)) {
1193         drive = find_disk(strtoul(drivename + 4, NULL, 0));
1194         if (drive == -1) {
1195             error("Unable to find requested MBR signature\n");
1196             goto bail;
1197         }
1198     } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
1199                drivename[1] == 'd') {
1200         hd = drivename[0] == 'h';
1201         drivename += 2;
1202         drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
1203     } else if (!strcmp(drivename, "boot")) {
1204         const union syslinux_derivative_info *sdi;
1205         sdi = syslinux_derivative_info();
1206         if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
1207             drive = 0x80;       /* Boot drive not available */
1208         else
1209             drive = sdi->disk.drive_number;
1210     } else {
1211         error("Unparsable drive specification\n");
1212         goto bail;
1213     }
1214
1215     /* DOS kernels want the drive number in BL instead of DL.  Indulge them. */
1216     regs.ebx.b[0] = regs.edx.b[0] = drive;
1217
1218     whichpart = 0;              /* Default */
1219     if (partition)
1220         whichpart = strtoul(partition, NULL, 0);
1221
1222     if (!(drive & 0x80) && whichpart) {
1223         error("Warning: Partitions of floppy devices may not work\n");
1224     }
1225
1226     /* 
1227      * grldr of Grub4dos wants the partition number in DH:
1228      * -1:   whole drive (default)
1229      * 0-3:  primary partitions
1230      * 4-*:  logical partitions
1231      */
1232     regs.edx.b[1] = whichpart - 1;
1233
1234     /* Get the disk geometry and disk access setup */
1235     if (get_disk_params(drive)) {
1236         error("Cannot get disk parameters\n");
1237         goto bail;
1238     }
1239
1240     /* Get MBR */
1241     if (!(mbr = read_sectors(0, 1))) {
1242         error("Cannot read Master Boot Record\n");
1243         goto bail;
1244     }
1245
1246     if (opt.hide) {
1247         if (whichpart < 1 || whichpart > 4)
1248             error("WARNING: hide specified without a non-primary partition\n");
1249         if (hide_unhide(mbr, whichpart))
1250             error("WARNING: failed to write MBR for 'hide'\n");
1251     }
1252
1253     /* Boot the MBR by default */
1254     if (whichpart) {
1255         /* Boot a partition */
1256         cur_part = get_first_partition(NULL);
1257         while (cur_part) {
1258             if (cur_part->index == whichpart)
1259                 /* Found the partition to boot */
1260                 break;
1261             cur_part = cur_part->next(cur_part);
1262         }
1263         if (!cur_part) {
1264             error("Requested partition not found!\n");
1265             goto bail;
1266         }
1267     }
1268
1269     /* Do the actual chainloading */
1270     load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
1271
1272     if (opt.loadfile) {
1273         fputs("Loading the boot file...\n", stdout);
1274         if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
1275             error("Failed to load the boot file\n");
1276             goto bail;
1277         }
1278         data[ndata].base = load_base;
1279         load_base = 0x7c00;     /* If we also load a boot sector */
1280
1281         /* Create boot info table: needed when you want to chainload
1282            another version of ISOLINUX (or another bootlaoder that needs
1283            the -boot-info-table switch of mkisofs)
1284            (will only work when run from ISOLINUX) */
1285         if (opt.isolinux) {
1286             const union syslinux_derivative_info *sdi;
1287             sdi = syslinux_derivative_info();
1288
1289             if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
1290                 /* Boot info table info (integers in little endian format)
1291
1292                    Offset Name         Size      Meaning
1293                    8     bi_pvd       4 bytes   LBA of primary volume descriptor
1294                    12     bi_file      4 bytes   LBA of boot file
1295                    16     bi_length    4 bytes   Boot file length in bytes
1296                    20     bi_csum      4 bytes   32-bit checksum
1297                    24     bi_reserved  40 bytes  Reserved
1298
1299                    The 32-bit checksum is the sum of all the 32-bit words in the
1300                    boot file starting at byte offset 64. All linear block
1301                    addresses (LBAs) are given in CD sectors (normally 2048 bytes).
1302
1303                    LBA of primary volume descriptor should already be set to 16. 
1304                  */
1305
1306                 isolinux_bin = (unsigned char *)data[ndata].data;
1307
1308                 /* Get LBA address of bootfile */
1309                 file_lba = get_file_lba(opt.loadfile);
1310
1311                 if (file_lba == 0) {
1312                     error("Failed to find LBA offset of the boot file\n");
1313                     goto bail;
1314                 }
1315                 /* Set it */
1316                 *((uint32_t *) & isolinux_bin[12]) = file_lba;
1317
1318                 /* Set boot file length */
1319                 *((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
1320
1321                 /* Calculate checksum */
1322                 checksum = (uint32_t *) & isolinux_bin[20];
1323                 chkhead = (uint32_t *) & isolinux_bin[64];
1324                 chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
1325                 *checksum = 0;
1326                 while (chkhead < chktail)
1327                     *checksum += *chkhead++;
1328
1329                 /*
1330                  * Deal with possible fractional dword at the end;
1331                  * this *should* never happen...
1332                  */
1333                 if (data[ndata].size & 3) {
1334                     uint32_t xword = 0;
1335                     memcpy(&xword, chkhead, data[ndata].size & 3);
1336                     *checksum += xword;
1337                 }
1338             } else {
1339                 error
1340                     ("The isolinux= option is only valid when run from ISOLINUX\n");
1341                 goto bail;
1342             }
1343         }
1344
1345         if (opt.grub) {
1346             regs.ip = 0x200;    /* jump 0x200 bytes into the loadfile */
1347
1348             /* 0xffffff00 seems to be GRUB ways to record that it's
1349                "root" is the whole disk (and not a partition). */
1350             *(uint32_t *) ((unsigned char *)data[ndata].data + 0x208) =
1351                 0xffffff00ul;
1352         }
1353
1354         ndata++;
1355     }
1356
1357     if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
1358         /* Actually read the boot sector */
1359         if (!cur_part) {
1360             data[ndata].data = mbr;
1361         } else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) {
1362             error("Cannot read boot sector\n");
1363             goto bail;
1364         }
1365         data[ndata].size = SECTOR;
1366         data[ndata].base = load_base;
1367
1368         if (!opt.loadfile) {
1369             const struct mbr *br =
1370                 (const struct mbr *)((char *)data[ndata].data +
1371                                      data[ndata].size - sizeof(struct mbr));
1372             if (br->sig != mbr_sig_magic) {
1373                 error
1374                     ("Boot sector signature not found (unbootable disk/partition?)\n");
1375                 goto bail;
1376             }
1377         }
1378         /*
1379          * To boot the Recovery Console of Windows NT/2K/XP we need to write
1380          * the string "cmdcons\0" to memory location 0000:7C03.
1381          * Memory location 0000:7C00 contains the bootsector of the partition.
1382          */
1383         if (cur_part && opt.cmldr) {
1384             memcpy((char *)data[ndata].data + 3, cmldr_signature,
1385                    sizeof cmldr_signature);
1386         }
1387
1388         /*
1389          * Modify the hidden sectors (partition offset) copy in memory;
1390          * this modifies the field used by FAT and NTFS filesystems, and
1391          * possibly other boot loaders which use the same format.
1392          */
1393         if (cur_part && opt.sethidden) {
1394             *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
1395         }
1396
1397         ndata++;
1398     }
1399
1400     if (cur_part && cur_part->record) {
1401         /* 0x7BE is the canonical place for the first partition entry. */
1402         data[ndata].data = (void *)cur_part->record;
1403         data[ndata].base = 0x7be;
1404         data[ndata].size = sizeof(*cur_part->record);
1405         ndata++;
1406         regs.esi.w[0] = 0x7be;
1407     }
1408
1409     do_boot(data, ndata, &regs);
1410
1411 bail:
1412     if (cur_part)
1413         free(cur_part->block);
1414     free(cur_part);
1415     free(mbr);
1416     return 255;
1417 }