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