2015-05-15 09:33:27 -04:00
|
|
|
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
|
|
2015-03-26 10:05:27 +01:00
|
|
|
/*
|
boot,bootloader: add support for shim fallback and setting EFI boot variables on install (#13511)
* boot: added function to set EFI variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: renamed trustedShimFallbackBinary to seedShimPath
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: refactored setting EFI boot variables at install
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: adjusted variable names and fixed variable initialization
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: improve setting Boot#### EFI variable
Notably, splits off the process of reading a Boot#### variable and
extracting its DevicePath into its own function `readBootVariable` which
can be mocked and otherwise simplifies the `setBootNumberVariable`
function.
Also, fixes behavior around the final BootFFFF variable. Previously, it
was not possible to select the BootFFFF variable if it was unused, due
to overflow concerns on uint16. Now, the behavior around BootFFFF is
identical to that of any other boot variable, by using an int internally
instead of uint16, which also allows a more robust check for whether
there were no matching variables.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: added unit tests for setting EFI Boot#### variable
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: refactored setting EFI boot variables
Rewrote EFI boot variable functions to more closely match the behavior
of shim fallback: https://github.com/rhboot/shim/blob/main/fallback.c
In particular, the following have changed:
1. Existing Boot#### variables must fully match the new load option to
be considered a match. In particular, the load option attributes,
label, and device path must all be byte-for-byte identical.
Previously, only the device paths were compared.
2. Matching Boot#### variables are no longer overwritten. Since the
variable data must now byte-for-byte match the new load option, there
is no need to overwrite the existing variable.
3. Since existing Boot#### variables are no longer overwritten, the
variable attributes are no longer checked for those variables.
Instead, it is assumed that the Boot#### variable attributes are
viable for it to be used as a boot option. This matches the behavior
of `rhboot/shim/fallback.c`, for better or for worse.
4. When modifying the BootOrder variable, boot option numbers are no
longer pruned if there is no matching Boot#### variable.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: introduce UefiBootloader to build EFI load options
Previously, the path of the shim binary relative to the EFI partition
was passed into `SetEfiBootVariables`. However, different bootloaders
may wish to set up `OptionalData` in the load option.
Additionally, not all `TrustedAssetBootloaders` will attempt to set
EFI boot variables, and not all bootloaders which should set EFI boot
variables necessarily support secure boot. Thus, these should be
decoupled.
This commit adds a new `UefiBootloader` interface with the
`ConstructShimEfiLoadOption` method, which builds an EFI load option
from the shim path for the given bootloader.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: fixed linting errors and improved EFI boot variable test clarity
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: improved unit test for grub EFI load option creation
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: set EFI boot variables in `MakeRunnableSystem`
Previously, attempted to set boot variables in
`MakeRecoverySystemBootable`, which is called by `MakeBootableImage`,
which is called when building the image file, rather than during install
mode.
`MakeRunnableSystem` is called on first boot during install mode, and
thus should be responsible for setting EFI boot variables.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: use seed bootloader when setting EFI variables
In install mode, the bootloader located in ubuntu-seed should be used
when setting the EFI boot variables. Previously, the bootloader in
ubuntu-boot was accidentally re-used.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: added simple test to execute setefibootvar.go code
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fixed standalone set EFI vars code test to work with different layouts
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: moved simple setefibootvar.go check to nested test
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: added check for idempotence when setting EFI boot variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: adjust comments, organization, and add TODO
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: fix setting EFI boot variables
Make function to search for EFI asset device path and construct load
option common so each UefiBootloader does not have to re-implement it.
Instead, the bootloader returns the description, asset file path, and
optional data, which can then be used to create the EFI load option.
Also, in `makeRunnableSystem`, the bootloader in ubuntu-seed must have
`NoSlashBoot` in order to correctly find the grub.cfg file and thus the
grub bootloader. This commit fixes this bug, and refactors a bit to
account for the changes in responsibilities between the bootloader and
the setefibootvars.go code.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: fixed grub EFI load option test with tmp rootdir
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
go.mod: move golang.org/x/text import next to other golang.org/x/ imports
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: adjust opts to look for recovery bootloader when setting EFI variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: do not overwrite BootOrder if unchanged, and unexport EFI variable helper functions
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: unexport `setEfiBootOrderVariable`
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: move code to detect bootloader and set EFI variables accordingly into dedicated function
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: unexport `setUbuntuSeedEfiBootVariables` and accompanying error
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: ensure nil optionalData for EFI variable is equivalent to 0-length slice
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: handle empty boot order and other boot var improvements
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: make setefibootvars functions linux-only
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* tests: add nested spread test for setting EFI vars
The test checks that EFI boot variables exist for the following:
1. A Boot#### variable pointing to the shim file path.
2. A BootOrder variable with the #### from the above Boot#### as first.
Since the layout of EFI assets is dependent on the gadget snap, the test
downloads and unpacks the gadget, then modifies the contents so that one
variant has the shim and grub binaries in `EFI/boot/` and another
variant has the shim and grub binaries in `EFI/ubuntu/` and the fallback
binary in `EFI/boot/`.
After building a core image around that modified gadget, the VM is
booted and the test checks that the EFI variables are set correctly.
Then, the test modifies the gadget to match the other variant's initial
layout, and then installs the newly modified gadget. This should trigger
re-setting EFI boot variables as well.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fix problems in spread test for setting EFI boot variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: disabled TPM on EFI boot vars test and separated gadget script
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fixed EFI vars test to use correct toolbox and include all EFI assets
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: modify-gadget.sh re-use existing gadget so edition is incremented
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fix mangled EFI var search string and other improvements
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: polish tests for setting EFI boot variables
Notably, allow tests/nested/core/core20-set-efi-boot-variables to run on
arm64 as well as amd64, simplify setefivars.go to search for multiple
assets on multiple architectures, and allow
tests/nested/manual/core20-set-efi-boot-vars to run on any ubuntu-2*.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* bootloader/grub.go: only consider new shim asset in boot entry for now
* tests/nested/core/core20-set-efi-boot-variables: fix details
* boot: update uefi variables on gadget update
* tests/nested/manual/core20-set-efi-boot-vars: work-around file not deleted
* tests/nested/manual/core20-set-efi-boot-vars: use fb.efi like other tests
* tests/nested/manual/core20-set-efi-boot-vars: drop use of toolbox snap
* tests/nested/manual/core20-set-efi-boot-vars: drop work-around for not deleted files
* tests/nested/manual/core20-set-efi-boot-vars: verify install does add a boot entry
* tests/nested/manual/core20-set-efi-boot-vars: run only on version that have UC
* tests/nested/manual/core20-set-efi-boot-vars: obey GADGET_CHANNEL
* tests/nested/manual/core20-set-efi-boot-vars: move get_boot_entry.py to libs
* tests/nested/manual/core20-set-efi-boot-vars: factorize copy of variables
... so we can reuse the script in other tests
* tests/nested/core/core20-set-efi-boot-variables: stop using toolbox snap
* tests/nested/core/core20-set-efi-boot-variables: only run on versions with UC available
* overlord/devicestate: test using EfiLoadOptionParameters
* boot: test that variables are set
* boot: test observers' UpdateBootEntry
* tests/nested/manual/core20-set-efi-boot-vars: also test without secure boot
* many: use trusted install observer when UEFI variables are supported
* boot/makebootable.go: rename sealer to observer
* boot/grub.go: fix function name in doc
* cmd/snap-bootstrap: verify that ObserveExistingTrustedRecoveryAssets is called
* boot: add tests for SetEfiBootVariables
* many: comment on calls to ObserveExistingTrustedRecoveryAssets
---------
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Co-authored-by: Oliver Calder <oliver.calder@canonical.com>
2024-06-03 10:03:50 +02:00
|
|
|
* Copyright (C) 2014-2024 Canonical Ltd
|
2015-03-26 10:05:27 +01:00
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License version 3 as
|
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2015-01-21 16:02:46 +00:00
|
|
|
|
2019-03-19 12:00:37 +01:00
|
|
|
package bootloader
|
2014-12-19 21:10:08 +00:00
|
|
|
|
2016-01-11 12:15:27 +01:00
|
|
|
import (
|
|
|
|
|
"errors"
|
2016-07-14 16:43:43 +02:00
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
2020-06-24 16:16:08 +02:00
|
|
|
"github.com/snapcore/snapd/bootloader/assets"
|
2019-09-05 19:32:04 +02:00
|
|
|
"github.com/snapcore/snapd/dirs"
|
2016-07-14 16:43:43 +02:00
|
|
|
"github.com/snapcore/snapd/osutil"
|
2023-07-17 13:48:40 +02:00
|
|
|
"github.com/snapcore/snapd/osutil/kcmdline"
|
2019-04-01 09:56:46 +01:00
|
|
|
"github.com/snapcore/snapd/snap"
|
2016-01-11 12:15:27 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2020-02-24 16:20:22 -06:00
|
|
|
// ErrBootloader is returned if the bootloader can not be determined.
|
2016-01-13 15:41:09 +01:00
|
|
|
ErrBootloader = errors.New("cannot determine bootloader")
|
2020-02-24 16:24:25 -06:00
|
|
|
|
2020-02-28 11:53:21 +01:00
|
|
|
// ErrNoTryKernelRef is returned if the bootloader finds no enabled
|
2020-02-24 16:24:25 -06:00
|
|
|
// try-kernel.
|
2020-02-28 11:53:21 +01:00
|
|
|
ErrNoTryKernelRef = errors.New("no try-kernel referenced")
|
boot,bootloader: add support for shim fallback and setting EFI boot variables on install (#13511)
* boot: added function to set EFI variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: renamed trustedShimFallbackBinary to seedShimPath
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: refactored setting EFI boot variables at install
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: adjusted variable names and fixed variable initialization
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: improve setting Boot#### EFI variable
Notably, splits off the process of reading a Boot#### variable and
extracting its DevicePath into its own function `readBootVariable` which
can be mocked and otherwise simplifies the `setBootNumberVariable`
function.
Also, fixes behavior around the final BootFFFF variable. Previously, it
was not possible to select the BootFFFF variable if it was unused, due
to overflow concerns on uint16. Now, the behavior around BootFFFF is
identical to that of any other boot variable, by using an int internally
instead of uint16, which also allows a more robust check for whether
there were no matching variables.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: added unit tests for setting EFI Boot#### variable
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: refactored setting EFI boot variables
Rewrote EFI boot variable functions to more closely match the behavior
of shim fallback: https://github.com/rhboot/shim/blob/main/fallback.c
In particular, the following have changed:
1. Existing Boot#### variables must fully match the new load option to
be considered a match. In particular, the load option attributes,
label, and device path must all be byte-for-byte identical.
Previously, only the device paths were compared.
2. Matching Boot#### variables are no longer overwritten. Since the
variable data must now byte-for-byte match the new load option, there
is no need to overwrite the existing variable.
3. Since existing Boot#### variables are no longer overwritten, the
variable attributes are no longer checked for those variables.
Instead, it is assumed that the Boot#### variable attributes are
viable for it to be used as a boot option. This matches the behavior
of `rhboot/shim/fallback.c`, for better or for worse.
4. When modifying the BootOrder variable, boot option numbers are no
longer pruned if there is no matching Boot#### variable.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: introduce UefiBootloader to build EFI load options
Previously, the path of the shim binary relative to the EFI partition
was passed into `SetEfiBootVariables`. However, different bootloaders
may wish to set up `OptionalData` in the load option.
Additionally, not all `TrustedAssetBootloaders` will attempt to set
EFI boot variables, and not all bootloaders which should set EFI boot
variables necessarily support secure boot. Thus, these should be
decoupled.
This commit adds a new `UefiBootloader` interface with the
`ConstructShimEfiLoadOption` method, which builds an EFI load option
from the shim path for the given bootloader.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: fixed linting errors and improved EFI boot variable test clarity
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: improved unit test for grub EFI load option creation
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: set EFI boot variables in `MakeRunnableSystem`
Previously, attempted to set boot variables in
`MakeRecoverySystemBootable`, which is called by `MakeBootableImage`,
which is called when building the image file, rather than during install
mode.
`MakeRunnableSystem` is called on first boot during install mode, and
thus should be responsible for setting EFI boot variables.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: use seed bootloader when setting EFI variables
In install mode, the bootloader located in ubuntu-seed should be used
when setting the EFI boot variables. Previously, the bootloader in
ubuntu-boot was accidentally re-used.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: added simple test to execute setefibootvar.go code
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fixed standalone set EFI vars code test to work with different layouts
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: moved simple setefibootvar.go check to nested test
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: added check for idempotence when setting EFI boot variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: adjust comments, organization, and add TODO
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: fix setting EFI boot variables
Make function to search for EFI asset device path and construct load
option common so each UefiBootloader does not have to re-implement it.
Instead, the bootloader returns the description, asset file path, and
optional data, which can then be used to create the EFI load option.
Also, in `makeRunnableSystem`, the bootloader in ubuntu-seed must have
`NoSlashBoot` in order to correctly find the grub.cfg file and thus the
grub bootloader. This commit fixes this bug, and refactors a bit to
account for the changes in responsibilities between the bootloader and
the setefibootvars.go code.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: fixed grub EFI load option test with tmp rootdir
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
go.mod: move golang.org/x/text import next to other golang.org/x/ imports
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: adjust opts to look for recovery bootloader when setting EFI variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: do not overwrite BootOrder if unchanged, and unexport EFI variable helper functions
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: unexport `setEfiBootOrderVariable`
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: move code to detect bootloader and set EFI variables accordingly into dedicated function
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: unexport `setUbuntuSeedEfiBootVariables` and accompanying error
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: ensure nil optionalData for EFI variable is equivalent to 0-length slice
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: handle empty boot order and other boot var improvements
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: make setefibootvars functions linux-only
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* tests: add nested spread test for setting EFI vars
The test checks that EFI boot variables exist for the following:
1. A Boot#### variable pointing to the shim file path.
2. A BootOrder variable with the #### from the above Boot#### as first.
Since the layout of EFI assets is dependent on the gadget snap, the test
downloads and unpacks the gadget, then modifies the contents so that one
variant has the shim and grub binaries in `EFI/boot/` and another
variant has the shim and grub binaries in `EFI/ubuntu/` and the fallback
binary in `EFI/boot/`.
After building a core image around that modified gadget, the VM is
booted and the test checks that the EFI variables are set correctly.
Then, the test modifies the gadget to match the other variant's initial
layout, and then installs the newly modified gadget. This should trigger
re-setting EFI boot variables as well.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fix problems in spread test for setting EFI boot variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: disabled TPM on EFI boot vars test and separated gadget script
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fixed EFI vars test to use correct toolbox and include all EFI assets
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: modify-gadget.sh re-use existing gadget so edition is incremented
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fix mangled EFI var search string and other improvements
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: polish tests for setting EFI boot variables
Notably, allow tests/nested/core/core20-set-efi-boot-variables to run on
arm64 as well as amd64, simplify setefivars.go to search for multiple
assets on multiple architectures, and allow
tests/nested/manual/core20-set-efi-boot-vars to run on any ubuntu-2*.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* bootloader/grub.go: only consider new shim asset in boot entry for now
* tests/nested/core/core20-set-efi-boot-variables: fix details
* boot: update uefi variables on gadget update
* tests/nested/manual/core20-set-efi-boot-vars: work-around file not deleted
* tests/nested/manual/core20-set-efi-boot-vars: use fb.efi like other tests
* tests/nested/manual/core20-set-efi-boot-vars: drop use of toolbox snap
* tests/nested/manual/core20-set-efi-boot-vars: drop work-around for not deleted files
* tests/nested/manual/core20-set-efi-boot-vars: verify install does add a boot entry
* tests/nested/manual/core20-set-efi-boot-vars: run only on version that have UC
* tests/nested/manual/core20-set-efi-boot-vars: obey GADGET_CHANNEL
* tests/nested/manual/core20-set-efi-boot-vars: move get_boot_entry.py to libs
* tests/nested/manual/core20-set-efi-boot-vars: factorize copy of variables
... so we can reuse the script in other tests
* tests/nested/core/core20-set-efi-boot-variables: stop using toolbox snap
* tests/nested/core/core20-set-efi-boot-variables: only run on versions with UC available
* overlord/devicestate: test using EfiLoadOptionParameters
* boot: test that variables are set
* boot: test observers' UpdateBootEntry
* tests/nested/manual/core20-set-efi-boot-vars: also test without secure boot
* many: use trusted install observer when UEFI variables are supported
* boot/makebootable.go: rename sealer to observer
* boot/grub.go: fix function name in doc
* cmd/snap-bootstrap: verify that ObserveExistingTrustedRecoveryAssets is called
* boot: add tests for SetEfiBootVariables
* many: comment on calls to ObserveExistingTrustedRecoveryAssets
---------
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Co-authored-by: Oliver Calder <oliver.calder@canonical.com>
2024-06-03 10:03:50 +02:00
|
|
|
|
|
|
|
|
// ErrNoBootChainFound is returned by ParametersForEfiLoadOption if no valid bootchain was found
|
|
|
|
|
ErrNoBootChainFound = errors.New("no valid bootchain found")
|
2016-01-11 12:15:27 +01:00
|
|
|
)
|
|
|
|
|
|
2020-08-31 21:56:26 +02:00
|
|
|
// Role indicates whether the bootloader is used for recovery or run mode.
|
|
|
|
|
type Role string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// RoleSole applies to the sole bootloader used by UC16/18.
|
|
|
|
|
RoleSole Role = ""
|
|
|
|
|
// RoleRunMode applies to the run mode booloader.
|
|
|
|
|
RoleRunMode Role = "run-mode"
|
|
|
|
|
// RoleRecovery apllies to the recovery bootloader.
|
|
|
|
|
RoleRecovery Role = "recovery"
|
|
|
|
|
)
|
|
|
|
|
|
2019-11-20 13:20:45 +01:00
|
|
|
// Options carries bootloader options.
|
|
|
|
|
type Options struct {
|
|
|
|
|
// PrepareImageTime indicates whether the booloader is being
|
|
|
|
|
// used at prepare-image time, that means not on a runtime
|
|
|
|
|
// system.
|
2023-06-27 10:04:45 +02:00
|
|
|
PrepareImageTime bool `json:"prepare-image-time,omitempty"`
|
2019-11-20 13:20:45 +01:00
|
|
|
|
2020-08-31 21:56:26 +02:00
|
|
|
// Role specifies to use the bootloader for the given role.
|
2023-06-27 10:04:45 +02:00
|
|
|
Role Role `json:"role,omitempty"`
|
2020-01-13 14:16:24 -06:00
|
|
|
|
2020-08-31 21:56:26 +02:00
|
|
|
// NoSlashBoot indicates to use the native layout of the
|
|
|
|
|
// bootloader partition and not the /boot mount.
|
|
|
|
|
// It applies only for RoleRunMode.
|
|
|
|
|
// It is implied and ignored for RoleRecovery.
|
|
|
|
|
// It is an error to set it for RoleSole.
|
2023-06-27 10:04:45 +02:00
|
|
|
NoSlashBoot bool `json:"no-slash-boot,omitempty"`
|
2020-08-31 21:56:26 +02:00
|
|
|
}
|
2020-01-16 14:10:36 +01:00
|
|
|
|
2020-08-31 21:56:26 +02:00
|
|
|
func (o *Options) validate() error {
|
|
|
|
|
if o == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if o.NoSlashBoot && o.Role == RoleSole {
|
|
|
|
|
return fmt.Errorf("internal error: bootloader.RoleSole doesn't expect NoSlashBoot set")
|
|
|
|
|
}
|
2020-12-03 12:03:07 -06:00
|
|
|
if o.PrepareImageTime && o.Role == RoleRunMode {
|
|
|
|
|
return fmt.Errorf("internal error: cannot use run mode bootloader at prepare-image time")
|
|
|
|
|
}
|
2020-08-31 21:56:26 +02:00
|
|
|
return nil
|
2019-11-20 13:20:45 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-21 07:57:37 +01:00
|
|
|
// Bootloader provides an interface to interact with the system
|
2020-02-24 16:20:22 -06:00
|
|
|
// bootloader.
|
2016-01-21 07:57:37 +01:00
|
|
|
type Bootloader interface {
|
2020-02-24 16:20:22 -06:00
|
|
|
// Return the value of the specified bootloader variable.
|
2016-10-18 19:38:48 +02:00
|
|
|
GetBootVars(names ...string) (map[string]string, error)
|
2014-12-19 21:10:08 +00:00
|
|
|
|
2020-02-24 16:20:22 -06:00
|
|
|
// Set the value of the specified bootloader variable.
|
2016-10-18 19:38:48 +02:00
|
|
|
SetBootVars(values map[string]string) error
|
2016-01-13 17:12:51 +01:00
|
|
|
|
2020-02-24 16:20:22 -06:00
|
|
|
// Name returns the bootloader name.
|
2016-05-02 15:42:23 +02:00
|
|
|
Name() string
|
2016-07-14 16:43:43 +02:00
|
|
|
|
2020-11-19 15:26:47 -06:00
|
|
|
// Present returns whether the bootloader is currently present on the
|
|
|
|
|
// system - in other words whether this bootloader has been installed to the
|
|
|
|
|
// current system. Implementations should only return non-nil error if they
|
|
|
|
|
// can positively identify that the bootloader is installed, but there is
|
|
|
|
|
// actually an error with the installation.
|
|
|
|
|
Present() (bool, error)
|
2019-04-01 09:56:46 +01:00
|
|
|
|
2019-11-20 13:20:45 +01:00
|
|
|
// InstallBootConfig will try to install the boot config in the
|
2020-11-16 09:05:02 +01:00
|
|
|
// given gadgetDir to rootdir.
|
2020-11-13 14:30:38 +01:00
|
|
|
InstallBootConfig(gadgetDir string, opts *Options) error
|
2019-11-20 13:20:45 +01:00
|
|
|
|
2020-01-14 22:43:56 -06:00
|
|
|
// ExtractKernelAssets extracts kernel assets from the given kernel snap.
|
2019-07-29 10:11:37 +01:00
|
|
|
ExtractKernelAssets(s snap.PlaceInfo, snapf snap.Container) error
|
2019-04-01 09:56:46 +01:00
|
|
|
|
|
|
|
|
// RemoveKernelAssets removes the assets for the given kernel snap.
|
|
|
|
|
RemoveKernelAssets(s snap.PlaceInfo) error
|
2016-07-14 16:43:43 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-09 11:18:37 +01:00
|
|
|
type RecoveryAwareBootloader interface {
|
|
|
|
|
Bootloader
|
|
|
|
|
SetRecoverySystemEnv(recoverySystemDir string, values map[string]string) error
|
2020-09-25 14:18:36 +02:00
|
|
|
GetRecoverySystemEnv(recoverySystemDir string, key string) (string, error)
|
2019-12-09 11:18:37 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-09 17:20:24 +01:00
|
|
|
type ExtractedRecoveryKernelImageBootloader interface {
|
2020-03-11 09:47:41 +01:00
|
|
|
Bootloader
|
2020-03-09 17:20:24 +01:00
|
|
|
ExtractRecoveryKernelAssets(recoverySystemDir string, s snap.PlaceInfo, snapf snap.Container) error
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-24 16:25:02 -06:00
|
|
|
// ExtractedRunKernelImageBootloader is a Bootloader that also supports specific
|
|
|
|
|
// methods needed to setup booting from an extracted kernel, which is needed to
|
|
|
|
|
// implement encryption and/or secure boot. Prototypical implementation is UC20
|
|
|
|
|
// grub implementation with FDE.
|
2020-01-13 14:23:55 -06:00
|
|
|
type ExtractedRunKernelImageBootloader interface {
|
|
|
|
|
Bootloader
|
2020-02-24 16:25:02 -06:00
|
|
|
|
|
|
|
|
// EnableKernel enables the specified kernel on ubuntu-boot to be used
|
2020-02-28 11:53:21 +01:00
|
|
|
// during normal boots. The specified kernel should already have been
|
2020-02-28 12:22:16 +01:00
|
|
|
// extracted. This is usually implemented with a "kernel.efi" symlink
|
2020-02-28 11:53:21 +01:00
|
|
|
// pointing to the extracted kernel image.
|
2020-02-24 16:25:02 -06:00
|
|
|
EnableKernel(snap.PlaceInfo) error
|
|
|
|
|
|
2020-02-28 11:53:21 +01:00
|
|
|
// EnableTryKernel enables the specified kernel on ubuntu-boot to be
|
|
|
|
|
// tried by the bootloader on a reboot, to be used in conjunction with
|
|
|
|
|
// setting "kernel_status" to "try". The specified kernel should already
|
2020-02-28 12:22:16 +01:00
|
|
|
// have been extracted. This is usually implemented with a
|
2020-02-24 16:25:02 -06:00
|
|
|
// "try-kernel.efi" symlink pointing to the extracted kernel image.
|
|
|
|
|
EnableTryKernel(snap.PlaceInfo) error
|
|
|
|
|
|
|
|
|
|
// Kernel returns the current enabled kernel on the bootloader, not
|
|
|
|
|
// necessarily the kernel that was used to boot the current session, but the
|
|
|
|
|
// kernel that is enabled to boot on "normal" boots.
|
|
|
|
|
// If error is not nil, the first argument shall be non-nil.
|
|
|
|
|
Kernel() (snap.PlaceInfo, error)
|
|
|
|
|
|
|
|
|
|
// TryKernel returns the current enabled try-kernel on the bootloader, if
|
|
|
|
|
// there is no such enabled try-kernel, then ErrNoTryKernelRef is returned.
|
|
|
|
|
// If error is not nil, the first argument shall be non-nil.
|
|
|
|
|
TryKernel() (snap.PlaceInfo, error)
|
|
|
|
|
|
|
|
|
|
// DisableTryKernel disables the current enabled try-kernel on the
|
|
|
|
|
// bootloader, if it exists. It does not need to return an error if the
|
|
|
|
|
// enabled try-kernel does not exist or is in an inconsistent state before
|
|
|
|
|
// disabling it, errors should only be returned when the implementation
|
|
|
|
|
// fails to disable the try-kernel.
|
|
|
|
|
DisableTryKernel() error
|
2020-01-13 14:23:55 -06:00
|
|
|
}
|
|
|
|
|
|
2021-04-15 11:12:00 +02:00
|
|
|
// ComamndLineComponents carries the components of the kernel command line. The
|
|
|
|
|
// bootloader is expected to combine the provided components, optionally
|
|
|
|
|
// including its built-in static set of arguments, and produce a command line
|
|
|
|
|
// that will be passed to the kernel during boot.
|
|
|
|
|
type CommandLineComponents struct {
|
|
|
|
|
// Argument related to mode selection.
|
|
|
|
|
ModeArg string
|
|
|
|
|
// Argument related to recovery system selection, relevant for given
|
|
|
|
|
// mode argument.
|
|
|
|
|
SystemArg string
|
|
|
|
|
// Extra arguments requested by the system.
|
|
|
|
|
ExtraArgs string
|
|
|
|
|
// A complete set of arguments that overrides both the built-in static
|
|
|
|
|
// set and ExtraArgs. Note that, it is an error if extra and full
|
|
|
|
|
// arguments are non-empty.
|
|
|
|
|
FullArgs string
|
2023-07-17 13:48:40 +02:00
|
|
|
// A list of patterns to remove arguments from the default command line
|
|
|
|
|
RemoveArgs []kcmdline.ArgumentPattern
|
2021-04-15 11:12:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *CommandLineComponents) Validate() error {
|
|
|
|
|
if c.ExtraArgs != "" && c.FullArgs != "" {
|
|
|
|
|
return fmt.Errorf("cannot use both full and extra components of command line")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-24 14:08:30 +02:00
|
|
|
// TrustedAssetsBootloader has boot assets that take part in the secure boot
|
2020-09-25 14:32:55 +02:00
|
|
|
// process and need to be tracked, while other boot assets (typically boot
|
|
|
|
|
// config) are managed by snapd.
|
2020-09-23 17:09:17 +02:00
|
|
|
type TrustedAssetsBootloader interface {
|
2020-07-11 21:27:30 +02:00
|
|
|
Bootloader
|
|
|
|
|
|
2020-06-29 20:19:52 +02:00
|
|
|
// ManagedAssets returns a list of boot assets managed by the bootloader
|
2020-10-09 13:51:58 +02:00
|
|
|
// in the boot filesystem. Does not require rootdir to be set.
|
2020-06-29 20:19:52 +02:00
|
|
|
ManagedAssets() []string
|
2020-11-20 12:57:57 +01:00
|
|
|
// UpdateBootConfig attempts to update the boot config assets used by
|
|
|
|
|
// the bootloader. Returns true when assets were updated.
|
|
|
|
|
UpdateBootConfig() (bool, error)
|
2020-07-27 16:22:08 +02:00
|
|
|
// CommandLine returns the kernel command line composed of mode and
|
2021-04-15 19:13:50 +02:00
|
|
|
// system arguments, followed by either a built-in bootloader specific
|
|
|
|
|
// static arguments corresponding to the on-disk boot asset edition, and
|
|
|
|
|
// any extra arguments or a separate set of arguments provided in the
|
|
|
|
|
// components. The command line may be different when using a recovery
|
|
|
|
|
// bootloader.
|
2021-04-15 11:12:00 +02:00
|
|
|
CommandLine(pieces CommandLineComponents) (string, error)
|
2020-07-28 16:59:36 +02:00
|
|
|
// CandidateCommandLine is similar to CommandLine, but uses the current
|
|
|
|
|
// edition of managed built-in boot assets as reference.
|
2021-04-15 11:12:00 +02:00
|
|
|
CandidateCommandLine(pieces CommandLineComponents) (string, error)
|
2020-06-23 12:45:59 +02:00
|
|
|
|
2023-07-13 15:53:42 +02:00
|
|
|
// DefaultCommandLine returns the default kernel command-line
|
|
|
|
|
// used by the bootloader excluding the recovery mode and
|
|
|
|
|
// system parameters.
|
2023-07-24 11:38:44 +02:00
|
|
|
DefaultCommandLine(candidate bool) (string, error)
|
2023-07-13 15:53:42 +02:00
|
|
|
|
2023-12-01 12:51:49 +01:00
|
|
|
// TrustedAssets returns a map of relative paths to asset
|
|
|
|
|
// identifers. The paths are inside the bootloader's rootdir
|
|
|
|
|
// that are measured in the boot process. The asset
|
2023-12-06 13:47:13 +01:00
|
|
|
// identifiers correspond to the backward compatible names
|
|
|
|
|
// recorded in the modeenv (CurrentTrustedBootAssets and
|
2023-12-01 12:51:49 +01:00
|
|
|
// CurrentTrustedRecoveryBootAssets).
|
|
|
|
|
TrustedAssets() (map[string]string, error)
|
2020-09-01 10:35:08 -03:00
|
|
|
|
2023-12-01 12:51:49 +01:00
|
|
|
// RecoveryBootChains returns the possible load chains for
|
|
|
|
|
// recovery modes. It should be called on a RoleRecovery
|
|
|
|
|
// bootloader.
|
|
|
|
|
RecoveryBootChains(kernelPath string) ([][]BootFile, error)
|
2020-09-01 10:35:08 -03:00
|
|
|
|
2023-12-01 12:51:49 +01:00
|
|
|
// BootChains returns the possible load chains for run mode.
|
|
|
|
|
// It should be called on a RoleRecovery bootloader passing
|
|
|
|
|
// the RoleRunMode bootloader.
|
|
|
|
|
BootChains(runBl Bootloader, kernelPath string) ([][]BootFile, error)
|
2020-08-10 11:31:50 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-18 12:44:33 +01:00
|
|
|
// NotScriptableBootloader cannot change the bootloader environment
|
|
|
|
|
// because it supports no scripting or cannot do any writes. This
|
|
|
|
|
// applies to piboot for the moment.
|
|
|
|
|
type NotScriptableBootloader interface {
|
|
|
|
|
Bootloader
|
|
|
|
|
|
|
|
|
|
// Sets boot variables from initramfs - this is needed in
|
|
|
|
|
// addition to SetBootVars() to prevent side effects like
|
|
|
|
|
// re-writing the bootloader configuration.
|
|
|
|
|
SetBootVarsFromInitramfs(values map[string]string) error
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-02 18:12:39 +01:00
|
|
|
// RebootBootloader needs arguments to the reboot syscall when snaps
|
2022-01-28 16:48:30 +01:00
|
|
|
// are being updated.
|
2022-02-02 18:12:39 +01:00
|
|
|
type RebootBootloader interface {
|
2022-01-28 16:48:30 +01:00
|
|
|
Bootloader
|
|
|
|
|
|
|
|
|
|
// GetRebootArguments returns the needed reboot arguments
|
2022-02-23 15:27:28 +01:00
|
|
|
GetRebootArguments() (string, error)
|
2022-01-28 16:48:30 +01:00
|
|
|
}
|
|
|
|
|
|
boot,bootloader: add support for shim fallback and setting EFI boot variables on install (#13511)
* boot: added function to set EFI variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: renamed trustedShimFallbackBinary to seedShimPath
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: refactored setting EFI boot variables at install
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: adjusted variable names and fixed variable initialization
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: improve setting Boot#### EFI variable
Notably, splits off the process of reading a Boot#### variable and
extracting its DevicePath into its own function `readBootVariable` which
can be mocked and otherwise simplifies the `setBootNumberVariable`
function.
Also, fixes behavior around the final BootFFFF variable. Previously, it
was not possible to select the BootFFFF variable if it was unused, due
to overflow concerns on uint16. Now, the behavior around BootFFFF is
identical to that of any other boot variable, by using an int internally
instead of uint16, which also allows a more robust check for whether
there were no matching variables.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: added unit tests for setting EFI Boot#### variable
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: refactored setting EFI boot variables
Rewrote EFI boot variable functions to more closely match the behavior
of shim fallback: https://github.com/rhboot/shim/blob/main/fallback.c
In particular, the following have changed:
1. Existing Boot#### variables must fully match the new load option to
be considered a match. In particular, the load option attributes,
label, and device path must all be byte-for-byte identical.
Previously, only the device paths were compared.
2. Matching Boot#### variables are no longer overwritten. Since the
variable data must now byte-for-byte match the new load option, there
is no need to overwrite the existing variable.
3. Since existing Boot#### variables are no longer overwritten, the
variable attributes are no longer checked for those variables.
Instead, it is assumed that the Boot#### variable attributes are
viable for it to be used as a boot option. This matches the behavior
of `rhboot/shim/fallback.c`, for better or for worse.
4. When modifying the BootOrder variable, boot option numbers are no
longer pruned if there is no matching Boot#### variable.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: introduce UefiBootloader to build EFI load options
Previously, the path of the shim binary relative to the EFI partition
was passed into `SetEfiBootVariables`. However, different bootloaders
may wish to set up `OptionalData` in the load option.
Additionally, not all `TrustedAssetBootloaders` will attempt to set
EFI boot variables, and not all bootloaders which should set EFI boot
variables necessarily support secure boot. Thus, these should be
decoupled.
This commit adds a new `UefiBootloader` interface with the
`ConstructShimEfiLoadOption` method, which builds an EFI load option
from the shim path for the given bootloader.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: fixed linting errors and improved EFI boot variable test clarity
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: improved unit test for grub EFI load option creation
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: set EFI boot variables in `MakeRunnableSystem`
Previously, attempted to set boot variables in
`MakeRecoverySystemBootable`, which is called by `MakeBootableImage`,
which is called when building the image file, rather than during install
mode.
`MakeRunnableSystem` is called on first boot during install mode, and
thus should be responsible for setting EFI boot variables.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: use seed bootloader when setting EFI variables
In install mode, the bootloader located in ubuntu-seed should be used
when setting the EFI boot variables. Previously, the bootloader in
ubuntu-boot was accidentally re-used.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: added simple test to execute setefibootvar.go code
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fixed standalone set EFI vars code test to work with different layouts
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: moved simple setefibootvar.go check to nested test
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: added check for idempotence when setting EFI boot variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: adjust comments, organization, and add TODO
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: fix setting EFI boot variables
Make function to search for EFI asset device path and construct load
option common so each UefiBootloader does not have to re-implement it.
Instead, the bootloader returns the description, asset file path, and
optional data, which can then be used to create the EFI load option.
Also, in `makeRunnableSystem`, the bootloader in ubuntu-seed must have
`NoSlashBoot` in order to correctly find the grub.cfg file and thus the
grub bootloader. This commit fixes this bug, and refactors a bit to
account for the changes in responsibilities between the bootloader and
the setefibootvars.go code.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
bootloader: fixed grub EFI load option test with tmp rootdir
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
go.mod: move golang.org/x/text import next to other golang.org/x/ imports
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: adjust opts to look for recovery bootloader when setting EFI variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: do not overwrite BootOrder if unchanged, and unexport EFI variable helper functions
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: unexport `setEfiBootOrderVariable`
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: move code to detect bootloader and set EFI variables accordingly into dedicated function
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: unexport `setUbuntuSeedEfiBootVariables` and accompanying error
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot,bootloader: ensure nil optionalData for EFI variable is equivalent to 0-length slice
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: handle empty boot order and other boot var improvements
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
boot: make setefibootvars functions linux-only
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* tests: add nested spread test for setting EFI vars
The test checks that EFI boot variables exist for the following:
1. A Boot#### variable pointing to the shim file path.
2. A BootOrder variable with the #### from the above Boot#### as first.
Since the layout of EFI assets is dependent on the gadget snap, the test
downloads and unpacks the gadget, then modifies the contents so that one
variant has the shim and grub binaries in `EFI/boot/` and another
variant has the shim and grub binaries in `EFI/ubuntu/` and the fallback
binary in `EFI/boot/`.
After building a core image around that modified gadget, the VM is
booted and the test checks that the EFI variables are set correctly.
Then, the test modifies the gadget to match the other variant's initial
layout, and then installs the newly modified gadget. This should trigger
re-setting EFI boot variables as well.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fix problems in spread test for setting EFI boot variables
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: disabled TPM on EFI boot vars test and separated gadget script
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fixed EFI vars test to use correct toolbox and include all EFI assets
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: modify-gadget.sh re-use existing gadget so edition is incremented
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: fix mangled EFI var search string and other improvements
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
tests: polish tests for setting EFI boot variables
Notably, allow tests/nested/core/core20-set-efi-boot-variables to run on
arm64 as well as amd64, simplify setefivars.go to search for multiple
assets on multiple architectures, and allow
tests/nested/manual/core20-set-efi-boot-vars to run on any ubuntu-2*.
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
* bootloader/grub.go: only consider new shim asset in boot entry for now
* tests/nested/core/core20-set-efi-boot-variables: fix details
* boot: update uefi variables on gadget update
* tests/nested/manual/core20-set-efi-boot-vars: work-around file not deleted
* tests/nested/manual/core20-set-efi-boot-vars: use fb.efi like other tests
* tests/nested/manual/core20-set-efi-boot-vars: drop use of toolbox snap
* tests/nested/manual/core20-set-efi-boot-vars: drop work-around for not deleted files
* tests/nested/manual/core20-set-efi-boot-vars: verify install does add a boot entry
* tests/nested/manual/core20-set-efi-boot-vars: run only on version that have UC
* tests/nested/manual/core20-set-efi-boot-vars: obey GADGET_CHANNEL
* tests/nested/manual/core20-set-efi-boot-vars: move get_boot_entry.py to libs
* tests/nested/manual/core20-set-efi-boot-vars: factorize copy of variables
... so we can reuse the script in other tests
* tests/nested/core/core20-set-efi-boot-variables: stop using toolbox snap
* tests/nested/core/core20-set-efi-boot-variables: only run on versions with UC available
* overlord/devicestate: test using EfiLoadOptionParameters
* boot: test that variables are set
* boot: test observers' UpdateBootEntry
* tests/nested/manual/core20-set-efi-boot-vars: also test without secure boot
* many: use trusted install observer when UEFI variables are supported
* boot/makebootable.go: rename sealer to observer
* boot/grub.go: fix function name in doc
* cmd/snap-bootstrap: verify that ObserveExistingTrustedRecoveryAssets is called
* boot: add tests for SetEfiBootVariables
* many: comment on calls to ObserveExistingTrustedRecoveryAssets
---------
Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
Co-authored-by: Oliver Calder <oliver.calder@canonical.com>
2024-06-03 10:03:50 +02:00
|
|
|
// UefiBootloader provides data for setting EFI boot variables.
|
|
|
|
|
type UefiBootloader interface {
|
|
|
|
|
Bootloader
|
|
|
|
|
|
|
|
|
|
// ParametersForEfiLoadOption returns the data which may be used to construct
|
|
|
|
|
// an EFI load option.
|
|
|
|
|
ParametersForEfiLoadOption(updatedAssets []string) (description string, assetPath string, optionalData []byte, err error)
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-13 14:30:38 +01:00
|
|
|
func genericInstallBootConfig(gadgetFile, systemFile string) error {
|
2019-11-20 13:20:45 +01:00
|
|
|
if err := os.MkdirAll(filepath.Dir(systemFile), 0755); err != nil {
|
2020-11-13 14:30:38 +01:00
|
|
|
return err
|
2019-11-20 13:20:45 +01:00
|
|
|
}
|
2020-11-13 14:30:38 +01:00
|
|
|
return osutil.CopyFile(gadgetFile, systemFile, osutil.CopyFlagOverwrite)
|
2019-11-20 13:20:45 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-13 14:30:38 +01:00
|
|
|
func genericSetBootConfigFromAsset(systemFile, assetName string) error {
|
2020-06-24 16:16:08 +02:00
|
|
|
bootConfig := assets.Internal(assetName)
|
|
|
|
|
if bootConfig == nil {
|
2020-11-13 14:30:38 +01:00
|
|
|
return fmt.Errorf("internal error: no boot asset for %q", assetName)
|
2020-06-24 16:16:08 +02:00
|
|
|
}
|
2020-06-17 14:12:10 +02:00
|
|
|
if err := os.MkdirAll(filepath.Dir(systemFile), 0755); err != nil {
|
2020-11-13 14:30:38 +01:00
|
|
|
return err
|
2020-06-17 14:12:10 +02:00
|
|
|
}
|
2020-11-13 14:30:38 +01:00
|
|
|
return osutil.AtomicWriteFile(systemFile, bootConfig, 0644, 0)
|
2020-06-17 14:12:10 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-20 12:57:57 +01:00
|
|
|
func genericUpdateBootConfigFromAssets(systemFile string, assetName string) (updated bool, err error) {
|
2020-06-23 12:46:48 +02:00
|
|
|
currentBootConfigEdition, err := editionFromDiskConfigAsset(systemFile)
|
|
|
|
|
if err != nil && err != errNoEdition {
|
2020-11-20 12:57:57 +01:00
|
|
|
return false, err
|
2020-06-23 12:46:48 +02:00
|
|
|
}
|
|
|
|
|
if err == errNoEdition {
|
2020-11-20 12:57:57 +01:00
|
|
|
return false, nil
|
2020-06-23 12:46:48 +02:00
|
|
|
}
|
|
|
|
|
newBootConfig := assets.Internal(assetName)
|
|
|
|
|
if len(newBootConfig) == 0 {
|
2020-11-20 12:57:57 +01:00
|
|
|
return false, fmt.Errorf("no boot config asset with name %q", assetName)
|
2020-06-23 12:46:48 +02:00
|
|
|
}
|
2020-06-26 14:10:48 +02:00
|
|
|
bc, err := configAssetFrom(newBootConfig)
|
2020-06-23 12:46:48 +02:00
|
|
|
if err != nil {
|
2020-11-20 12:57:57 +01:00
|
|
|
return false, err
|
2020-06-23 12:46:48 +02:00
|
|
|
}
|
2020-06-26 14:10:48 +02:00
|
|
|
if bc.Edition() <= currentBootConfigEdition {
|
2020-06-23 12:46:48 +02:00
|
|
|
// edition of the candidate boot config is lower than or equal
|
|
|
|
|
// to one currently installed
|
2020-11-20 12:57:57 +01:00
|
|
|
return false, nil
|
2020-06-23 12:46:48 +02:00
|
|
|
}
|
2020-11-20 12:57:57 +01:00
|
|
|
if err := osutil.AtomicWriteFile(systemFile, bc.Raw(), 0644, 0); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
return true, nil
|
2020-06-23 12:46:48 +02:00
|
|
|
}
|
|
|
|
|
|
2016-08-10 12:46:06 +02:00
|
|
|
// InstallBootConfig installs the bootloader config from the gadget
|
|
|
|
|
// snap dir into the right place.
|
2019-11-20 13:20:45 +01:00
|
|
|
func InstallBootConfig(gadgetDir, rootDir string, opts *Options) error {
|
2020-08-31 21:56:26 +02:00
|
|
|
if err := opts.validate(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2020-11-13 14:30:38 +01:00
|
|
|
bl, err := ForGadget(gadgetDir, rootDir, opts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("cannot find boot config in %q", gadgetDir)
|
2016-07-14 16:43:43 +02:00
|
|
|
}
|
2020-11-13 14:30:38 +01:00
|
|
|
return bl.InstallBootConfig(gadgetDir, opts)
|
2014-12-19 21:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
2020-08-12 12:52:25 +02:00
|
|
|
type bootloaderNewFunc func(rootdir string, opts *Options) Bootloader
|
|
|
|
|
|
2019-07-29 10:11:37 +01:00
|
|
|
var (
|
2020-08-12 15:01:01 +02:00
|
|
|
// bootloaders list all possible bootloaders by their constructor
|
|
|
|
|
// function.
|
2020-08-12 12:52:25 +02:00
|
|
|
bootloaders = []bootloaderNewFunc{
|
|
|
|
|
newUboot,
|
2020-08-13 07:05:51 +02:00
|
|
|
newGrub,
|
2020-08-12 12:52:25 +02:00
|
|
|
newAndroidBoot,
|
|
|
|
|
newLk,
|
2021-11-02 16:20:27 +01:00
|
|
|
newPiboot,
|
2020-08-12 12:52:25 +02:00
|
|
|
}
|
2019-07-29 10:11:37 +01:00
|
|
|
)
|
2016-06-08 18:11:10 +02:00
|
|
|
|
2020-08-12 15:01:01 +02:00
|
|
|
var (
|
|
|
|
|
forcedBootloader Bootloader
|
|
|
|
|
forcedError error
|
|
|
|
|
)
|
|
|
|
|
|
2019-09-05 19:32:04 +02:00
|
|
|
// Find returns the bootloader for the system
|
|
|
|
|
// or an error if no bootloader is found.
|
2019-11-20 13:20:45 +01:00
|
|
|
//
|
|
|
|
|
// The rootdir option is useful for image creation operations. It
|
2019-12-10 09:21:52 +01:00
|
|
|
// can also be used to find the recovery bootloader, e.g. on uc20:
|
2023-01-09 14:39:58 +00:00
|
|
|
//
|
|
|
|
|
// bootloader.Find("/run/mnt/ubuntu-seed")
|
2019-09-06 11:11:53 +02:00
|
|
|
func Find(rootdir string, opts *Options) (Bootloader, error) {
|
2020-08-31 21:56:26 +02:00
|
|
|
if err := opts.validate(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-07-29 10:11:37 +01:00
|
|
|
if forcedBootloader != nil || forcedError != nil {
|
|
|
|
|
return forcedBootloader, forcedError
|
2016-06-08 18:11:10 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-05 19:32:04 +02:00
|
|
|
if rootdir == "" {
|
|
|
|
|
rootdir = dirs.GlobalRootDir
|
|
|
|
|
}
|
2019-09-06 11:11:53 +02:00
|
|
|
if opts == nil {
|
|
|
|
|
opts = &Options{}
|
|
|
|
|
}
|
2019-09-05 19:32:04 +02:00
|
|
|
|
2020-11-19 15:26:47 -06:00
|
|
|
// note that the order of this is not deterministic
|
2020-08-12 12:52:25 +02:00
|
|
|
for _, blNew := range bootloaders {
|
|
|
|
|
bl := blNew(rootdir, opts)
|
2020-11-19 15:26:47 -06:00
|
|
|
present, err := bl.Present()
|
|
|
|
|
if err != nil {
|
2020-11-20 09:03:22 -06:00
|
|
|
return nil, fmt.Errorf("bootloader %q found but not usable: %v", bl.Name(), err)
|
2020-11-19 15:26:47 -06:00
|
|
|
}
|
|
|
|
|
if present {
|
2020-08-12 12:52:25 +02:00
|
|
|
return bl, nil
|
|
|
|
|
}
|
2015-02-12 17:43:31 +01:00
|
|
|
}
|
2015-02-12 22:35:24 +01:00
|
|
|
// no, weeeee
|
2015-02-13 14:29:27 +01:00
|
|
|
return nil, ErrBootloader
|
2015-02-12 17:43:31 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-13 14:14:04 -06:00
|
|
|
// Force can be used to force Find to always find the specified bootloader; use
|
|
|
|
|
// nil to reset to normal lookup.
|
2019-03-21 08:51:31 +01:00
|
|
|
func Force(booloader Bootloader) {
|
2016-06-08 18:11:10 +02:00
|
|
|
forcedBootloader = booloader
|
2019-07-29 10:11:37 +01:00
|
|
|
forcedError = nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 15:08:29 -06:00
|
|
|
// ForceError can be used to force Find to return an error; use nil to
|
2019-07-29 10:11:37 +01:00
|
|
|
// reset to normal lookup.
|
|
|
|
|
func ForceError(err error) {
|
|
|
|
|
forcedBootloader = nil
|
|
|
|
|
forcedError = err
|
2016-06-08 18:11:10 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-13 15:03:04 -05:00
|
|
|
func extractKernelAssetsToBootDir(dstDir string, snapf snap.Container, assets []string) error {
|
2019-04-01 09:56:46 +01:00
|
|
|
// now do the kernel specific bits
|
|
|
|
|
if err := os.MkdirAll(dstDir, 0755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
dir, err := os.Open(dstDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer dir.Close()
|
|
|
|
|
|
2020-01-13 14:18:23 -06:00
|
|
|
for _, src := range assets {
|
2019-04-01 09:56:46 +01:00
|
|
|
if err := snapf.Unpack(src, dstDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := dir.Sync(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-13 14:18:23 -06:00
|
|
|
return nil
|
2019-04-01 09:56:46 +01:00
|
|
|
}
|
|
|
|
|
|
2019-06-12 09:53:12 +02:00
|
|
|
func removeKernelAssetsFromBootDir(bootDir string, s snap.PlaceInfo) error {
|
2019-04-01 09:56:46 +01:00
|
|
|
// remove the kernel blob
|
2020-02-14 09:54:04 -06:00
|
|
|
blobName := s.Filename()
|
2019-04-01 09:56:46 +01:00
|
|
|
dstDir := filepath.Join(bootDir, blobName)
|
|
|
|
|
if err := os.RemoveAll(dstDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2020-08-12 12:52:25 +02:00
|
|
|
|
2020-08-12 15:01:01 +02:00
|
|
|
// ForGadget returns a bootloader matching a given gadget by inspecting the
|
2020-08-13 07:05:51 +02:00
|
|
|
// contents of gadget directory or an error if no matching bootloader is found.
|
2020-08-12 12:52:25 +02:00
|
|
|
func ForGadget(gadgetDir, rootDir string, opts *Options) (Bootloader, error) {
|
2020-08-31 21:56:26 +02:00
|
|
|
if err := opts.validate(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-08-13 13:35:31 +02:00
|
|
|
if forcedBootloader != nil || forcedError != nil {
|
|
|
|
|
return forcedBootloader, forcedError
|
|
|
|
|
}
|
2020-08-12 12:52:25 +02:00
|
|
|
for _, blNew := range bootloaders {
|
|
|
|
|
bl := blNew(rootDir, opts)
|
2020-11-13 14:30:38 +01:00
|
|
|
markerConf := filepath.Join(gadgetDir, bl.Name()+".conf")
|
2020-08-12 12:52:25 +02:00
|
|
|
// do we have a marker file?
|
2020-11-13 14:30:38 +01:00
|
|
|
if osutil.FileExists(markerConf) {
|
2020-08-12 12:52:25 +02:00
|
|
|
return bl, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil, ErrBootloader
|
|
|
|
|
}
|
2020-08-31 18:48:03 -03:00
|
|
|
|
2020-09-02 12:59:38 +02:00
|
|
|
// BootFile represents each file in the chains of trusted assets and
|
|
|
|
|
// kernels used in the boot process. For example a boot file can be an
|
|
|
|
|
// EFI binary or a snap file containing an EFI binary.
|
2020-09-01 15:38:49 -03:00
|
|
|
type BootFile struct {
|
2020-09-01 16:15:13 -03:00
|
|
|
// Path is the path to the file in the filesystem or, if Snap
|
|
|
|
|
// is set, the relative path inside the snap file.
|
2020-08-31 18:48:03 -03:00
|
|
|
Path string
|
2020-09-01 16:15:13 -03:00
|
|
|
// Snap contains the path to the snap file if a snap file is used.
|
|
|
|
|
Snap string
|
2020-09-02 12:59:38 +02:00
|
|
|
// Role is set to the role of the bootloader this boot file
|
2020-09-01 15:52:00 -03:00
|
|
|
// originates from.
|
|
|
|
|
Role Role
|
2020-08-31 18:48:03 -03:00
|
|
|
}
|
|
|
|
|
|
2020-09-01 16:15:13 -03:00
|
|
|
func NewBootFile(snap, path string, role Role) BootFile {
|
2020-09-01 15:38:49 -03:00
|
|
|
return BootFile{
|
2020-09-01 16:15:13 -03:00
|
|
|
Snap: snap,
|
|
|
|
|
Path: path,
|
|
|
|
|
Role: role,
|
2020-08-31 18:48:03 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-01 15:38:49 -03:00
|
|
|
// WithPath returns a copy of the BootFile with path updated to the
|
2020-08-31 21:19:33 -03:00
|
|
|
// specified value.
|
2020-09-01 15:38:49 -03:00
|
|
|
func (b BootFile) WithPath(path string) BootFile {
|
2020-08-31 18:48:03 -03:00
|
|
|
b.Path = path
|
|
|
|
|
return b
|
|
|
|
|
}
|