Merge branch 'master' into chaindev
[syslinux.git] / com32 / chain / mangle.c
blobe4d4be36373e5f9ce1a4eb484fda7b0fabd3eea3
1 #include <com32.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <syslinux/config.h>
7 #include "common.h"
8 #include "chain.h"
9 #include "options.h"
10 #include "utility.h"
11 #include "partiter.h"
12 #include "mangle.h"
14 static const char cmldr_signature[8] = "cmdcons";
16 /* Create boot info table: needed when you want to chainload
17 * another version of ISOLINUX (or another bootlaoder that needs
18 * the -boot-info-table switch of mkisofs)
19 * (will only work when run from ISOLINUX)
21 int manglef_isolinux(struct data_area *data)
23 const union syslinux_derivative_info *sdi;
24 unsigned char *isolinux_bin;
25 uint32_t *checksum, *chkhead, *chktail;
26 uint32_t file_lba = 0;
28 if (!(opt.file && opt.isolinux))
29 return 0;
31 sdi = syslinux_derivative_info();
33 if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
34 error ("The isolinux= option is only valid when run from ISOLINUX.\n");
35 goto bail;
38 /* Boot info table info (integers in little endian format)
40 Offset Name Size Meaning
41 8 bi_pvd 4 bytes LBA of primary volume descriptor
42 12 bi_file 4 bytes LBA of boot file
43 16 bi_length 4 bytes Boot file length in bytes
44 20 bi_csum 4 bytes 32-bit checksum
45 24 bi_reserved 40 bytes Reserved
47 The 32-bit checksum is the sum of all the 32-bit words in the
48 boot file starting at byte offset 64. All linear block
49 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
51 LBA of primary volume descriptor should already be set to 16.
54 isolinux_bin = (unsigned char *)data->data;
56 /* Get LBA address of bootfile */
57 file_lba = get_file_lba(opt.file);
59 if (file_lba == 0) {
60 error("Failed to find LBA offset of the boot file\n");
61 goto bail;
63 /* Set it */
64 *((uint32_t *) & isolinux_bin[12]) = file_lba;
66 /* Set boot file length */
67 *((uint32_t *) & isolinux_bin[16]) = data->size;
69 /* Calculate checksum */
70 checksum = (uint32_t *) & isolinux_bin[20];
71 chkhead = (uint32_t *) & isolinux_bin[64];
72 chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
73 *checksum = 0;
74 while (chkhead < chktail)
75 *checksum += *chkhead++;
78 * Deal with possible fractional dword at the end;
79 * this *should* never happen...
81 if (data->size & 3) {
82 uint32_t xword = 0;
83 memcpy(&xword, chkhead, data->size & 3);
84 *checksum += xword;
86 return 0;
87 bail:
88 return -1;
92 * Legacy grub's stage2 chainloading
94 int manglef_grub(const struct part_iter *iter, struct data_area *data)
96 /* Layout of stage2 file (from byte 0x0 to 0x270) */
97 struct grub_stage2_patch_area {
98 /* 0x0 to 0x205 */
99 char unknown[0x206];
100 /* 0x206: compatibility version number major */
101 uint8_t compat_version_major;
102 /* 0x207: compatibility version number minor */
103 uint8_t compat_version_minor;
105 /* 0x208: install_partition variable */
106 struct {
107 /* 0x208: sub-partition in sub-partition part2 */
108 uint8_t part3;
109 /* 0x209: sub-partition in top-level partition */
110 uint8_t part2;
111 /* 0x20a: top-level partiton number */
112 uint8_t part1;
113 /* 0x20b: BIOS drive number (must be 0) */
114 uint8_t drive;
115 } __attribute__ ((packed)) install_partition;
117 /* 0x20c: deprecated (historical reason only) */
118 uint32_t saved_entryno;
119 /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
120 uint8_t stage2_id;
121 /* 0x211: force LBA */
122 uint8_t force_lba;
123 /* 0x212: version string (will probably be 0.97) */
124 char version_string[5];
125 /* 0x217: config filename */
126 char config_file[89];
127 /* 0x270: start of code (after jump from 0x200) */
128 char codestart[1];
129 } __attribute__ ((packed)) *stage2;
131 if (!(opt.file && opt.grub))
132 return 0;
134 if (data->size < sizeof(struct grub_stage2_patch_area)) {
135 error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
136 goto bail;
138 stage2 = data->data;
141 * Check the compatibility version number to see if we loaded a real
142 * stage2 file or a stage2 file that we support.
144 if (stage2->compat_version_major != 3
145 || stage2->compat_version_minor != 2) {
146 error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
147 goto bail;
151 * GRUB Legacy wants the partition number in the install_partition
152 * variable, located at offset 0x208 of stage2.
153 * When GRUB Legacy is loaded, it is located at memory address 0x8208.
155 * It looks very similar to the "boot information format" of the
156 * Multiboot specification:
157 * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
159 * 0x208 = part3: sub-partition in sub-partition part2
160 * 0x209 = part2: sub-partition in top-level partition
161 * 0x20a = part1: top-level partition number
162 * 0x20b = drive: BIOS drive number (must be 0)
164 * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
165 * another location.
167 * Partition numbers always start from zero.
168 * Unused partition bytes must be set to 0xFF.
170 * We only care about top-level partition, so we only need to change
171 * "part1" to the appropriate value:
172 * -1: whole drive (default) (-1 = 0xFF)
173 * 0-3: primary partitions
174 * 4-*: logical partitions
176 stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
179 * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
180 * config filename. The filename passed via grubcfg= will overwrite
181 * the default config filename "/boot/grub/menu.lst".
183 if (opt.grubcfg) {
184 if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
185 error ("The config filename length can't exceed 88 characters.\n");
186 goto bail;
189 strcpy((char *)stage2->config_file, opt.grubcfg);
192 return 0;
193 bail:
194 return -1;
196 #if 0
198 * Dell's DRMK chainloading.
200 int manglef_drmk(struct data_area *data)
203 * DRMK entry is different than MS-DOS/PC-DOS
204 * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
205 * We only really need 4 new, usable bytes at the end.
208 if (!(opt.file && opt.drmk))
209 return 0;
211 uint32_t tsize = (data->size + 19) & 0xfffffff0;
212 const union syslinux_derivative_info *sdi;
213 uint64_t fs_lba;
215 sdi = syslinux_derivative_info();
216 /* We should lookup the Syslinux partition offset and use it */
217 fs_lba = *sdi->disk.partoffset;
220 * fs_lba should be verified against the disk as some DRMK
221 * variants will check and fail if it does not match
223 dprintf(" fs_lba offset is %d\n", fs_lba);
224 /* DRMK only uses a DWORD */
225 if (fs_lba > 0xffffffff) {
226 error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
228 opt.regs.ss = opt.regs.fs = opt.regs.gs = 0; /* Used before initialized */
229 if (!realloc(data->data, tsize)) {
230 error("Failed to realloc for DRMK.\n");
231 goto bail;
233 data->size = tsize;
234 /* ds:bp is assumed by DRMK to be the boot sector */
235 /* offset 28 is the FAT HiddenSectors value */
236 opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
237 /* "Patch" into tail of the new space */
238 *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba;
240 return 0;
241 bail:
242 return -1;
244 #endif
245 /* Adjust BPB common function */
246 static int mangle_bpb(const struct part_iter *iter, struct data_area *data)
248 unsigned int off;
249 int type = bpb_detect(data->data);
251 /* BPB: hidden sectors 32bit*/
252 if (type >= bpbV34) {
253 if (iter->start_lba < ~0u)
254 *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
255 else
256 /* won't really help much, but ... */
257 *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
259 /* BPB: hidden sectors 16bit*/
260 if (bpbV30 <= type && type <= bpbV32) {
261 if (iter->start_lba < 0xFFFF)
262 *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba;
263 else
264 /* won't really help much, but ... */
265 *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
267 /* BPB: legacy geometry */
268 if (type >= bpbV30) {
269 if (iter->di.cbios)
270 *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt);
271 else {
272 if (iter->di.disk & 0x80)
273 *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
274 else
275 *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
278 /* BPB: drive */
279 if (drvoff_detect(type, &off)) {
280 *(uint8_t *)((char *)data->data + off) = (uint8_t)
281 (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
284 return 0;
288 * Adjust BPB of a BPB-compatible file
290 int manglef_bpb(const struct part_iter *iter, struct data_area *data)
292 if (!(opt.file && opt.filebpb))
293 return 0;
295 return mangle_bpb(iter, data);
299 * Adjust BPB of a sector
301 int mangles_bpb(const struct part_iter *iter, struct data_area *data)
303 if (!(opt.sect && opt.setbpb))
304 return 0;
306 return mangle_bpb(iter, data);
310 * This function performs full BPB patching, analogously to syslinux's
311 * native BSS.
313 int manglesf_bss(struct data_area *sec, struct data_area *fil)
315 int type1, type2;
316 unsigned int cnt = 0;
318 if (!(opt.sect && opt.file && opt.bss))
319 return 0;
321 type1 = bpb_detect(fil->data);
322 type2 = bpb_detect(sec->data);
324 if (!type1 || !type2) {
325 error("Couldn't determine the BPB type for option 'bss'.\n");
326 goto bail;
328 if (type1 != type2) {
329 error("Option 'bss' can't be used,\n"
330 "when a sector and a file have incompatible BPBs.\n");
331 goto bail;
334 /* Copy common 2.0 data */
335 memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
337 /* Copy 3.0+ data */
338 if (type1 <= bpbV30) {
339 cnt = 0x06;
340 } else if (type1 <= bpbV32) {
341 cnt = 0x08;
342 } else if (type1 <= bpbV34) {
343 cnt = 0x0C;
344 } else if (type1 <= bpbV40) {
345 cnt = 0x2E;
346 } else if (type1 <= bpbVNT) {
347 cnt = 0x3C;
348 } else if (type1 <= bpbV70) {
349 cnt = 0x42;
351 memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
353 return 0;
354 bail:
355 return -1;
359 * Save sector.
361 int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
363 if (!(opt.sect && opt.save))
364 return 0;
366 if (memcmp(org, data->data, data->size)) {
367 if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) {
368 error("Cannot write the updated sector.\n");
369 goto bail;
371 /* function can be called again */
372 memcpy(org, data->data, data->size);
375 return 0;
376 bail:
377 return -1;
381 * To boot the Recovery Console of Windows NT/2K/XP we need to write
382 * the string "cmdcons\0" to memory location 0000:7C03.
383 * Memory location 0000:7C00 contains the bootsector of the partition.
385 int mangles_cmldr(struct data_area *data)
387 if (!(opt.sect && opt.cmldr))
388 return 0;
390 memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
391 return 0;
394 /* Set common registers */
395 int mangler_common(const struct part_iter *iter)
397 /* Set initial registry values */
398 if (opt.file) {
399 opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
400 opt.regs.ip = (uint16_t)opt.fip;
401 } else {
402 opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
403 opt.regs.ip = (uint16_t)opt.sip;
406 if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
407 opt.regs.esp.l = 0x7C00;
409 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
410 opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
412 return 0;
415 /* ds:si & ds:bp */
416 int mangler_handover(const struct part_iter *iter, const struct data_area *data)
418 if (opt.sect && opt.file && opt.maps && !opt.hptr) {
419 opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
420 opt.regs.ds = (uint16_t)opt.sseg;
421 opt.regs.eax.l = 0;
422 } else if (opt.hand) {
423 /* base is really 0x7be */
424 opt.regs.esi.l = opt.regs.ebp.l = data->base;
425 opt.regs.ds = 0;
426 if (iter->type == typegpt)
427 opt.regs.eax.l = 0x54504721; /* '!GPT' */
428 else
429 opt.regs.eax.l = 0;
432 return 0;
436 * GRLDR of GRUB4DOS wants the partition number in DH:
437 * -1: whole drive (default)
438 * 0-3: primary partitions
439 * 4-*: logical partitions
441 int mangler_grldr(const struct part_iter *iter)
443 if (opt.grldr)
444 opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
446 return 0;
449 static int mpe_sethide(struct part_iter *miter, struct part_iter *iter)
451 struct disk_dos_part_entry *mdp, *dp;
452 static const uint16_t mask =
453 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
454 (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
455 uint8_t t;
457 mdp = (struct disk_dos_part_entry *)miter->record;
458 dp = (struct disk_dos_part_entry *)iter->record;
459 t = dp->ostype;
461 if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
462 /* It's a hideable partition type */
463 if (miter->index == iter->index || opt.hide & 4)
464 t &= (uint8_t)(~0x10u); /* unhide */
465 else
466 t |= 0x10u; /* hide */
468 if (dp->ostype != t) {
469 dp->ostype = t;
471 * the type of the partition being booted has to be adjusted in
472 * the match iterator (miter) as well
474 if (miter->index == iter->index) {
475 mdp->ostype = t;
477 return -1;
479 return 0;
482 static int mpe_setchs(const struct disk_info *di,
483 struct disk_dos_part_entry *dp,
484 uint32_t lba1)
486 uint32_t ochs1, ochs2;
488 ochs1 = *(uint32_t *)dp->start;
489 ochs2 = *(uint32_t *)dp->end;
491 *(uint32_t *)dp->start =
492 lba2chs(di, lba1, l2c_cadd) |
493 (*(uint32_t *)dp->start & 0xFF000000);
495 *(uint32_t *)dp->end =
496 lba2chs(di, lba1 + dp->length - 1, l2c_cadd) |
497 (*(uint32_t *)dp->end & 0xFF000000);
499 return
500 *(uint32_t *)dp->start != ochs1 ||
501 *(uint32_t *)dp->end != ochs2;
504 int manglepe_mbrchshide(struct part_iter *miter)
506 int wb = 0, werr = 0;
507 struct part_iter *iter = NULL;
508 struct disk_dos_part_entry *dp;
509 int ridx;
511 if (!(opt.mbrchs || opt.hide))
512 return 0;
514 if (miter->type != typedos) {
515 error("Partition entry mangling ('[un]hide[all]', 'mbrchs')\n"
516 "is meaningful only for legacy partition scheme.\n");
517 return -1;
519 if (opt.hide &&
520 ((miter->index < 1 && opt.hide < 4) || /* try to hide a disk */
521 (miter->index > 4 && opt.hide == 1))) /* try to hide a part when limited to pri */
522 error("WARNING: It's impossible to hide the selected partition (or you selected a disk).\n");
524 if (!(iter = pi_begin(&miter->di, 1))) /* turn on stepall */
525 return -1;
527 while (!pi_next(&iter) && !werr && (opt.hide & 2 || opt.mbrchs)) {
528 ridx = iter->rawindex;
529 dp = (struct disk_dos_part_entry *)iter->record;
531 if (dp->ostype) {
532 if (opt.hide & 2 || (opt.hide & 1 && ridx <= 4)) {
533 wb |= mpe_sethide(miter, iter);
535 if (opt.mbrchs) {
536 wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
537 if (ridx > 4)
538 wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
542 if (ridx >= 4 && wb && !werr) {
543 werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
544 wb = 0;
547 /* last write */
548 if (wb && !werr)
549 werr |= disk_write_sectors(&miter->di, iter->sub.dos.cebr_lba, iter->data, 1);
551 pi_del(&iter);
552 if (werr)
553 error("WARNING: failed to write E/MBR for partition\n"
554 "mangling options ('[un]hide[all]', 'mbrchs').\n");
555 return 0;
558 /* vim: set ts=8 sts=4 sw=4 noet: */