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

Logo #119

Closed
wants to merge 5 commits into from
Closed

Logo #119

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
6 changes: 5 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ RUN microdnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=0 \
tar \
unzip \
util-linux \
flashrom \
python \
python-devel \
which && \
microdnf clean all && \
ln -s /usr/bin/microdnf /usr/bin/yum # since dell dsu expects yum
ln -s /usr/bin/microdnf /usr/bin/yum # since dell dsu expects yum && \
pip install uefi_firmware==v1.11


# Delete /tmp/* as we don't need those included in the image.
Expand Down
14 changes: 14 additions & 0 deletions actions/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ type TPMCollector interface {
TPMs(ctx context.Context) ([]*common.TPM, error)
}

// Checksum collectors

// FirmwareChecksumCollector defines an interface to collect firmware checksums
type FirmwareChecksumCollector interface {
UtilAttributeGetter
BIOSLogoChecksum(ctx context.Context) (sha256 [32]byte, err error)
}

// UEFIVarsCollector defines an interface to collect EFI variables
type UEFIVarsCollector interface {
UtilAttributeGetter
UEFIVariables(ctx context.Context) (keyValues map[string]string, err error)
}

// Updaters

// DriveUpdater defines an interface to update drive firmware
Expand Down
98 changes: 97 additions & 1 deletion actions/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package actions

