gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/bootloader/uboot.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 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 "os" 25 "path/filepath" 26 27 "github.com/snapcore/snapd/bootloader/ubootenv" 28 "github.com/snapcore/snapd/osutil" 29 "github.com/snapcore/snapd/snap" 30 ) 31 32 // sanity - uboot implements the required interfaces 33 var ( 34 _ Bootloader = (*uboot)(nil) 35 _ ExtractedRecoveryKernelImageBootloader = (*uboot)(nil) 36 ) 37 38 type uboot struct { 39 rootdir string 40 basedir string 41 42 ubootEnvFileName string 43 } 44 45 func (u *uboot) setDefaults() { 46 u.basedir = "/boot/uboot/" 47 u.ubootEnvFileName = "uboot.env" 48 } 49 50 func (u *uboot) processBlOpts(blOpts *Options) { 51 if blOpts != nil { 52 switch { 53 case blOpts.Role == RoleRecovery || blOpts.NoSlashBoot: 54 // RoleRecovery or NoSlashBoot imply we use 55 // the "boot.sel" simple text format file in 56 // /uboot/ubuntu as it exists on the partition 57 // directly 58 u.basedir = "/uboot/ubuntu/" 59 fallthrough 60 case blOpts.Role == RoleRunMode: 61 // if RoleRunMode (and no NoSlashBoot), we 62 // expect to find /boot/uboot/boot.sel 63 u.ubootEnvFileName = "boot.sel" 64 } 65 } 66 } 67 68 // newUboot create a new Uboot bootloader object 69 func newUboot(rootdir string, blOpts *Options) Bootloader { 70 u := &uboot{ 71 rootdir: rootdir, 72 } 73 u.setDefaults() 74 u.processBlOpts(blOpts) 75 76 return u 77 } 78 79 func (u *uboot) Name() string { 80 return "uboot" 81 } 82 83 func (u *uboot) dir() string { 84 if u.rootdir == "" { 85 panic("internal error: unset rootdir") 86 } 87 return filepath.Join(u.rootdir, u.basedir) 88 } 89 90 func (u *uboot) InstallBootConfig(gadgetDir string, blOpts *Options) error { 91 gadgetFile := filepath.Join(gadgetDir, u.Name()+".conf") 92 // if the gadget file is empty, then we don't install anything 93 // this is because there are some gadgets, namely the 20 pi gadget right 94 // now, that don't use a uboot.env to boot and instead use a boot.scr, and 95 // installing a uboot.env file of any form in the root directory will break 96 // the boot.scr, so for these setups we just don't install anything 97 // TODO:UC20: how can we do this better? maybe parse the file to get the 98 // actual format? 99 st, err := os.Stat(gadgetFile) 100 if err != nil { 101 return err 102 } 103 if st.Size() == 0 { 104 // we have an empty uboot.conf, and hence a uboot bootloader in the 105 // gadget, but nothing to copy in this case and instead just install our 106 // own boot.sel file 107 u.processBlOpts(blOpts) 108 109 err := os.MkdirAll(filepath.Dir(u.envFile()), 0755) 110 if err != nil { 111 return err 112 } 113 114 // TODO:UC20: what's a reasonable size for this file? 115 env, err := ubootenv.Create(u.envFile(), 4096) 116 if err != nil { 117 return err 118 } 119 120 if err := env.Save(); err != nil { 121 return nil 122 } 123 124 return nil 125 } 126 127 // InstallBootConfig gets called on a uboot that does not come from newUboot 128 // so we need to apply the defaults here 129 u.setDefaults() 130 131 if blOpts != nil && blOpts.Role == RoleRecovery { 132 // not supported yet, this is traditional uboot.env from gadget 133 // TODO:UC20: support this use-case 134 return fmt.Errorf("non-empty uboot.env not supported on UC20 yet") 135 } 136 137 systemFile := u.envFile() 138 return genericInstallBootConfig(gadgetFile, systemFile) 139 } 140 141 func (u *uboot) Present() (bool, error) { 142 return osutil.FileExists(u.envFile()), nil 143 } 144 145 func (u *uboot) envFile() string { 146 return filepath.Join(u.dir(), u.ubootEnvFileName) 147 } 148 149 func (u *uboot) SetBootVars(values map[string]string) error { 150 env, err := ubootenv.OpenWithFlags(u.envFile(), ubootenv.OpenBestEffort) 151 if err != nil { 152 return err 153 } 154 155 dirty := false 156 for k, v := range values { 157 // already set to the right value, nothing to do 158 if env.Get(k) == v { 159 continue 160 } 161 env.Set(k, v) 162 dirty = true 163 } 164 165 if dirty { 166 return env.Save() 167 } 168 169 return nil 170 } 171 172 func (u *uboot) GetBootVars(names ...string) (map[string]string, error) { 173 out := map[string]string{} 174 175 env, err := ubootenv.OpenWithFlags(u.envFile(), ubootenv.OpenBestEffort) 176 if err != nil { 177 return nil, err 178 } 179 180 for _, name := range names { 181 out[name] = env.Get(name) 182 } 183 184 return out, nil 185 } 186 187 func (u *uboot) ExtractKernelAssets(s snap.PlaceInfo, snapf snap.Container) error { 188 dstDir := filepath.Join(u.dir(), s.Filename()) 189 assets := []string{"kernel.img", "initrd.img", "dtbs/*"} 190 return extractKernelAssetsToBootDir(dstDir, snapf, assets) 191 } 192 193 func (u *uboot) ExtractRecoveryKernelAssets(recoverySystemDir string, s snap.PlaceInfo, snapf snap.Container) error { 194 if recoverySystemDir == "" { 195 return fmt.Errorf("internal error: recoverySystemDir unset") 196 } 197 198 recoverySystemUbootKernelAssetsDir := filepath.Join(u.rootdir, recoverySystemDir, "kernel") 199 assets := []string{"kernel.img", "initrd.img", "dtbs/*"} 200 return extractKernelAssetsToBootDir(recoverySystemUbootKernelAssetsDir, snapf, assets) 201 } 202 203 func (u *uboot) RemoveKernelAssets(s snap.PlaceInfo) error { 204 return removeKernelAssetsFromBootDir(u.dir(), s) 205 }