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