1 /* ----------------------------------------------------------------------- *
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
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.
14 * ----------------------------------------------------------------------- */
17 * Please see doc/chain.txt for the detailed documentation.
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>
46 static int fixed_cnt
= 128; /* see comments in main() */
48 static int overlap(const struct data_area
*a
, const struct data_area
*b
)
51 a
->base
+ a
->size
> b
->base
&&
52 b
->base
+ b
->size
> a
->base
;
55 static int is_phys(uint8_t sdifs
)
58 sdifs
== SYSLINUX_FS_SYSLINUX
||
59 sdifs
== SYSLINUX_FS_EXTLINUX
||
60 sdifs
== SYSLINUX_FS_ISOLINUX
;
64 * Search for a specific drive, based on the MBR signature.
65 * Return drive and iterator at 0th position.
67 static int find_by_sig(uint32_t mbr_sig
,
68 struct part_iter
**_boot_part
)
70 struct part_iter
*boot_part
= NULL
;
71 struct disk_info diskinfo
;
74 for (drive
= 0x80; drive
< 0x80 + fixed_cnt
; drive
++) {
75 if (disk_get_params(drive
, &diskinfo
))
76 continue; /* Drive doesn't exist */
77 if (!(boot_part
= pi_begin(&diskinfo
, 0)))
79 /* Check for a MBR disk */
80 if (boot_part
->type
!= typedos
) {
84 if (boot_part
->sub
.dos
.disk_sig
== mbr_sig
) {
90 *_boot_part
= boot_part
;
95 * Search for a specific drive/partition, based on the GPT GUID.
96 * Return drive and iterator at proper position.
98 static int find_by_guid(const struct guid
*gpt_guid
,
99 struct part_iter
**_boot_part
)
101 struct part_iter
*boot_part
= NULL
;
102 struct disk_info diskinfo
;
105 for (drive
= 0x80; drive
< 0x80 + fixed_cnt
; drive
++) {
106 if (disk_get_params(drive
, &diskinfo
))
107 continue; /* Drive doesn't exist */
108 if (!(boot_part
= pi_begin(&diskinfo
, 0)))
110 /* Check for a GPT disk */
111 if (boot_part
->type
!= typegpt
) {
115 /* Check for a matching GPT disk guid */
116 if (!memcmp(&boot_part
->sub
.gpt
.disk_guid
, gpt_guid
, sizeof(*gpt_guid
))) {
119 /* disk guid doesn't match, maybe partition guid will */
120 while (!pi_next(&boot_part
)) {
121 if (!memcmp(&boot_part
->sub
.gpt
.part_guid
, gpt_guid
, sizeof(*gpt_guid
)))
127 *_boot_part
= boot_part
;
132 * Search for a specific drive/partition, based on the GPT label.
133 * Return drive and iterator at proper position.
135 static int find_by_label(const char *label
, struct part_iter
**_boot_part
)
137 struct part_iter
*boot_part
= NULL
;
138 struct disk_info diskinfo
;
141 for (drive
= 0x80; drive
< 0x80 + fixed_cnt
; drive
++) {
142 if (disk_get_params(drive
, &diskinfo
))
143 continue; /* Drive doesn't exist */
144 if (!(boot_part
= pi_begin(&diskinfo
, 0)))
146 /* Check for a GPT disk */
147 if (!(boot_part
->type
== typegpt
)) {
151 /* Check for a matching partition */
152 while (!pi_next(&boot_part
)) {
153 if (!strcmp(label
, boot_part
->sub
.gpt
.part_label
))
159 *_boot_part
= boot_part
;
163 static void do_boot(struct data_area
*data
, int ndata
)
165 uint16_t *const bios_fbm
= (uint16_t *) 0x413;
166 addr_t dosmem
= (addr_t
)(*bios_fbm
<< 10); /* Technically a low bound */
167 struct syslinux_memmap
*mmap
;
168 struct syslinux_movelist
*mlist
= NULL
;
170 uint8_t driveno
= opt
.regs
.edx
.b
[0];
171 uint8_t swapdrive
= driveno
& 0x80;
174 mmap
= syslinux_memory_map();
177 error("Cannot read system memory map\n");
182 for (i
= 0; i
< ndata
; i
++) {
183 if (data
[i
].base
+ data
[i
].size
> endimage
)
184 endimage
= data
[i
].base
+ data
[i
].size
;
186 if (endimage
> dosmem
)
189 for (i
= 0; i
< ndata
; i
++) {
190 if (syslinux_add_movelist(&mlist
, data
[i
].base
,
191 (addr_t
) data
[i
].data
, data
[i
].size
))
195 if (opt
.swap
&& driveno
!= swapdrive
) {
196 static const uint8_t swapstub_master
[] = {
197 /* The actual swap code */
198 0x53, /* 00: push bx */
199 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
200 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
201 0x5b, /* 08: pop bx */
202 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
203 0x90, 0x90, /* 0E: nop; nop */
204 /* Code to install this in the right location */
205 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
206 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
207 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
208 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
209 0x4f, /* 1F: dec di */
210 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
211 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
212 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
213 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
214 0x8e, 0xc7, /* 32: mov es,di */
215 0x31, 0xff, /* 34: xor di,di */
216 0xf3, 0x66, 0xa5, /* 36: rep movsd */
217 0xbe, 0, 0, /* 39: mov si,0 */
218 0xbf, 0, 0, /* 3C: mov di,0 */
219 0x8e, 0xde, /* 3F: mov ds,si */
220 0x8e, 0xc7, /* 41: mov es,di */
221 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
222 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
223 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
224 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
225 /* pad out to segment boundary */
226 0x90, 0x90, /* 5A: ... */
227 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
229 static uint8_t swapstub
[1024];
232 /* Note: we can't rely on either INT 13h nor the dosmem
233 vector to be correct at this stage, so we have to use an
234 installer stub to put things in the right place.
235 Round the installer location to a 1K boundary so the only
236 possible overlap is the identity mapping. */
237 endimage
= (endimage
+ 1023u) & ~1023u;
239 /* Create swap stub */
240 memcpy(swapstub
, swapstub_master
, sizeof swapstub_master
);
241 *(uint16_t *) & swapstub
[0x3a] = opt
.regs
.ds
;
242 *(uint16_t *) & swapstub
[0x3d] = opt
.regs
.es
;
243 *(uint32_t *) & swapstub
[0x45] = opt
.regs
.ecx
.l
;
244 *(uint32_t *) & swapstub
[0x4b] = opt
.regs
.esi
.l
;
245 *(uint32_t *) & swapstub
[0x51] = opt
.regs
.edi
.l
;
246 *(uint16_t *) & swapstub
[0x56] = opt
.regs
.ip
;
247 *(uint16_t *) & swapstub
[0x58] = opt
.regs
.cs
;
248 p
= &swapstub
[sizeof swapstub_master
];
250 /* Mapping table; start out with identity mapping everything */
251 for (i
= 0; i
< 256; i
++)
254 /* And the actual swap */
255 p
[driveno
] = swapdrive
;
256 p
[swapdrive
] = driveno
;
258 /* Adjust registers */
259 opt
.regs
.ds
= opt
.regs
.cs
= (uint16_t)(endimage
>> 4);
260 opt
.regs
.esi
.l
= opt
.regs
.es
= 0;
261 opt
.regs
.ecx
.l
= sizeof swapstub
>> 2;
262 opt
.regs
.ip
= 0x10; /* Installer offset */
263 opt
.regs
.ebx
.b
[0] = opt
.regs
.edx
.b
[0] = swapdrive
;
265 if (syslinux_add_movelist(&mlist
, endimage
, (addr_t
) swapstub
,
269 endimage
+= sizeof swapstub
;
272 /* Tell the shuffler not to muck with this area... */
273 syslinux_add_memmap(&mmap
, endimage
, 0xa0000 - endimage
, SMT_RESERVED
);
275 /* Force text mode */
276 syslinux_force_text_mode();
278 fputs("Booting...\n", stdout
);
279 syslinux_shuffle_boot_rm(mlist
, mmap
, opt
.keeppxe
, &opt
.regs
);
280 error("Chainboot failed!\n");
284 error("Loader file too large\n");
288 error("Out of memory\n");
292 int find_dp(struct part_iter
**_iter
)
294 struct part_iter
*iter
= NULL
;
295 struct disk_info diskinfo
;
296 struct guid gpt_guid
;
298 int drive
, hd
, partition
;
299 const union syslinux_derivative_info
*sdi
;
301 sdi
= syslinux_derivative_info();
303 if (!strncmp(opt
.drivename
, "mbr", 3)) {
304 if (find_by_sig(strtoul(opt
.drivename
+ 4, NULL
, 0), &iter
) < 0) {
305 error("Unable to find requested MBR signature.\n");
308 } else if (!strncmp(opt
.drivename
, "guid", 4)) {
309 if (str_to_guid(opt
.drivename
+ 5, &gpt_guid
))
311 if (find_by_guid(&gpt_guid
, &iter
) < 0) {
312 error("Unable to find requested GPT disk or partition by guid.\n");
315 } else if (!strncmp(opt
.drivename
, "label", 5)) {
316 if (!opt
.drivename
[6]) {
317 error("No label specified.\n");
320 if (find_by_label(opt
.drivename
+ 6, &iter
) < 0) {
321 error("Unable to find requested GPT partition by label.\n");
324 } else if ((opt
.drivename
[0] == 'h' || opt
.drivename
[0] == 'f') &&
325 opt
.drivename
[1] == 'd') {
326 hd
= opt
.drivename
[0] == 'h' ? 0x80 : 0;
328 drive
= hd
| strtol(opt
.drivename
, NULL
, 0);
330 if (disk_get_params(drive
, &diskinfo
))
332 /* this will start iteration over FDD, possibly raw */
333 if (!(iter
= pi_begin(&diskinfo
, 0)))
336 } else if (!strcmp(opt
.drivename
, "boot") || !strcmp(opt
.drivename
, "fs")) {
337 if (!is_phys(sdi
->c
.filesystem
)) {
338 error("When syslinux is not booted from physical disk (or its emulation),\n"
339 "'boot' and 'fs' are meaningless.\n");
342 /* offsets match, but in case it changes in the future */
343 if (sdi
->c
.filesystem
== SYSLINUX_FS_ISOLINUX
) {
344 drive
= sdi
->iso
.drive_number
;
345 fs_lba
= *sdi
->iso
.partoffset
;
347 drive
= sdi
->disk
.drive_number
;
348 fs_lba
= *sdi
->disk
.partoffset
;
350 if (disk_get_params(drive
, &diskinfo
))
352 /* this will start iteration over disk emulation, possibly raw */
353 if (!(iter
= pi_begin(&diskinfo
, 0)))
356 /* 'fs' => we should lookup the syslinux partition number and use it */
357 if (!strcmp(opt
.drivename
, "fs")) {
358 while (!pi_next(&iter
)) {
359 if (iter
->start_lba
== fs_lba
)
362 /* broken part structure or other problems */
364 error("Can't find myself on the drive I booted from.\n");
369 error("Unparsable drive specification.\n");
372 /* main options done - only thing left is explicit partition specification,
373 * if we're still at the disk stage with the iterator AND user supplied
374 * partition number (including disk pseudo-partition).
376 if (!iter
->index
&& opt
.partition
) {
377 partition
= strtol(opt
.partition
, NULL
, 0);
378 /* search for matching part#, including disk */
380 if (iter
->index
== partition
)
382 } while (!pi_next(&iter
));
384 error("Requested disk / partition combination not found.\n");
389 if (!(iter
->di
.disk
& 0x80) && iter
->index
) {
390 error("WARNING: Partitions on floppy devices may not work.\n");
402 static int setup_handover(const struct part_iter
*iter
,
403 struct data_area
*data
)
405 struct disk_dos_part_entry
*ha
;
409 if (!iter
->index
) { /* implies typeraw or non-iterated */
411 /* RAW handover protocol */
412 synth_size
= sizeof(struct disk_dos_part_entry
);
413 ha
= malloc(synth_size
);
415 error("Could not build RAW hand-over record!\n");
419 if (iter
->di
.lbacnt
< len
)
420 len
= (uint32_t)iter
->di
.lbacnt
;
421 *(uint32_t *)ha
->start
= lba2chs(&iter
->di
, 0, l2c_cadd
);
422 *(uint32_t *)ha
->end
= lba2chs(&iter
->di
, len
- 1, l2c_cadd
);
423 ha
->active_flag
= 0x80;
424 ha
->ostype
= 0xDA; /* "Non-FS Data", anything is good here though ... */
427 } else if (iter
->type
== typegpt
) {
428 const struct disk_gpt_part_entry
*gp
;
430 /* GPT handover protocol */
431 gp
= (const struct disk_gpt_part_entry
*)iter
->record
;
432 lba_count
= gp
->lba_last
- gp
->lba_first
+ 1;
433 synth_size
= sizeof(struct disk_dos_part_entry
) +
434 sizeof(uint32_t) + (uint32_t)iter
->sub
.gpt
.pe_size
;
435 ha
= malloc(synth_size
);
437 error("Could not build GPT hand-over record!\n");
440 *(uint32_t *)ha
->start
= lba2chs(&iter
->di
, gp
->lba_first
, l2c_cadd
);
441 *(uint32_t *)ha
->end
= lba2chs(&iter
->di
, gp
->lba_last
, l2c_cadd
);
442 ha
->active_flag
= 0x80;
444 /* All bits set by default */
447 /* If these fit the precision, pass them on */
448 if (iter
->start_lba
< ha
->start_lba
)
449 ha
->start_lba
= (uint32_t)iter
->start_lba
;
450 if (lba_count
< ha
->length
)
451 ha
->length
= (uint32_t)lba_count
;
452 /* Next comes the GPT partition record length */
453 plen
= (uint32_t *) (ha
+ 1);
454 plen
[0] = (uint32_t)iter
->sub
.gpt
.pe_size
;
455 /* Next comes the GPT partition record copy */
456 memcpy(plen
+ 1, gp
, plen
[0]);
458 dprintf("GPT handover:\n");
459 disk_dos_part_dump(ha
);
460 disk_gpt_part_dump((struct disk_gpt_part_entry
*)(plen
+ 1));
462 } else if (iter
->type
== typedos
) {
463 const struct disk_dos_part_entry
*dp
;
464 /* MBR handover protocol */
465 dp
= (const struct disk_dos_part_entry
*)iter
->record
;
466 synth_size
= sizeof(struct disk_dos_part_entry
);
467 ha
= malloc(synth_size
);
469 error("Could not build MBR hand-over record!\n");
472 *(uint32_t *)ha
->start
= lba2chs(&iter
->di
, iter
->start_lba
, l2c_cadd
);
473 *(uint32_t *)ha
->end
= lba2chs(&iter
->di
, iter
->start_lba
+ dp
->length
- 1, l2c_cadd
);
474 ha
->active_flag
= dp
->active_flag
;
475 ha
->ostype
= dp
->ostype
;
476 ha
->start_lba
= (uint32_t)iter
->start_lba
; /* fine, we iterate over legacy scheme */
477 ha
->length
= dp
->length
;
479 dprintf("MBR handover:\n");
480 disk_dos_part_dump(ha
);
482 /* shouldn't ever happen */
488 data
->size
= synth_size
;
489 data
->data
= (void *)ha
;
496 int main(int argc
, char *argv
[])
498 struct part_iter
*iter
= NULL
;
501 struct data_area fdat
, hdat
, sdat
, data
[3];
505 /* openconsole(&dev_null_r, &dev_stdcon_w);*/
507 /* Prepare and set defaults */
508 memset(&fdat
, 0, sizeof(fdat
));
509 memset(&hdat
, 0, sizeof(hdat
));
510 memset(&sdat
, 0, sizeof(sdat
));
511 memset(&opt
, 0, sizeof(opt
));
512 opt
.sect
= true; /* by def. load sector */
513 opt
.maps
= true; /* by def. map sector */
514 opt
.hand
= true; /* by def. prepare handover */
515 opt
.chain
= true; /* by def. do chainload */
516 opt
.foff
= opt
.soff
= opt
.fip
= opt
.sip
= 0x7C00;
517 opt
.drivename
= "boot";
522 /* Parse arguments */
523 if (parse_args(argc
, argv
))
526 /* Get max fixed disk number */
527 fixed_cnt
= *(uint8_t *)(0x475);
530 * hmm, looks like we can't do that
531 * any better options than hardcoded 0x80 - 0xFF ?
535 /* Get disk/part iterator matching user supplied options */
539 /* Perform initial partition entry mangling */
540 if (manglepe_mbrchshide(iter
))
543 /* Load the boot file */
545 fdat
.base
= (opt
.fseg
<< 4) + opt
.foff
;
547 if (loadfile(opt
.file
, &fdat
.data
, &fdat
.size
)) {
548 error("Couldn't read the boot file.\n");
551 if (fdat
.base
+ fdat
.size
- 1 > ADDRMAX
) {
552 error("The boot file is too big to load at this address.\n");
557 /* Load the sector */
559 sdat
.base
= (opt
.sseg
<< 4) + opt
.soff
;
560 sdat
.size
= iter
->di
.bps
;
562 if (sdat
.base
+ sdat
.size
- 1 > ADDRMAX
) {
563 error("The sector cannot be loaded at such high address.\n");
566 if (opt
.file
&& opt
.maps
&& overlap(&fdat
, &sdat
)) {
567 error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
570 if (!(sdat
.data
= disk_read_sectors(&iter
->di
, iter
->start_lba
, 1))) {
571 error("Couldn't read the sector.\n");
575 if (!(sbck
= malloc(sdat
.size
))) {
576 error("Couldn't allocate cmp-buf for option 'save'.\n");
579 memcpy(sbck
, sdat
.data
, sdat
.size
);
584 /* Prep the handover */
586 if (setup_handover(iter
, &hdat
))
588 /* Verify possible conflicts */
589 if ( ( opt
.file
&& overlap(&fdat
, &hdat
)) ||
590 ( opt
.sect
&& overlap(&sdat
, &hdat
) && opt
.maps
) ) {
591 error("WARNING: Handover area won't be prepared,\n"
592 "as it would conflict with the boot file and/or the sector.\n");
597 /* Adjust registers */
599 mangler_common(iter
);
600 mangler_handover(iter
, &hdat
);
603 /* Patching functions */
605 if (manglef_isolinux(&fdat
))
608 if (manglef_grub(iter
, &fdat
))
611 if (manglef_drmk(&fdat
))
614 if (manglef_bpb(iter
, &fdat
))
617 if (mangles_bpb(iter
, &sdat
))
620 if (mangles_save(iter
, &sdat
, sbck
))
623 if (manglesf_bss(&sdat
, &fdat
))
626 /* This *must* be after BPB saving or copying */
627 if (mangles_cmldr(&sdat
))
631 * Prepare boot-time mmap data. We should to it here, as manglers could
632 * potentially alter some of the data.
636 memcpy(data
+ ndata
++, &fdat
, sizeof(fdat
));
637 if (opt
.sect
&& opt
.maps
)
638 memcpy(data
+ ndata
++, &sdat
, sizeof(sdat
));
640 memcpy(data
+ ndata
++, &hdat
, sizeof(hdat
));
643 printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %llu, %u\n"
644 "iter->di C, H, S: %u, %u, %u\n",
645 iter
->di
.disk
, iter
->di
.bps
,
646 iter
->di
.lbacnt
, iter
->di
.cyl
* iter
->di
.head
* iter
->di
.spt
,
647 iter
->di
.cyl
, iter
->di
.head
, iter
->di
.spt
);
648 printf("iter idx: %d\n", iter
->index
);
649 printf("iter lba: %llu\n", iter
->start_lba
);
651 printf("hand lba: %u\n",
652 ((struct disk_dos_part_entry
*)hdat
.data
)->start_lba
);
656 puts("Press any key to continue booting...");
660 if (ndata
&& opt
.chain
) /* boot only if we actually chainload */
661 do_boot(data
, ndata
);
663 error("Service-only run completed, exiting.\n");
666 /* Free allocated areas */
674 /* vim: set ts=8 sts=4 sw=4 noet: */