Skip to content

Instantly share code, notes, and snippets.

@jclulow
Created March 15, 2019 04:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jclulow/2b1cc7a41437cb0690b7e605560cdee1 to your computer and use it in GitHub Desktop.
Save jclulow/2b1cc7a41437cb0690b7e605560cdee1 to your computer and use it in GitHub Desktop.
First, grab the MBR:
dd \
if=/dev/zvol/rdsk/zones/ce799de5-247d-4e6d-878c-de074ef35c15-disk1 \
of=/tmp/fromdisk \
bs=512 count=32 skip=0
Inspect with MDB:
mdb -V ia16 /tmp/fromdisk
Check that it's a valid MBR by looking at the signature:
> 1fe/BB
0x1fe: 55 aa
The signature is intact. Look at partition 1:
> 1be/BnBBBnBnBBBnDnD
0x1be: 0
ff ff ff
ee
ff ff ff
1
461373439
Type is 0xEE (the GPT protective MBR), and the partition ranges from LBA 1
to LBA 461373439, which seems right for this disk.
The BIOS will load the MBR into memory at 0x7C00 and then jump there. Check
the disassembly:
> 0::dis -n 1
0: jmp +0x3c <0x3e>
2: nop
Step forward to the jump offset:
> 3e::dis -n 0t16
0x3e: cld
0x3f: xorw %ax,%ax
0x41: movw %ax,%es
0x43: movw %ax,%ds
0x45: movw %ax,%ss
0x47: movw $0xe00,%sp
0x4a: movw $0x7c58,%si
0x4d: movw $0x658,%di
0x50: movw $0x1a8,%cx
0x53: repz movsb (%si),(%di)
Note that this is code from "sys/boot/i386/pmbr/pmbr.s". There are some
fixed offsets into this sector, which we can get from "installboot.h":
#define STAGE1_STAGE2_SIZE (0xfc) /* 16bits */
#define STAGE1_STAGE2_LBA (0xfe) /* 64bits */
Looking at those values in the MBR:
> fc/d
0xfc: 1
> fe/E
0xfe: 524544
So the MBR code will load 1 sector from LBA 524544 into memory at 0x7C00
and then jump there (mimicking the BIOS). Let's grab that sector now:
dd \
if=/dev/zvol/rdsk/zones/ce799de5-247d-4e6d-878c-de074ef35c15-disk1 \
of=/tmp/fromdisk \
bs=512 count=1 skip=524544
Using ::dis we can see that this block looks substantially the same as the MBR,
which comments in "installboot.c" confirm. Looking, then, at the offsets in
_this_ block:
> fc/d
0xfc: 332
> fe/E
0xfe: 525568
i.e., we'll load 332 sectors from LBA 525568. Let's get that from the disk:
dd \
if=/dev/zvol/rdsk/zones/ce799de5-247d-4e6d-878c-de074ef35c15-disk1 \
of=/tmp/fromdisk \
bs=512 count=332 skip=525568
On a _working_ system we would expect to see instructions from
"sys/boot/i386/gptzfsboot/gptldr.S"; e.g., starting with "xor %cx, %cx".
On _this_ system, we see what does not really appear to be useful
program text:
> 0::dis
0: orb $0xb1,%al
2: movw $0x0,%dx
5: addb %al,(%bx,%si)
7: addb %cl,0x13(%bx,%si)
0xb: addb %al,(%bx,%si)
...
Dumping the actual bytes:
> 0::dump -g 1 -l 32
\/ 1 2 3 4 5 6 7 8 9 a b c d e f v123456789abcdef
00: 0c b1 ba 00 00 00 00 00 88 13 00 00 00 00 00 00 ................
10: be 81 0a 00 00 00 00 00 d9 89 a8 50 45 6d 25 c2 ...........PEm%.
20: 06 f2 87 5c 00 00 00 00 01 00 00 00 00 00 00 00 ...\............
30: bc e3 40 0b 00 00 00 00 01 00 00 00 00 00 00 00 ..@.............
Indeed, if we look at offset 0xB0, adjusting for endianness we see MMP_MAGIC:
> 0b0::dump -g 4 -e
b0: a11cea11 00000000 00000000 00000000
This is LBA 525568 + byte offset 176 (0xB0).
To fix this, we'll need a pristine copy of the "gptzfsboot" loader image. I
grabbed the one from boot environment we're trying to rescue in the pool:
# wc -c /var/tmp/gptzfsboot
169984 /var/tmp/gptzfsboot
The loader image is patched by installboot(1M) to include the LBA of the
beginning of the partition, misusing a field of the fake multiboot header that
appears in the image. The 64-bit LBA value is written starting at the address
of the "bss_end_addr" member in the multiboot header. Make a copy of the
"gptzfsboot" file and locate the multiboot header within:
# cp /var/tmp/gptzfsboot /tmp/edited
# mdb -V ia16 /tmp/edited
> 0,1000::dump -e -g 4 -u ! grep -A1 -i 1BADB002
7a0: 1badb002 00010000 e4514ffe 00000000
7b0: 00000000 000290d4 00000000 00000000
Note that the multiboot magic (0x1BADB002) appears at the top of the
multiboot header, and the "bss_end_addr" is the seventh uint32_t-sized
member. By default the value is zero:
> 7b8/E
0x7b8: 0
Replace it with the LBA of the start of the partition as a 64-bit value:
> $W
> 7b8/Z 0t524544
0x7b8: 0 = 0x80100
Now, write the patched boot block to the disk in the appropriate place:
dd \
if=/tmp/edited \
of=/dev/zvol/rdsk/zones/ce799de5-247d-4e6d-878c-de074ef35c15-disk1 \
bs=512 count=332 oseek=525568
The machine will now boot!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment