From 058dd028ac7d8ae217d1b3b00213751d63034195 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 5 Sep 2008 14:49:42 -0700 Subject: [PATCH] ISOLINUX: support for hybrid mode (CD-ROM/USB key) Still a work in progress. Signed-off-by: H. Peter Anvin --- .gitignore | 1 + core/isolinux.asm | 255 +++++++++++++++++++++++++++++++++++++++++++++++++---- mbr/Makefile | 12 +-- mbr/isohdpfx.S | 212 ++++++++++++++++++++++++++++++++++++++++++++ utils/Makefile | 6 +- utils/isohybrid.in | 165 ++++++++++++++++++++++++++++++++++ 6 files changed, 628 insertions(+), 23 deletions(-) create mode 100644 mbr/isohdpfx.S create mode 100644 utils/isohybrid.in diff --git a/.gitignore b/.gitignore index d4724d8..7657c3d 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ /linux/syslinux /linux/syslinux-nomtools /utils/gethostip +/utils/isohybrid /utils/mkdiskimage /version.h /version.mk diff --git a/core/isolinux.asm b/core/isolinux.asm index f93b24e..060a3cf 100644 --- a/core/isolinux.asm +++ b/core/isolinux.asm @@ -118,10 +118,13 @@ ImageDwords resd 1 ; isolinux.bin size, dwords InitStack resd 1 ; Initial stack pointer (SS:SP) DiskSys resw 1 ; Last INT 13h call ImageSectors resw 1 ; isolinux.bin size, sectors +GetlinsecPtr resw 1 ; The sector-read pointer DiskError resb 1 ; Error code for disk I/O -DriveNumber resb 1 ; CD-ROM BIOS drive number +DriveNumber resb 1 ; CD-ROM BIOS drive number ISOFlags resb 1 ; Flags for ISO directory search RetryCount resb 1 ; Used for disk access retries +bsSecPerTrack resw 1 ; Used in hybrid mode +bsHeads resw 1 ; Used in hybrid mode _spec_start equ $ @@ -225,12 +228,46 @@ bi_file: dd 0 ; LBA of boot file bi_length: dd 0xdeadbeef ; Length of boot file bi_csum: dd 0xdeadbeef ; Checksum of boot file bi_reserved: times 10 dd 0xdeadbeef ; Reserved +bi_end: + + ; Custom entry point for the hybrid-mode disk. + ; The following values will have been pushed onto the + ; entry stack: + ; - CBIOS Heads + ; - CBIOS Sectors + ; - CBIOS flag + ; - DX (including drive number) + ; - DI + ; - ES + ; (top of stack) +%ifndef DEBUG_MESSAGES +_hybrid_signature: + dd 0x7078c0fb ; An arbitrary number... + +_start_hybrid: + pop ax + mov si,getlinsec_ebios + and ax,ax + jnz .ebios + mov si,getlinsec_cbios +.ebios: + pop word [cs:bsSecPerTrack] + pop word [cs:bsHeads] + + pop dx + pop di + pop es + jmp _start_common +%endif -_start1: mov [cs:InitStack],sp ; Save initial stack pointer +_start1: + mov si,getlinsec_cdrom +_start_common: + mov [cs:InitStack],sp ; Save initial stack pointer mov [cs:InitStack+2],ss xor ax,ax mov ss,ax - mov sp,StackBuf ; Set up stack + mov sp,StackBuf ; Set up stack push es ; Save initial ES:DI -> $PnP pointer push di mov ds,ax @@ -238,8 +275,10 @@ _start1: mov [cs:InitStack],sp ; Save initial stack pointer mov fs,ax mov gs,ax sti - cld + + mov [GetlinsecPtr],si + ; Show signs of life mov si,syslinux_banner call writestr_early @@ -253,7 +292,7 @@ _start1: mov [cs:InitStack],sp ; Save initial stack pointer ; 64-2048 ; initial_csum: xor edi,edi - mov si,_start1 + mov si,bi_end mov cx,(SECTOR_SIZE-64) >> 2 .loop: lodsd add edi,eax @@ -285,6 +324,10 @@ initial_csum: xor edi,edi ; Other nonzero fields inc word [dsp_sectors] + ; Are we just pretending to be a CD-ROM? + cmp word [GetlinsecPtr],getlinsec_cdrom + jne found_drive ; If so, no spec packet... + ; Now figure out what we're actually doing ; Note: use passed-in DL value rather than 7Fh because ; at least some BIOSes will get the wrong value otherwise @@ -333,7 +376,7 @@ found_drive: %endif ; No such luck. Get the Boot Record Volume, assuming single - ; session disk, and that we're the first entry in the chain + ; session disk, and that we're the first entry in the chain. mov eax,17 ; Assumed address of BRV mov bx,trackbuf call getonesec @@ -356,7 +399,7 @@ set_file: found_file: ; Set up boot file sizes mov eax,[bi_length] - sub eax,SECTOR_SIZE-3 + sub eax,SECTOR_SIZE-3 ; ... minus sector loaded shr eax,2 ; bytes->dwords mov [ImageDwords],eax ; boot file dwords add eax,(2047 >> 2) @@ -685,16 +728,191 @@ getonesec: ; ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. ; -; Note that we can't always do this as a single request, because at least -; Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick -; to 32 sectors (64K) per request. -; ; Input: ; EAX - Linear sector number ; ES:BX - Target buffer ; BP - Sector count ; -getlinsec: +getlinsec: jmp word [cs:GetlinsecPtr] + +%ifndef DEBUG_MESSAGES + +; +; First, the variants that we use when actually loading off a disk +; (hybrid mode.) These are adapted versions of the equivalent routines +; in ldlinux.asm. +; + +; +; getlinsec_ebios: +; +; getlinsec implementation for floppy/HDD EBIOS (EDD) +; +getlinsec_ebios: + shl eax,2 ; Convert to HDD sectors + shl bp,2 + +.loop: + push bp ; Sectors left +.retry2: + call maxtrans ; Enforce maximum transfer size + movzx edi,bp ; Sectors we are about to read + mov cx,retry_count +.retry: + + ; Form DAPA on stack + push edx + push eax + push es + push bx + push di + push word 16 + mov si,sp + pushad + mov dl,[DriveNumber] + push ds + push ss + pop ds ; DS <- SS + mov ah,42h ; Extended Read + int 13h + pop ds + popad + lea sp,[si+16] ; Remove DAPA + jc .error + pop bp + add eax,edi ; Advance sector pointer + sub bp,di ; Sectors left + shl di,9 ; 512-byte sectors + add bx,di ; Advance buffer pointer + and bp,bp + jnz .loop + + ret + +.error: + ; Some systems seem to get "stuck" in an error state when + ; using EBIOS. Doesn't happen when using CBIOS, which is + ; good, since some other systems get timeout failures + ; waiting for the floppy disk to spin up. + + pushad ; Try resetting the device + xor ax,ax + mov dl,[DriveNumber] + int 13h + popad + loop .retry ; CX-- and jump if not zero + + ;shr word [MaxTransfer],1 ; Reduce the transfer size + ;jnz .retry2 + + ; Total failure. Try falling back to CBIOS. + mov word [GetlinsecPtr], getlinsec_cbios + ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer + + pop bp + jmp getlinsec_cbios.loop + +; +; getlinsec_cbios: +; +; getlinsec implementation for legacy CBIOS +; +getlinsec_cbios: + shl eax,2 ; Convert to HDD sectors + shl bp,2 + +.loop: + push edx + push eax + push bp + push bx + + movzx esi,word [bsSecPerTrack] + movzx edi,word [bsHeads] + ; + ; Dividing by sectors to get (track,sector): we may have + ; up to 2^18 tracks, so we need to use 32-bit arithmetric. + ; + div esi + xor cx,cx + xchg cx,dx ; CX <- sector index (0-based) + ; EDX <- 0 + ; eax = track # + div edi ; Convert track to head/cyl + + ; We should test this, but it doesn't fit... + ; cmp eax,1023 + ; ja .error + + ; + ; Now we have AX = cyl, DX = head, CX = sector (0-based), + ; BP = sectors to transfer, SI = bsSecPerTrack, + ; ES:BX = data target + ; + + call maxtrans ; Enforce maximum transfer size + + ; Must not cross track boundaries, so BP <= SI-CX + sub si,cx + cmp bp,si + jna .bp_ok + mov bp,si +.bp_ok: + + shl ah,6 ; Because IBM was STOOPID + ; and thought 8 bits were enough + ; then thought 10 bits were enough... + inc cx ; Sector numbers are 1-based, sigh + or cl,ah + mov ch,al + mov dh,dl + mov dl,[DriveNumber] + xchg ax,bp ; Sector to transfer count + mov ah,02h ; Read sectors + mov bp,retry_count +.retry: + pushad + int 13h + popad + jc .error +.resume: + movzx ecx,al ; ECX <- sectors transferred + shl ax,9 ; Convert sectors in AL to bytes in AX + pop bx + add bx,ax + pop bp + pop eax + pop edx + add eax,ecx + sub bp,cx + jnz .loop + ret + +.error: + dec bp + jnz .retry + + xchg ax,bp ; Sectors transferred <- 0 + shr word [MaxTransfer],1 + jnz .resume + jmp disk_error + +; +; Truncate BP to MaxTransfer +; +maxtrans: + cmp bp,[MaxTransfer] + jna .ok + mov bp,[MaxTransfer] +.ok: ret + +%endif + +; +; This is the variant we use for real CD-ROMs: +; LBA, 2K sectors, some special error handling. +; +getlinsec_cdrom: mov si,dapa ; Load up the DAPA mov [si+4],bx mov bx,es @@ -774,6 +992,7 @@ xint13: mov byte [RetryCount],retry_count ; kaboom: write a message and bail out. Wait for a user keypress, ; then do a hard reboot. ; +disk_error: kaboom: RESET_STACK_AND_SEGS AX mov si,err_bootfailed @@ -833,11 +1052,13 @@ MaxTransfer dw 32 ; Max sectors per transfer rl_checkpt equ $ ; Must be <= 800h rl_checkpt_off equ ($-$$) -;%ifndef DEPEND -;%if rl_checkpt_off > 0x800 -;%error "Sector 0 overflow" -;%endif -;%endif +%ifndef DEPEND +%if rl_checkpt_off > 0x800 +; This only works for NASM 2.03+, but it's really nice then... +%assign SPILL rl_checkpt_off-0x800 +%error Sector 0 overflow by SPILL bytes +%endif +%endif ; ---------------------------------------------------------------------------- ; End of code and data that have to be in the first sector diff --git a/mbr/Makefile b/mbr/Makefile index 7952e60..a57b394 100644 --- a/mbr/Makefile +++ b/mbr/Makefile @@ -17,23 +17,25 @@ topdir = .. include $(topdir)/MCONFIG.embedded -all: mbr.bin gptmbr.bin +all: mbr.bin gptmbr.bin isohdpfx.bin .PRECIOUS: %.o %.o: %.S $(CC) $(SFLAGS) -Wa,-a=$*.lst -c -o $@ $< -mbr.elf: mbr.o mbr.ld +.PRECIOUS: %.elf +%.elf: %.o mbr.ld $(LD) $(LDFLAGS) -T mbr.ld -e _start -o $@ $< mbr.bin: mbr.elf checksize.pl $(OBJCOPY) -O binary $< $@ $(PERL) checksize.pl mbr.bin 440 -mbr_bin.c: mbr.bin +isohdpfx.bin: isohdpfx.elf checksize.pl + $(OBJCOPY) -O binary $< $@ + $(PERL) checksize.pl mbr.bin 432 -gptmbr.elf: gptmbr.o mbr.ld - $(LD) $(LDFLAGS) -T mbr.ld -e _start -o $@ $< +mbr_bin.c: mbr.bin gptmbr.bin: gptmbr.elf checksize.pl $(OBJCOPY) -O binary $< $@ diff --git a/mbr/isohdpfx.S b/mbr/isohdpfx.S new file mode 100644 index 0000000..f2bc7dc --- /dev/null +++ b/mbr/isohdpfx.S @@ -0,0 +1,212 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * Modified MBR code used on an ISO image in hybrid mode. + * + * This doesn't follow the El Torito spec at all -- it is just a stub + * loader of a hard-coded offset, but that's good enough to load + * ISOLINUX. + */ + + .code16 + .text + +HYBRID_MAGIC = 0x7078c0fb +isolinux_hybrid_signature = 0x7c00+64 +isolinux_start_hybrid = 0x7c00+64+4 + + .globl bootsec +/* Important: the top 6 words on the stack are passed to isolinux.bin */ +stack = 0x7c00 +driveno = (stack-6) +sectors = (stack-8) +heads = (stack-10) +secpercyl = (stack-14) + +BIOS_page = 0x462 + + /* gas/ld has issues with doing this as absolute addresses... */ + .section ".bootsec", "a", @nobits + .globl bootsec +bootsec: + .space 512 + + .text + .globl _start +_start: + + cli + xorw %ax, %ax + movw %ax, %ds + movw %ax, %ss + movw $stack, %sp + movw %sp, %si + pushw %es /* es:di -> $PnP header */ + pushw %di + pushw %dx /* dl -> drive number */ + movw %ax, %es + sti + cld + + /* Copy down to 0:0x600 */ + movw $_start, %di + movw $(512/2), %cx + rep; movsw + + ljmpw $0, $next + +next: + /* Check to see if we have EBIOS */ + pushw %dx /* drive number */ + movb $0x41, %ah /* %al == 0 already */ + movw $0x55aa, %bx + xorw %cx, %cx + xorb %dh, %dh + stc + int $0x13 + jc 1f + cmpw $0xaa55, %bx + jne 1f + shrw %cx /* Bit 0 = fixed disk subset */ + jnc 1f + + /* We have EBIOS; patch in the following code at + read_sector_cbios: movb $0x42, %ah ; jmp read_common */ + movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \ + (read_sector_cbios) + +1: + popw %dx + + /* Get (C)HS geometry */ + movb $0x08, %ah + int $0x13 + andw $0x3f, %cx /* Sector count */ + pushw %cx /* Save sectors on the stack */ + movzbw %dh, %ax /* dh = max head */ + incw %ax /* From 0-based max to count */ + pushw %ax /* Save heads on the stack */ + mulw %cx /* Heads*sectors -> sectors per cylinder */ + + /* Save sectors/cylinder on the stack */ + pushw %dx /* High word */ + pushw %ax /* Low word */ + + /* + * Load sectors. We do this one at a time mostly to avoid + * pitfalls and to share code with the stock MBR code. + */ + movw $0x7c00, %bx + movw $4, %cx /* Sector count */ + movl (lba_offset), %eax + +2: + call read_sector + jc disk_error + incl %eax + addw $512, %bx + loopw 2b + + /* + * Okay, that actually worked... update the stack pointer + * and jump into isolinux.bin... + */ + cmpl $HYBRID_MAGIC,(isolinux_hybrid_signature) + jne bad_signature + + cli + movw $heads, %sp + jmp isolinux_start_hybrid + +bad_signature: + call error + .ascii "isolinux.bin missing or corrupt.\r\n" + +/* + * read_sector: read a single sector pointed to by %eax to %es:%bx. + * CF is set on error. All registers saved. + */ +read_sector: + pushal + xorl %edx, %edx + pushl %edx /* MSW of LBA */ + pushl %eax /* LSW of LBA */ + pushw %es /* Buffer segment */ + pushw %bx /* Buffer offset */ + pushw $1 /* Sector count */ + pushw $16 /* Size of packet */ + movw %sp, %si + + /* This chunk is skipped if we have ebios */ + /* Do not clobber %eax before this chunk! */ + /* This also relies on %bx and %edx as set up above. */ +read_sector_cbios: + divl (secpercyl) + shlb $6, %ah + movb %ah, %cl + movb %al, %ch + xchgw %dx, %ax + divb (sectors) + movb %al, %dh + orb %ah, %cl + incw %cx /* Sectors are 1-based */ + movw $0x0201, %ax + +read_common: + movb (driveno), %dl + int $0x13 + addw $16, %sp /* Drop DAPA */ + popal + ret + +disk_error: + call error + .ascii "Operating system load error.\r\n" + +/* + * Print error messages. This is invoked with "call", with the + * error message at the return address. + */ +error: + popw %si +2: + lodsb + movb $0x0e, %ah + movb (BIOS_page), %bh + movb $0x07, %bl + int $0x10 /* May destroy %bp */ + cmpb $10, %al /* Newline? */ + jne 2b + + int $0x18 /* Boot failure */ +die: + hlt + jmp die + + /* Address of pointer to isolinux.bin */ +lba_offset = _start+432 diff --git a/utils/Makefile b/utils/Makefile index 64f8236..de8c999 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -20,7 +20,7 @@ include $(topdir)/MCONFIG CFLAGS = -W -Wall -Os -fomit-frame-pointer -D_FILE_OFFSET_BITS=64 LDFLAGS = -O2 -s -TARGETS = mkdiskimage gethostip +TARGETS = mkdiskimage isohybrid gethostip ASIS = keytab-lilo lss16toppm md5pass ppmtolss16 sha1pass syslinux2ansi all: $(TARGETS) @@ -32,6 +32,10 @@ mkdiskimage: mkdiskimage.in ../mbr/mbr.bin bin2hex.pl $(PERL) bin2hex.pl < ../mbr/mbr.bin | cat mkdiskimage.in - > $@ chmod a+x $@ +isohybrid: isohybrid.in ../mbr/isohdpfx.bin bin2hex.pl + $(PERL) bin2hex.pl < ../mbr/isohdpfx.bin | cat isohybrid.in - > $@ + chmod a+x $@ + gethostip: gethostip.o $(CC) $(LDFLAGS) -o $@ $^ diff --git a/utils/isohybrid.in b/utils/isohybrid.in new file mode 100644 index 0000000..a0487ca --- /dev/null +++ b/utils/isohybrid.in @@ -0,0 +1,165 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2002-2008 H. Peter Anvin - All Rights Reserved +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Post-process an ISO 9660 image generated with mkisofs/genisoimage +# to allow "hybrid booting" as a CD-ROM or as a hard disk. +# + +use bytes; +use integer; +use Fcntl; +use Errno; +use Cwd; +use IO::Handle; # For flush() + +# Use this fake geometry (zipdrive-style...) +$h = 64; $s = 32; + +sub get_random() { + # Get a 32-bit random number + my $rfd, $rnd; + my $rid; + + if (sysopen($rfd, '/dev/urandom', O_RDONLY) && + sysread($rfd, $rnd, 4) == 4) { + $rid = unpack("V", $rnd); + } + + close($rfd) if (defined($rfd)); + return $rid if (defined($rid)); + + # This sucks but is better than nothing... + return ($$+time()) & 0xffffffff; +} + + +($file) = @ARGV; +open(FILE, "+< $file\0") or die "$0: cannot open $file: $!\n"; +binmode FILE; + +# +# First, actually figure out where mkisofs hid isolinux.bin +# +seek(FILE, 17*2048, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $boot_record, 2048) == 2048 or die "$0: $file: read error\n"; +($br_sign, $br_cat_offset) = unpack("a71V", $boot_record); +if ($br_sign ne ("\0CD001\1EL TORITO SPECIFICATION" . ("\0" x 41))) { + die "$0: $file: no boot record found\n"; +} +seek(FILE, $br_cat_offset*2048, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $boot_cat, 2048) == 2048 or die "$0: $file: read error\n"; + +# We must have a Validation Entry followed by a Default Entry... +# no fanciness allowed for the Hybrid mode [XXX: might relax this later] +@ve = unpack("v16", $boot_cat); +$cs = 0; +for ($i = 0; $i < 16; $i++) { + $cs += $ve[$i]; +} +if ($ve[0] != 0x0001 || $ve[15] != 0xaa55 || $cs & 0xffff) { + die "$0: $file: invalid boot catalog\n"; +} +($de_boot, $de_media, $de_seg, $de_sys, $de_mbz1, $de_count, + $de_lba, $de_mbz2) = unpack("CCvCCvVv", substr($boot_cat, 32, 32)); +if ($de_boot != 0x88 || $de_media != 0 || + ($de_segment != 0 && $de_segment != 0x7c0) || $de_count != 4) { + die "$0: $file: unexpected boot catalog parameters\n"; +} + +# Get the total size of the image +(@imgstat = stat(FILE)) or die "$0: $file: $!\n"; +$imgsize = $imgstat[7]; +if (!$imgsize) { + die "$0: $file: cannot determine length of file\n"; +} +# Target image size: round up to a multiple of $h*$s*512 +$cylsize = $h*$s*512; +$frac = $imgsize % $cylsize; +$padding = ($frac > 0) ? $cylsize - $frac : 0; +$imgsize += $padding; +$c = $imgsize/$cylsize; +if ($c > 1024) { + print STDERR "Warning: more than 1024 cylinders ($c).\n"; + print STDERR "Not all BIOSes will be able to boot this device.\n"; + $cc = 1024; +} else { + $cc = $c; +} + +# Now $de_lba should contain the CD sector number for isolinux.bin +seek(FILE, 0, SEEK_SET) or die "$0: $file: $!\n"; + +# Print the MBR and partition table +$mbr = ''; +while ( $line = ) { + chomp $line; + foreach $byte ( split(/\s+/, $line) ) { + $mbr .= chr(hex($byte)); + } +} +if ( length($mbr) > 432 ) { + die "$0: Bad MBR code\n"; +} + +$mbr .= "\0" x (432 - length($mbr)); + +$mbr .= pack("VV", $de_lba*4, 0); # Offset 432: LBA of isolinux.bin +if (defined($id)) { + $id = to_int($id); +} else { + $id = get_random(); +} +$mbr .= pack("V", $id); # Offset 440: MBR ID +$mbr .= "\0\0"; # Offset 446: actual partition table + +# Print partition table +$psize = $c*$h*$s-$s; +$bhead = 0; +$bsect = 1; +$bcyl = 0; +$ehead = $h-1; +$esect = $s + ((($cc-1) & 0x300) >> 2); +$ecyl = ($cc-1) & 0xff; +$fstype = 0x83; # Linux (any better ideas?) +$pentry = 1; +if ( $c > 1024 ) { + $fstype = 0x0e; +} elsif ( $psize > 65536 ) { + $fstype = 0x06; +} else { + $fstype = 0x04; +} +for ( $i = 1 ; $i <= 4 ; $i++ ) { + if ( $i == $pentry ) { + $mbr .= pack("CCCCCCCCVV", 0x80, $bhead, $bsect, $bcyl, $fstype, + $ehead, $esect, $ecyl, $s, $psize); + } else { + $mbr .= "\0" x 16; + } +} +$mbr .= "\x55\xaa"; + +print FILE $mbr; + +# Pad the image to a fake cylinder boundary +seek(FILE, $imgstat[7], SEEK_SET) or die "$0: $file: $!\n"; +if ($padding) { + print FILE "\0" x $padding; +} + +# Done... +close(FILE); + +exit 0; +__END__ -- 2.7.4