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 }