gitee.com/mysnapcore/mysnapd@v0.1.0/bootloader/piboot_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2021 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 bootloader_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  	"time"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"gitee.com/mysnapcore/mysnapd/boot"
    32  	"gitee.com/mysnapcore/mysnapd/bootloader"
    33  	"gitee.com/mysnapcore/mysnapd/bootloader/ubootenv"
    34  	"gitee.com/mysnapcore/mysnapd/osutil"
    35  	"gitee.com/mysnapcore/mysnapd/snap"
    36  	"gitee.com/mysnapcore/mysnapd/snap/snapfile"
    37  	"gitee.com/mysnapcore/mysnapd/snap/snaptest"
    38  	"gitee.com/mysnapcore/mysnapd/testutil"
    39  )
    40  
    41  type pibootTestSuite struct {
    42  	baseBootenvTestSuite
    43  }
    44  
    45  var _ = Suite(&pibootTestSuite{})
    46  
    47  func (s *pibootTestSuite) TestNewPiboot(c *C) {
    48  	// no files means bl is not present, but we can still create the bl object
    49  	p := bootloader.NewPiboot(s.rootdir, nil)
    50  	c.Assert(p, NotNil)
    51  	c.Assert(p.Name(), Equals, "piboot")
    52  
    53  	present, err := p.Present()
    54  	c.Assert(err, IsNil)
    55  	c.Assert(present, Equals, false)
    56  
    57  	// now with files present, the bl is present
    58  	r := bootloader.MockPibootFiles(c, s.rootdir, nil)
    59  	defer r()
    60  	present, err = p.Present()
    61  	c.Assert(err, IsNil)
    62  	c.Assert(present, Equals, true)
    63  }
    64  
    65  func (s *pibootTestSuite) TestPibootGetEnvVar(c *C) {
    66  	// We need PrepareImageTime due to fixed reference to /run/mnt otherwise
    67  	opts := bootloader.Options{PrepareImageTime: true,
    68  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
    69  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
    70  	defer r()
    71  	p := bootloader.NewPiboot(s.rootdir, &opts)
    72  	c.Assert(p, NotNil)
    73  	err := p.SetBootVars(map[string]string{
    74  		"snap_mode": "",
    75  		"snap_core": "4",
    76  	})
    77  	c.Assert(err, IsNil)
    78  
    79  	m, err := p.GetBootVars("snap_mode", "snap_core")
    80  	c.Assert(err, IsNil)
    81  	c.Assert(m, DeepEquals, map[string]string{
    82  		"snap_mode": "",
    83  		"snap_core": "4",
    84  	})
    85  }
    86  
    87  func (s *pibootTestSuite) TestGetBootloaderWithPiboot(c *C) {
    88  	r := bootloader.MockPibootFiles(c, s.rootdir, nil)
    89  	defer r()
    90  
    91  	bootloader, err := bootloader.Find(s.rootdir, nil)
    92  	c.Assert(err, IsNil)
    93  	c.Assert(bootloader.Name(), Equals, "piboot")
    94  }
    95  
    96  func (s *pibootTestSuite) testPibootSetEnvWriteOnlyIfChanged(c *C, fromInitramfs bool) {
    97  	opts := bootloader.Options{PrepareImageTime: true,
    98  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
    99  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   100  	defer r()
   101  	p := bootloader.NewPiboot(s.rootdir, &opts)
   102  	c.Assert(p, NotNil)
   103  
   104  	envFile := bootloader.PibootConfigFile(p)
   105  	env, err := ubootenv.OpenWithFlags(envFile, ubootenv.OpenBestEffort)
   106  	c.Assert(err, IsNil)
   107  	env.Set("snap_ab", "b")
   108  	env.Set("snap_mode", "")
   109  	err = env.Save()
   110  	c.Assert(err, IsNil)
   111  
   112  	st, err := os.Stat(envFile)
   113  	c.Assert(err, IsNil)
   114  	time.Sleep(100 * time.Millisecond)
   115  
   116  	// note that we set to the same var to the same value as above
   117  	if fromInitramfs {
   118  		nsbl, ok := p.(bootloader.NotScriptableBootloader)
   119  		c.Assert(ok, Equals, true)
   120  		err = nsbl.SetBootVarsFromInitramfs(map[string]string{"snap_ab": "b"})
   121  	} else {
   122  		err = p.SetBootVars(map[string]string{"snap_ab": "b"})
   123  	}
   124  	c.Assert(err, IsNil)
   125  
   126  	st2, err := os.Stat(envFile)
   127  	c.Assert(err, IsNil)
   128  	c.Assert(st.ModTime(), Equals, st2.ModTime())
   129  }
   130  
   131  func (s *pibootTestSuite) TestPibootSetEnvWriteOnlyIfChanged(c *C) {
   132  	// Run test from rootfs and from initramfs
   133  	fromInitramfs := false
   134  	s.testPibootSetEnvWriteOnlyIfChanged(c, fromInitramfs)
   135  	fromInitramfs = true
   136  	s.testPibootSetEnvWriteOnlyIfChanged(c, fromInitramfs)
   137  }
   138  
   139  func (s *pibootTestSuite) testExtractKernelAssets(c *C, dtbDir string) {
   140  	opts := bootloader.Options{PrepareImageTime: true,
   141  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   142  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   143  	defer r()
   144  	p := bootloader.NewPiboot(s.rootdir, &opts)
   145  
   146  	files := [][]string{
   147  		{"kernel.img", "I'm a kernel"},
   148  		{"initrd.img", "...and I'm an initrd"},
   149  		{filepath.Join(dtbDir, "foo.dtb"), "g'day, I'm foo.dtb"},
   150  		{"dtbs/overlays/bar.dtbo", "hello, I'm bar.dtbo"},
   151  		// must be last
   152  		{"meta/kernel.yaml", "version: 4.2"},
   153  	}
   154  	fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
   155  	snapf, err := snapfile.Open(fn)
   156  	c.Assert(err, IsNil)
   157  
   158  	assetsDir, err := ioutil.TempDir("", "kernel-assets")
   159  	c.Assert(err, IsNil)
   160  	defer os.RemoveAll(assetsDir)
   161  
   162  	err = bootloader.LayoutKernelAssetsToDir(p, snapf, assetsDir)
   163  	c.Assert(err, IsNil)
   164  	// Do again, as extracting might be called again for an
   165  	// already extracted kernel.
   166  	err = bootloader.LayoutKernelAssetsToDir(p, snapf, assetsDir)
   167  	c.Assert(err, IsNil)
   168  
   169  	// Extraction folders for files slice
   170  	destDirs := []string{
   171  		assetsDir, assetsDir, assetsDir, filepath.Join(assetsDir, "overlays"),
   172  	}
   173  	for i, dir := range destDirs {
   174  		fullFn := filepath.Join(dir, filepath.Base(files[i][0]))
   175  		c.Check(fullFn, testutil.FileEquals, files[i][1])
   176  	}
   177  
   178  	// Check that file required by piboot is created
   179  	readmeFn := filepath.Join(assetsDir, "overlays", "README")
   180  	c.Check(readmeFn, testutil.FilePresent)
   181  }
   182  
   183  func (s *pibootTestSuite) TestExtractKernelAssets(c *C) {
   184  	// armhf and arm64 kernel snaps store dtbs in different places
   185  	s.testExtractKernelAssets(c, "dtbs")
   186  	s.testExtractKernelAssets(c, "dtbs/broadcom")
   187  }
   188  
   189  func (s *pibootTestSuite) testExtractRecoveryKernelAssets(c *C, dtbDir string) {
   190  	opts := bootloader.Options{PrepareImageTime: true,
   191  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   192  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   193  	defer r()
   194  	p := bootloader.NewPiboot(s.rootdir, &opts)
   195  
   196  	files := [][]string{
   197  		{"kernel.img", "I'm a kernel"},
   198  		{"initrd.img", "...and I'm an initrd"},
   199  		{filepath.Join(dtbDir, "foo.dtb"), "g'day, I'm foo.dtb"},
   200  		{"dtbs/overlays/bar.dtbo", "hello, I'm bar.dtbo"},
   201  		// must be last
   202  		{"meta/kernel.yaml", "version: 4.2"},
   203  	}
   204  	si := &snap.SideInfo{
   205  		RealName: "ubuntu-kernel",
   206  		Revision: snap.R(42),
   207  	}
   208  	fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
   209  	snapf, err := snapfile.Open(fn)
   210  	c.Assert(err, IsNil)
   211  
   212  	info, err := snap.ReadInfoFromSnapFile(snapf, si)
   213  	c.Assert(err, IsNil)
   214  
   215  	// try with empty recovery dir first to check the errors
   216  	err = p.ExtractRecoveryKernelAssets("", info, snapf)
   217  	c.Assert(err, ErrorMatches, "internal error: recoverySystemDir unset")
   218  
   219  	// now the expected behavior
   220  	err = p.ExtractRecoveryKernelAssets("recovery-dir", info, snapf)
   221  	c.Assert(err, IsNil)
   222  
   223  	// Extraction folders for files slice
   224  	assetsDir := filepath.Join(s.rootdir, "recovery-dir", "kernel")
   225  	destDirs := []string{
   226  		assetsDir, assetsDir, assetsDir, filepath.Join(assetsDir, "overlays"),
   227  	}
   228  	for i, dir := range destDirs {
   229  		fullFn := filepath.Join(dir, filepath.Base(files[i][0]))
   230  		c.Check(fullFn, testutil.FileEquals, files[i][1])
   231  	}
   232  
   233  	// Check that file required by piboot is created
   234  	readmeFn := filepath.Join(assetsDir, "overlays", "README")
   235  	c.Check(readmeFn, testutil.FilePresent)
   236  }
   237  
   238  func (s *pibootTestSuite) TestExtractRecoveryKernelAssets(c *C) {
   239  	// armhf and arm64 kernel snaps store dtbs in different places
   240  	s.testExtractRecoveryKernelAssets(c, "dtbs")
   241  	s.testExtractRecoveryKernelAssets(c, "dtbs/broadcom")
   242  }
   243  
   244  func (s *pibootTestSuite) TestPibootUC20OptsPlacement(c *C) {
   245  	tt := []struct {
   246  		blOpts  *bootloader.Options
   247  		expEnv  string
   248  		comment string
   249  	}{
   250  		{
   251  			&bootloader.Options{PrepareImageTime: true,
   252  				Role: bootloader.RoleRunMode, NoSlashBoot: true},
   253  			"/piboot/ubuntu/piboot.conf",
   254  			"uc20 install mode piboot.conf",
   255  		},
   256  		{
   257  			&bootloader.Options{PrepareImageTime: true,
   258  				Role: bootloader.RoleRunMode},
   259  			"/boot/piboot/piboot.conf",
   260  			"uc20 run mode piboot.conf",
   261  		},
   262  		{
   263  			&bootloader.Options{PrepareImageTime: true,
   264  				Role: bootloader.RoleRecovery},
   265  			"/piboot/ubuntu/piboot.conf",
   266  			"uc20 recovery piboot.conf",
   267  		},
   268  	}
   269  
   270  	for _, t := range tt {
   271  		dir := c.MkDir()
   272  		restore := bootloader.MockPibootFiles(c, dir, t.blOpts)
   273  		p := bootloader.NewPiboot(dir, t.blOpts)
   274  		c.Assert(p, NotNil, Commentf(t.comment))
   275  		c.Assert(bootloader.PibootConfigFile(p), Equals,
   276  			filepath.Join(dir, t.expEnv), Commentf(t.comment))
   277  
   278  		// if we set boot vars on the piboot, we can open the config file and
   279  		// get the same variables
   280  		c.Assert(p.SetBootVars(map[string]string{"hello": "there"}), IsNil)
   281  		env, err := ubootenv.OpenWithFlags(filepath.Join(dir, t.expEnv),
   282  			ubootenv.OpenBestEffort)
   283  		c.Assert(err, IsNil)
   284  		c.Assert(env.Get("hello"), Equals, "there")
   285  		restore()
   286  	}
   287  }
   288  
   289  func (s *pibootTestSuite) TestCreateConfig(c *C) {
   290  	opts := bootloader.Options{PrepareImageTime: false,
   291  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   292  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   293  	defer r()
   294  	p := bootloader.NewPiboot(s.rootdir, &opts)
   295  
   296  	err := p.SetBootVars(map[string]string{
   297  		"snap_kernel":         "pi-kernel_1",
   298  		"snapd_recovery_mode": "run",
   299  		"kernel_status":       boot.DefaultStatus})
   300  	c.Assert(err, IsNil)
   301  
   302  	files := []struct {
   303  		path string
   304  		data string
   305  	}{
   306  		{
   307  			path: filepath.Join(s.rootdir, "config.txt"),
   308  			data: "\nos_prefix=/piboot/ubuntu/pi-kernel_1/\n",
   309  		},
   310  		{
   311  			path: filepath.Join(s.rootdir, "piboot/ubuntu/pi-kernel_1/cmdline.txt"),
   312  			data: " snapd_recovery_mode=run\n",
   313  		},
   314  	}
   315  	for _, fInfo := range files {
   316  		readData, err := ioutil.ReadFile(fInfo.path)
   317  		c.Assert(err, IsNil)
   318  		c.Assert(string(readData), Equals, fInfo.data)
   319  	}
   320  }
   321  
   322  func (s *pibootTestSuite) TestCreateTrybootCfg(c *C) {
   323  	opts := bootloader.Options{PrepareImageTime: false,
   324  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   325  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   326  	defer r()
   327  	p := bootloader.NewPiboot(s.rootdir, &opts)
   328  
   329  	err := p.SetBootVars(map[string]string{
   330  		"snap_kernel":         "pi-kernel_1",
   331  		"snap_try_kernel":     "pi-kernel_2",
   332  		"snapd_recovery_mode": "run",
   333  		"kernel_status":       boot.TryStatus})
   334  	c.Assert(err, IsNil)
   335  
   336  	files := []struct {
   337  		path string
   338  		data string
   339  	}{
   340  		{
   341  			path: filepath.Join(s.rootdir, "tryboot.txt"),
   342  			data: "\nos_prefix=/piboot/ubuntu/pi-kernel_2/\n",
   343  		},
   344  		{
   345  			path: filepath.Join(s.rootdir, "piboot/ubuntu/pi-kernel_2/cmdline.txt"),
   346  			data: " snapd_recovery_mode=run kernel_status=trying\n",
   347  		},
   348  	}
   349  	for _, fInfo := range files {
   350  		readData, err := ioutil.ReadFile(fInfo.path)
   351  		c.Assert(err, IsNil)
   352  		c.Assert(string(readData), Equals, fInfo.data)
   353  	}
   354  
   355  	// Now set variables like in an after update reboot
   356  	err = p.SetBootVars(map[string]string{
   357  		"snap_kernel":         "pi-kernel_2",
   358  		"snap_try_kernel":     "",
   359  		"snapd_recovery_mode": "run",
   360  		"kernel_status":       boot.DefaultStatus})
   361  	c.Assert(err, IsNil)
   362  
   363  	c.Assert(osutil.FileExists(filepath.Join(s.rootdir, "tryboot.txt")), Equals, false)
   364  
   365  	files = []struct {
   366  		path string
   367  		data string
   368  	}{
   369  		{
   370  			path: filepath.Join(s.rootdir, "config.txt"),
   371  			data: "\nos_prefix=/piboot/ubuntu/pi-kernel_2/\n",
   372  		},
   373  		{
   374  			path: filepath.Join(s.rootdir, "piboot/ubuntu/pi-kernel_2/cmdline.txt"),
   375  			data: " snapd_recovery_mode=run\n",
   376  		},
   377  	}
   378  	for _, fInfo := range files {
   379  		readData, err := ioutil.ReadFile(fInfo.path)
   380  		c.Assert(err, IsNil)
   381  		c.Assert(string(readData), Equals, fInfo.data)
   382  	}
   383  }
   384  
   385  func (s *pibootTestSuite) TestCreateConfigCurrentNotEmpty(c *C) {
   386  	opts := bootloader.Options{PrepareImageTime: false,
   387  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   388  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   389  	defer r()
   390  
   391  	// Get some extra kernel command line parameters
   392  	err := ioutil.WriteFile(filepath.Join(s.rootdir, "cmdline.txt"),
   393  		[]byte("opt1=foo bar\n"), 0644)
   394  	c.Assert(err, IsNil)
   395  	// Add some options to already existing config.txt
   396  	err = ioutil.WriteFile(filepath.Join(s.rootdir, "config.txt"),
   397  		[]byte("rpi.option1=val\nos_prefix=1\nrpi.option2=val\n"), 0644)
   398  	c.Assert(err, IsNil)
   399  	p := bootloader.NewPiboot(s.rootdir, &opts)
   400  
   401  	err = p.SetBootVars(map[string]string{
   402  		"snap_kernel":         "pi-kernel_1",
   403  		"snapd_recovery_mode": "run",
   404  		"kernel_status":       boot.DefaultStatus})
   405  	c.Assert(err, IsNil)
   406  
   407  	files := []struct {
   408  		path string
   409  		data string
   410  	}{
   411  		{
   412  			path: filepath.Join(s.rootdir, "config.txt"),
   413  			data: "rpi.option1=val\nos_prefix=/piboot/ubuntu/pi-kernel_1/\nrpi.option2=val\n",
   414  		},
   415  		{
   416  			path: filepath.Join(s.rootdir, "piboot/ubuntu/pi-kernel_1/cmdline.txt"),
   417  			data: "opt1=foo bar snapd_recovery_mode=run\n",
   418  		},
   419  	}
   420  	for _, fInfo := range files {
   421  		readData, err := ioutil.ReadFile(fInfo.path)
   422  		c.Assert(err, IsNil)
   423  		c.Assert(string(readData), Equals, fInfo.data)
   424  	}
   425  
   426  	// Now set variables like in an update
   427  	err = p.SetBootVars(map[string]string{
   428  		"snap_kernel":         "pi-kernel_1",
   429  		"snap_try_kernel":     "pi-kernel_2",
   430  		"snapd_recovery_mode": "run",
   431  		"kernel_status":       boot.TryStatus})
   432  	c.Assert(err, IsNil)
   433  
   434  	files = []struct {
   435  		path string
   436  		data string
   437  	}{
   438  		{
   439  			path: filepath.Join(s.rootdir, "tryboot.txt"),
   440  			data: "rpi.option1=val\nos_prefix=/piboot/ubuntu/pi-kernel_2/\nrpi.option2=val\n",
   441  		},
   442  		{
   443  			path: filepath.Join(s.rootdir, "config.txt"),
   444  			data: "rpi.option1=val\nos_prefix=/piboot/ubuntu/pi-kernel_1/\nrpi.option2=val\n",
   445  		},
   446  		{
   447  			path: filepath.Join(s.rootdir, "piboot/ubuntu/pi-kernel_2/cmdline.txt"),
   448  			data: "opt1=foo bar snapd_recovery_mode=run kernel_status=trying\n",
   449  		},
   450  	}
   451  	for _, fInfo := range files {
   452  		readData, err := ioutil.ReadFile(fInfo.path)
   453  		c.Assert(err, IsNil)
   454  		c.Assert(string(readData), Equals, fInfo.data)
   455  	}
   456  }
   457  
   458  func (s *pibootTestSuite) TestOnlyOneOsPrefix(c *C) {
   459  	opts := bootloader.Options{PrepareImageTime: false,
   460  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   461  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   462  	defer r()
   463  
   464  	// Introuce two os_prefix lines
   465  	err := ioutil.WriteFile(filepath.Join(s.rootdir, "config.txt"),
   466  		[]byte("os_prefix=1\nos_prefix=2\n"), 0644)
   467  	c.Assert(err, IsNil)
   468  	p := bootloader.NewPiboot(s.rootdir, &opts)
   469  
   470  	err = p.SetBootVars(map[string]string{
   471  		"snap_kernel":         "pi-kernel_1",
   472  		"snapd_recovery_mode": "run",
   473  		"kernel_status":       boot.DefaultStatus})
   474  	c.Assert(err, IsNil)
   475  
   476  	files := []struct {
   477  		path string
   478  		data string
   479  	}{
   480  		{
   481  			path: filepath.Join(s.rootdir, "config.txt"),
   482  			data: "os_prefix=/piboot/ubuntu/pi-kernel_1/\n# os_prefix=2\n",
   483  		},
   484  		{
   485  			path: filepath.Join(s.rootdir, "piboot/ubuntu/pi-kernel_1/cmdline.txt"),
   486  			data: " snapd_recovery_mode=run\n",
   487  		},
   488  	}
   489  	for _, fInfo := range files {
   490  		readData, err := ioutil.ReadFile(fInfo.path)
   491  		c.Assert(err, IsNil)
   492  		c.Assert(string(readData), Equals, fInfo.data)
   493  	}
   494  }
   495  
   496  func (s *pibootTestSuite) TestGetRebootArguments(c *C) {
   497  	opts := bootloader.Options{PrepareImageTime: false,
   498  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   499  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   500  	defer r()
   501  	p := bootloader.NewPiboot(s.rootdir, &opts)
   502  	c.Assert(p, NotNil)
   503  	rbl, ok := p.(bootloader.RebootBootloader)
   504  	c.Assert(ok, Equals, true)
   505  
   506  	args, err := rbl.GetRebootArguments()
   507  	c.Assert(err, IsNil)
   508  	c.Assert(args, Equals, "")
   509  
   510  	err = p.SetBootVars(map[string]string{"kernel_status": "try"})
   511  	c.Assert(err, IsNil)
   512  
   513  	args, err = rbl.GetRebootArguments()
   514  	c.Assert(err, IsNil)
   515  	c.Assert(args, Equals, "0 tryboot")
   516  	err = p.SetBootVars(map[string]string{"kernel_status": ""})
   517  	c.Assert(err, IsNil)
   518  }
   519  
   520  func (s *pibootTestSuite) TestGetRebootArgumentsNoEnv(c *C) {
   521  	opts := bootloader.Options{PrepareImageTime: false,
   522  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   523  	p := bootloader.NewPiboot(s.rootdir, &opts)
   524  	c.Assert(p, NotNil)
   525  	rbl, ok := p.(bootloader.RebootBootloader)
   526  	c.Assert(ok, Equals, true)
   527  
   528  	args, err := rbl.GetRebootArguments()
   529  	c.Assert(err, ErrorMatches, "open .*/piboot.conf: no such file or directory")
   530  	c.Assert(args, Equals, "")
   531  }
   532  
   533  func (s *pibootTestSuite) TestSetBootVarsFromInitramfs(c *C) {
   534  	opts := bootloader.Options{PrepareImageTime: false,
   535  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   536  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   537  	defer r()
   538  	p := bootloader.NewPiboot(s.rootdir, &opts)
   539  	c.Assert(p, NotNil)
   540  	nsbl, ok := p.(bootloader.NotScriptableBootloader)
   541  	c.Assert(ok, Equals, true)
   542  
   543  	err := nsbl.SetBootVarsFromInitramfs(map[string]string{"kernel_status": "trying"})
   544  	c.Assert(err, IsNil)
   545  
   546  	m, err := p.GetBootVars("kernel_status")
   547  	c.Assert(err, IsNil)
   548  	c.Assert(m, DeepEquals, map[string]string{
   549  		"kernel_status": "trying",
   550  	})
   551  }
   552  
   553  func (s *pibootTestSuite) testExtractKernelAssetsAndRemove(c *C, dtbDir string) {
   554  	opts := bootloader.Options{PrepareImageTime: false,
   555  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   556  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   557  	defer r()
   558  	p := bootloader.NewPiboot(s.rootdir, &opts)
   559  	c.Assert(p, NotNil)
   560  
   561  	files := [][]string{
   562  		{"kernel.img", "I'm a kernel"},
   563  		{"initrd.img", "...and I'm an initrd"},
   564  		{filepath.Join(dtbDir, "foo.dtb"), "g'day, I'm foo.dtb"},
   565  		{"dtbs/overlays/bar.dtbo", "hello, I'm bar.dtbo"},
   566  		// must be last
   567  		{"meta/kernel.yaml", "version: 4.2"},
   568  	}
   569  	si := &snap.SideInfo{
   570  		RealName: "ubuntu-kernel",
   571  		Revision: snap.R(42),
   572  	}
   573  	fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
   574  	snapf, err := snapfile.Open(fn)
   575  	c.Assert(err, IsNil)
   576  
   577  	info, err := snap.ReadInfoFromSnapFile(snapf, si)
   578  	c.Assert(err, IsNil)
   579  
   580  	err = p.ExtractKernelAssets(info, snapf)
   581  	c.Assert(err, IsNil)
   582  
   583  	// this is where the kernel/initrd is unpacked
   584  	kernelAssetsDir := filepath.Join(s.rootdir, "piboot", "ubuntu", "ubuntu-kernel_42.snap")
   585  
   586  	for _, def := range files {
   587  		if def[0] == "meta/kernel.yaml" {
   588  			break
   589  		}
   590  
   591  		destPath := def[0]
   592  		if strings.HasPrefix(destPath, "dtbs/broadcom/") {
   593  			destPath = strings.TrimPrefix(destPath, "dtbs/broadcom/")
   594  		} else if strings.HasPrefix(destPath, "dtbs/") {
   595  			destPath = strings.TrimPrefix(destPath, "dtbs/")
   596  		}
   597  		fullFn := filepath.Join(kernelAssetsDir, destPath)
   598  		c.Check(fullFn, testutil.FileEquals, def[1])
   599  	}
   600  
   601  	// remove
   602  	err = p.RemoveKernelAssets(info)
   603  	c.Assert(err, IsNil)
   604  
   605  	c.Check(osutil.FileExists(kernelAssetsDir), Equals, false)
   606  }
   607  
   608  func (s *pibootTestSuite) TestExtractKernelAssetsAndRemove(c *C) {
   609  	// armhf and arm64 kernel snaps store dtbs in different places
   610  	s.testExtractKernelAssetsAndRemove(c, "dtbs")
   611  	s.testExtractKernelAssetsAndRemove(c, "dtbs/broadcom")
   612  }
   613  
   614  func (s *pibootTestSuite) testExtractKernelAssetsOnRPi4CheckEeprom(c *C, rpiRevisionCode, eepromTimeStamp []byte, errExpected bool) {
   615  	opts := bootloader.Options{PrepareImageTime: false,
   616  		Role: bootloader.RoleRunMode, NoSlashBoot: true}
   617  	r := bootloader.MockPibootFiles(c, s.rootdir, &opts)
   618  	defer r()
   619  	r = bootloader.MockRPi4Files(c, s.rootdir, rpiRevisionCode, eepromTimeStamp)
   620  	defer r()
   621  	p := bootloader.NewPiboot(s.rootdir, &opts)
   622  	c.Assert(p, NotNil)
   623  
   624  	files := [][]string{
   625  		{"kernel.img", "I'm a kernel"},
   626  		{"initrd.img", "...and I'm an initrd"},
   627  		{"dtbs/broadcom/foo.dtb", "g'day, I'm foo.dtb"},
   628  		{"dtbs/overlays/bar.dtbo", "hello, I'm bar.dtbo"},
   629  		// must be last
   630  		{"meta/kernel.yaml", "version: 4.2"},
   631  	}
   632  	si := &snap.SideInfo{
   633  		RealName: "ubuntu-kernel",
   634  		Revision: snap.R(42),
   635  	}
   636  	fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
   637  	snapf, err := snapfile.Open(fn)
   638  	c.Assert(err, IsNil)
   639  
   640  	info, err := snap.ReadInfoFromSnapFile(snapf, si)
   641  	c.Assert(err, IsNil)
   642  
   643  	err = p.ExtractKernelAssets(info, snapf)
   644  	if errExpected {
   645  		c.Check(err.Error(), Equals,
   646  			"your EEPROM does not support tryboot, please upgrade to a newer one before installing Ubuntu Core - see http://forum.snapcraft.io/t/29455 for more details")
   647  		return
   648  	}
   649  
   650  	c.Assert(err, IsNil)
   651  
   652  	// this is where the kernel/initrd is unpacked
   653  	kernelAssetsDir := filepath.Join(s.rootdir, "piboot", "ubuntu", "ubuntu-kernel_42.snap")
   654  
   655  	for _, def := range files {
   656  		if def[0] == "meta/kernel.yaml" {
   657  			break
   658  		}
   659  
   660  		destPath := def[0]
   661  		if strings.HasPrefix(destPath, "dtbs/broadcom/") {
   662  			destPath = strings.TrimPrefix(destPath, "dtbs/broadcom/")
   663  		} else if strings.HasPrefix(destPath, "dtbs/") {
   664  			destPath = strings.TrimPrefix(destPath, "dtbs/")
   665  		}
   666  		fullFn := filepath.Join(kernelAssetsDir, destPath)
   667  		c.Check(fullFn, testutil.FileEquals, def[1])
   668  	}
   669  
   670  	// remove
   671  	err = p.RemoveKernelAssets(info)
   672  	c.Assert(err, IsNil)
   673  
   674  	c.Check(osutil.FileExists(kernelAssetsDir), Equals, false)
   675  }
   676  
   677  func (s *pibootTestSuite) TestExtractKernelAssetsOnRPi4CheckEeprom(c *C) {
   678  	// Rev code is RPi4, eeprom supports tryboot
   679  	expectFailure := false
   680  	s.testExtractKernelAssetsOnRPi4CheckEeprom(c,
   681  		[]byte{0x00, 0xc0, 0x31, 0x11},
   682  		[]byte{0x61, 0xf0, 0x09, 0x91},
   683  		expectFailure)
   684  	// Rev code is RPi4, eeprom does not support tryboot
   685  	expectFailure = true
   686  	s.testExtractKernelAssetsOnRPi4CheckEeprom(c,
   687  		[]byte{0x00, 0xc0, 0x31, 0x11},
   688  		[]byte{0x60, 0x53, 0x15, 0x32},
   689  		expectFailure)
   690  	// Rev code is RPi3
   691  	expectFailure = false
   692  	s.testExtractKernelAssetsOnRPi4CheckEeprom(c,
   693  		[]byte{0x00, 0xa0, 0x20, 0x82},
   694  		[]byte{},
   695  		expectFailure)
   696  }