Some working release.
authorlace <>
Wed, 22 Feb 2006 13:37:45 +0000 (13:37 +0000)
committerlace <>
Wed, 22 Feb 2006 13:37:45 +0000 (13:37 +0000)
.cvsignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
biosautoraid.S [new file with mode: 0644]
biosautoraid.pl [new file with mode: 0755]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..3a67e3e
--- /dev/null
@@ -0,0 +1 @@
+biosautoraid.bin
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d857aa6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+# $Id$
+
+
+all: biosautoraid.bin
+
+biosautoraid.bin: biosautoraid.S biosautoraid.pl
+       $(CC) -Wall -nostdlib -Wl,--oformat,binary $< -o $@
+       chmod -x $@
+       ./biosautoraid.pl --bin=$@
+
+.PHONY: clean
+clean:
+       $(RM) biosautoraid.bin
+
diff --git a/biosautoraid.S b/biosautoraid.S
new file mode 100644 (file)
index 0000000..f7f0ff5
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * $Id$
+ *
+ * Automatically retry all BIOS reads 0x80<->0x81 (and if (drive&0x80) drive^=1; in genral)
+ *
+ * Derived from:
+ */
+#if 0
+       http://cvs.sourceforge.net/viewcvs.py/*checkout*/surprise/surprise/misc/int13sniff.S
+#endif
+
+/* !!!  All defines below must be COMMENTED-OUT !
+ * !!!  Define to value 0 has no effect.
+ */
+
+/* Reduce size by omitting DOS-boot header signature to fit in 512 bytes.
+ * Cylinder 0 is requierd, sectors per track == 63 assumed!
+ * This constant is counted from 0 !!!
+ */
+#define BACKUP_SECTOR 63
+
+/* Reduce size by omitting DOS-boot header signature to fit in 512 bytes.
+ */
+#define STRIP_BOOT_HEADER 1
+
+/* Maintainers: Disable .org macroinstructions: ONLY FOR compilation tuning!
+ *  - image produced doen't have any functionality!
+#define DISABLE_ORG 1
+ */
+
+/* Internal defines */
+#define MEM_KBYTES    0x413    /* BIOS variable */
+
+#define MAX_BOOT_SIZE_100  0xb6        /* LILO: leave some space for NT's and DR DOS' dirty */
+#define MAX_BOOT_SIZE (MAX_BOOT_SIZE_100+0x100)
+
+
+/* Macros: */
+
+       .macro  outchar char
+       movw    $0x0E00 | \char,%ax
+       movw    $7,%bx
+       int     $0x10
+       .endm
+
+#ifdef DISABLE_ORG
+#define MYORG(offset)
+#else
+#define MYORG(offset) .org (offset) + START            /* "+ START" MUST be on the end of line - WHY?!? */
+#endif
+
+       .macro  pushALL
+       pushw   %ax
+       pushw   %bx
+       pushw   %cx
+       pushw   %dx
+       pushw   %si
+       pushw   %di
+       pushw   %bp
+       .endm
+
+       .macro  popALL
+       popw    %bp
+       popw    %di
+       popw    %si
+       popw    %dx
+       popw    %cx
+       popw    %bx
+       popw    %ax
+       .endm
+
+#define PUSHALL_SIZE (7*2)
+#define PUSHALL_SI   ((1/*%bp*/+1/*%di*/)*2)
+
+       .macro  CallJumpVector0x13
+       pushf                   /* create 'lret' stack by this pushf... */
+       pushw   %cs             /* ...this segment... */
+       call    JumpVector0x13  /* ...and this return address */
+       .endm
+
+
+
+/* Code starts here */
+/********************/
+
+       .arch   i8086,nojumps
+       .code16
+       .text
+       .globl  _start
+_start:
+
+/* init stack */
+START:
+       cli                     /* just a paranoia, shouldn't be needed */
+       jmp     InitRawStack    /* We don't want to have 0x90 on offset 2 (one of recognization rules by DOS) */
+       .org    START+0x03
+
+       .macro  places offset length string
+       .org    (\offset) + START
+       .ascii  "\string"
+       .org    (\offset) + (\length) + START           /* we can't check whether it isn't too short :-(  */
+       .endm
+       .macro  placex offset length bytes
+       .org    (\offset) + START
+       .byte   \bytes
+       .org    (\offset) + (\length) + START           /* we can't check whether it isn't too short :-(  */
+       .endm
+
+/* We rather supply empty (=zeroed) FAT filesystem table
+ */
+#ifndef STRIP_BOOT_HEADER
+       places  0x03,8,"BIOSRAID"               /* OEM ID */
+       placex  0x43,3,"0x5E,0x81,0xA2"         /* Volume Serial Number: 5E81A2?? as "SERIAL??" */
+       placex  0x43+3,1,BACKUP_SECTOR          /* Volume Serial Number: 5E81A23F as "SERIAL??" */
+       places  0x47,11,"BIOSauRAID\0"          /* Volume Label */
+       places  0x52,8,"BootOnly"               /* Filesystem ID */
+#else
+       places  0x03,8,"BIOSRAI2"               /* OEM ID */
+       placex  0x03+8,1,BACKUP_SECTOR          /* Sanity checked by: biosautoraid.pl */
+       placex  0x03+9,1,MAX_BOOT_SIZE_100      /* Sanity checked by: biosautoraid.pl */
+#endif
+
+InitRawStack:
+#ifndef STRIP_BOOT_HEADER
+       .org    0x5A + START
+#endif
+
+       xorw    %ax,%ax
+       movw    %ax,%ss
+       movw    $0x7C00,%sp
+       sti
+       outchar '1'
+       ljmp    $0x7C0/*segment*/ , $InitContinue7C0-START/*offset*/
+
+InitContinue7C0:
+#if 0
+       outchar '2'
+#endif
+
+/* move to top memory */
+       xorw    %dx,%dx         /* DX=null */
+       movw    %dx,%ds
+       movw    %ds:MEM_KBYTES,%ax
+       decw    %ax             /* Allocation size: 1KB */
+       movw    %ax,%ds:MEM_KBYTES
+       movb    $6,%cl
+       shlw    %cl,%ax
+       movw    %ax,%es
+       xorw    %di,%di         /* buffer=ES:DI */
+       movw    $0x7C00,%si
+       movw    $0x200/2,%cx
+       rep
+       movsw
+       pushw   %es
+       movw    $InitContinueTop-START,%ax
+       pushw   %ax
+       lret
+
+PrintTrailNL:
+       movw    $TrailNL_Msg-START,%si
+       jmp     PrintString_noPref
+
+/* String is given in SI, returns updated SI */
+PrintString:
+       movb    $'%',%al
+       call    PrintChar
+PrintString_noPref:
+       pushw   %cs
+       popw    %ds
+PrintString_loop:
+       cld
+       lodsb
+       testb   %al,%al
+       jz      Return1
+       movw    $PrintString_loop-START,%bx     /* from now do just 'ret' to close the loop */
+       pushw   %bx
+/* fallthru */
+
+/* Character is given in AL
+ * Preserves: DS, ES, SI, DI
+ * Destroys: AX (even AL!), BX, CX, DX
+ * DS is sometimes left preserved, sometimes set to DS=CS
+ */
+PrintChar:
+       movb    $0x0E,%ah       /* print character */
+       movw    $7,%bx
+       pushw   %ax
+       int     $0x10
+       popw    %ax
+Return1:
+       ret
+
+
+SF13_02_Err_Msg:
+       .ascii  "Read16"
+SnifferErrTail_MSG:
+       .ascii  "=error!"
+TrailNL_Msg:
+       .byte   13,10,0
+
+
+/* Main initialization */
+/***********************/
+/* DS invalid, ES==CS */
+InitContinueTop:
+       outchar ':'
+
+       pushw   %cs
+       cli
+       popw    %ss
+       movw    $1*1024,%sp             /* Allocated 1 KB */
+       sti
+
+       movw    $HelloMsg-START,%si
+       call    PrintString
+
+/* now install our SniffFunction0x13 sniffer */
+
+       xorw    %ax,%ax
+       movw    %ax,%ds
+       movw    $0x13*4,%si
+       pushw   %cs
+       popw    %es
+       movw    $OrigVector0x13-START,%di
+       pushw   %si
+       movw    $SniffFunction0x13-START,%ax
+       cld
+       cli
+       movsw
+       movsw
+       popw    %di     /* = $0x13*4 */
+       pushw   %ds
+       popw    %es     /* = null */
+       stosw           /* $SniffFunction0x13-START */
+       pushw   %cs
+       popw    %ax
+       stosw
+       movw    $0x18*4,%di     /* ROM basic - failed boot */
+       movw    $SniffFunction0x18-START,%ax
+       stosw
+       pushw   %cs
+       popw    %ax
+       stosw
+       sti
+       /* we WANT DS left with 0x0000 */
+       /* we WANT ES left with 0x0000 */
+
+/* and give the system control to disk=0x80/masterboot */
+       movw    $0x0201,%ax     /* READ16 1 sector */
+       movw    $0x7C00,%bx     /* buffer=ES:BX= 0x0000:0x7C00 */
+       pushw   %ds
+       pushw   %bx             /* BX/DS on stack for later 'lret', S=2, prepared for far ret */
+/* Interface to retry-capable INT13, set everything except CX */
+/* function may destroy DS value! */
+ReadSectors:
+       /* These two numbers get remapped to: BACKUP_SECTOR */
+       movw    $0x0080,%dx     /* Head 0, Drive 0x80 */
+       movw    $0x0001,%cx     /* Cylinder 0, Sector 1 */
+       int     $0x13
+       jnc     PassControl
+       pushw   %dx     /* save drive for resetting the controller */
+       movw    $SF13_02_Err_Msg-START,%si
+       call    PrintString
+       movb    $0x00,%ah       /* reset controller - FDC or HDC */
+       popw    %dx             /* restore the drive */
+       int     $0x13
+       jmp     ReadSectors
+
+PassControl:
+       cmpw    $0xAA55,%es:(0x7C00+0x1FE)              /* 0x55,0xAA */
+       movw    $BadSignatureMsg-START,%si
+       jne     PrintFatal
+       cli                     /* IMPORTANT: Boot sectors must be run with CLI! */
+       lret                    /* lret to 0x0000:0x7C00 */
+
+SniffFunction0x18:
+       movw    $Interrupt0x18Msg-START,%si     /* %ds gets fixed in PrintFatal */
+/* fallthru */
+PrintFatal:
+       call    PrintString
+PrintFatal_dead:
+       jmp     PrintFatal_dead
+
+
+/**************************************************************/
+/* Section for INT13 sniffing                                 */
+/* THIS point may start to be after 0x200 boundary            */
+/**************************************************************/
+
+SF13_not02:
+       cmpb    $0x41,%ah       /* presence32 */
+       jne     JumpVector0x13popa
+       cmpw    $0x55AA,%bx
+       jne     JumpVector0x13popa
+       stc
+       jmp     SF13_leave
+
+JumpVector0x13popa:
+       popALL
+       popw    %ds
+       incw    %sp
+       incw    %sp     /* trash discarded */
+       incw    %sp
+       incw    %sp     /* original %ax discarded */
+       popw    %dx
+JumpVector0x13:
+       .byte   0xEA    /* ljmp */
+OrigVector0x13:
+       .skip   4
+
+
+SniffFunction0x13:
+       pushw   %dx     /* original %dx */
+       pushw   %ax     /* original %ax */
+       pushw   %ax     /* trash */
+       pushw   %ds
+       pushALL
+
+#if 0
+       pushw   %cx
+       pushw   %dx
+       xorw    %cx,%cx
+       movw    $0x1,%dx
+wait0: loopw   wait0
+       decw    %dx
+       jnz     wait0
+       popw    %dx
+       popw    %cx
+#endif
+
+       cmpb    $0x02,%ah       /* read16 */
+       jne     SF13_not02
+SF13_do02_retry:
+
+#if 0
+       pushw   %cx
+       pushw   %dx
+       xorw    %cx,%cx
+       movw    $0x400,%dx
+wait1: loopw   wait1
+       decw    %dx
+       jnz     wait1
+       popw    %dx
+       popw    %cx
+#endif
+
+       call    CallVector0x13
+       jnc     SF13_selfcheck
+       movw    $SF13_02_Err_Msg-START,%si
+       pushw   %dx
+       call    PrintString
+       popw    %dx
+       test    $0x80,%dl
+       jz      SF13_leave              /* just a floppy? */
+       test    $0x7E,%dl
+       jnz     SF13_leave              /* 0x82+ disk? It may be correct unsuccessful read. */
+       pushw   %bp
+       movw    %sp,%bp
+       xorb    $0x01,/*%dl:*/ %ss:2/*%bp*/+2/*%bp*/+2/*%di*/+2/*%si*/(%bp)
+SF13_do02_retry_popw_bp:
+       popw    %bp
+       jmp     SF13_do02_retry
+
+/* Prevent: Error 17
+ * as we must hide ourselves to let read GRUB itself frmo the masterboot.
+ */
+SF13_selfcheck:
+       test    $0x80,%dl
+       jz      SF13_leave              /* just a floppy? */
+       test    $0x7E,%dl
+       jnz     SF13_leave              /* 0x82+ disk? It may be correct unsuccessful read. */
+       cmpw    $0x0001,%cx             /* Cylinder 0, Sector 1 ? */
+       jne     SF13_leave_zero
+       cmpb    $0x00,%dh               /* Head 0 ? */
+       jne     SF13_leave_zero
+       movb    $BACKUP_SECTOR/63,%dh
+       movw    $1+(BACKUP_SECTOR%63),%cx
+SF13_self_retry:
+       movw    $0x0201,%ax
+       CallJumpVector0x13
+       jnc     SF13_self_ok
+       xorb    $0x01,%dl
+       jmp     SF13_self_retry
+
+SF13_self_ok:
+       pushw   %bp
+       movw    %sp,%bp
+       andb    $0xFE,%ss:2/*%bp*/+PUSHALL_SIZE+2/*%ds*/+2/*trash*/+2/*orig-%ax*/+2/*orig-%dx*/+4/*ret-seg:offs*/(%bp)  /* flags; clear CF */
+       popw    %bp
+
+SF13_leave_zero:
+#if 0
+       pushw   %bp
+       movw    %sp,%bp
+       movb    /*%al:*/ %ss:2/*%bp*/+2/*%bp*/+2/*%di*/+2/*%si*/+2/*%dx*/+2/*%cx*/+2/*%bx*/(%bp),%al    /* count */
+       movw    /*%cx:*/ %ss:2/*%bp*/+2/*%bp*/+2/*%di*/+2/*%si*/+2/*%dx*/(%bp),%cx      /* cylinder, sector */
+       cmpb    $BACKUP_SECTOR/63,/*%dh:*/ %ss:2/*%bp*/+2/*%bp*/+2/*%di*/+2/*%si*/+1/*%dl*/(%bp)        /* head */
+       popw    %bp
+       jne     SF13_leave
+SF13_leave_zero_loop:
+       andb    %al,%al
+       jz      SF13_leave
+       cmpw    $1+(BACKUP_SECTOR%63),%cx
+       je      SF13_leave_zero_found
+       incw    %cx
+       addw    $0x200,%bx
+       decb    %al                     /* --count */
+       jmp     SF13_leave_zero_loop
+
+SF13_leave_zero_found:
+       movw    %bx,%di
+       movw    $0x200/2,%cx
+       xorw    %ax,%ax
+       rep
+       stosw
+#endif
+SF13_leave:
+       popALL
+       popw    %ds
+       incw    %sp
+       incw    %sp     /* trash discarded */
+       incw    %sp
+       incw    %sp     /* original %ax discarded */
+       popw    %dx
+       iret
+
+CallVector0x13:
+       popw    %ax     /* return address */
+       pushw   %bp
+       movw    %sp,%bp
+       movw    %ax,%ss:2/*%bp*/+PUSHALL_SIZE+2/*%ds*/(%bp)     /* store %ax to 'trash' */
+       movw    %ss:2/*%bp*/+PUSHALL_SIZE+2/*%ds*/+2/*trash*/(%bp),%ax  /* restore original %ax */
+       movw    %ax,%ss:2/*%bp*/+2/*%bp*/+2/*%di*/+2/*%si*/+2/*%dx*/+2/*%cx*/+2/*%bx*/(%bp)     /* rewrite from original %ax */
+       popw    %bp
+       popALL
+       popw    %ds
+       CallJumpVector0x13
+       pushw   %ds
+       pushALL                 /* stack is back to normal NOW */
+       pushw   %bp             /* trash - just to prepare return address16 */
+       pushw   %ax
+       pushw   %bp
+       movw    %sp,%bp
+       movw    %ss:2/*%bp*/+2/*%ax*/+2/*return16*/+PUSHALL_SIZE+2/*%ds*/(%bp),%ax
+       movw    %ax,%ss:2/*%bp*/+2/*ax*/(%bp)
+       pushf
+       pop     %ax
+       movw    %ax,%ss:2/*%bp*/+2/*%ax*/+2/*return16*/+PUSHALL_SIZE+2/*%ds*/+2/*trash*/+2/*orig-%ax*/+2/*orig-%dx*/+4/*ret-seg:offs*/(%bp)
+       popw    %bp
+       popw    %ax
+       ret                     /* to prepared return16 and now will be stack in normal again */
+
+/* Main upper half initialization messages */
+/*******************************************/
+
+HelloMsg:
+#if 0
+       .ascii  "BIOSautoRAID, $Id$"
+#else
+       .ascii  "BaR"
+#endif
+       .byte   13,10
+       .byte   0
+BadSignatureMsg:
+#if 0
+       .asciz  "Disk 0x80 has invalid signature!"
+#else
+       .asciz  "bs"
+#endif
+
+
+/* Interrupt sniffing messages */
+/*******************************/
+
+Interrupt0x18Msg:
+#if 0
+       .asciz  "Interrupt 0x18 - Failed boot!"
+#else
+       .asciz  "18"
+#endif
+
+
+/**************************************************************/
+/* Final sector signature                                     */
+/**************************************************************/
+
+       MYORG(MAX_BOOT_SIZE)
+       /* Partition table */
+
+       MYORG(0x1FE)
+       .byte   0x55,0xAA
+
+
+/* vi:ts=8:sw=8
+ */
diff --git a/biosautoraid.pl b/biosautoraid.pl
new file mode 100755 (executable)
index 0000000..3dd40cd
--- /dev/null
@@ -0,0 +1,133 @@
+#! /usr/bin/perl
+use strict;
+use warnings;
+use bytes;
+use Getopt::Long;
+use Fcntl qw(SEEK_SET);
+
+
+sub signature($$$$;$)
+{
+my($hay,$hay_filename,$offset,$needle,$msg)=@_;
+
+       die if length($needle)%1;
+       my $len_bytes=length($needle)/2;
+       substr($hay,$offset,$len_bytes) eq pack("H*",$needle)
+                       or die "\"$hay_filename\" error reading signature 0x$needle @".sprintf("0x%X",$offset)
+                                       .(!$msg ? "" : "; $msg")."\n";
+}
+
+my $BACKUP_SECTOR_START=63;
+my $MAX_BOOT_SIZE=0x1b6;       # LILO: leave some space for NT's and DR DOS' dirty
+
+
+my $bin_filename="./biosautoraid.bin";
+my $opt_install;
+my $opt_uninstall;
+my $options_error=!GetOptions(
+               "bin=s"      ,\$bin_filename,
+               "i|install"  ,\$opt_install,
+               "u|uninstall",\$opt_uninstall,
+               );
+local *BIN;
+open BIN,$bin_filename or die "open \"$bin_filename\": $!";
+my $bin=do { local $/; <BIN>; } or die "read \"$bin_filename\": $!";
+close BIN or die "close \"$bin_filename\": $!";
+
+my $bin_len=length($bin);
+$bin_len>0 && !($bin_len%0x200) or die "Invalid length of \"$bin_filename\": $bin_len";
+my $bin_short=($bin_len==0x200);       # => no 0xBEEF
+
+signature $bin,$bin_filename,0x1FE     ,"55AA";
+substr($bin,$MAX_BOOT_SIZE,0x1FE-$MAX_BOOT_SIZE)=~/^\x00*$/s or die "No empty magic+partitiontable: $bin_filename";
+
+if (substr($bin,3,8) eq "BIOSRAID") {
+       signature $bin,$bin_filename,0x03      ,unpack("H*","BIOSRAID");
+       signature $bin,$bin_filename,0x43      ,"5E81A2".sprintf("%X",$BACKUP_SECTOR_START);
+       signature $bin,$bin_filename,$bin_len-2,"BEEF" if !$bin_short;
+       }
+else {
+       signature $bin,$bin_filename,0x03      ,unpack("H*","BIOSRAI2")
+                       .sprintf("%X%X",$BACKUP_SECTOR_START,$MAX_BOOT_SIZE-0x100);
+       }
+
+my $all_ok;
+END {
+       warn "Syntax: $0 [--bin=<biosautoraid.bin>] [{-i|--install|-u|--uninstall} <device>]" if !$all_ok;
+       }
+
+if (!@ARGV && !$options_error) {
+       print "OK, \"$bin_filename\" checked.\n";
+       $all_ok=1;
+       exit 0;
+       }
+
+die "Type -i|--install or -u|--uninstall"                     if (!$opt_install && !$opt_uninstall) || $options_error;
+die "Both -i|--install and -u|--uninstall forbidden together" if   $opt_install &&  $opt_uninstall;
+
+@ARGV<=1 or die;
+my $device_filename=$ARGV[0];
+
+local *DEVICE;
+open DEVICE,"+<".$device_filename or die "open \"$device_filename\": $!";
+
+my $backup_offset=$BACKUP_SECTOR_START*0x200;
+
+sysseek DEVICE,0,SEEK_SET or die "Error seeking $device_filename";
+my $master;
+$bin_len==sysread DEVICE,$master,$bin_len or die "read \"$device_filename\": $!";
+length($master)==$bin_len or die "read \"$device_filename\": ".length($master)."!=$bin_len";
+signature $master,$device_filename,0x1FE,"55AA";
+
+sysseek DEVICE,$backup_offset,SEEK_SET or die "Error seeking $device_filename";
+my $backup;
+$bin_len==sysread DEVICE,$backup,$bin_len or die "read \"$device_filename\": $!";
+length($backup)==$bin_len or die "read \"$device_filename\": ".length($backup)."!=$bin_len";
+
+my $zeroes="\x00"x$bin_len;
+die if length($zeroes)!=$bin_len;
+
+$bin=substr($bin,0,$MAX_BOOT_SIZE).substr($master,$MAX_BOOT_SIZE,0x1FE-$MAX_BOOT_SIZE).substr($bin,0x1FE);
+
+my $installed=substr($master,0x03,7) eq "BIOSRAI";
+print "Already instaled? ".($installed ? "YES" : "NO")."\n";
+
+die "Nothing to uninstall!\n" if $opt_uninstall && !$installed;
+
+if ($installed) {
+       signature $master,$device_filename,$bin_len-2,"BEEF","Different installed SECTOR_LEN?" if !$bin_short;
+       signature $master,$device_filename,0x03      ,unpack("H*","BIOSRAI");
+       signature $master,$device_filename,0x43      ,"5E81A2".sprintf("%X",$BACKUP_SECTOR_START) if !$bin_short;
+       signature $backup,"backup",0x1FE,"55AA";
+       }
+if ($opt_install) {
+       if (!$installed) {
+               if ($backup ne $zeroes) {
+                       # Permit different DOS-volume area and 0x1FC..0x1FD bytes.
+                       die "Sensitive bytes at backup sectors" if !(1
+                               && (0
+                                       || substr($backup,0x200) eq substr($zeroes,0x200)
+                                       || (substr($backup,$bin_len-2) eq pack("H*","BEEF") && !$bin_short)
+                                       )
+                               && substr($backup,0,0x03) eq substr($master,0,0x03)
+                               && substr($backup,0x5A,$MAX_BOOT_SIZE-0x5A) eq substr($master,0x5A,$MAX_BOOT_SIZE-0x5A)
+                               );
+                       warn "WARNING: Dropping obsolete (but similiar) backup! Use --uninstall next time.\n";
+                       }
+               sysseek DEVICE,$backup_offset,SEEK_SET or die "Error seeking $device_filename";
+               $bin_len==syswrite DEVICE,$master or die "Error backing up the master";
+               }
+       sysseek DEVICE,0,SEEK_SET or die "Error seeking $device_filename";
+       $bin_len==syswrite DEVICE,$bin or die "Error writing new master of BIOSautoRAID";
+       }
+if ($opt_uninstall) {
+       sysseek DEVICE,0,SEEK_SET or die "Error seeking $device_filename";
+       $bin_len==syswrite DEVICE,$backup or die "Error writing backup back to the master";
+       sysseek DEVICE,$backup_offset,SEEK_SET or die "Error seeking $device_filename";
+       $bin_len==syswrite DEVICE,$zeroes or die "Error clearing the backup area";
+       }
+
+close DEVICE or die "close \"$device_filename\": $!";
+
+print "OK; ".($opt_install ? ($installed ? "re" : "")."installed" : "uninstalled")."\n";
+$all_ok=1;