gitee.com/mysnapcore/mysnapd@v0.1.0/boot/makebootable.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2022 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package boot
    21  
    22  import (
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  
    27  	"gitee.com/mysnapcore/mysnapd/asserts"
    28  	"gitee.com/mysnapcore/mysnapd/bootloader"
    29  	"gitee.com/mysnapcore/mysnapd/dirs"
    30  	"gitee.com/mysnapcore/mysnapd/osutil"
    31  	"gitee.com/mysnapcore/mysnapd/snap"
    32  	"gitee.com/mysnapcore/mysnapd/snap/snapfile"
    33  )
    34  
    35  // BootableSet represents the boot snaps of a system to be made bootable.
    36  type BootableSet struct {
    37  	Base       *snap.Info
    38  	BasePath   string
    39  	Kernel     *snap.Info
    40  	KernelPath string
    41  	Gadget     *snap.Info
    42  	GadgetPath string
    43  
    44  	RecoverySystemLabel string
    45  	// RecoverySystemDir is a path to a directory with recovery system
    46  	// assets. The path is relative to the recovery bootloader root
    47  	// directory.
    48  	RecoverySystemDir string
    49  
    50  	UnpackedGadgetDir string
    51  
    52  	// Recovery is set when making the recovery partition bootable.
    53  	Recovery bool
    54  }
    55  
    56  // MakeBootableImage sets up the given bootable set and target filesystem
    57  // such that the image can be booted.
    58  //
    59  // rootdir points to an image filesystem (UC 16/18) or an image recovery
    60  // filesystem (UC20 at prepare-image time).
    61  // On UC20, bootWith.Recovery must be true, as this function makes the recovery
    62  // system bootable. It does not make a run system bootable, for that
    63  // functionality see MakeRunnableSystem, which is meant to be used at runtime
    64  // from UC20 install mode.
    65  // For a UC20 image a set of boot flags that will be set in the recovery
    66  // boot environment can be specified.
    67  func MakeBootableImage(model *asserts.Model, rootdir string, bootWith *BootableSet, bootFlags []string) error {
    68  	if model.Grade() == asserts.ModelGradeUnset {
    69  		if len(bootFlags) != 0 {
    70  			return fmt.Errorf("no boot flags support for UC16/18")
    71  		}
    72  		return makeBootable16(model, rootdir, bootWith)
    73  	}
    74  
    75  	if !bootWith.Recovery {
    76  		return fmt.Errorf("internal error: MakeBootableImage called at runtime, use MakeRunnableSystem instead")
    77  	}
    78  	return makeBootable20(rootdir, bootWith, bootFlags)
    79  }
    80  
    81  // MakeBootablePartition configures a partition mounted on rootdir
    82  // using information from bootWith and bootFlags. Contrarily to
    83  // MakeBootableImage this happens in a live system.
    84  func MakeBootablePartition(partDir string, opts *bootloader.Options, bootWith *BootableSet, bootMode string, bootFlags []string) error {
    85  	if bootWith.RecoverySystemDir != "" {
    86  		return fmt.Errorf("internal error: RecoverySystemDir unexpectedly set for MakeBootablePartition")
    87  	}
    88  	return configureBootloader(partDir, opts, bootWith, bootMode, bootFlags)
    89  }
    90  
    91  // makeBootable16 setups the image filesystem for boot with UC16
    92  // and UC18 models. This entails:
    93  //  - installing the bootloader configuration from the gadget
    94  //  - creating symlinks for boot snaps from seed to the runtime blob dir
    95  //  - setting boot env vars pointing to the revisions of the boot snaps to use
    96  //  - extracting kernel assets as needed by the bootloader
    97  func makeBootable16(model *asserts.Model, rootdir string, bootWith *BootableSet) error {
    98  	opts := &bootloader.Options{
    99  		PrepareImageTime: true,
   100  	}
   101  
   102  	// install the bootloader configuration from the gadget
   103  	if err := bootloader.InstallBootConfig(bootWith.UnpackedGadgetDir, rootdir, opts); err != nil {
   104  		return err
   105  	}
   106  
   107  	// setup symlinks for kernel and boot base from the blob directory
   108  	// to the seed snaps
   109  
   110  	snapBlobDir := dirs.SnapBlobDirUnder(rootdir)
   111  	if err := os.MkdirAll(snapBlobDir, 0755); err != nil {
   112  		return err
   113  	}
   114  
   115  	for _, fn := range []string{bootWith.BasePath, bootWith.KernelPath} {
   116  		dst := filepath.Join(snapBlobDir, filepath.Base(fn))
   117  		// construct a relative symlink from the blob dir
   118  		// to the seed snap file
   119  		relSymlink, err := filepath.Rel(snapBlobDir, fn)
   120  		if err != nil {
   121  			return fmt.Errorf("cannot build symlink for boot snap: %v", err)
   122  		}
   123  		if err := os.Symlink(relSymlink, dst); err != nil {
   124  			return err
   125  		}
   126  	}
   127  
   128  	// Set bootvars for kernel/core snaps so the system boots and
   129  	// does the first-time initialization. There is also no
   130  	// mounted kernel/core/base snap, but just the blobs.
   131  	bl, err := bootloader.Find(rootdir, opts)
   132  	if err != nil {
   133  		return fmt.Errorf("cannot set kernel/core boot variables: %s", err)
   134  	}
   135  
   136  	m := map[string]string{
   137  		"snap_mode":       "",
   138  		"snap_try_core":   "",
   139  		"snap_try_kernel": "",
   140  	}
   141  	if model.DisplayName() != "" {
   142  		m["snap_menuentry"] = model.DisplayName()
   143  	}
   144  
   145  	setBoot := func(name, fn string) {
   146  		m[name] = filepath.Base(fn)
   147  	}
   148  	// base
   149  	setBoot("snap_core", bootWith.BasePath)
   150  
   151  	// kernel
   152  	kernelf, err := snapfile.Open(bootWith.KernelPath)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	if err := bl.ExtractKernelAssets(bootWith.Kernel, kernelf); err != nil {
   157  		return err
   158  	}
   159  	setBoot("snap_kernel", bootWith.KernelPath)
   160  
   161  	if err := bl.SetBootVars(m); err != nil {
   162  		return err
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  func configureBootloader(rootdir string, opts *bootloader.Options, bootWith *BootableSet, bootMode string, bootFlags []string) error {
   169  	blVars := make(map[string]string, 3)
   170  	if len(bootFlags) != 0 {
   171  		if err := setImageBootFlags(bootFlags, blVars); err != nil {
   172  			return err
   173  		}
   174  	}
   175  
   176  	// install the bootloader configuration from the gadget
   177  	if err := bootloader.InstallBootConfig(bootWith.UnpackedGadgetDir, rootdir, opts); err != nil {
   178  		return err
   179  	}
   180  
   181  	// now install the recovery system specific boot config
   182  	bl, err := bootloader.Find(rootdir, opts)
   183  	if err != nil {
   184  		return fmt.Errorf("internal error: cannot find bootloader: %v", err)
   185  	}
   186  
   187  	blVars["snapd_recovery_mode"] = bootMode
   188  	if bootWith.RecoverySystemLabel != "" {
   189  		// record which recovery system is to be used on the bootloader, note
   190  		// that this goes on the main bootloader environment, and not on the
   191  		// recovery system bootloader environment, for example for grub
   192  		// bootloader, this env var is set on the ubuntu-seed root grubenv, and
   193  		// not on the recovery system grubenv in the systems/20200314/ subdir on
   194  		// ubuntu-seed
   195  		blVars["snapd_recovery_system"] = bootWith.RecoverySystemLabel
   196  	}
   197  
   198  	if err := bl.SetBootVars(blVars); err != nil {
   199  		return fmt.Errorf("cannot set recovery environment: %v", err)
   200  	}
   201  
   202  	return nil
   203  }
   204  
   205  func makeBootable20(rootdir string, bootWith *BootableSet, bootFlags []string) error {
   206  	// we can only make a single recovery system bootable right now
   207  	recoverySystems, err := filepath.Glob(filepath.Join(rootdir, "systems/*"))
   208  	if err != nil {
   209  		return fmt.Errorf("cannot validate recovery systems: %v", err)
   210  	}
   211  	if len(recoverySystems) > 1 {
   212  		return fmt.Errorf("cannot make multiple recovery systems bootable yet")
   213  	}
   214  
   215  	if bootWith.RecoverySystemLabel == "" {
   216  		return fmt.Errorf("internal error: recovery system label unset")
   217  	}
   218  
   219  	opts := &bootloader.Options{
   220  		PrepareImageTime: true,
   221  		// setup the recovery bootloader
   222  		Role: bootloader.RoleRecovery,
   223  	}
   224  	if err := configureBootloader(rootdir, opts, bootWith, ModeInstall, bootFlags); err != nil {
   225  		return fmt.Errorf("cannot install bootloader: %v", err)
   226  	}
   227  
   228  	return MakeRecoverySystemBootable(rootdir, bootWith.RecoverySystemDir, &RecoverySystemBootableSet{
   229  		Kernel:           bootWith.Kernel,
   230  		KernelPath:       bootWith.KernelPath,
   231  		GadgetSnapOrDir:  bootWith.UnpackedGadgetDir,
   232  		PrepareImageTime: true,
   233  	})
   234  }
   235  
   236  // RecoverySystemBootableSet is a set of snaps relevant to booting a recovery
   237  // system.
   238  type RecoverySystemBootableSet struct {
   239  	Kernel          *snap.Info
   240  	KernelPath      string
   241  	GadgetSnapOrDir string
   242  	// PrepareImageTime is true when the structure is being used when
   243  	// preparing a bootable system image.
   244  	PrepareImageTime bool
   245  }
   246  
   247  // MakeRecoverySystemBootable prepares a recovery system under a path relative
   248  // to recovery bootloader's rootdir for booting.
   249  func MakeRecoverySystemBootable(rootdir string, relativeRecoverySystemDir string, bootWith *RecoverySystemBootableSet) error {
   250  	opts := &bootloader.Options{
   251  		// XXX: this is only needed by LK, it is unclear whether LK does
   252  		// too much when extracting recovery kernel assets, in the end
   253  		// it is currently not possible to create a recovery system at
   254  		// runtime when using LK.
   255  		PrepareImageTime: bootWith.PrepareImageTime,
   256  		// setup the recovery bootloader
   257  		Role: bootloader.RoleRecovery,
   258  	}
   259  
   260  	bl, err := bootloader.Find(rootdir, opts)
   261  	if err != nil {
   262  		return fmt.Errorf("internal error: cannot find bootloader: %v", err)
   263  	}
   264  
   265  	// on e.g. ARM we need to extract the kernel assets on the recovery
   266  	// system as well, but the bootloader does not load any environment from
   267  	// the recovery system
   268  	erkbl, ok := bl.(bootloader.ExtractedRecoveryKernelImageBootloader)
   269  	if ok {
   270  		kernelf, err := snapfile.Open(bootWith.KernelPath)
   271  		if err != nil {
   272  			return err
   273  		}
   274  
   275  		err = erkbl.ExtractRecoveryKernelAssets(
   276  			relativeRecoverySystemDir,
   277  			bootWith.Kernel,
   278  			kernelf,
   279  		)
   280  		if err != nil {
   281  			return fmt.Errorf("cannot extract recovery system kernel assets: %v", err)
   282  		}
   283  
   284  		return nil
   285  	}
   286  
   287  	rbl, ok := bl.(bootloader.RecoveryAwareBootloader)
   288  	if !ok {
   289  		return fmt.Errorf("cannot use %s bootloader: does not support recovery systems", bl.Name())
   290  	}
   291  	kernelPath, err := filepath.Rel(rootdir, bootWith.KernelPath)
   292  	if err != nil {
   293  		return fmt.Errorf("cannot construct kernel boot path: %v", err)
   294  	}
   295  	recoveryBlVars := map[string]string{
   296  		"snapd_recovery_kernel": filepath.Join("/", kernelPath),
   297  	}
   298  	if _, ok := bl.(bootloader.TrustedAssetsBootloader); ok {
   299  		recoveryCmdlineArgs, err := bootVarsForTrustedCommandLineFromGadget(bootWith.GadgetSnapOrDir)
   300  		if err != nil {
   301  			return fmt.Errorf("cannot obtain recovery system command line: %v", err)
   302  		}
   303  		for k, v := range recoveryCmdlineArgs {
   304  			recoveryBlVars[k] = v
   305  		}
   306  	}
   307  
   308  	if err := rbl.SetRecoverySystemEnv(relativeRecoverySystemDir, recoveryBlVars); err != nil {
   309  		return fmt.Errorf("cannot set recovery system environment: %v", err)
   310  	}
   311  	return nil
   312  }
   313  
   314  type makeRunnableOptions struct {
   315  	Standalone     bool
   316  	AfterDataReset bool
   317  }
   318  
   319  func copyBootSnap(orig string, dstInfo *snap.Info, dstSnapBlobDir string) error {
   320  	// if the source path is a symlink, don't copy the symlink, copy the
   321  	// target file instead of copying the symlink, as the initramfs won't
   322  	// follow the symlink when it goes to mount the base and kernel snaps by
   323  	// design as the initramfs should only be using trusted things from
   324  	// ubuntu-data to boot in run mode
   325  	if osutil.IsSymlink(orig) {
   326  		link, err := os.Readlink(orig)
   327  		if err != nil {
   328  			return err
   329  		}
   330  		orig = link
   331  	}
   332  	// note that we need to use the "Filename()" here because unasserted
   333  	// snaps will have names like pc-kernel_5.19.4.snap but snapd expects
   334  	// "pc-kernel_x1.snap"
   335  	dst := filepath.Join(dstSnapBlobDir, dstInfo.Filename())
   336  	if err := osutil.CopyFile(orig, dst, osutil.CopyFlagPreserveAll|osutil.CopyFlagSync); err != nil {
   337  		return err
   338  	}
   339  	return nil
   340  }
   341  
   342  func makeRunnableSystem(model *asserts.Model, bootWith *BootableSet, sealer *TrustedAssetsInstallObserver, makeOpts makeRunnableOptions) error {
   343  	if model.Grade() == asserts.ModelGradeUnset {
   344  		return fmt.Errorf("internal error: cannot make pre-UC20 system runnable")
   345  	}
   346  	if bootWith.RecoverySystemDir != "" {
   347  		return fmt.Errorf("internal error: RecoverySystemDir unexpectedly set for MakeRunnableSystem")
   348  	}
   349  
   350  	// TODO:UC20:
   351  	// - figure out what to do for uboot gadgets, currently we require them to
   352  	//   install the boot.sel onto ubuntu-boot directly, but the file should be
   353  	//   managed by snapd instead
   354  
   355  	// copy kernel/base/gadget into the ubuntu-data partition
   356  	snapBlobDir := dirs.SnapBlobDirUnder(InstallHostWritableDir(model))
   357  	if err := os.MkdirAll(snapBlobDir, 0755); err != nil {
   358  		return err
   359  	}
   360  	for _, origDest := range []struct {
   361  		orig     string
   362  		destInfo *snap.Info
   363  	}{
   364  		{orig: bootWith.BasePath, destInfo: bootWith.Base},
   365  		{orig: bootWith.KernelPath, destInfo: bootWith.Kernel},
   366  		{orig: bootWith.GadgetPath, destInfo: bootWith.Gadget}} {
   367  		if err := copyBootSnap(origDest.orig, origDest.destInfo, snapBlobDir); err != nil {
   368  			return err
   369  		}
   370  	}
   371  
   372  	// replicate the boot assets cache in host's writable
   373  	if err := CopyBootAssetsCacheToRoot(InstallHostWritableDir(model)); err != nil {
   374  		return fmt.Errorf("cannot replicate boot assets cache: %v", err)
   375  	}
   376  
   377  	var currentTrustedBootAssets bootAssetsMap
   378  	var currentTrustedRecoveryBootAssets bootAssetsMap
   379  	if sealer != nil {
   380  		currentTrustedBootAssets = sealer.currentTrustedBootAssetsMap()
   381  		currentTrustedRecoveryBootAssets = sealer.currentTrustedRecoveryBootAssetsMap()
   382  	}
   383  	recoverySystemLabel := bootWith.RecoverySystemLabel
   384  	// write modeenv on the ubuntu-data partition
   385  	modeenv := &Modeenv{
   386  		Mode:           "run",
   387  		RecoverySystem: recoverySystemLabel,
   388  		// default to the system we were installed from
   389  		CurrentRecoverySystems: []string{recoverySystemLabel},
   390  		// which is also considered to be good
   391  		GoodRecoverySystems:              []string{recoverySystemLabel},
   392  		CurrentTrustedBootAssets:         currentTrustedBootAssets,
   393  		CurrentTrustedRecoveryBootAssets: currentTrustedRecoveryBootAssets,
   394  		// kernel command lines are set later once a boot config is
   395  		// installed
   396  		CurrentKernelCommandLines: nil,
   397  		// keep this comment to make gofmt 1.9 happy
   398  		Base:           bootWith.Base.Filename(),
   399  		Gadget:         bootWith.Gadget.Filename(),
   400  		CurrentKernels: []string{bootWith.Kernel.Filename()},
   401  		BrandID:        model.BrandID(),
   402  		Model:          model.Model(),
   403  		// TODO: test this
   404  		Classic:        model.Classic(),
   405  		Grade:          string(model.Grade()),
   406  		ModelSignKeyID: model.SignKeyID(),
   407  	}
   408  
   409  	// get the ubuntu-boot bootloader and extract the kernel there
   410  	opts := &bootloader.Options{
   411  		// Bootloader for run mode
   412  		Role: bootloader.RoleRunMode,
   413  		// At this point the run mode bootloader is under the native
   414  		// run partition layout, no /boot mount.
   415  		NoSlashBoot: true,
   416  	}
   417  	// the bootloader config may have been installed when the ubuntu-boot
   418  	// partition was created, but for a trusted assets the bootloader config
   419  	// will be installed further down; for now identify the run mode
   420  	// bootloader by looking at the gadget
   421  	bl, err := bootloader.ForGadget(bootWith.UnpackedGadgetDir, InitramfsUbuntuBootDir, opts)
   422  	if err != nil {
   423  		return fmt.Errorf("internal error: cannot identify run system bootloader: %v", err)
   424  	}
   425  
   426  	// extract the kernel first and mark kernel_status ready
   427  	kernelf, err := snapfile.Open(bootWith.KernelPath)
   428  	if err != nil {
   429  		return err
   430  	}
   431  
   432  	err = bl.ExtractKernelAssets(bootWith.Kernel, kernelf)
   433  	if err != nil {
   434  		return err
   435  	}
   436  
   437  	blVars := map[string]string{
   438  		"kernel_status": "",
   439  	}
   440  
   441  	ebl, ok := bl.(bootloader.ExtractedRunKernelImageBootloader)
   442  	if ok {
   443  		// the bootloader supports additional extracted kernel handling
   444  
   445  		// enable the kernel on the bootloader and finally transition to
   446  		// run-mode last in case we get rebooted in between anywhere here
   447  
   448  		// it's okay to enable the kernel before writing the boot vars, because
   449  		// we haven't written snapd_recovery_mode=run, which is the critical
   450  		// thing that will inform the bootloader to try booting from ubuntu-boot
   451  		if err := ebl.EnableKernel(bootWith.Kernel); err != nil {
   452  			return err
   453  		}
   454  	} else {
   455  		// the bootloader does not support additional handling of
   456  		// extracted kernel images, we must name the kernel to be used
   457  		// explicitly in bootloader variables
   458  		blVars["snap_kernel"] = bootWith.Kernel.Filename()
   459  	}
   460  
   461  	// set the ubuntu-boot bootloader variables before triggering transition to
   462  	// try and boot from ubuntu-boot (that transition happens when we write
   463  	// snapd_recovery_mode below)
   464  	if err := bl.SetBootVars(blVars); err != nil {
   465  		return fmt.Errorf("cannot set run system environment: %v", err)
   466  	}
   467  
   468  	_, ok = bl.(bootloader.TrustedAssetsBootloader)
   469  	if ok {
   470  		// the bootloader can manage its boot config
   471  
   472  		// installing boot config must be performed after the boot
   473  		// partition has been populated with gadget data
   474  		if err := bl.InstallBootConfig(bootWith.UnpackedGadgetDir, opts); err != nil {
   475  			return fmt.Errorf("cannot install managed bootloader assets: %v", err)
   476  		}
   477  		// determine the expected command line
   478  		cmdline, err := ComposeCandidateCommandLine(model, bootWith.UnpackedGadgetDir)
   479  		if err != nil {
   480  			return fmt.Errorf("cannot compose the candidate command line: %v", err)
   481  		}
   482  		modeenv.CurrentKernelCommandLines = bootCommandLines{cmdline}
   483  
   484  		cmdlineVars, err := bootVarsForTrustedCommandLineFromGadget(bootWith.UnpackedGadgetDir)
   485  		if err != nil {
   486  			return fmt.Errorf("cannot prepare bootloader variables for kernel command line: %v", err)
   487  		}
   488  		if err := bl.SetBootVars(cmdlineVars); err != nil {
   489  			return fmt.Errorf("cannot set run system kernel command line arguments: %v", err)
   490  		}
   491  	}
   492  
   493  	// all fields that needed to be set in the modeenv must have been set by
   494  	// now, write modeenv to disk
   495  	if err := modeenv.WriteTo(InstallHostWritableDir(model)); err != nil {
   496  		return fmt.Errorf("cannot write modeenv: %v", err)
   497  	}
   498  
   499  	if sealer != nil {
   500  		hasHook, err := HasFDESetupHook(bootWith.Kernel)
   501  		if err != nil {
   502  			return fmt.Errorf("cannot check for fde-setup hook: %v", err)
   503  		}
   504  
   505  		flags := sealKeyToModeenvFlags{
   506  			HasFDESetupHook: hasHook,
   507  			FactoryReset:    makeOpts.AfterDataReset,
   508  		}
   509  		if makeOpts.Standalone {
   510  			flags.SnapsDir = snapBlobDir
   511  		}
   512  		// seal the encryption key to the parameters specified in modeenv
   513  		if err := sealKeyToModeenv(sealer.dataEncryptionKey, sealer.saveEncryptionKey, model, modeenv, flags); err != nil {
   514  			return err
   515  		}
   516  	}
   517  
   518  	// so far so good, we managed to install the system, so it can be used
   519  	// for recovery as well
   520  	if err := MarkRecoveryCapableSystem(recoverySystemLabel); err != nil {
   521  		return fmt.Errorf("cannot record %q as a recovery capable system: %v", recoverySystemLabel, err)
   522  	}
   523  	return nil
   524  }
   525  
   526  // MakeRunnableSystem is like MakeBootableImage in that it sets up a system to
   527  // be able to boot, but is unique in that it is intended to be called from UC20
   528  // install mode and makes the run system bootable (hence it is called
   529  // "runnable").
   530  // Note that this function does not update the recovery bootloader env to
   531  // actually transition to run mode here, that is left to the caller via
   532  // something like boot.EnsureNextBootToRunMode(). This is to enable separately
   533  // setting up a run system and actually transitioning to it, with hooks, etc.
   534  // running in between.
   535  func MakeRunnableSystem(model *asserts.Model, bootWith *BootableSet, sealer *TrustedAssetsInstallObserver) error {
   536  	return makeRunnableSystem(model, bootWith, sealer, makeRunnableOptions{})
   537  }
   538  
   539  // MakeRunnableStandaloneSystem operates like MakeRunnableSystem but does
   540  // assume that the run system being set up is related to the current
   541  // system. This is appropriate e.g when installing from a classic installer.
   542  func MakeRunnableStandaloneSystem(model *asserts.Model, bootWith *BootableSet, sealer *TrustedAssetsInstallObserver) error {
   543  	// TODO consider merging this back into MakeRunnableSystem but need
   544  	// to consider the properties of the different input used for sealing
   545  	return makeRunnableSystem(model, bootWith, sealer, makeRunnableOptions{
   546  		Standalone: true,
   547  	})
   548  }
   549  
   550  // MakeRunnableSystemAfterDataReset sets up the system to be able to boot, but it is
   551  // intended to be called from UC20 factory reset mode right before switching
   552  // back to the new run system.
   553  func MakeRunnableSystemAfterDataReset(model *asserts.Model, bootWith *BootableSet, sealer *TrustedAssetsInstallObserver) error {
   554  	return makeRunnableSystem(model, bootWith, sealer, makeRunnableOptions{
   555  		AfterDataReset: true,
   556  	})
   557  }