m/p/efivarfs: allow multiple paths in LoadOption
The FilePathList in an EFI LOAD_OPTION technically allows multiple
device paths separated by end of path markers. These additional paths
beyond the first one do not have any standardized function, but some
EFI vendors use them to store data. Currently we fail to parse them
because they contain more than one end of path marker. Fix this by
adding an ExtraPaths field to LoadOption and implementing marshalling/
unmarshalling for it. I chose to use a separate field in the Go
struct because these additional paths do not have the same functionality
as the first one (they are not bootable).
Change-Id: I96df17915966a973a7553fe9864a0b3e6f6b61e8
Reviewed-on: https://review.monogon.dev/c/monogon/+/2037
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/pkg/efivarfs/boot.go b/metropolis/pkg/efivarfs/boot.go
index 71324fe..8fe1a55 100644
--- a/metropolis/pkg/efivarfs/boot.go
+++ b/metropolis/pkg/efivarfs/boot.go
@@ -64,6 +64,9 @@
// Path to the UEFI PE executable to execute when this load option is being
// loaded.
FilePath DevicePath
+ // ExtraPaths contains additional device paths with vendor-specific
+ // behavior. Can generally be left empty.
+ ExtraPaths []DevicePath
// OptionalData gets passed as an argument to the executed PE executable.
// If zero-length a NULL value is passed to the executable.
OptionalData []byte
@@ -85,8 +88,15 @@
if err != nil {
return nil, fmt.Errorf("failed marshalling FilePath: %w", err)
}
+ for _, ep := range e.ExtraPaths {
+ epRaw, err := ep.Marshal()
+ if err != nil {
+ return nil, fmt.Errorf("failed marshalling ExtraPath: %w", err)
+ }
+ filePathRaw = append(filePathRaw, epRaw...)
+ }
if len(filePathRaw) > math.MaxUint16 {
- return nil, fmt.Errorf("failed marshalling FilePath: value too big (%d)", len(filePathRaw))
+ return nil, fmt.Errorf("failed marshalling FilePath/ExtraPath: value too big (%d)", len(filePathRaw))
}
data = append16(data, uint16(len(filePathRaw)))
if strings.IndexByte(e.Description, 0x00) != -1 {
@@ -131,10 +141,19 @@
return nil, fmt.Errorf("declared length of FilePath (%d) overruns available data (%d)", lenPath, len(data)-descriptionEnd)
}
filePathData := data[descriptionEnd : descriptionEnd+int(lenPath)]
- opt.FilePath, err = UnmarshalDevicePath(filePathData)
+ opt.FilePath, filePathData, err = UnmarshalDevicePath(filePathData)
if err != nil {
return nil, fmt.Errorf("failed unmarshaling FilePath: %w", err)
}
+ for len(filePathData) > 0 {
+ var extraPath DevicePath
+ extraPath, filePathData, err = UnmarshalDevicePath(filePathData)
+ if err != nil {
+ return nil, fmt.Errorf("failed unmarshaling ExtraPath: %w", err)
+ }
+ opt.ExtraPaths = append(opt.ExtraPaths, extraPath)
+ }
+
if descriptionEnd+int(lenPath) < len(data) {
opt.OptionalData = data[descriptionEnd+int(lenPath):]
}