Skip to content

Commit

Permalink
feat(action): add blob support into coreboot build
Browse files Browse the repository at this point in the history
  • Loading branch information
AtomicFS committed Sep 21, 2023
1 parent 63281e9 commit 72199ef
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 86 deletions.
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"addinivalue",
"addoption",
"autopep",
"cmds",
"commitlint",
"composefile",
"coreboot",
Expand Down
70 changes: 68 additions & 2 deletions action/recipes/coreboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,82 @@ package recipes

import (
"context"
"fmt"
"os"
"path/filepath"

"dagger.io/dagger"
"github.com/9elements/firmware-action/action/container"
"github.com/9elements/firmware-action/action/filesystem"
)

type ctxKey string

const (
blobsLocation = "3rdparty/blobs/mainboard/${MAINBOARD_DIR}"
blobsLocationCtxKey ctxKey = "BLOBS_LOCATION"
additionalCommandsCtxKey ctxKey = "ADDITIONAL_COMMANDS_TO_RUN"
)

// coreboot builds coreboot with all blobs and stuff
func coreboot(ctx context.Context, client *dagger.Client, common *commonOpts, dockerfileDirectoryPath string, opts *corebootOpts, artifacts *[]container.Artifacts) error {
// TODO: get blobs in place!
_ = opts
envVars := map[string]string{}

additionalCommandsToRun := [][]string{} // Used to modify defconfig inside container

// Handle blobs
// To keep using 'buildWithKernelBuildSystem' without breaking Linux kernel build
// there has to be a bit hackish solution ... :(
// Biggest problem is that most preparation should be done here, but the container
// is not available yet. So this solution exploits the context to pass over few things.
// Firstly copy all the blobs into temporary location, which will then be mounted into
// building container.
// Then use './util/scripts/config' script in coreboot repository to update configuration
// options for said blobs (this must run inside container).

// Collect all blobs into temporary directory
tmpDir, err := os.MkdirTemp("", "blobs")
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
for blob := range opts.blobs {
src := opts.blobs[blob].actionInput
dst := filepath.Join(
blobsLocation,
opts.blobs[blob].destinationFilename,
)
tmpDst := filepath.Join(
tmpDir,
opts.blobs[blob].destinationFilename,
)

// Copy into proper places
if opts.blobs[blob].isDirectory {
// Directory
if err := filesystem.CopyDir(src, tmpDst); err != nil {
return err
}
} else {
// File
if err := filesystem.CopyFile(src, tmpDst); err != nil {
return err
}
}

// Fix defconfig
additionalCommandsToRun = append(
additionalCommandsToRun,
// The '"sh", "-c"' is needed to get access to environment variables
// meaning replace '${MAINBOARD_DIR}' in 'blobsLocation' with actual path
[]string{"sh", "-c", fmt.Sprintf("./util/scripts/config --set-str %s \"%s\"", opts.blobs[blob].kconfigKey, dst)},
)
}

// Add additionalCommandsToRun into context
ctx = context.WithValue(ctx, additionalCommandsCtxKey, additionalCommandsToRun)
ctx = context.WithValue(ctx, blobsLocationCtxKey, tmpDir)

// Build
return buildWithKernelBuildSystem(ctx, client, common, dockerfileDirectoryPath, envVars, artifacts)
}
191 changes: 124 additions & 67 deletions action/recipes/coreboot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,78 +23,135 @@ func TestCoreboot(t *testing.T) {
t.Skip("skipping test in short mode")
}

const corebootVersion = "4.19"
ctx := context.Background()
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stdout))
assert.NoError(t, err)
defer client.Close()

// Prepare options
tmpDir := t.TempDir()
opts := map[string]string{
"target": "coreboot",
"sdk_version": fmt.Sprintf("coreboot_%s:main", corebootVersion),
"architecture": "x86",
"repo_path": filepath.Join(tmpDir, "coreboot"),
"defconfig_path": "seabios_defconfig",
"containerWorkDir": "/coreboot",
"GITHUB_WORKSPACE": "/coreboot",
"output": "output",
}
getFunc := func(key string) string {
return opts[key]
}
common, err := commonGetOpts(getFunc, getFunc)
assert.NoError(t, err)
corebootOpts := corebootOpts{}

// Change current working directory
pwd, err := os.Getwd()
defer os.Chdir(pwd) // nolint:errcheck
assert.NoError(t, err)
err = os.Chdir(tmpDir)
assert.NoError(t, err)

// Clone coreboot repo
cmd := exec.Command("git", "clone", "--branch", corebootVersion, "--depth", "1", "https://review.coreboot.org/coreboot")
err = cmd.Run()
assert.NoError(t, err)

// Copy over defconfig file into tmpDir
repoRootPath, err := filepath.Abs(filepath.Join(pwd, "../.."))
assert.NoError(t, err)
// common.repoPath = path to end user repository (in this case somewhere in /tmp)
// repoRootPath = path to our repository with this code (contains configuration files for testing)
err = filesystem.CopyFile(
filepath.Join(repoRootPath, fmt.Sprintf("tests/coreboot_%s/seabios.defconfig", corebootVersion)),
filepath.Join(tmpDir, common.defconfigPath),
)
assert.NoError(t, err)

