Compressed ROM images now work.
authorMichael Brown <mcb30@etherboot.org>
Mon, 16 Jul 2007 15:58:38 +0000 (16:58 +0100)
committerMichael Brown <mcb30@etherboot.org>
Mon, 16 Jul 2007 15:58:38 +0000 (16:58 +0100)
src/Makefile
src/Makefile.housekeeping
src/arch/i386/prefix/libprefix.S
src/arch/i386/prefix/nonrv2b.S [new file with mode: 0644]
src/arch/i386/prefix/romprefix.S
src/arch/i386/prefix/unnrv2b.S
src/arch/i386/scripts/i386.lds
src/util/.gitignore
src/util/zbin.c [new file with mode: 0644]

index 4f50456..d328070 100644 (file)
@@ -90,6 +90,7 @@ MKCONFIG      ?= $(PERL) ./util/mkconfig.pl
 SYMCHECK       ?= $(PERL) ./util/symcheck.pl
 SORTOBJDUMP    ?= $(PERL) ./util/sortobjdump.pl
 NRV2B          ?= ./util/nrv2b
+ZBIN           ?= ./util/zbin
 DOXYGEN                ?= doxygen
 
 # Location to place generated files
index 12041ce..ff52de1 100644 (file)
@@ -307,11 +307,23 @@ $(BIN)/%.tmp : $(BLIB) $(MAKEDEPS) $(LDSCRIPT)
 $(BIN)/%.map : $(BIN)/%.tmp
        @less $(BIN)/$*.tmp.map
 
+# Extract compression information from intermediate object file
+#
+$(BIN)/%.zinfo : $(BIN)/%.tmp
+       $(QM)echo "  [ZINFO] $@"
+       $(Q)$(OBJCOPY) -O binary -j .zinfo $< $@
+
 # Build raw binary file from intermediate object file
 #
 $(BIN)/%.bin : $(BIN)/%.tmp
        $(QM)echo "  [BIN] $@"
-       $(Q)$(OBJCOPY) -O binary $< $@
+       $(Q)$(OBJCOPY) -O binary -R .zinfo $< $@
+
+# Compress raw binary file
+#
+$(BIN)/%.zbin : $(BIN)/%.bin $(BIN)/%.zinfo $(ZBIN)
+       $(QM)echo "  [ZBIN] $@"
+       $(Q)$(ZBIN) $(BIN)/$*.bin $(BIN)/$*.zinfo > $@
 
 # Build bochs symbol table
 $(BIN)/%.bxs : $(BIN)/%.tmp
@@ -356,7 +368,7 @@ define media_template
        @$(MKDIR) -p $(dir $(2))
        @$(RM) $(2)
        @$(TOUCH) $(2)
-       @echo -e '$$(BIN)/%$(1) : $$(BIN)/%$(1).bin' \
+       @echo -e '$$(BIN)/%$(1) : $$(BIN)/%$(1).zbin' \
                  '\n\t$$(QM)echo "  [FINISH] $$@"' \
                  '\n\t$$(Q)$$(CP) $$< $$@' \
                  '\n\t$$(Q)$$(FINALISE_$(1))' \
@@ -383,13 +395,19 @@ include $(MEDIA_DEPS)
 allroms allzroms : all%s : $(foreach ROM,$(ROMS),$(BIN)/$(ROM).%)
 all%s : $(foreach DRIVER,$(DRIVERS),$(BIN)/$(DRIVER).%)
 
-# The compressor utility
+# The compression utilities
 #
 $(NRV2B) : util/nrv2b.c $(MAKEDEPS)
