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 }