github.com/linuxboot/fiano@v1.2.0/pkg/compression/x86.go (about)

     1  // Copyright 2018 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package compression
     6  
     7  // LZMAX86 implements Compressor and includes the X86 filter which is popular
     8  // in some UEFI implementations.
     9  type LZMAX86 struct {
    10  	// The X86 filter is layered on top of an existing LZMA implementation.
    11  	lzma Compressor
    12  }
    13  
    14  // Name returns the type of compression employed.
    15  func (c *LZMAX86) Name() string {
    16  	return "LZMAX86"
    17  }
    18  
    19  // Decode decodes LZMA data with the x86 extension.
    20  func (c *LZMAX86) Decode(encodedData []byte) ([]byte, error) {
    21  	decodedData, err := c.lzma.Decode(encodedData)
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  	var x86State uint32
    26  	x86Convert(decodedData, uint(len(decodedData)), 0, &x86State, false)
    27  	return decodedData, nil
    28  }
    29  
    30  // Encode encodes LZMA data with the x86 extension.
    31  func (c *LZMAX86) Encode(decodedData []byte) ([]byte, error) {
    32  	// x86Convert modifies the input, so a copy is recommened.
    33  	decodedDataCpy := make([]byte, len(decodedData))
    34  	copy(decodedDataCpy, decodedData)
    35  
    36  	var x86State uint32
    37  	x86Convert(decodedDataCpy, uint(len(decodedDataCpy)), 0, &x86State, true)
    38  	return c.lzma.Encode(decodedDataCpy)
    39  }
    40  
    41  // Adapted from: https://github.com/tianocore/edk2/blob/00f5e11913a8706a1733da2b591502d59f848a99/BaseTools/Source/C/LzmaCompress/Sdk/C/Bra86.c
    42  func x86Convert(data []byte, size uint, ip uint32, state *uint32, encoding bool) uint {
    43  	var pos uint
    44  	mask := *state & 7
    45  	if size < 5 {
    46  		return 0
    47  	}
    48  	size -= 4
    49  	ip += 5
    50  
    51  	for {
    52  		p := pos
    53  		for ; p < size; p++ {
    54  			if data[p]&0xFE == 0xE8 {
    55  				break
    56  			}
    57  		}
    58  
    59  		{
    60  			d := p - pos
    61  			pos = p
    62  			if p >= size {
    63  				if d > 2 {
    64  					*state = 0
    65  				} else {
    66  					*state = mask >> d
    67  				}
    68  				return pos
    69  			}
    70  			if d > 2 {
    71  				mask = 0
    72  			} else {
    73  				mask >>= d
    74  				if mask != 0 && (mask > 4 || mask == 3 || test86MSByte(data[p+uint(mask>>1)+1])) {
    75  					mask = (mask >> 1) | 4
    76  					pos++
    77  					continue
    78  				}
    79  			}
    80  		}
    81  
    82  		if test86MSByte(data[p+4]) {
    83  			v := (uint32(data[p+4]) << 24) + (uint32(data[p+3]) << 16) + (uint32(data[p+2]) << 8) + uint32(data[p+1])
    84  			cur := ip + uint32(pos)
    85  			pos += 5
    86  			if encoding {
    87  				v += cur
    88  			} else {
    89  				v -= cur
    90  			}
    91  			if mask != 0 {
    92  				sh := uint((mask & 6) << 2)
    93  				if test86MSByte(uint8(v >> sh)) {
    94  					v ^= (uint32(0x100) << sh) - 1
    95  					if encoding {
    96  						v += cur
    97  					} else {
    98  						v -= cur
    99  					}
   100  				}
   101  				mask = 0
   102  			}
   103  			data[p+1] = uint8(v)
   104  			data[p+2] = uint8(v >> 8)
   105  			data[p+3] = uint8(v >> 16)
   106  			data[p+4] = uint8(0 - ((v >> 24) & 1))
   107  		} else {
   108  			mask = (mask >> 1) | 4
   109  			pos++
   110  		}
   111  	}
   112  }
   113  
   114  func test86MSByte(b byte) bool {
   115  	return (b+1)&0xFE == 0
   116  }