// Artifacts
outputPath := filepath.Join(tmpDir, common.outputDir)
err = os.MkdirAll(outputPath, os.ModePerm)
assert.NoError(t, err)
artifacts := []container.Artifacts{
testCases := []struct {
name string
corebootVersion string
corebootOptions corebootOpts
cmds [][]string
wantErr error
}{
{
ContainerPath: filepath.Join(common.containerWorkDir, "build", "coreboot.rom"),
ContainerDir: false,
HostPath: outputPath,
HostDir: true,
name: "normal build for QEMU",
corebootVersion: "4.19",
corebootOptions: corebootOpts{},
wantErr: nil,
},
{
ContainerPath: filepath.Join(common.containerWorkDir, "defconfig"),
ContainerDir: false,
HostPath: outputPath,
HostDir: true,
name: "binary payload - file does not exists",
corebootVersion: "4.19",
corebootOptions: corebootOpts{
blobs: []blobDef{
{
actionInput: "my_payload",
destinationFilename: "payload",
kconfigKey: "CONFIG_PAYLOAD_FILE",
isDirectory: false,
},
},
},
wantErr: os.ErrNotExist,
},
{
name: "binary payload - file exists but empty",
corebootVersion: "4.19",
corebootOptions: corebootOpts{
blobs: []blobDef{
{
actionInput: "intel_me.bin",
destinationFilename: "me.bin",
kconfigKey: "CONFIG_ME_BIN_PATH",
isDirectory: false,
},
},
},
cmds: [][]string{
{"touch", "intel_me.bin"},
},
wantErr: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stdout))
assert.NoError(t, err)
defer client.Close()

// Prepare options
tmpDir := t.TempDir()
opts := map[string]string{
"target": "coreboot",
"sdk_version": fmt.Sprintf("coreboot_%s:main", tc.corebootVersion),
"architecture": "x86",
"repo_path": filepath.Join(tmpDir, "coreboot"),
"defconfig_path": "seabios_defconfig",
"containerWorkDir": "/coreboot",
"GITHUB_WORKSPACE": "/coreboot",
"output": "output",
}
getFunc := func(key string) string {
return opts[key]
}
common, err := commonGetOpts(getFunc, getFunc)
assert.NoError(t, err)
corebootOpts := tc.corebootOptions

// Change current working directory
pwd, err := os.Getwd()
defer os.Chdir(pwd) // nolint:errcheck
assert.NoError(t, err)
err = os.Chdir(tmpDir)
assert.NoError(t, err)

// Clone coreboot repo
cmd := exec.Command("git", "clone", "--branch", tc.corebootVersion, "--depth", "1", "https://review.coreboot.org/coreboot")
err = cmd.Run()
assert.NoError(t, err)

// Try to build coreboot
err = coreboot(ctx, client, &common, "", &corebootOpts, &artifacts)
assert.NoError(t, err)
// Copy over defconfig file into tmpDir
repoRootPath, err := filepath.Abs(filepath.Join(pwd, "../.."))
assert.NoError(t, err)
// common.repoPath = path to end user repository (in this case somewhere in /tmp)
// repoRootPath = path to our repository with this code (contains configuration files for testing)
err = filesystem.CopyFile(
filepath.Join(repoRootPath, fmt.Sprintf("tests/coreboot_%s/seabios.defconfig", tc.corebootVersion)),
filepath.Join(tmpDir, common.defconfigPath),
)
assert.NoError(t, err)

// Check artifacts
assert.ErrorIs(t, filesystem.CheckFileExists(filepath.Join(outputPath, "coreboot.rom")), os.ErrExist)
assert.ErrorIs(t, filesystem.CheckFileExists(filepath.Join(outputPath, "defconfig")), os.ErrExist)
// Artifacts
outputPath := filepath.Join(tmpDir, common.outputDir)
err = os.MkdirAll(outputPath, os.ModePerm)
assert.NoError(t, err)
artifacts := []container.Artifacts{
{
ContainerPath: filepath.Join(common.containerWorkDir, "build", "coreboot.rom"),
ContainerDir: false,
HostPath: outputPath,
HostDir: true,
},
{
ContainerPath: filepath.Join(common.containerWorkDir, "defconfig"),
ContainerDir: false,
HostPath: outputPath,
HostDir: true,
},
}

// Prep
for cmd := range tc.cmds {
err = exec.Command(tc.cmds[cmd][0], tc.cmds[cmd][1:]...).Run()
assert.NoError(t, err)
}
// Try to build coreboot
err = coreboot(ctx, client, &common, "", &corebootOpts, &artifacts)
assert.ErrorIs(t, err, tc.wantErr)

// Check artifacts
if tc.wantErr == nil {
assert.ErrorIs(t, filesystem.CheckFileExists(filepath.Join(outputPath, "coreboot.rom")), os.ErrExist)
assert.ErrorIs(t, filesystem.CheckFileExists(filepath.Join(outputPath, "defconfig")), os.ErrExist)
}
})
}
}
1 change: 0 additions & 1 deletion action/recipes/edk2.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (

// edk2 builds edk2
func edk2(ctx context.Context, client *dagger.Client, common *commonOpts, dockerfileDirectoryPath string, opts *edk2Opts, artifacts *[]container.Artifacts) error {
// TODO: get blobs in place!
envVars := map[string]string{
"WORKSPACE": common.containerWorkDir,
"EDK_TOOLS_PATH": "/tools/Edk2/BaseTools",
Expand Down
Loading

0 comments on commit 72199ef

Please sign in to comment.