github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/bootloader/lk.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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
    21  
    22  import (
    23  	"fmt"
    24  	"io"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	"github.com/snapcore/snapd/bootloader/lkenv"
    30  	"github.com/snapcore/snapd/logger"
    31  	"github.com/snapcore/snapd/snap"
    32  )
    33  
    34  type lk struct {
    35  	rootdir       string
    36  	inRuntimeMode bool
    37  }
    38  
    39  // newLk create a new lk bootloader object
    40  func newLk(rootdir string, opts *Options) Bootloader {
    41  	l := &lk{rootdir: rootdir}
    42  
    43  	if opts != nil {
    44  		// XXX: in the long run we want this to go away, we probably add
    45  		//      something like "boot.PrepareImage()" and add an (optional)
    46  		//      method "PrepareImage" to the bootloader interface that is
    47  		//      used to setup a bootloader from prepare-image if things
    48  		//      are very different from runtime vs image-building mode.
    49  		//
    50  		// determine mode we are in, runtime or image build
    51  		l.inRuntimeMode = !opts.PrepareImageTime
    52  	}
    53  
    54  	return l
    55  }
    56  
    57  func (l *lk) setRootDir(rootdir string) {
    58  	l.rootdir = rootdir
    59  }
    60  
    61  func (l *lk) Name() string {
    62  	return "lk"
    63  }
    64  
    65  func (l *lk) dir() string {
    66  	// we have two scenarios, image building and runtime
    67  	// during image building we store environment into file
    68  	// at runtime environment is written directly into dedicated partition
    69  	if l.inRuntimeMode {
    70  		return filepath.Join(l.rootdir, "/dev/disk/by-partlabel/")
    71  	}
    72  	return filepath.Join(l.rootdir, "/boot/lk/")
    73  }
    74  
    75  func (l *lk) InstallBootConfig(gadgetDir string, opts *Options) (bool, error) {
    76  	gadgetFile := filepath.Join(gadgetDir, l.Name()+".conf")
    77  	systemFile := l.ConfigFile()
    78  	return genericInstallBootConfig(gadgetFile, systemFile)
    79  }
    80  
    81  func (l *lk) ConfigFile() string {
    82  	return l.envFile()
    83  }
    84  
    85  func (l *lk) envFile() string {
    86  	// as for dir, we have two scenarios, image building and runtime
    87  	if l.inRuntimeMode {
    88  		// TO-DO: this should be eventually fetched from gadget.yaml
    89  		return filepath.Join(l.dir(), "snapbootsel")
    90  	}
    91  	return filepath.Join(l.dir(), "snapbootsel.bin")
    92  }
    93  
    94  func (l *lk) GetBootVars(names ...string) (map[string]string, error) {
    95  	out := make(map[string]string)
    96  
    97  	env := lkenv.NewEnv(l.envFile())
    98  	if err := env.Load(); err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	for _, name := range names {
   103  		out[name] = env.Get(name)
   104  	}
   105  
   106  	return out, nil
   107  }
   108  
   109  func (l *lk) SetBootVars(values map[string]string) error {
   110  	env := lkenv.NewEnv(l.envFile())
   111  	if err := env.Load(); err != nil && !os.IsNotExist(err) {
   112  		return err
   113  	}
   114  
   115  	// update environment only if something change
   116  	dirty := false
   117  	for k, v := range values {
   118  		// already set to the right value, nothing to do
   119  		if env.Get(k) == v {
   120  			continue
   121  		}
   122  		env.Set(k, v)
   123  		dirty = true
   124  	}
   125  
   126  	if dirty {
   127  		return env.Save()
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  // ExtractKernelAssets extract kernel assets per bootloader specifics
   134  // lk bootloader requires boot partition to hold valid boot image
   135  // there are two boot partition available, one holding current bootimage
   136  // kernel assets are extracted to other (free) boot partition
   137  // in case this function is called as part of image creation,
   138  // boot image is extracted to the file
   139  func (l *lk) ExtractKernelAssets(s snap.PlaceInfo, snapf snap.Container) error {
   140  	blobName := s.Filename()
   141  
   142  	logger.Debugf("ExtractKernelAssets (%s)", blobName)
   143  
   144  	env := lkenv.NewEnv(l.envFile())
   145  	if err := env.Load(); err != nil && !os.IsNotExist(err) {
   146  		return err
   147  	}
   148  
   149  	bootPartition, err := env.FindFreeBootPartition(blobName)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	if l.inRuntimeMode {
   155  		logger.Debugf("ExtractKernelAssets handling run time usecase")
   156  		// this is live system, extracted bootimg needs to be flashed to
   157  		// free bootimg partition and env has to be updated with
   158  		// new kernel snap to bootimg partition mapping
   159  		tmpdir, err := ioutil.TempDir("", "bootimg")
   160  		if err != nil {
   161  			return fmt.Errorf("cannot create temp directory: %v", err)
   162  		}
   163  		defer os.RemoveAll(tmpdir)
   164  
   165  		bootImg := env.GetBootImageName()
   166  		if err := snapf.Unpack(bootImg, tmpdir); err != nil {
   167  			return fmt.Errorf("cannot unpack %s: %v", bootImg, err)
   168  		}
   169  		// write boot.img to free boot partition
   170  		bootimgName := filepath.Join(tmpdir, bootImg)
   171  		bif, err := os.Open(bootimgName)
   172  		if err != nil {
   173  			return fmt.Errorf("cannot open unpacked %s: %v", bootImg, err)
   174  		}
   175  		defer bif.Close()
   176  		bpart := filepath.Join(l.dir(), bootPartition)
   177  
   178  		bpf, err := os.OpenFile(bpart, os.O_WRONLY, 0660)
   179  		if err != nil {
   180  			return fmt.Errorf("cannot open boot partition [%s]: %v", bpart, err)
   181  		}
   182  		defer bpf.Close()
   183  
   184  		if _, err := io.Copy(bpf, bif); err != nil {
   185  			return err
   186  		}
   187  	} else {
   188  		// we are preparing image, just extract boot image to bootloader directory
   189  		logger.Debugf("ExtractKernelAssets handling image prepare")
   190  		if err := snapf.Unpack(env.GetBootImageName(), l.dir()); err != nil {
   191  			return fmt.Errorf("cannot open unpacked %s: %v", env.GetBootImageName(), err)
   192  		}
   193  	}
   194  	if err := env.SetBootPartition(bootPartition, blobName); err != nil {
   195  		return err
   196  	}
   197  
   198  	return env.Save()
   199  }
   200  
   201  func (l *lk) RemoveKernelAssets(s snap.PlaceInfo) error {
   202  	blobName := s.Filename()
   203  	logger.Debugf("RemoveKernelAssets (%s)", blobName)
   204  	env := lkenv.NewEnv(l.envFile())
   205  	if err := env.Load(); err != nil && !os.IsNotExist(err) {
   206  		return err
   207  	}
   208  	err := env.RemoveKernelRevisionFromBootPartition(blobName)
   209  	if err == nil {
   210  		// found and removed the revision from the bootimg matrix, need to
   211  		// update the env to persist the change
   212  		return env.Save()
   213  	}
   214  	return nil
   215  }