Skip to content

Commit

Permalink
bootloader/lk: add support for UC20 lk bootloader with V2 lkenv structs
Browse files Browse the repository at this point in the history
This commit adds support for the UC20 LittleKernel bootloader to snapd.

The big changes for using V2 are that we now use a secure bootloader set kernel
command line parameter to identify what disk to look for bootloader environment
structures, and that we use different lkenv structs for different role
bootloaders.

Eventually we should make V1 also use a secure bootloader set kernel command
line parameter to identify what disk to look for bootloader environment
structures on too, as using the partition label like this is vulnerable to
attack by attaching a USB disk with the same partition label to the system.

Signed-off-by: Ian Johnson <ian.johnson@canonical.com>
  • Loading branch information
anonymouse64 committed Nov 25, 2020
1 parent b022e2b commit 6e98320
Show file tree
Hide file tree
Showing 3 changed files with 579 additions and 82 deletions.
103 changes: 91 additions & 12 deletions bootloader/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (

"github.com/snapcore/snapd/bootloader/lkenv"
"github.com/snapcore/snapd/bootloader/ubootenv"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/osutil/disks"
)

// creates a new Androidboot bootloader object
Expand Down Expand Up @@ -72,14 +74,16 @@ func MockGrubFiles(c *C, rootdir string) {
c.Assert(err, IsNil)
}

func NewLk(rootdir string, opts *Options) Bootloader {
func NewLk(rootdir string, opts *Options) ExtractedRecoveryKernelImageBootloader {
if opts == nil {
opts = &Options{}
opts = &Options{
Role: RoleSole,
}
}
return newLk(rootdir, opts)
return newLk(rootdir, opts).(ExtractedRecoveryKernelImageBootloader)
}

func LkConfigFile(b Bootloader) string {
func LkConfigFile(b Bootloader) (string, error) {
lk := b.(*lk)
return lk.envFile()
}
Expand All @@ -89,23 +93,98 @@ func UbootConfigFile(b Bootloader) string {
return u.envFile()
}

func MockLkFiles(c *C, rootdir string, opts *Options) {
func MockLkFiles(c *C, rootdir string, opts *Options) (restore func()) {
var cleanups []func()
if opts == nil {
opts = &Options{}
// default to v1, uc16/uc18 version for test simplicity
opts = &Options{
Role: RoleSole,
}
}

l := &lk{
rootdir: rootdir,
inRuntimeMode: !opts.PrepareImageTime,
role: opts.Role,
}

var version lkenv.Version
switch opts.Role {
case RoleSole:
version = lkenv.V1
case RoleRunMode:
version = lkenv.V2Run
case RoleRecovery:
version = lkenv.V2Recovery
}

// setup some role specific things
if opts.Role == RoleRunMode || opts.Role == RoleRecovery {
// then we need to setup some additional files - namely the kernel
// command line and a mock disk for that
lkBootDisk := &disks.MockDiskMapping{
FilesystemLabelToPartUUID: map[string]string{
"snapbootsel": "snapbootsel-partuuid",
"snapbootselbak": "snapbootselbak-partuuid",
"snaprecoverysel": "snaprecoverysel-partuuid",
"snaprecoveryselbak": "snaprecoveryselbak-partuuid",
"boot_a": "boot-a-partuuid",
"boot_b": "boot-b-partuuid",
},
DiskHasPartitions: true,
DevNum: "lk-boot-disk-dev-num",
}

m := map[string]*disks.MockDiskMapping{
"lk-boot-disk": lkBootDisk,
}

// mock the disk
r := disks.MockDeviceNameDisksToPartitionMapping(m)
cleanups = append(cleanups, r)

// create the disk files so they exist for

// now mock the kernel command line
cmdLine := filepath.Join(c.MkDir(), "cmdline")
ioutil.WriteFile(cmdLine, []byte("snapd_lk_boot_disk=lk-boot-disk"), 0644)
r = osutil.MockProcCmdline(cmdLine)
cleanups = append(cleanups, r)
}
l := &lk{rootdir: rootdir, inRuntimeMode: !opts.PrepareImageTime}
err := os.MkdirAll(l.dir(), 0755)
c.Assert(err, IsNil)

// first create empty env file
// next create empty env file
buf := make([]byte, 4096)
err = ioutil.WriteFile(l.envFile(), buf, 0660)
f, err := l.envFile()
c.Assert(err, IsNil)

c.Assert(os.MkdirAll(filepath.Dir(f), 0755), IsNil)
err = ioutil.WriteFile(f, buf, 0660)
c.Assert(err, IsNil)

// now write env in it with correct crc
env := lkenv.NewEnv(l.envFile(), lkenv.V1)
env := lkenv.NewEnv(f, version)
env.InitializeBootPartitions("boot_a", "boot_b")
err = env.Save()
c.Assert(err, IsNil)

// also make the empty files for the boot_a and boot_b partitions for uc20
// roles
if opts.Role == RoleRunMode || opts.Role == RoleRecovery {
for _, label := range []string{"boot_a", "boot_b"} {
disk, err := disks.DiskFromDeviceName("lk-boot-disk")
c.Assert(err, IsNil)
partUUID, err := disk.FindMatchingPartitionUUID(label)
c.Assert(err, IsNil)
bootFile := filepath.Join(rootdir, "/dev/disk/by-partuuid", partUUID)
c.Assert(os.MkdirAll(filepath.Dir(bootFile), 0755), IsNil)
c.Assert(ioutil.WriteFile(bootFile, nil, 0755), IsNil)
}
}
return func() {
for _, r := range cleanups {
r()
}
}
}

func LkRuntimeMode(b Bootloader) bool {
Expand Down

0 comments on commit 6e98320

Please sign in to comment.