-       $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG \
+       $(QM)echo "  [HOSTCC] $@"
+       $(Q)$(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG \
                       -DBITSIZE=32 -DENDIAN=0 -o $@ $<
 CLEANUP        += $(NRV2B)
 
+$(ZBIN) : util/zbin.c util/nrv2b.c $(MAKEDEPS)
+       $(QM)echo "  [HOSTCC] $@"
+       $(Q)$(HOST_CC) -O2 -o $@ $<
+CLEANUP += $(ZBIN)
+
 # Auto-incrementing build serial number.  Append "bs" to your list of
 # build targets to get a serial number printed at the end of the
 # build.  Enable -DBUILD_SERIAL in order to see it when the code runs.
index d155451..0966dcb 100644 (file)
@@ -37,6 +37,9 @@
  */
 #define HIGHMEM_LOADPOINT ( 4 << 20 )
 
+/* Image compression enabled */
+#define COMPRESS 1
+
 #define CR0_PE 1
 
        .arch i386
@@ -81,7 +84,11 @@ install_block:
        
        /* Do the copy */
        cld
-       rep addr32 movsb /* or "call decompress16" */
+#if COMPRESS
+       call    decompress16
+#else
+       call    nodecompress16
+#endif
 
        /* Zero remaining space */
        movl    %eax, %edi
diff --git a/src/arch/i386/prefix/nonrv2b.S b/src/arch/i386/prefix/nonrv2b.S
new file mode 100644 (file)
index 0000000..d40396b
--- /dev/null
@@ -0,0 +1,18 @@
+/* Placeholder for decompress16 in non-compressed images */
+
+       .text
+       .arch i386
+       .section ".prefix.lib", "ax", @progbits
+
+       .code16
+       .globl  nodecompress16
+nodecompress16:
+       rep addr32 movsb
+       ret
+
+       /* File split information for the compressor */
+       .section ".zinfo", "a"
+       .ascii  "COPY"
+       .long   _prefix_load_offset
+       .long   _load_size
+       .long   _max_align
index f68c14e..3e6cd2d 100644 (file)
@@ -14,7 +14,7 @@
        .org    0x00
 romheader:
        .word   0xAA55                  /* BIOS extension signature */
-       .byte   _rom_size               /* Size in 512-byte blocks */
+romheader_size:        .byte   _rom_size       /* Size in 512-byte blocks */
        jmp     init_vector             /* Initialisation vector */
        .org    0x16
        .word   undiheader
@@ -34,7 +34,7 @@ pciheader:
        .byte   0x02                    /* Device Base Type code */
        .byte   0x00                    /* Device Sub-Type code */
        .byte   0x00                    /* Device Interface Type code */
-       .word   _rom_size               /* Image length same as offset 02h */
+pciheader_size:        .word   _rom_size       /* Image length same as offset 02h */
        .word   0x0001                  /* revision level of code/data */
        .byte   0x00                    /* code type */
        .byte   0x80                    /* Flags (last PCI data structure) */
@@ -215,3 +215,15 @@ print_message:
        popw    %ax
        ret
        .size print_message, . - print_message
+
+
+       /* Data update information for the compressor */
+       .section ".zinfo.fixup", "a"
+       .ascii  "SUBB"
+       .long   romheader_size
+       .long   512
+       .long   0
+       .ascii  "SUBW"
+       .long   pciheader_size
+       .long   512
+       .long   0
index 4e3090e..1cf570d 100644 (file)
@@ -35,7 +35,7 @@
 
        .text
        .arch i386
-       .section ".prefix", "ax", @progbits
+       .section ".prefix.lib", "ax", @progbits
 
 #ifdef CODE16
 /****************************************************************************
@@ -54,6 +54,7 @@
  * NOTE: It would be possible to build a smaller version of the
  * decompression code for -DKEEP_IT_REAL by using
  *    #define REG(x) x
+ *    #define MOVSB movsb
  * to use 16-bit registers where possible.  This would impose limits
  * that the compressed data size must be in the range [1,65533-%si]
  * and the uncompressed data size must be in the range [1,65536-%di]
@@ -66,6 +67,7 @@
  */
 
 #define REG(x) e ## x
+#define MOVSB addr32 movsb
 
        .code16
        .globl  decompress16
@@ -109,11 +111,10 @@ decompress:
        cld
        xor     %xBP, %xBP
        dec     %xBP            /* last_m_off = -1 */
-       add     $4, %xSI        /* Skip "file length" field */
        jmp     dcl1_n2b
        
 decompr_literals_n2b:
-       movsb
+       MOVSB
 decompr_loop_n2b:
        addl    %ebx, %ebx
        jnz     dcl2_n2b
@@ -157,7 +158,7 @@ decompr_got_mlen_n2b:
        push    %xSI
        lea     (%xBP,%xDI), %xSI       /* m_pos = dst + olen + -m_off  */
        rep
-       es movsb                /* dst[olen++] = *m_pos++ while(m_len > 0) */
+       es MOVSB                /* dst[olen++] = *m_pos++ while(m_len > 0) */
        pop     %xSI
        jmp     decompr_loop_n2b
 
@@ -179,3 +180,23 @@ decompr_end_n2b:
        popl    %ebx
        pop     %xAX
        ret
+
+
+       /* File split information for the compressor */
+       .section ".zinfo", "a"
+       .ascii  "COPY"
+       .long   _prefix_load_offset
+       .long   _prefix_progbits_size
+       .long   _max_align
+       .ascii  "PACK"
+       .long   _text16_load_offset
+       .long   _text16_progbits_size
+       .long   _max_align
+       .ascii  "PACK"
+       .long   _data16_load_offset
+       .long   _data16_progbits_size
+       .long   _max_align
+       .ascii  "PACK"
+       .long   _textdata_load_offset
+       .long   _textdata_progbits_size
+       .long   _max_align
index 7b7b95d..8ff7c45 100644 (file)
@@ -164,6 +164,24 @@ SECTIONS {
 
     _end = .;
 
+    /*
+     * Compressor information block
+     */
+
+    _zinfo_link_addr = 0;
+    . = _zinfo_link_addr;
+    _zinfo = .;
+
+    .zinfo : AT ( _zinfo_load_offset + __zinfo ) {
+       __zinfo = .;
+       _entry = .;
+       *(.zinfo)
+       *(.zinfo.*)
+       _ezinfo_progbits = .;
+    }
+    
+    _ezinfo = .;
+
     /*
      * Dispose of the comment and note sections to make the link map
      * easier to read
@@ -215,6 +233,13 @@ SECTIONS {
 
     _load_size             = . - _load_addr;
 
+    .                     -= _zinfo_link_addr;
+    _zinfo_load_offset     = ALIGN ( _max_align );
+    _zinfo_load_addr       = _zinfo_link_addr + _zinfo_load_offset;
+    _zinfo_size                    = _ezinfo - _zinfo;
+    _zinfo_progbits_size    = _ezinfo_progbits - _zinfo;
+    .                      = _zinfo_load_addr + _zinfo_progbits_size;
+
     _payload_offset        = _text16_load_offset;
 
     /*
index 9550102..98adc2d 100644 (file)
@@ -1,3 +1,4 @@
 nrv2b
+zbin
 hijack
 prototester
diff --git a/src/util/zbin.c b/src/util/zbin.c
new file mode 100644 (file)
index 0000000..f47fa36
--- /dev/null
@@ -0,0 +1,325 @@
+#include <stdio.h>
+#include <sys/stat.h>
+
+#define ENCODE
+#define VERBOSE
+#include "nrv2b.c"
+FILE *infile, *outfile;
+
+struct input_file {
+       void *buf;
+       size_t len;
+};
+
+struct output_file {
+       void *buf;
+       size_t len;
+       size_t max_len;
+};
+
+struct zinfo_common {
+       char type[4];
+       char pad[12];
+};
+
+struct zinfo_copy {
+       char type[4];
+       uint32_t offset;
+       uint32_t len;
+       uint32_t align;
+};
+
+struct zinfo_pack {
+       char type[4];
+       uint32_t offset;
+       uint32_t len;
+       uint32_t align;
+};
+
+struct zinfo_subtract {
+       char type[4];
+       uint32_t offset;
+       uint32_t divisor;
+       uint32_t pad;
+};
+
+union zinfo_record {
+       struct zinfo_common common;
+       struct zinfo_copy copy;
+       struct zinfo_pack pack;
+       struct zinfo_subtract subtract;
+};
+
+struct zinfo_file {
+       union zinfo_record *zinfo;
+       unsigned int num_entries;
+};
+
+static int read_file ( const char *filename, void **buf, size_t *len ) {
+       FILE *file;
+       struct stat stat;
+
+       file = fopen ( filename, "r" );
+       if ( ! file ) {
+               fprintf ( stderr, "Could not open %s: %s\n", filename,
+                         strerror ( errno ) );
+               goto err;
+       }
+
+       if ( fstat ( fileno ( file ), &stat ) < 0 ) {
+               fprintf ( stderr, "Could not stat %s: %s\n", filename,
+                         strerror ( errno ) );
+               goto err;
+       }
+
+       *len = stat.st_size;
+       *buf = malloc ( *len );
+       if ( ! *buf ) {
+               fprintf ( stderr, "Could not malloc() %d bytes for %s: %s\n",
+                         *len, filename, strerror ( errno ) );
+               goto err;
+       }
+
+       if ( fread ( *buf, 1, *len, file ) != *len ) {
+               fprintf ( stderr, "Could not read %d bytes from %s: %s\n",
+                         *len, filename, strerror ( errno ) );
+               goto err;
+       }
+
+       fclose ( file );
+       return 0;
+
+ err:
+       fclose ( file );
+       return -1;
+}
+
+static int read_input_file ( const char *filename,
+                            struct input_file *input ) {
+       return read_file ( filename, &input->buf, &input->len );
+}
+
+static int read_zinfo_file ( const char *filename,
+                            struct zinfo_file *zinfo ) {
+       void *buf;
+       size_t len;
+
+       if ( read_file ( filename, &buf, &len ) < 0 )
+               return -1;
+
+       if ( ( len % sizeof ( *(zinfo->zinfo) ) ) != 0 ) {
+               fprintf ( stderr, ".zinfo file %s has invalid length %d\n",
+                         filename, len );
+               return -1;
+       }
+
+       zinfo->zinfo = buf;
+       zinfo->num_entries = ( len / sizeof ( *(zinfo->zinfo) ) );
+       return 0;
+}
+
+static int alloc_output_file ( size_t max_len, struct output_file *output ) {
+       output->len = 0;
+       output->max_len = ( max_len );
+       output->buf = malloc ( max_len );
+       if ( ! output->buf ) {
+               fprintf ( stderr, "Could not allocate %d bytes for output\n",
+                         max_len );
+               return -1;
+       }
+       memset ( output->buf, 0xff, sizeof ( output->buf ) );
+       return 0;
+}
+
+static int process_zinfo_copy ( struct input_file *input,
+                               struct output_file *output,
+                               union zinfo_record *zinfo ) {
+       struct zinfo_copy *copy = &zinfo->copy;
+       size_t offset = copy->offset;
+       size_t len = copy->len;
+       unsigned int align = copy->align;
+
+       if ( ( offset + len ) > input->len ) {
+               fprintf ( stderr, "Input buffer overrun on copy\n" );
+               return -1;
+       }
+
+       output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) );
+       if ( ( output->len + len ) > output->max_len ) {
+               fprintf ( stderr, "Output buffer overrun on copy\n" );
+               return -1;
+       }
+
+       memcpy ( ( output->buf + output->len ),
+                ( input->buf + offset ), len );
+       output->len += len;
+       return 0;
+}
+
+static int process_zinfo_pack ( struct input_file *input,
+                               struct output_file *output,
+                               union zinfo_record *zinfo ) {
+       struct zinfo_pack *pack = &zinfo->pack;
+       size_t offset = pack->offset;
+       size_t len = pack->len;
+       unsigned int align = pack->align;
+       unsigned long packed_len;
+
+       if ( ( offset + len ) > input->len ) {
+               fprintf ( stderr, "Input buffer overrun on pack\n" );
+               return -1;
+       }
+
+       output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) );
+       if ( output->len > output->max_len ) {
+               fprintf ( stderr, "Output buffer overrun on pack\n" );
+               return -1;
+       }
+
+       if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len,
+                                    ( output->buf + output->len ),
+                                    &packed_len, 0 ) != UCL_E_OK ) {
+               fprintf ( stderr, "Compression failure\n" );
+               return -1;
+       }
+
+       output->len += packed_len;
+       if ( output->len > output->max_len ) {
+               fprintf ( stderr, "Output buffer overrun on pack\n" );
+               return -1;
+       }
+
+       return 0;
+}
+
+static int process_zinfo_subtract ( struct input_file *input,
+                                   struct output_file *output,
+                                   struct zinfo_subtract *subtract,
+                                   size_t datasize ) {
+       size_t offset = subtract->offset;
+       void *target;
+       long delta;
+
+       if ( ( offset + datasize ) > output->len ) {
+               fprintf ( stderr, "Subtract at %#zx outside output buffer\n",
+                         offset );
+               return -1;
+       }
+
+       target = ( output->buf + offset );
+       delta = ( ( output->len / subtract->divisor ) -
+                 ( input->len / subtract->divisor ) );
+
+       switch ( datasize ) {
+       case 1: {
+               uint8_t *byte = target;
+               *byte += delta;
+               break; }
+       case 2: {
+               uint16_t *word = target;
+               *word += delta;
+               break; }
+       case 4: {
+               uint32_t *dword = target;
+               *dword += delta;
+               break; }
+       default:
+               fprintf ( stderr, "Unsupported subtract datasize %d\n",
+                         datasize );
+               return -1;
+       }
+       return 0;
+}
+
+static int process_zinfo_subb ( struct input_file *input,
+                               struct output_file *output,
+                               union zinfo_record *zinfo ) {
+       return process_zinfo_subtract ( input, output, &zinfo->subtract, 1 );
+}
+
+static int process_zinfo_subw ( struct input_file *input,
+                               struct output_file *output,
+                               union zinfo_record *zinfo ) {
+       return process_zinfo_subtract ( input, output, &zinfo->subtract, 2 );
+}
+
+static int process_zinfo_subl ( struct input_file *input,
+                               struct output_file *output,
+                               union zinfo_record *zinfo ) {
+       return process_zinfo_subtract ( input, output, &zinfo->subtract, 4 );
+}
+
+struct zinfo_processor {
+       char *type;
+       int ( * process ) ( struct input_file *input,
+                           struct output_file *output,
+                           union zinfo_record *zinfo );
+};
+
+static struct zinfo_processor zinfo_processors[] = {
+       { "COPY", process_zinfo_copy },
+       { "PACK", process_zinfo_pack },
+       { "SUBB", process_zinfo_subb },
+       { "SUBW", process_zinfo_subw },
+       { "SUBL", process_zinfo_subl },
+};
+
+static int process_zinfo ( struct input_file *input,
+                          struct output_file *output,
+                          union zinfo_record *zinfo ) {
+       struct zinfo_common *common = &zinfo->common;
+       struct zinfo_processor *processor;
+       char type[ sizeof ( common->type ) + 1 ] = "";
+       unsigned int i;
+
+       strncat ( type, common->type, sizeof ( type ) - 1 );
+       for ( i = 0 ; i < ( sizeof ( zinfo_processors ) /
+                           sizeof ( zinfo_processors[0] ) ) ; i++ ) {
+               processor = &zinfo_processors[i];
+               if ( strcmp ( processor->type, type ) == 0 )
+                       return processor->process ( input, output, zinfo );
+       }
+
+       fprintf ( stderr, "Unknown zinfo record type \"%s\"\n", &type[0] );
+       return -1;
+}
+
+static int write_output_file ( struct output_file *output ) {
+       if ( fwrite ( output->buf, 1, output->len, stdout ) != output->len ) {
+               fprintf ( stderr, "Could not write %d bytes of output: %s\n",
+                         output->len, strerror ( errno ) );
+               return -1;
+       }
+       return 0;
+}
+
+int main ( int argc, char **argv ) {
+       struct input_file input;
+       struct output_file output;
+       struct zinfo_file zinfo;
+       unsigned int i;
+
+       if ( argc != 3 ) {
+               fprintf ( stderr, "Syntax: %s file.bin file.zinfo "
+                         "> file.zbin\n", argv[0] );
+               exit ( 1 );
+       }
+
+       if ( read_input_file ( argv[1], &input ) < 0 )
+               exit ( 1 );
+       if ( read_zinfo_file ( argv[2], &zinfo ) < 0 )
+               exit ( 1 );
+       if ( alloc_output_file ( ( input.len * 4 ), &output ) < 0 )
+               exit ( 1 );
+
+       for ( i = 0 ; i < zinfo.num_entries ; i++ ) {
+               if ( process_zinfo ( &input, &output,
+                                    &zinfo.zinfo[i] ) < 0 )
+                       exit ( 1 );
+       }
+
+       if ( write_output_file ( &output ) < 0 )
+               exit ( 1 );
+
+       return 0;
+}