m/p/efivarfs: refactor

This accomplishes three things:

First, split out the variable access layer from the rest of the code.
This cleans up the attribute handling, which is now done centrally as
well as making the high-level functions very short and clean. They now
also return better errors.

Second this introduces proper types for LoadOption, which can now also
be unmarshaled which was a requirement for A/B updates. This required
implementation of EFI's DevicePath structure.
While refactoring the higher-level functions for this, this also
fixes a bug where the variable index (the 4 hex nibbles at the end) were
improperly generated as lowercase hex.

Third, this adds new high-level functions for interacting with more
boot-related variables needed for the A/B effort.

Change-Id: I53490fa4898a5e7a5498ecc05a9078bd2d66c26e
Reviewed-on: https://review.monogon.dev/c/monogon/+/1855
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/build/mkimage/osimage/osimage.go b/metropolis/node/build/mkimage/osimage/osimage.go
index d730e68..97fa7c0 100644
--- a/metropolis/node/build/mkimage/osimage/osimage.go
+++ b/metropolis/node/build/mkimage/osimage/osimage.go
@@ -302,7 +302,7 @@
 // argument. In case a regular file already exists at params.OutputPath,
 // the function will fail. It returns nil on success or an error, if one
 // did occur.
-func Create(params *Params) (*efivarfs.BootEntry, error) {
+func Create(params *Params) (*efivarfs.LoadOption, error) {
 	// Validate each parameter before use.
 	if params.OutputPath == "" {
 		return nil, fmt.Errorf("image output path must be set")
@@ -377,13 +377,19 @@
 	if err != nil {
 		return nil, fmt.Errorf("couldn't parse the GPT GUID: %w", err)
 	}
-	be := efivarfs.BootEntry{
-		Description:     "Metropolis",
-		Path:            EFIPayloadPath,
-		PartitionGUID:   guid,
-		PartitionNumber: 1,
-		PartitionStart:  esp.Start,
-		PartitionSize:   esp.End - esp.Start + 1,
+	be := efivarfs.LoadOption{
+		Description: "Metropolis",
+		FilePath: efivarfs.DevicePath{
+			&efivarfs.HardDrivePath{
+				PartitionNumber:     1,
+				PartitionStartBlock: esp.Start,
+				PartitionSizeBlocks: esp.End - esp.Start + 1,
+				PartitionMatch: efivarfs.PartitionGPT{
+					PartitionUUID: guid,
+				},
+			},
+			efivarfs.FilePath(EFIPayloadPath),
+		},
 	}
 	// Close the image and return the EFI boot entry.
 	if err := diskImg.File.Close(); err != nil {