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