import (
"context"
"fmt"
"runtime/debug"
"strings"

Expand All @@ -11,6 +12,7 @@ import (
"github.com/r3labs/diff/v2"
"golang.org/x/exp/slices"

"github.com/metal-toolbox/ironlib/firmware"
"github.com/metal-toolbox/ironlib/model"
"github.com/metal-toolbox/ironlib/utils"
)
Expand Down Expand Up @@ -53,6 +55,8 @@ type Collectors struct {
CPLDCollector
BIOSCollector
TPMCollector
FirmwareChecksumCollector
UEFIVarsCollector
StorageControllerCollectors []StorageControllerCollector
DriveCollectors []DriveCollector
DriveCapabilitiesCollectors []DriveCapabilityCollector
Expand All @@ -68,7 +72,9 @@ func (c *Collectors) Empty() bool {
c.TPMCollector == nil &&
len(c.StorageControllerCollectors) == 0 &&
len(c.DriveCollectors) == 0 &&
len(c.DriveCapabilitiesCollectors) == 0 {
len(c.DriveCapabilitiesCollectors) == 0 &&
c.UEFIVarsCollector == nil &&
c.FirmwareChecksumCollector == nil {
return true
}

Expand Down Expand Up @@ -139,6 +145,9 @@ func NewInventoryCollectorAction(options ...Option) *InventoryCollectorAction {
utils.NewHdparmCmd(a.trace),
utils.NewNvmeCmd(a.trace),
},
FirmwareChecksumCollector: firmware.NewChecksumCollector(a.trace),
// implement uefi vars collector and plug in here
// UEFIVarsCollector: ,
}
}

Expand Down Expand Up @@ -226,6 +235,18 @@ func (a *InventoryCollectorAction) Collect(ctx context.Context, device *common.D
return errors.Wrap(err, "error retrieving TPM inventory")
}

// Collect Firmware checksums
err = a.CollectFirmwareChecksums(ctx)
if err != nil && a.failOnError {
return errors.Wrap(err, "error retrieving Firmware checksums")
}

// Collect UEFI variables
err = a.CollectUEFIVariables(ctx)
if err != nil && a.failOnError {
return errors.Wrap(err, "error retrieving UEFI variables")
}

// Update StorageControllerCollectors based on controller vendor attributes
if a.dynamicCollection {
for _, sc := range a.device.StorageControllers {
Expand Down Expand Up @@ -650,6 +671,81 @@ func (a *InventoryCollectorAction) CollectTPMs(ctx context.Context) error {
return nil
}

// CollectFirmwareChecksums executes the Firmware checksum collector and updates the component metadata.
func (a *InventoryCollectorAction) CollectFirmwareChecksums(ctx context.Context) error {
// nolint:errcheck // deferred method catches a panic, error check not required.
defer func() error {
if r := recover(); r != nil && a.failOnError {
return errors.Wrap(ErrPanic, string(debug.Stack()))
}

return nil
}()

if a.collectors.FirmwareChecksumCollector == nil {
return nil
}

// skip collector if its been disabled
collectorKind, _, _ := a.collectors.FirmwareChecksumCollector.Attributes()
if slices.Contains(a.disabledCollectorUtilities, collectorKind) {
return nil
}

sum, err := a.collectors.BIOSLogoChecksum(ctx)
if err != nil {
return err
}

if len(sum) == 0 || a.device.BIOS == nil {
return nil
}

// not sure if this is the ideal way to cover the byte array
// maybe the interface method should return a checksum string instead?
a.device.BIOS.Metadata["bios-checksum"] = fmt.Sprintf("%s", sum)

return nil
}

// CollectUEFIVariables executes the UEFI variable collector and stores them on the device object
func (a *InventoryCollectorAction) CollectUEFIVariables(ctx context.Context) error {
// nolint:errcheck // deferred method catches a panic, error check not required.
defer func() error {
if r := recover(); r != nil && a.failOnError {
return errors.Wrap(ErrPanic, string(debug.Stack()))
}

return nil
}()

if a.collectors.UEFIVarsCollector == nil {
return nil
}

// skip collector if its been disabled
collectorKind, _, _ := a.collectors.UEFIVarsCollector.Attributes()
if slices.Contains(a.disabledCollectorUtilities, collectorKind) {
return nil
}

keyValues, err := a.collectors.UEFIVariables(ctx)
if err != nil {
return err
}

if len(keyValues) == 0 || a.device.BIOS == nil {
return nil
}

for k, v := range keyValues {
// do we want a prefix?
a.device.Metadata["EFI_VAR-"+k] = v
}

return nil
}

// CollectStorageControllers executes the StorageControllers collectors and updates device storage controller data
//
// nolint:gocyclo // this is fine for now
Expand Down
32 changes: 32 additions & 0 deletions firmware/extract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package firmware

import (
"context"

"github.com/metal-toolbox/ironlib/model"
)

// ChecksumCollector implements the
type ChecksumCollector struct {
// logger (?)
}

func NewChecksumCollector(trace bool) *ChecksumCollector {
return &ChecksumCollector{}
}

// Attributes implements the actions.UtilAttributeGetter interface
//
// this is implemented to verify both executables are available
// but can be excluded if considered its not required, since the executables should be available through the image.
func (c *ChecksumCollector) Attributes() (utilName model.CollectorUtility, absolutePath string, err error) {
return
}

func (f *ChecksumCollector) BIOSLogoChecksum(ctx context.Context) (sha256 [32]byte, err error) {

Check warning on line 26 in firmware/extract.go

View workflow job for this annotation

GitHub Actions / lint-test

receiver-naming: receiver name f should be consistent with previous receiver name c for ChecksumCollector (revive)

Check warning on line 26 in firmware/extract.go

View workflow job for this annotation

GitHub Actions / lint-test

receiver-naming: receiver name f should be consistent with previous receiver name c for ChecksumCollector (revive)
// dump bios with flashrom util
// extract logo with uefi_firmware_parser util
// return sha256sum of logo

return [32]byte{}, nil
}
56 changes: 56 additions & 0 deletions utils/flashrom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package utils

import (
"context"
"os"

"github.com/metal-toolbox/ironlib/model"
)

const (
EnvFlashromUtility = "IRONLIB_UTIL_FLASHROM"
)

type Flashrom struct {
Executor Executor
}

// Return a new flashrom executor
func NewFlashromCmd(trace bool) *Flashrom {
utility := "flashrom"

// lookup env var for util
if eVar := os.Getenv(EnvFlashromUtility); eVar != "" {
utility = eVar
}

e := NewExecutor(utility)
e.SetEnv([]string{"LC_ALL=C.UTF-8"})

if !trace {
e.SetQuiet()
}

return &Flashrom{Executor: e}
}

// Attributes implements the actions.UtilAttributeGetter interface
func (f *Flashrom) Attributes() (utilName model.CollectorUtility, absolutePath string, err error) {
// Call CheckExecutable first so that the Executable CmdPath is resolved.
er := f.Executor.CheckExecutable()

return "flashrom", f.Executor.CmdPath(), er
}

// ExtractBIOSImage writes the BIOS image to the given file system path.
func (f *Flashrom) ExtractBIOSImage(ctx context.Context, path string) error {
// flashrom -p internal --ifd -i bios -r /tmp/bios_region.img
f.Executor.SetArgs([]string{"-p", "internal", "--ifd", "-i", "bios", "-r", path})

_, err := f.Executor.ExecWithContext(ctx)
if err != nil {
return err
}

return nil
}
68 changes: 68 additions & 0 deletions utils/uefi_firmware_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package utils

import (
"context"
"os"

"github.com/metal-toolbox/ironlib/model"
)

// TODO: for a future point in time
// The fiano library is in Go and could replace the code if its capable of extracting the Logo bmp image
// https://github.com/linuxboot/fiano

const (
EnvUefiFirmwareParserUtility = "IRONLIB_UTIL_UTIL_UEFI_FIRMWARE_PARSER"
)

type UefiFirmwareParser struct {
Executor Executor
}

// Return a new UefiFirmwareParser executor
func NewUefiFirmwareParserCmd(trace bool) *UefiFirmwareParser {
utility := "uefi-firmware-parser"

// lookup env var for util
if eVar := os.Getenv(EnvUefiFirmwareParserUtility); eVar != "" {
utility = eVar
}

e := NewExecutor(utility)
e.SetEnv([]string{"LC_ALL=C.UTF-8"})

if !trace {
e.SetQuiet()
}

return &UefiFirmwareParser{Executor: e}
}

// Attributes implements the actions.UtilAttributeGetter interface
func (u *UefiFirmwareParser) Attributes() (utilName model.CollectorUtility, absolutePath string, err error) {
// Call CheckExecutable first so that the Executable CmdPath is resolved.
er := u.Executor.CheckExecutable()

return "uefi-firmware-parser", u.Executor.CmdPath(), er
}

// ExtractLogoBMP extracts the Logo BMP image.
func (u *UefiFirmwareParser) ExtractLogoBMP(ctx context.Context, path string) error {

// mkdir dump && uefi-firmware-parser -b bios_region.img -o dump -e
//
// # list out GUIDs in firmware
// uefi-firmware-parser -b bios_region.img > parsed_regions
//
// # locate the logo
// grep -i bmp parsed_regions
// File 349: 7bb28b99-61bb-11d5-9a5d-0090273fc14d (EFI_DEFAULT_BMP_LOGO_GUID) type 0x02, attr 0x00, state 0x07, size 0x13a2b (80427 bytes), (freeform)
//
// # find the section raw dump identified by the GUID
// find ./ | grep 7bb28b99-61bb-11d5-9a5d-0090273fc14d | grep raw
// ./dump/volume-23658496/file-7bb28b99-61bb-11d5-9a5d-0090273fc14d/section0/section0.raw
//
// mv ./dump/volume-23658496/file-7bb28b99-61bb-11d5-9a5d-0090273fc14d/section0/section0.raw /tmp/logo.bmp

return nil
}
3 changes: 3 additions & 0 deletions utils/uefi_vars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package utils

// UEFIVarsCollector implementation goes here
Loading