-
Notifications
You must be signed in to change notification settings - Fork 562
/
uboot.go
219 lines (182 loc) · 5.89 KB
/
uboot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2014-2022 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package bootloader
import (
"fmt"
"os"
"path/filepath"
"github.com/snapcore/snapd/bootloader/ubootenv"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
)
// uboot implements the required interfaces
var (
_ Bootloader = (*uboot)(nil)
_ ExtractedRecoveryKernelImageBootloader = (*uboot)(nil)
)
type uboot struct {
rootdir string
basedir string
ubootEnvFileName string
}
func (u *uboot) setDefaults() {
u.basedir = "/boot/uboot/"
u.ubootEnvFileName = "uboot.env"
}
func (u *uboot) processBlOpts(blOpts *Options) {
if blOpts != nil {
switch {
case blOpts.Role == RoleRecovery || blOpts.NoSlashBoot:
// RoleRecovery or NoSlashBoot imply we use
// the "boot.sel" simple text format file in
// /uboot/ubuntu as it exists on the partition
// directly
u.basedir = "/uboot/ubuntu/"
fallthrough
case blOpts.Role == RoleRunMode:
// if RoleRunMode (and no NoSlashBoot), we
// expect to find /boot/uboot/boot.sel
u.ubootEnvFileName = "boot.sel"
}
}
}
// newUboot create a new Uboot bootloader object
func newUboot(rootdir string, blOpts *Options) Bootloader {
u := &uboot{
rootdir: rootdir,
}
u.setDefaults()
u.processBlOpts(blOpts)
return u
}
func (u *uboot) Name() string {
return "uboot"
}
func (u *uboot) dir() string {
if u.rootdir == "" {
panic("internal error: unset rootdir")
}
return filepath.Join(u.rootdir, u.basedir)
}
func (u *uboot) useHeaderFlagByte(gadgetDir string) bool {
// if there is a "pattern" boot.sel in the gadget snap, we follow its
// lead. If opening it as a uboot env fails in any way we just go with
// the default.
gadgetEnv, err := ubootenv.OpenWithFlags(filepath.Join(gadgetDir, u.ubootEnvFileName), ubootenv.OpenBestEffort)
if err == nil {
return gadgetEnv.HeaderFlagByte()
}
// Otherwise we use the (historical) default and assume uboot is built with
// SYS_REDUNDAND_ENVIRONMENT=y
return true
}
func (u *uboot) InstallBootConfig(gadgetDir string, blOpts *Options) error {
gadgetFile := filepath.Join(gadgetDir, u.Name()+".conf")
// if the gadget file is empty, then we don't install anything
// this is because there are some gadgets, namely the 20 pi gadget right
// now, that don't use a uboot.env to boot and instead use a boot.scr, and
// installing a uboot.env file of any form in the root directory will break
// the boot.scr, so for these setups we just don't install anything
// TODO:UC20: how can we do this better? maybe parse the file to get the
// actual format?
st, err := os.Stat(gadgetFile)
if err != nil {
return err
}
if st.Size() == 0 {
// we have an empty uboot.conf, and hence a uboot bootloader in the
// gadget, but nothing to copy in this case and instead just install our
// own boot.sel file
u.processBlOpts(blOpts)
err := os.MkdirAll(filepath.Dir(u.envFile()), 0755)
if err != nil {
return err
}
// TODO:UC20: what's a reasonable size for this file?
env, err := ubootenv.Create(u.envFile(), 4096, ubootenv.CreateOptions{HeaderFlagByte: u.useHeaderFlagByte(gadgetDir)})
if err != nil {
return err
}
if err := env.Save(); err != nil {
return nil
}
return nil
}
// InstallBootConfig gets called on a uboot that does not come from newUboot
// so we need to apply the defaults here
u.setDefaults()
if blOpts != nil && blOpts.Role == RoleRecovery {
// not supported yet, this is traditional uboot.env from gadget
// TODO:UC20: support this use-case
return fmt.Errorf("non-empty uboot.env not supported on UC20+ yet")
}
systemFile := u.envFile()
return genericInstallBootConfig(gadgetFile, systemFile)
}
func (u *uboot) Present() (bool, error) {
return osutil.FileExists(u.envFile()), nil
}
func (u *uboot) envFile() string {
return filepath.Join(u.dir(), u.ubootEnvFileName)
}
func (u *uboot) SetBootVars(values map[string]string) error {
env, err := ubootenv.OpenWithFlags(u.envFile(), ubootenv.OpenBestEffort)
if err != nil {
return err
}
dirty := false
for k, v := range values {
// already set to the right value, nothing to do
if env.Get(k) == v {
continue
}
env.Set(k, v)
dirty = true
}
if dirty {
return env.Save()
}
return nil
}
func (u *uboot) GetBootVars(names ...string) (map[string]string, error) {
out := map[string]string{}
env, err := ubootenv.OpenWithFlags(u.envFile(), ubootenv.OpenBestEffort)
if err != nil {
return nil, err
}
for _, name := range names {
out[name] = env.Get(name)
}
return out, nil
}
func (u *uboot) ExtractKernelAssets(s snap.PlaceInfo, snapf snap.Container) error {
dstDir := filepath.Join(u.dir(), s.Filename())
assets := []string{"kernel.img", "initrd.img", "dtbs/*"}
return extractKernelAssetsToBootDir(dstDir, snapf, assets)
}
func (u *uboot) ExtractRecoveryKernelAssets(recoverySystemDir string, s snap.PlaceInfo, snapf snap.Container) error {
if recoverySystemDir == "" {
return fmt.Errorf("internal error: recoverySystemDir unset")
}
recoverySystemUbootKernelAssetsDir := filepath.Join(u.rootdir, recoverySystemDir, "kernel")
assets := []string{"kernel.img", "initrd.img", "dtbs/*"}
return extractKernelAssetsToBootDir(recoverySystemUbootKernelAssetsDir, snapf, assets)
}
func (u *uboot) RemoveKernelAssets(s snap.PlaceInfo) error {
return removeKernelAssetsFromBootDir(u.dir(), s)
}