blob: b95d2cd4ba9b401f096cde926829f97c946e5ebd [file] [log] [blame]
Simon Glass89b86b82011-07-17 23:49:49 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This module builds a firmware image for a tegra-based board.
6
7This modules uses a few rudimentary other libraries for its activity.
8
9Here are the names we give to the various files we deal with. It is important
10to keep these consistent!
11
12 uboot u-boot.bin (with no device tree)
13 fdt the fdt blob
14 bct the BCT file
15 bootstub uboot + fdt
16 signed (uboot + fdt + bct) signed blob
17"""
18
19import os
20import re
21
22import cros_output
23from fdt import Fdt
24from pack_firmware import PackFirmware
25import shutil
26import tempfile
27from tools import Tools
28from write_firmware import WriteFirmware
29
30# This data is required by bmpblk_utility. Does it ever change?
31# It was stored with the chromeos-bootimage ebuild, but we want
32# this utility to work outside the chroot.
33yaml_data = '''
34bmpblock: 1.0
35
36images:
37 devmode: DeveloperBmp/DeveloperBmp.bmp
38 recovery: RecoveryBmp/RecoveryBmp.bmp
39 rec_yuck: RecoveryNoOSBmp/RecoveryNoOSBmp.bmp
40 rec_insert: RecoveryMissingOSBmp/RecoveryMissingOSBmp.bmp
41
42screens:
43 dev_en:
44 - [0, 0, devmode]
45 rec_en:
46 - [0, 0, recovery]
47 yuck_en:
48 - [0, 0, rec_yuck]
49 ins_en:
50 - [0, 0, rec_insert]
51
52localizations:
53 - [ dev_en, rec_en, yuck_en, ins_en ]
54'''
55
56class Bundle:
57 """This class encapsulates the entire bundle firmware logic."""
58
59 def __init__(self, options, args):
60 self.options = options
61 self.args = args
62 self._out = cros_output.Output(options.verbosity)
63
64 def __del__(self):
65 self._out.ClearProgress()
66
67 def _CheckOptions(self):
68 """Check provided options and select defaults."""
69 options = self.options
70 build_root = os.path.join('##', 'build', options.board, 'u-boot')
71 if not options.fdt:
72 options.fdt = os.path.join(build_root, 'dtb', '%s.dtb' %
73 re.sub('_', '-', options.board))
74 if not options.uboot:
75 options.uboot = os.path.join(build_root, 'u-boot.bin')
76 if not options.bct:
77 options.bct = os.path.join(build_root, 'bct', 'board.bct')
78
79 def _CheckTools(self):
80 """Check that all required tools are present.
81
82 Raises:
83 CmdError if a required tool is not found.
84 """
85 if self.options.write:
86 self._tools.CheckTool('nvflash')
87 self._tools.CheckTool('dtput', 'dtc')
88 self._tools.CheckTool('dtget', 'dtc')
89
90 def _CreateGoogleBinaryBlock(self):
91 """Create a GBB for the image.
92
93 Returns:
94 Path of the created GBB file.
95
96 Raises:
97 CmdError if a command fails.
98 """
99 hwid = self.fdt.GetString('/config/hwid')
100 gbb_size = self.fdt.GetFlashPartSize('ro', 'gbb')
101 dir = self._tools.outdir
102
103 # Get LCD dimensions from the device tree.
104 screen_geometry = '%sx%s' % (self.fdt.GetInt('/lcd/width'),
105 self.fdt.GetInt('/lcd/height'))
106
107 # This is the magic directory that make_bmp_image writes to!
108 out_dir = 'out_%s' % re.sub(' ', '_', hwid)
109 bmp_dir = os.path.join(dir, out_dir)
110 self._out.Progress('Creating bitmaps')
111 self._tools.Run('make_bmp_image', [hwid, screen_geometry, 'arm'], cwd=dir)
112
113 self._out.Progress('Creating bitmap block')
114 yaml = 'config.yaml'
115 self._tools.WriteFile(os.path.join(bmp_dir, yaml), yaml_data)
116 self._tools.Run('bmpblk_utility', ['-z', '2', '-c', yaml, 'bmpblk.bin'],
117 cwd=bmp_dir)
118
119 self._out.Progress('Creating GBB')
120 sizes = [0x100, 0x1000, gbb_size - 0x2180, 0x1000]
121 sizes = ['%#x' % size for size in sizes]
122 gbb = 'gbb.bin'
123 keydir = self._tools.Filename(self.options.key)
124 self._tools.Run('gbb_utility', ['-c', ','.join(sizes), gbb], cwd=dir)
125 self._tools.Run('gbb_utility', ['-s',
126 '--hwid=%s' % hwid,
127 '--rootkey=%s/root_key.vbpubk' % keydir,
128 '--recoverykey=%s/recovery_key.vbpubk' % keydir,
129 '--bmpfv=%s' % os.path.join(out_dir, 'bmpblk.bin'),
130 gbb],
131 cwd=dir)
132 return os.path.join(dir, gbb)
133
134 def _SignBootstub(self, bct, bootstub, text_base, name):
135 """Sign an image so that the Tegra SOC will boot it.
136
137 Args:
138 bct: BCT file to use.
139 bootstub: Boot stub (U-Boot + fdt) file to sign.
140 text_base: Address of text base for image.
141 name: root of basename to use for signed image.
142
143 Returns:
144 filename of signed image.
145
146 Raises:
147 CmdError if a command fails.
148 """
149 # First create a config file - this is how we instruct cbootimage
150 signed = os.path.join(self._tools.outdir, 'signed%s.bin' % name)
151 self._out.Progress('Signing Bootstub')
152 config = os.path.join(self._tools.outdir, 'boot%s.cfg' % name)
153 fd = open(config, 'w')
154 fd.write('Version = 1;\n')
155 fd.write('Redundancy = 1;\n')
156 fd.write('Bctfile = %s;\n' % bct)
157 fd.write('BootLoader = %s,%#x,%#x,Complete;\n' % (bootstub, text_base,
158 text_base))
159 fd.close()
160
161 self._tools.Run('cbootimage', [config, signed])
162 self._tools.OutputSize('BCT', bct)
163 self._tools.OutputSize('Signed image', signed)
164 return signed
165
166 def _PrepareFdt(self, fdt):
167 """Prepare an fdt with any additions selected, and return its contents.
168
169 Args:
170 fdt: Input fdt filename
171
172 Returns:
173 String containing new fdt, after adding boot command, etc.
174 """
175 fdt = self.fdt.Copy(os.path.join(self._tools.outdir, 'updated.dtb'))
176 if self.options.bootcmd:
177 fdt.PutString('/config/bootcmd', self.options.bootcmd)
178 self._out.Info('Boot command: %s' % self.options.bootcmd)
179 if self.options.add_config_str:
180 for config in self.options.add_config_str:
181 fdt.PutString('/config/%s' % config[0], config[1])
182 if self.options.add_config_int:
183 for config in self.options.add_config_int:
184 try:
185 value = int(config[1])
186 except ValueError as str:
187 raise CmdError("Cannot convert config option '%s' to integer" %
188 config[1])
189 fdt.PutInteger('/config/%s' % config[0], value)
190 return self._tools.ReadFile(fdt.fname)
191
192 def _CreateBootStub(self, uboot, fdt, text_base):
193 """Create a boot stub and a signed boot stub.
194
195 Args:
196 uboot: Path to u-boot.bin (may be chroot-relative)
197 fdt: A Fdt object to use as the base Fdt
198 text_base: Address of text base for image.
199
200 Returns:
201 Tuple containing:
202 Full path to u-boot.bin.
203 Full path to bootstub.
204
205 Raises:
206 CmdError if a command fails.
207 """
208 options = self.options
209 uboot_data = self._tools.ReadFile(uboot)
210 fdt_data = self._PrepareFdt(fdt)
211 bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin')
212 self._tools.WriteFile(bootstub, uboot_data + fdt_data)
213 self._tools.OutputSize('U-Boot binary', options.uboot)
214 self._tools.OutputSize('U-Boot fdt', options.fdt)
215 self._tools.OutputSize('Combined binary', bootstub)
216
217 # sign the bootstub; this is a combination of the board specific
218 # bct and the stub u-boot image.
219 signed = self._SignBootstub(self._tools.Filename(options.bct), bootstub,
220 text_base, '')
221 return self._tools.Filename(uboot), bootstub, signed
222
223 def _PackOutput(self, msg):
224 """Helper function to write output from PackFirmware (verbose level 2).
225
226 This is passed to PackFirmware for it to use to write output.
227
228 Args:
229 msg: Message to display.
230 """
231 self._out.Notice(msg)
232
233 def _CreateImage(self, gbb, text_base):
234 """Create a full firmware image, along with various by-products.
235
236 This uses the provided u-boot.bin, fdt and bct to create a firmware
237 image containing all the required parts. If the GBB is not supplied
238 then this will just return a signed U-Boot as the image.
239
240 Args:
241 gbb Full path to the GBB file, or empty if a GBB is not required.
242 text_base: Address of text base for image.
243
244 Raises:
245 CmdError if a command fails.
246 """
247
248 options = self.options
249 self._out.Notice("Model: %s" % self.fdt.GetString('/model'))
250
251 # Create the boot stub, which is U-Boot plus an fdt and bct
252 uboot, bootstub, signed = self._CreateBootStub(options.uboot,
253 self.fdt, text_base)
254
255 if gbb:
256 pack = PackFirmware(self._tools, self._out)
257 image = os.path.join(self._tools.outdir, 'image.bin')
258 fwid = self._tools.GetChromeosVersion()
259 self._out.Notice('Firmware ID: %s' % fwid)
260 pack.SetupFiles(boot=bootstub, signed=signed, gbb=gbb,
261 fwid=fwid, keydir=options.key)
262 pack.SelectFdt(self.fdt)
263 pack.PackImage(self._tools.outdir, image)
264 else:
265 image = signed
266
267 self._tools.OutputSize('Final image', image)
268 return uboot, image
269
270 def Start(self):
271 """This performs all the requested operations for this script.
272
273 - Checks options, tools, output directory, fdt.
274 - Creates GBB and image.
275 - Writes image to board.
276 """
277 options = self.options
278 self._CheckOptions()
279 self._tools = Tools(self._out)
280 self._CheckTools()
281
282 self._tools.PrepareOutputDir(options.outdir, options.preserve)
283 self.fdt = Fdt(self._tools, options.fdt)
284
285 text_base = self.fdt.GetInt('/chromeos-config/textbase');
286 gbb = ''
287 if not options.small:
288 gbb = self._CreateGoogleBinaryBlock()
289
290 # This creates the actual image.
291 uboot, image = self._CreateImage(gbb, text_base)
292 if options.output:
293 shutil.copyfile(image, options.output)
294 self._out.Notice("Output image '%s'" % options.output)
295
296 # Write it to the board if required.
297 if options.write:
298 write = WriteFirmware(self._tools, self.fdt, self._out, text_base)
299 if write.FlashImage(uboot, options.bct, image):
300 self._out.Progress('Image uploaded - please wait for flashing to '
301 'complete')
302 else:
303 raise CmdError('Image upload failed - please check board connection')