[e16e8f2] | 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 | * Permission is hereby granted, free of charge, to any person |
---|
| 9 | * obtaining a copy of this software and associated documentation |
---|
| 10 | * files (the "Software"), to deal in the Software without |
---|
| 11 | * restriction, including without limitation the rights to use, |
---|
| 12 | * copy, modify, merge, publish, distribute, sublicense, and/or |
---|
| 13 | * sell copies of the Software, and to permit persons to whom |
---|
| 14 | * the Software is furnished to do so, subject to the following |
---|
| 15 | * conditions: |
---|
| 16 | * |
---|
| 17 | * The above copyright notice and this permission notice shall |
---|
| 18 | * be included in all copies or substantial portions of the Software. |
---|
| 19 | * |
---|
| 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
---|
| 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
---|
| 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
---|
| 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
---|
| 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
---|
| 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
---|
| 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
---|
| 27 | * OTHER DEALINGS IN THE SOFTWARE. |
---|
| 28 | * |
---|
| 29 | * ----------------------------------------------------------------------- */ |
---|
| 30 | |
---|
| 31 | #include <com32.h> |
---|
| 32 | #include <stdlib.h> |
---|
| 33 | #include <stdio.h> |
---|
| 34 | #include <string.h> |
---|
| 35 | #include <stdint.h> |
---|
| 36 | #include <dprintf.h> |
---|
| 37 | #include <syslinux/config.h> |
---|
| 38 | #include "chain.h" |
---|
| 39 | #include "options.h" |
---|
| 40 | #include "utility.h" |
---|
| 41 | #include "partiter.h" |
---|
| 42 | #include "mangle.h" |
---|
| 43 | |
---|
| 44 | static const char cmldr_signature[8] = "cmdcons"; |
---|
| 45 | |
---|
| 46 | /* Create boot info table: needed when you want to chainload |
---|
| 47 | * another version of ISOLINUX (or another bootlaoder that needs |
---|
| 48 | * the -boot-info-table switch of mkisofs) |
---|
| 49 | * (will only work when run from ISOLINUX) |
---|
| 50 | */ |
---|
| 51 | int manglef_isolinux(struct data_area *data) |
---|
| 52 | { |
---|
| 53 | const union syslinux_derivative_info *sdi; |
---|
| 54 | unsigned char *isolinux_bin; |
---|
| 55 | uint32_t *checksum, *chkhead, *chktail; |
---|
| 56 | uint32_t file_lba = 0; |
---|
| 57 | |
---|
| 58 | if (!(opt.file && opt.isolinux)) |
---|
| 59 | return 0; |
---|
| 60 | |
---|
| 61 | sdi = syslinux_derivative_info(); |
---|
| 62 | |
---|
| 63 | if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) { |
---|
| 64 | error("The isolinux= option is only valid when run from ISOLINUX."); |
---|
| 65 | goto bail; |
---|
| 66 | } |
---|
| 67 | |
---|
| 68 | /* Boot info table info (integers in little endian format) |
---|
| 69 | |
---|
| 70 | Offset Name Size Meaning |
---|
| 71 | 8 bi_pvd 4 bytes LBA of primary volume descriptor |
---|
| 72 | 12 bi_file 4 bytes LBA of boot file |
---|
| 73 | 16 bi_length 4 bytes Boot file length in bytes |
---|
| 74 | 20 bi_csum 4 bytes 32-bit checksum |
---|
| 75 | 24 bi_reserved 40 bytes Reserved |
---|
| 76 | |
---|
| 77 | The 32-bit checksum is the sum of all the 32-bit words in the |
---|
| 78 | boot file starting at byte offset 64. All linear block |
---|
| 79 | addresses (LBAs) are given in CD sectors (normally 2048 bytes). |
---|
| 80 | |
---|
| 81 | LBA of primary volume descriptor should already be set to 16. |
---|
| 82 | */ |
---|
| 83 | |
---|
| 84 | isolinux_bin = (unsigned char *)data->data; |
---|
| 85 | |
---|
| 86 | /* Get LBA address of bootfile */ |
---|
| 87 | file_lba = get_file_lba(opt.file); |
---|
| 88 | |
---|
| 89 | if (file_lba == 0) { |
---|
| 90 | error("Failed to find LBA offset of the boot file."); |
---|
| 91 | goto bail; |
---|
| 92 | } |
---|
| 93 | /* Set it */ |
---|
| 94 | *((uint32_t *) & isolinux_bin[12]) = file_lba; |
---|
| 95 | |
---|
| 96 | /* Set boot file length */ |
---|
| 97 | *((uint32_t *) & isolinux_bin[16]) = data->size; |
---|
| 98 | |
---|
| 99 | /* Calculate checksum */ |
---|
| 100 | checksum = (uint32_t *) & isolinux_bin[20]; |
---|
| 101 | chkhead = (uint32_t *) & isolinux_bin[64]; |
---|
| 102 | chktail = (uint32_t *) & isolinux_bin[data->size & ~3u]; |
---|
| 103 | *checksum = 0; |
---|
| 104 | while (chkhead < chktail) |
---|
| 105 | *checksum += *chkhead++; |
---|
| 106 | |
---|
| 107 | /* |
---|
| 108 | * Deal with possible fractional dword at the end; |
---|
| 109 | * this *should* never happen... |
---|
| 110 | */ |
---|
| 111 | if (data->size & 3) { |
---|
| 112 | uint32_t xword = 0; |
---|
| 113 | memcpy(&xword, chkhead, data->size & 3); |
---|
| 114 | *checksum += xword; |
---|
| 115 | } |
---|
| 116 | return 0; |
---|
| 117 | bail: |
---|
| 118 | return -1; |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | /* |
---|
| 122 | * Legacy grub's stage2 chainloading |
---|
| 123 | */ |
---|
| 124 | int manglef_grub(const struct part_iter *iter, struct data_area *data) |
---|
| 125 | { |
---|
| 126 | /* Layout of stage2 file (from byte 0x0 to 0x270) */ |
---|
| 127 | struct grub_stage2_patch_area { |
---|
| 128 | /* 0x0 to 0x205 */ |
---|
| 129 | char unknown[0x206]; |
---|
| 130 | /* 0x206: compatibility version number major */ |
---|
| 131 | uint8_t compat_version_major; |
---|
| 132 | /* 0x207: compatibility version number minor */ |
---|
| 133 | uint8_t compat_version_minor; |
---|
| 134 | |
---|
| 135 | /* 0x208: install_partition variable */ |
---|
| 136 | struct { |
---|
| 137 | /* 0x208: sub-partition in sub-partition part2 */ |
---|
| 138 | uint8_t part3; |
---|
| 139 | /* 0x209: sub-partition in top-level partition */ |
---|
| 140 | uint8_t part2; |
---|
| 141 | /* 0x20a: top-level partiton number */ |
---|
| 142 | uint8_t part1; |
---|
| 143 | /* 0x20b: BIOS drive number (must be 0) */ |
---|
| 144 | uint8_t drive; |
---|
| 145 | } __attribute__ ((packed)) install_partition; |
---|
| 146 | |
---|
| 147 | /* 0x20c: deprecated (historical reason only) */ |
---|
| 148 | uint32_t saved_entryno; |
---|
| 149 | /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */ |
---|
| 150 | uint8_t stage2_id; |
---|
| 151 | /* 0x211: force LBA */ |
---|
| 152 | uint8_t force_lba; |
---|
| 153 | /* 0x212: version string (will probably be 0.97) */ |
---|
| 154 | char version_string[5]; |
---|
| 155 | /* 0x217: config filename */ |
---|
| 156 | char config_file[89]; |
---|
| 157 | /* 0x270: start of code (after jump from 0x200) */ |
---|
| 158 | char codestart[1]; |
---|
| 159 | } __attribute__ ((packed)) *stage2; |
---|
| 160 | |
---|
| 161 | if (!(opt.file && opt.grub)) |
---|
| 162 | return 0; |
---|
| 163 | |
---|
| 164 | if (data->size < sizeof *stage2) { |
---|
| 165 | error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy."); |
---|
| 166 | goto bail; |
---|
| 167 | } |
---|
| 168 | stage2 = data->data; |
---|
| 169 | |
---|
| 170 | /* |
---|
| 171 | * Check the compatibility version number to see if we loaded a real |
---|
| 172 | * stage2 file or a stage2 file that we support. |
---|
| 173 | */ |
---|
| 174 | if (stage2->compat_version_major != 3 |
---|
| 175 | || stage2->compat_version_minor != 2) { |
---|
| 176 | error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary."); |
---|
| 177 | goto bail; |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | /* |
---|
| 181 | * GRUB Legacy wants the partition number in the install_partition |
---|
| 182 | * variable, located at offset 0x208 of stage2. |
---|
| 183 | * When GRUB Legacy is loaded, it is located at memory address 0x8208. |
---|
| 184 | * |
---|
| 185 | * It looks very similar to the "boot information format" of the |
---|
| 186 | * Multiboot specification: |
---|
| 187 | * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format |
---|
| 188 | * |
---|
| 189 | * 0x208 = part3: sub-partition in sub-partition part2 |
---|
| 190 | * 0x209 = part2: sub-partition in top-level partition |
---|
| 191 | * 0x20a = part1: top-level partition number |
---|
| 192 | * 0x20b = drive: BIOS drive number (must be 0) |
---|
| 193 | * |
---|
| 194 | * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at |
---|
| 195 | * another location. |
---|
| 196 | * |
---|
| 197 | * Partition numbers always start from zero. |
---|
| 198 | * Unused partition bytes must be set to 0xFF. |
---|
| 199 | * |
---|
| 200 | * We only care about top-level partition, so we only need to change |
---|
| 201 | * "part1" to the appropriate value: |
---|
| 202 | * -1: whole drive (default) (-1 = 0xFF) |
---|
| 203 | * 0-3: primary partitions |
---|
| 204 | * 4-*: logical partitions |
---|
| 205 | */ |
---|
| 206 | stage2->install_partition.part1 = iter->index - 1; |
---|
| 207 | |
---|
| 208 | /* |
---|
| 209 | * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the |
---|
| 210 | * config filename. The filename passed via grubcfg= will overwrite |
---|
| 211 | * the default config filename "/boot/grub/menu.lst". |
---|
| 212 | */ |
---|
| 213 | if (opt.grubcfg) { |
---|
| 214 | if (strlen(opt.grubcfg) >= sizeof stage2->config_file) { |
---|
| 215 | error("The config filename length can't exceed 88 characters."); |
---|
| 216 | goto bail; |
---|
| 217 | } |
---|
| 218 | |
---|
| 219 | strcpy((char *)stage2->config_file, opt.grubcfg); |
---|
| 220 | } |
---|
| 221 | |
---|
| 222 | return 0; |
---|
| 223 | bail: |
---|
| 224 | return -1; |
---|
| 225 | } |
---|
| 226 | #if 0 |
---|
| 227 | /* |
---|
| 228 | * Dell's DRMK chainloading. |
---|
| 229 | */ |
---|
| 230 | int manglef_drmk(struct data_area *data) |
---|
| 231 | { |
---|
| 232 | /* |
---|
| 233 | * DRMK entry is different than MS-DOS/PC-DOS |
---|
| 234 | * A new size, aligned to 16 bytes to ease use of ds:[bp+28]. |
---|
| 235 | * We only really need 4 new, usable bytes at the end. |
---|
| 236 | */ |
---|
| 237 | |
---|
| 238 | if (!(opt.file && opt.drmk)) |
---|
| 239 | return 0; |
---|
| 240 | |
---|
| 241 | uint32_t tsize = (data->size + 19) & 0xfffffff0; |
---|
| 242 | const union syslinux_derivative_info *sdi; |
---|
| 243 | uint64_t fs_lba; |
---|
| 244 | |
---|
| 245 | sdi = syslinux_derivative_info(); |
---|
| 246 | /* We should lookup the Syslinux partition offset and use it */ |
---|
| 247 | fs_lba = *sdi->disk.partoffset; |
---|
| 248 | |
---|
| 249 | /* |
---|
| 250 | * fs_lba should be verified against the disk as some DRMK |
---|
| 251 | * variants will check and fail if it does not match |
---|
| 252 | */ |
---|
| 253 | dprintf(" fs_lba offset is %d\n", fs_lba); |
---|
| 254 | /* DRMK only uses a DWORD */ |
---|
| 255 | if (fs_lba > 0xffffffff) { |
---|
| 256 | error("LBA very large; Only using lower 32 bits; DRMK will probably fail."); |
---|
| 257 | } |
---|
| 258 | opt.regs.ss = opt.regs.fs = opt.regs.gs = 0; /* Used before initialized */ |
---|
| 259 | if (!realloc(data->data, tsize)) { |
---|
| 260 | error("Failed to realloc for DRMK."); |
---|
| 261 | goto bail; |
---|
| 262 | } |
---|
| 263 | data->size = tsize; |
---|
| 264 | /* ds:bp is assumed by DRMK to be the boot sector */ |
---|
| 265 | /* offset 28 is the FAT HiddenSectors value */ |
---|
| 266 | opt.regs.ds = (tsize >> 4) + (opt.fseg - 2); |
---|
| 267 | /* "Patch" into tail of the new space */ |
---|
| 268 | *(uint32_t *)((char*)data->data + tsize - 4) = fs_lba; |
---|
| 269 | |
---|
| 270 | return 0; |
---|
| 271 | bail: |
---|
| 272 | return -1; |
---|
| 273 | } |
---|
| 274 | #endif |
---|
| 275 | /* Adjust BPB common function */ |
---|
| 276 | static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag) |
---|
| 277 | { |
---|
| 278 | int type = bpb_detect(data->data, tag); |
---|
| 279 | int off = drvoff_detect(type); |
---|
| 280 | |
---|
| 281 | /* BPB: hidden sectors 64bit - exFAT only for now */ |
---|
| 282 | if (type == bpbEXF) |
---|
| 283 | *(uint64_t *) ((char *)data->data + 0x40) = iter->abs_lba; |
---|
| 284 | /* BPB: hidden sectors 32bit*/ |
---|
| 285 | else if (bpbV34 <= type && type <= bpbV70) { |
---|
| 286 | if (iter->abs_lba < ~0u) |
---|
| 287 | *(uint32_t *) ((char *)data->data + 0x1c) = iter->abs_lba; |
---|
| 288 | else |
---|
| 289 | /* won't really help much, but ... */ |
---|
| 290 | *(uint32_t *) ((char *)data->data + 0x1c) = ~0u; |
---|
| 291 | /* BPB: hidden sectors 16bit*/ |
---|
| 292 | } else if (bpbV30 <= type && type <= bpbV32) { |
---|
| 293 | if (iter->abs_lba < 0xFFFF) |
---|
| 294 | *(uint16_t *) ((char *)data->data + 0x1c) = iter->abs_lba; |
---|
| 295 | else |
---|
| 296 | /* won't really help much, but ... */ |
---|
| 297 | *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u; |
---|
| 298 | } |
---|
| 299 | |
---|
| 300 | /* BPB: legacy geometry */ |
---|
| 301 | if (bpbV30 <= type && type <= bpbV70) { |
---|
| 302 | if (iter->di.cbios) |
---|
| 303 | *(uint32_t *)((char *)data->data + 0x18) = (iter->di.head << 16) | iter->di.spt; |
---|
| 304 | else { |
---|
| 305 | if (iter->di.disk & 0x80) |
---|
| 306 | *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F; |
---|
| 307 | else |
---|
| 308 | *(uint32_t *)((char *)data->data + 0x18) = 0x00020012; |
---|
| 309 | } |
---|
| 310 | } |
---|
| 311 | /* BPB: drive */ |
---|
| 312 | if (off >= 0) { |
---|
| 313 | *(uint8_t *)((char *)data->data + off) = (opt.swap ? iter->di.disk & 0x80 : iter->di.disk); |
---|
| 314 | } |
---|
| 315 | |
---|
| 316 | return 0; |
---|
| 317 | } |
---|
| 318 | |
---|
| 319 | /* |
---|
| 320 | * Adjust BPB of a BPB-compatible file |
---|
| 321 | */ |
---|
| 322 | int manglef_bpb(const struct part_iter *iter, struct data_area *data) |
---|
| 323 | { |
---|
| 324 | if (!(opt.file && opt.filebpb)) |
---|
| 325 | return 0; |
---|
| 326 | |
---|
| 327 | return mangle_bpb(iter, data, "file"); |
---|
| 328 | } |
---|
| 329 | |
---|
| 330 | /* |
---|
| 331 | * Adjust BPB of a sector |
---|
| 332 | */ |
---|
| 333 | int mangles_bpb(const struct part_iter *iter, struct data_area *data) |
---|
| 334 | { |
---|
| 335 | if (!(opt.sect && opt.setbpb)) |
---|
| 336 | return 0; |
---|
| 337 | |
---|
| 338 | return mangle_bpb(iter, data, "sect"); |
---|
| 339 | } |
---|
| 340 | |
---|
| 341 | /* |
---|
| 342 | * This function performs full BPB patching, analogously to syslinux's |
---|
| 343 | * native BSS. |
---|
| 344 | */ |
---|
| 345 | int manglesf_bss(struct data_area *sec, struct data_area *fil) |
---|
| 346 | { |
---|
| 347 | int type1, type2; |
---|
| 348 | size_t cnt = 0; |
---|
| 349 | |
---|
| 350 | if (!(opt.sect && opt.file && opt.bss)) |
---|
| 351 | return 0; |
---|
| 352 | |
---|
| 353 | type1 = bpb_detect(fil->data, "bss/file"); |
---|
| 354 | type2 = bpb_detect(sec->data, "bss/sect"); |
---|
| 355 | |
---|
| 356 | if (!type1 || !type2) { |
---|
| 357 | error("Couldn't determine the BPB type for option 'bss'."); |
---|
| 358 | goto bail; |
---|
| 359 | } |
---|
| 360 | if (type1 != type2) { |
---|
| 361 | error("Option 'bss' can't be used,\n" |
---|
| 362 | "when a sector and a file have incompatible BPBs."); |
---|
| 363 | goto bail; |
---|
| 364 | } |
---|
| 365 | |
---|
| 366 | /* Copy common 2.0 data */ |
---|
| 367 | memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D); |
---|
| 368 | |
---|
| 369 | /* Copy 3.0+ data */ |
---|
| 370 | if (type1 <= bpbV30) { |
---|
| 371 | cnt = 0x06; |
---|
| 372 | } else if (type1 <= bpbV32) { |
---|
| 373 | cnt = 0x08; |
---|
| 374 | } else if (type1 <= bpbV34) { |
---|
| 375 | cnt = 0x0C; |
---|
| 376 | } else if (type1 <= bpbV40) { |
---|
| 377 | cnt = 0x2E; |
---|
| 378 | } else if (type1 <= bpbVNT) { |
---|
| 379 | cnt = 0x3C; |
---|
| 380 | } else if (type1 <= bpbV70) { |
---|
| 381 | cnt = 0x42; |
---|
| 382 | } else if (type1 <= bpbEXF) { |
---|
| 383 | cnt = 0x60; |
---|
| 384 | } |
---|
| 385 | memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt); |
---|
| 386 | |
---|
| 387 | return 0; |
---|
| 388 | bail: |
---|
| 389 | return -1; |
---|
| 390 | } |
---|
| 391 | |
---|
| 392 | /* |
---|
| 393 | * Save sector. |
---|
| 394 | */ |
---|
| 395 | int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org) |
---|
| 396 | { |
---|
| 397 | if (!(opt.sect && opt.save)) |
---|
| 398 | return 0; |
---|
| 399 | |
---|
| 400 | if (memcmp(org, data->data, data->size)) { |
---|
| 401 | if (disk_write_sectors(&iter->di, iter->abs_lba, data->data, 1)) { |
---|
| 402 | error("Cannot write the updated sector."); |
---|
| 403 | goto bail; |
---|
| 404 | } |
---|
| 405 | /* function can be called again */ |
---|
| 406 | memcpy(org, data->data, data->size); |
---|
| 407 | } |
---|
| 408 | |
---|
| 409 | return 0; |
---|
| 410 | bail: |
---|
| 411 | return -1; |
---|
| 412 | } |
---|
| 413 | |
---|
| 414 | /* |
---|
| 415 | * To boot the Recovery Console of Windows NT/2K/XP we need to write |
---|
| 416 | * the string "cmdcons\0" to memory location 0000:7C03. |
---|
| 417 | * Memory location 0000:7C00 contains the bootsector of the partition. |
---|
| 418 | */ |
---|
| 419 | int mangles_cmldr(struct data_area *data) |
---|
| 420 | { |
---|
| 421 | if (!(opt.sect && opt.cmldr)) |
---|
| 422 | return 0; |
---|
| 423 | |
---|
| 424 | memcpy((char *)data->data + 3, cmldr_signature, sizeof cmldr_signature); |
---|
| 425 | return 0; |
---|
| 426 | } |
---|
| 427 | |
---|
| 428 | /* Set common registers */ |
---|
| 429 | int mangler_init(const struct part_iter *iter) |
---|
| 430 | { |
---|
| 431 | /* Set initial registry values */ |
---|
| 432 | if (opt.file) { |
---|
| 433 | opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.fseg; |
---|
| 434 | opt.regs.ip = opt.fip; |
---|
| 435 | } else { |
---|
| 436 | opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.sseg; |
---|
| 437 | opt.regs.ip = opt.sip; |
---|
| 438 | } |
---|
| 439 | |
---|
| 440 | if (opt.regs.ip == 0x7C00 && !opt.regs.cs) |
---|
| 441 | opt.regs.esp.l = 0x7C00; |
---|
| 442 | |
---|
| 443 | /* DOS kernels want the drive number in BL instead of DL. Indulge them. */ |
---|
| 444 | opt.regs.ebx.b[0] = opt.regs.edx.b[0] = iter->di.disk; |
---|
| 445 | |
---|
| 446 | return 0; |
---|
| 447 | } |
---|
| 448 | |
---|
| 449 | /* ds:si & ds:bp */ |
---|
| 450 | int mangler_handover(const struct part_iter *iter, const struct data_area *data) |
---|
| 451 | { |
---|
| 452 | if (opt.file && opt.maps && !opt.hptr) { |
---|
| 453 | opt.regs.esi.l = opt.regs.ebp.l = opt.soff; |
---|
| 454 | opt.regs.ds = opt.sseg; |
---|
| 455 | opt.regs.eax.l = 0; |
---|
| 456 | } else if (opt.hand) { |
---|
| 457 | /* base is really 0x7be */ |
---|
| 458 | opt.regs.esi.l = opt.regs.ebp.l = data->base; |
---|
| 459 | opt.regs.ds = 0; |
---|
| 460 | if (iter->index && iter->type == typegpt) /* must be iterated and GPT */ |
---|
| 461 | opt.regs.eax.l = 0x54504721; /* '!GPT' */ |
---|
| 462 | else |
---|
| 463 | opt.regs.eax.l = 0; |
---|
| 464 | } |
---|
| 465 | |
---|
| 466 | return 0; |
---|
| 467 | } |
---|
| 468 | |
---|
| 469 | /* |
---|
| 470 | * GRLDR of GRUB4DOS wants the partition number in DH: |
---|
| 471 | * -1: whole drive (default) |
---|
| 472 | * 0-3: primary partitions |
---|
| 473 | * 4-*: logical partitions |
---|
| 474 | */ |
---|
| 475 | int mangler_grldr(const struct part_iter *iter) |
---|
| 476 | { |
---|
| 477 | if (opt.grldr) |
---|
| 478 | opt.regs.edx.b[1] = iter->index - 1; |
---|
| 479 | |
---|
| 480 | return 0; |
---|
| 481 | } |
---|
| 482 | |
---|
| 483 | /* |
---|
| 484 | * try to copy values from temporary iterator, if positions match |
---|
| 485 | */ |
---|
| 486 | static void mbrcpy(struct part_iter *diter, struct part_iter *siter) |
---|
| 487 | { |
---|
| 488 | if (diter->dos.cebr_lba == siter->dos.cebr_lba && |
---|
| 489 | diter->di.disk == siter->di.disk) { |
---|
| 490 | memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr)); |
---|
| 491 | } |
---|
| 492 | } |
---|
| 493 | |
---|
| 494 | static int fliphide(struct part_iter *iter, struct part_iter *miter) |
---|
| 495 | { |
---|
| 496 | struct disk_dos_part_entry *dp; |
---|
| 497 | static const uint16_t mask = |
---|
| 498 | (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | |
---|
| 499 | (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e); |
---|
| 500 | uint8_t t; |
---|
| 501 | |
---|
| 502 | dp = (struct disk_dos_part_entry *)iter->record; |
---|
| 503 | t = dp->ostype; |
---|
| 504 | |
---|
| 505 | if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) { |
---|
| 506 | /* It's a hideable partition type */ |
---|
| 507 | if (miter->index == iter->index || opt.hide & HIDE_REV) |
---|
| 508 | t &= ~0x10u; /* unhide */ |
---|
| 509 | else |
---|
| 510 | t |= 0x10u; /* hide */ |
---|
| 511 | } |
---|
| 512 | if (dp->ostype != t) { |
---|
| 513 | dp->ostype = t; |
---|
| 514 | return -1; |
---|
| 515 | } |
---|
| 516 | return 0; |
---|
| 517 | } |
---|
| 518 | |
---|
| 519 | /* |
---|
| 520 | * miter - iterator we match against |
---|
| 521 | * hide bits meaning: |
---|
| 522 | * ..| - enable (1) / disable (0) |
---|
| 523 | * .|. - all (1) / pri (0) |
---|
| 524 | * |.. - unhide (1) / hide (0) |
---|
| 525 | */ |
---|
| 526 | int manglepe_hide(struct part_iter *miter) |
---|
| 527 | { |
---|
| 528 | int wb = 0, werr = 0; |
---|
| 529 | struct part_iter *iter = NULL; |
---|
| 530 | int ridx; |
---|
| 531 | |
---|
| 532 | if (!(opt.hide & HIDE_ON)) |
---|
| 533 | return 0; |
---|
| 534 | |
---|
| 535 | if (miter->type != typedos) { |
---|
| 536 | error("Option '[un]hide[all]' works only for legacy (DOS) partition scheme."); |
---|
| 537 | return -1; |
---|
| 538 | } |
---|
| 539 | |
---|
| 540 | if (miter->index > 4 && !(opt.hide & HIDE_EXT)) |
---|
| 541 | warn("Specified partition is logical, so it can't be unhidden without 'unhideall'."); |
---|
| 542 | |
---|
| 543 | if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags))) |
---|
| 544 | return -1; |
---|
| 545 | |
---|
| 546 | while (!pi_next(iter) && !werr) { |
---|
| 547 | ridx = iter->index0; |
---|
| 548 | if (!(opt.hide & HIDE_EXT) && ridx > 3) |
---|
| 549 | break; /* skip when we're constrained to pri only */ |
---|
| 550 | |
---|
| 551 | if (iter->index != -1) |
---|
| 552 | wb |= fliphide(iter, miter); |
---|
| 553 | |
---|
| 554 | /* |
---|
| 555 | * we have to update mbr and each extended partition, but only if |
---|
| 556 | * changes (wb) were detected and there was no prior write error (werr) |
---|
| 557 | */ |
---|
| 558 | if (ridx >= 3 && wb && !werr) { |
---|
| 559 | mbrcpy(miter, iter); |
---|
| 560 | werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1); |
---|
| 561 | wb = 0; |
---|
| 562 | } |
---|
| 563 | } |
---|
| 564 | |
---|
| 565 | if (iter->status < 0) |
---|
| 566 | goto bail; |
---|
| 567 | |
---|
| 568 | /* last update */ |
---|
| 569 | if (wb && !werr) { |
---|
| 570 | mbrcpy(miter, iter); |
---|
| 571 | werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1); |
---|
| 572 | } |
---|
| 573 | if (werr) |
---|
| 574 | warn("Failed to write E/MBR during '[un]hide[all]'."); |
---|
| 575 | |
---|
| 576 | bail: |
---|
| 577 | pi_del(&iter); |
---|
| 578 | return 0; |
---|
| 579 | } |
---|
| 580 | |
---|
| 581 | static int updchs(struct part_iter *iter, int ext) |
---|
| 582 | { |
---|
| 583 | struct disk_dos_part_entry *dp; |
---|
| 584 | uint32_t ochs1, ochs2, lba; |
---|
| 585 | |
---|
| 586 | dp = (struct disk_dos_part_entry *)iter->record; |
---|
| 587 | if (!ext) { |
---|
| 588 | /* primary or logical */ |
---|
| 589 | lba = (uint32_t)iter->abs_lba; |
---|
| 590 | } else { |
---|
| 591 | /* extended */ |
---|
| 592 | dp += 1; |
---|
| 593 | lba = iter->dos.nebr_lba; |
---|
| 594 | } |
---|
| 595 | ochs1 = *(uint32_t *)dp->start; |
---|
| 596 | ochs2 = *(uint32_t *)dp->end; |
---|
| 597 | |
---|
| 598 | /* |
---|
| 599 | * We have to be a bit more careful here in case of 0 start and/or length; |
---|
| 600 | * start = 0 would be converted to the beginning of the disk (C/H/S = |
---|
| 601 | * 0/0/1) or the [B]EBR, length = 0 would actually set the end CHS to be |
---|
| 602 | * lower than the start CHS. |
---|
| 603 | * |
---|
| 604 | * Both are harmless in case of a hole (and in non-hole case will make |
---|
| 605 | * partiter complain about corrupt layout if PIF_STRICT is set), but it |
---|
| 606 | * makes everything look silly and not really correct. |
---|
| 607 | * |
---|
| 608 | * Thus the approach as seen below. |
---|
| 609 | */ |
---|
| 610 | |
---|
| 611 | if (dp->start_lba || iter->index != -1) { |
---|
| 612 | lba2chs(&dp->start, &iter->di, lba, L2C_CADD); |
---|
| 613 | } else { |
---|
| 614 | memset(&dp->start, 0, sizeof dp->start); |
---|
| 615 | } |
---|
| 616 | |
---|
| 617 | if ((dp->start_lba || iter->index != -1) && dp->length) { |
---|
| 618 | lba2chs(&dp->end, &iter->di, lba + dp->length - 1, L2C_CADD); |
---|
| 619 | } else { |
---|
| 620 | memset(&dp->end, 0, sizeof dp->end); |
---|
| 621 | } |
---|
| 622 | |
---|
| 623 | return |
---|
| 624 | *(uint32_t *)dp->start != ochs1 || |
---|
| 625 | *(uint32_t *)dp->end != ochs2; |
---|
| 626 | } |
---|
| 627 | |
---|
| 628 | /* |
---|
| 629 | * miter - iterator we match against |
---|
| 630 | */ |
---|
| 631 | int manglepe_fixchs(struct part_iter *miter) |
---|
| 632 | { |
---|
| 633 | int wb = 0, werr = 0; |
---|
| 634 | struct part_iter *iter = NULL; |
---|
| 635 | int ridx; |
---|
| 636 | |
---|
| 637 | if (!opt.fixchs) |
---|
| 638 | return 0; |
---|
| 639 | |
---|
| 640 | if (miter->type != typedos) { |
---|
| 641 | error("Option 'fixchs' works only for legacy (DOS) partition scheme."); |
---|
| 642 | return -1; |
---|
| 643 | } |
---|
| 644 | |
---|
| 645 | if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags))) |
---|
| 646 | return -1; |
---|
| 647 | |
---|
| 648 | while (!pi_next(iter) && !werr) { |
---|
| 649 | ridx = iter->index0; |
---|
| 650 | |
---|
| 651 | wb |= updchs(iter, 0); |
---|
| 652 | if (ridx > 3) |
---|
| 653 | wb |= updchs(iter, 1); |
---|
| 654 | |
---|
| 655 | /* |
---|
| 656 | * we have to update mbr and each extended partition, but only if |
---|
| 657 | * changes (wb) were detected and there was no prior write error (werr) |
---|
| 658 | */ |
---|
| 659 | if (ridx >= 3 && wb && !werr) { |
---|
| 660 | mbrcpy(miter, iter); |
---|
| 661 | werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1); |
---|
| 662 | wb = 0; |
---|
| 663 | } |
---|
| 664 | } |
---|
| 665 | |
---|
| 666 | if (iter->status < 0) |
---|
| 667 | goto bail; |
---|
| 668 | |
---|
| 669 | /* last update */ |
---|
| 670 | if (wb && !werr) { |
---|
| 671 | mbrcpy(miter, iter); |
---|
| 672 | werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1); |
---|
| 673 | } |
---|
| 674 | if (werr) |
---|
| 675 | warn("Failed to write E/MBR during 'fixchs'."); |
---|
| 676 | |
---|
| 677 | bail: |
---|
| 678 | pi_del(&iter); |
---|
| 679 | return 0; |
---|
| 680 | } |
---|
| 681 | |
---|
| 682 | /* vim: set ts=8 sts=4 sw=4 noet: */ |
---|