X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=qemu%2Froms%2Fipxe%2Fsrc%2Futil%2Fzbin.c;h=1862a3827283a2e101883141a278f2423e0857e2;hb=a14b48d18a9ed03ec191cf16b162206998a895ce;hp=3b7cf95b3df7491dc3c5ef78f087bdfd1f3de2a6;hpb=e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb;p=kvmfornfv.git diff --git a/qemu/roms/ipxe/src/util/zbin.c b/qemu/roms/ipxe/src/util/zbin.c index 3b7cf95b3..1862a3827 100644 --- a/qemu/roms/ipxe/src/util/zbin.c +++ b/qemu/roms/ipxe/src/util/zbin.c @@ -1,13 +1,21 @@ +#include #include +#include +#include +#include #include - -#define ENCODE -#define VERBOSE -#include "nrv2b.c" -FILE *infile, *outfile; +#include #define DEBUG 0 +/* LZMA filter choices. Must match those used by unlzma.S */ +#define LZMA_LC 2 +#define LZMA_LP 0 +#define LZMA_PB 0 + +/* LZMA preset choice. This is a policy decision */ +#define LZMA_PRESET ( LZMA_PRESET_DEFAULT | LZMA_PRESET_EXTREME ) + struct input_file { void *buf; size_t len; @@ -177,13 +185,75 @@ static int process_zinfo_copy ( struct input_file *input, return 0; } +#define OPCODE_CALL 0xe8 +#define OPCODE_JMP 0xe9 + +static void bcj_filter ( void *data, size_t len ) { + struct { + uint8_t opcode; + int32_t target; + } __attribute__ (( packed )) *jump; + ssize_t limit = ( len - sizeof ( *jump ) ); + ssize_t offset; + + /* liblzma does include an x86 BCJ filter, but it's hideously + * convoluted and undocumented. This BCJ filter is + * substantially simpler and achieves the same compression (at + * the cost of requiring the decompressor to know the size of + * the decompressed data, which we already have in iPXE). + */ + for ( offset = 0 ; offset <= limit ; offset++ ) { + jump = ( data + offset ); + + /* Skip instructions that are not followed by a rel32 address */ + if ( ( jump->opcode != OPCODE_CALL ) && + ( jump->opcode != OPCODE_JMP ) ) + continue; + + /* Convert rel32 address to an absolute address. To + * avoid false positives (which damage the compression + * ratio), we should check that the jump target is + * within the range [0,limit). + * + * Some output values would then end up being mapped + * from two distinct input values, making the + * transformation irreversible. To solve this, we + * transform such values back into the part of the + * range which would otherwise correspond to no input + * values. + */ + if ( ( jump->target >= -offset ) && + ( jump->target < ( limit - offset ) ) ) { + /* Convert relative addresses in the range + * [-offset,limit-offset) to absolute + * addresses in the range [0,limit). + */ + jump->target += offset; + } else if ( ( jump->target >= ( limit - offset ) ) && + ( jump->target < limit ) ) { + /* Convert positive numbers in the range + * [limit-offset,limit) to negative numbers in + * the range [-offset,0). + */ + jump->target -= limit; + } + offset += sizeof ( jump->target ); + }; +} + 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 long packed_len; + size_t packed_len = 0; + size_t remaining = ( output->max_len - output->len ); + lzma_options_lzma options; + const lzma_filter filters[] = { + { .id = LZMA_FILTER_LZMA1, .options = &options }, + { .id = LZMA_VLI_UNKNOWN } + }; if ( ( offset + len ) > input->len ) { fprintf ( stderr, "Input buffer overrun on pack\n" ); @@ -196,9 +266,15 @@ static int process_zinfo_pack ( struct input_file *input, return -1; } - if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len, - ( output->buf + output->len ), - &packed_len, 0 ) != UCL_E_OK ) { + bcj_filter ( ( input->buf + offset ), len ); + + lzma_lzma_preset ( &options, LZMA_PRESET ); + options.lc = LZMA_LC; + options.lp = LZMA_LP; + options.pb = LZMA_PB; + if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ), + len, ( output->buf + output->len ), + &packed_len, remaining ) != LZMA_OK ) { fprintf ( stderr, "Compression failure\n" ); return -1; } @@ -206,7 +282,7 @@ static int process_zinfo_pack ( struct input_file *input, if ( DEBUG ) { fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ), output->len, - ( size_t )( output->len + packed_len ) ); + ( output->len + packed_len ) ); } output->len += packed_len;