Skip to content

Commit

Permalink
[pkg/compression] Support zlib
Browse files Browse the repository at this point in the history
zlib compression is used in some AMI-based firmwares for AMD CPUs.
Implement the Compressor interface and unit-tests for it.

Information about the ZLIB section header taken from UEFITool which
added support in the A63 release.[^1][^2]

[^1]: https://github.com/LongSoft/UEFITool/releases/tag/A63
[^2]: LongSoft/UEFITool@8d5659c92b08

Signed-off-by: Julian Prein <[email protected]>
  • Loading branch information
druckdev authored and orangecms committed Jun 1, 2023
1 parent 78260ce commit 596cbff
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 0 deletions.
3 changes: 3 additions & 0 deletions pkg/compression/compression.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Compressor interface {
var (
LZMAGUID = *guid.MustParse("EE4E5898-3914-4259-9D6E-DC7BD79403CF")
LZMAX86GUID = *guid.MustParse("D42AE6BD-1352-4BFB-909A-CA72A6EAE889")
ZLIBGUID = *guid.MustParse("CE3233F5-2CD6-4D87-9152-4A238BB6D1C4")
)

// CompressorFromGUID returns a Compressor for the corresponding GUIDed Section.
Expand All @@ -51,6 +52,8 @@ func CompressorFromGUID(guid *guid.GUID) Compressor {
// into xz. It does not make much difference because
// the x86 filter is not the bottleneck.
return &LZMAX86{lzma}
case ZLIBGUID:
return &ZLIB{}
}
return nil
}
13 changes: 13 additions & 0 deletions pkg/compression/compression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ var tests = []struct {
decodedFilename: "testdata/random.bin",
compressor: &LZMAX86{&SystemLZMA{"xz"}},
},
{
name: "random data ZLIB",
encodedFilename: "testdata/random.bin.zlib",
decodedFilename: "testdata/random.bin",
compressor: &ZLIB{},
},
}

func TestEncodeDecode(t *testing.T) {
Expand Down Expand Up @@ -122,6 +128,13 @@ func TestCompressorFromGUID(t *testing.T) {
encodedFilename: "testdata/random.bin.lzma86",
decodedFilename: "testdata/random.bin",
},
{
name: "zlib",
guid: &ZLIBGUID,
expected: &ZLIB{},
encodedFilename: "testdata/random.bin.zlib",
decodedFilename: "testdata/random.bin",
},
}
for _, tt := range compressors {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Binary file added pkg/compression/testdata/random.bin.zlib
Binary file not shown.
83 changes: 83 additions & 0 deletions pkg/compression/zlib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2023 the LinuxBoot Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package compression

import (
"bytes"
"compress/zlib"
"encoding/binary"
"errors"
"io"
)

const (
zlibCompressionLevel = 9
zlibSectionHeaderSize = 256
zlibSizeOffset = 20
)

// ZLIB implements Compressor and uses the zlib package from the standard
// library
type ZLIB struct{}

// Name returns the type of compression employed.
func (c *ZLIB) Name() string {
return "ZLIB"
}

// Decode decodes a byte slice of ZLIB data.
func (c *ZLIB) Decode(encodedData []byte) ([]byte, error) {
if len(encodedData) < 256 {
return nil, errors.New("Zlib.Decode: missing section header")
}

// Check size in ZLIB section header
size := binary.LittleEndian.Uint32(
encodedData[zlibSizeOffset : zlibSizeOffset+4],
)
if size != uint32(len(encodedData)-zlibSectionHeaderSize) {
return nil, errors.New("ZLIB.Decode: size mismatch")
}

// Remove section header
r, err := zlib.NewReader(
bytes.NewBuffer(encodedData[zlibSectionHeaderSize:]),
)
if err != nil {
return nil, err
}

decodedData, err := io.ReadAll(r)
r.Close()
if err != nil {
return nil, err
}

return decodedData, nil
}

// Encode encodes a byte slice with ZLIB.
func (c *ZLIB) Encode(decodedData []byte) ([]byte, error) {
var encodedData bytes.Buffer

w, err := zlib.NewWriterLevel(&encodedData, zlibCompressionLevel)
if err != nil {
return nil, err
}

_, err = w.Write(decodedData)
w.Close()
if err != nil {
return nil, err
}

// Add ZLIB section header containing the compressed size and zero padding.
zlib_header := make([]byte, zlibSectionHeaderSize)
binary.LittleEndian.PutUint32(
zlib_header[zlibSizeOffset:],
uint32(len(encodedData.Bytes())),
)
return append(zlib_header, encodedData.Bytes()[:]...), nil
}

0 comments on commit 596cbff

Please sign in to comment.