Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/compression: Implement system brotli compression #418

Merged
merged 3 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/compression/compression.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/linuxboot/fiano/pkg/guid"
)

var brotliPath = flag.String("brotliPath", "brotli", "Path to system brotli command used for brotli encoding.")
var xzPath = flag.String("xzPath", "xz", "Path to system xz command used for lzma encoding. If unset, an internal lzma implementation is used.")

// Compressor defines a single compression scheme (such as LZMA).
Expand All @@ -29,6 +30,7 @@ type Compressor interface {

// Well-known GUIDs for GUIDed sections containing compressed data.
var (
BROTLIGUID = *guid.MustParse("3D532050-5CDA-4FD0-879E-0F7F630D5AFB")
LZMAGUID = *guid.MustParse("EE4E5898-3914-4259-9D6E-DC7BD79403CF")
LZMAX86GUID = *guid.MustParse("D42AE6BD-1352-4BFB-909A-CA72A6EAE889")
ZLIBGUID = *guid.MustParse("CE3233F5-2CD6-4D87-9152-4A238BB6D1C4")
Expand All @@ -45,6 +47,8 @@ func CompressorFromGUID(guid *guid.GUID) Compressor {
lzma = &LZMA{}
}
switch *guid {
case BROTLIGUID:
return &SystemBROTLI{*brotliPath}
case LZMAGUID:
return lzma
case LZMAX86GUID:
Expand Down
64 changes: 64 additions & 0 deletions pkg/compression/systembrotli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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"
"encoding/binary"
"os/exec"
)

// SystemBROTLI implements Compression and calls out to the system's compressor
type SystemBROTLI struct {
brotliPath string
}

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

// Decode decodes a byte slice of BROTLI data.
func (c *SystemBROTLI) Decode(encodedData []byte) ([]byte, error) {
// The start of the brotli section contains an 8 byte header describing
// the final uncompressed size. The real data starts at 0x10

cmd := exec.Command(c.brotliPath, "--stdout", "-d")
cmd.Stdin = bytes.NewBuffer(encodedData[0x10:])
orangecms marked this conversation as resolved.
Show resolved Hide resolved

decodedData, err := cmd.Output()
if err != nil {
return nil, err
}

return decodedData, nil
}

// Encode encodes a byte slice with BROTLI.
func (c *SystemBROTLI) Encode(decodedData []byte) ([]byte, error) {
cmd := exec.Command(c.brotliPath, "--stdout", "-q", "9")
cmd.Stdin = bytes.NewBuffer(decodedData)

encodedData, err := cmd.Output()
if err != nil {
return nil, err
}

// Generate the size of the decoded data for the header
decodedSize := &bytes.Buffer{}
if err := binary.Write(decodedSize, binary.LittleEndian, uint64(len(decodedData))); err != nil {
return nil, err
}

// This seems to be the buffer size needed by the UEFI decompressor
// 0x03000000 should suffice. The EDK2 base tools generates this header
// using Brotli internals. This needs to be tuned somehow
scratchBufferSize := []byte{0x00, 0x00, 0x00, 0x03, 0, 0, 0, 0}
header := append(decodedSize.Bytes(), scratchBufferSize...)

encodedData = append(header, encodedData...)

return encodedData, nil
}
2 changes: 2 additions & 0 deletions pkg/uefi/section.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ func CreateSection(t SectionType, buf []byte, encap []Firmware, g *guid.GUID) (*
guidDefHeader := &SectionGUIDDefined{}
guidDefHeader.GUID = *g
switch *g {
case compression.BROTLIGUID:
guidDefHeader.Compression = "BROTLI"
case compression.LZMAGUID:
guidDefHeader.Compression = "LZMA"
case compression.LZMAX86GUID:
Expand Down