diff --git a/src/Makefile b/src/Makefile index 4f50456a..d3280702 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 12041ce6..ff52de1d 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -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. diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index d1554513..0966dcbd 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -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 index 00000000..d40396b3 --- /dev/null +++ b/src/arch/i386/prefix/nonrv2b.S @@ -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 diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S index f68c14e0..3e6cd2d0 100644 --- a/src/arch/i386/prefix/romprefix.S +++ b/src/arch/i386/prefix/romprefix.S @@ -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 diff --git a/src/arch/i386/prefix/unnrv2b.S b/src/arch/i386/prefix/unnrv2b.S index 4e3090e2..1cf570d0 100644 --- a/src/arch/i386/prefix/unnrv2b.S +++ b/src/arch/i386/prefix/unnrv2b.S @@ -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 diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds index 7b7b95da..8ff7c45a 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/i386/scripts/i386.lds @@ -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; /* diff --git a/src/util/.gitignore b/src/util/.gitignore index 9550102a..98adc2df 100644 --- a/src/util/.gitignore +++ b/src/util/.gitignore @@ -1,3 +1,4 @@ nrv2b +zbin hijack prototester diff --git a/src/util/zbin.c b/src/util/zbin.c new file mode 100644 index 00000000..f47fa36b --- /dev/null +++ b/src/util/zbin.c @@ -0,0 +1,325 @@ +#include +#include + +#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; +}