blob: 2e1117d13b2e52288d87450747e3ea0793ec6cbd [file] [log] [blame]
Simon Glassa3f29ec2011-07-17 09:36: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
5import os
6import re
7import time
8import tools
9from tools import CmdError
10
11def RoundUp(value, boundary):
12 """Align a value to the next power of 2 boundary.
13
14 Args:
15 value: The value to align.
16 boundary: The boundary value, e.g. 4096. Must be a power of 2.
17
18 Returns:
19 The rounded-up value.
20 """
21 return (value + boundary - 1) & ~(boundary - 1)
22
23
24class WriteFirmware:
25 """Write firmware to a Tegra 2 board using USB A-A cable.
26
27 This class handles re-reflashing a board with new firmware using the Tegra's
28 built-in boot ROM feature. This works by putting the chip into a special mode
29 where it ignores any available firmware and instead reads it from a connected
30 host machine over USB.
31
32 In our case we use that feature to send U-Boot along with a suitable payload
33 and instructions to flash it to SPI flash. The payload is itself normally a
34 full Chrome OS image consisting of U-Boot, some keys and verification
35 information, images and a map of the flash memory.
36 """
Simon Glass290a1802011-07-17 13:54:32 -070037 def __init__(self, tools, fdt, output):
Simon Glassa3f29ec2011-07-17 09:36:49 -070038 """Set up a new WriteFirmware object.
39
40 Args:
41 tools: A tools library for us to use.
42 fdt: An fdt which gives us some info that we need.
43 output: An output object to use for printing progress and messages.
Simon Glassa3f29ec2011-07-17 09:36:49 -070044 """
45 self._tools = tools
46 self._fdt = fdt
47 self._out = output
Simon Glass290a1802011-07-17 13:54:32 -070048 self._text_base = self._fdt.GetInt('/chromeos-config/textbase');
Simon Glassa3f29ec2011-07-17 09:36:49 -070049
Simon Glass5b5fd642011-08-17 12:24:01 -070050 # For speed, use the 'update' algorithm and don't verify
51 self.update = True
52 self.verify = False
53
54 def _GetFlashScript(self, payload_size, update, verify):
Simon Glassa3f29ec2011-07-17 09:36:49 -070055 """Get the U-Boot boot command needed to flash U-Boot.
56
57 We leave a marker in the string for the load address of the image,
58 since this depends on the size of this script. This can be replaced by
59 the caller provided that the marker length is unchanged.
60
61 Args:
62 payload_size: Size of payload in bytes.
Simon Glass5b5fd642011-08-17 12:24:01 -070063 update: Use faster update algorithm rather then full device erase
64 verify: Verify the write by doing a readback and CRC
Simon Glassa3f29ec2011-07-17 09:36:49 -070065
66 Returns:
67 A tuple containing:
68 The script, as a string ready to use as a U-Boot boot command, with an
69 embedded marker for the load address.
70 The marker string, which the caller should replace with the correct
71 load address as 8 hex digits, without changing its length.
72 """
73 replace_me = 'zsHEXYla'
74 cmds = [
75 'setenv address 0x%s' % replace_me,
76 'setenv firmware_size %#x' % payload_size,
77 'setenv length %#x' % RoundUp(payload_size, 4096),
Simon Glass5b5fd642011-08-17 12:24:01 -070078 'setenv _crc "crc32 ${address} ${firmware_size}"',
79 'setenv _init "echo Initing SPI; sf probe 0"',
80 'setenv _erase "echo Erasing SPI; sf erase 0 ${length}"',
81 'setenv _write "echo Writing SPI; sf write ${address} 0 ${length}"',
82 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
83 'setenv _read "echo Reading SPI; sf read ${address} 0 ${length}"',
84 'setenv _update "echo Updating SPI; sf update ${address} 0 ${length}"',
Simon Glassa3f29ec2011-07-17 09:36:49 -070085
86 'echo Firmware loaded to ${address}, size ${firmware_size}, '
87 'length ${length}',
88 'run _crc',
89 'run _init',
Simon Glass5b5fd642011-08-17 12:24:01 -070090 ]
91 if update:
92 cmds += ['time run _update']
93 else:
94 cmds += ['run _erase', 'run _write']
95 if verify:
96 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -070097 'run _clear',
98 'run _read',
99 'run _crc',
100 'echo If the two CRCs above are equal, flash was successful.'
Simon Glass5b5fd642011-08-17 12:24:01 -0700101 ]
102 else:
103 cmds += ['echo Skipping verify']
Simon Glassa3f29ec2011-07-17 09:36:49 -0700104 script = '; '.join(cmds)
105 return script, replace_me
106
Simon Glass5b5fd642011-08-17 12:24:01 -0700107 def PrepareFlasher(self, uboot, payload, update, verify):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700108 """Get a flasher ready for sending to the board.
109
110 The flasher is an executable image consisting of:
111
112 - U-Boot (u-boot.bin);
113 - a special FDT to tell it what to do in the form of a run command;
114 - (we could add some empty space here, in case U-Boot is not built to
115 be relocatable);
116 - the payload (which is a full flash image, or signed U-Boot + fdt).
117
118 Args:
119 uboot: Full path to u-boot.bin.
120 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700121 update: Use faster update algorithm rather then full device erase
122 verify: Verify the write by doing a readback and CRC
Simon Glassa3f29ec2011-07-17 09:36:49 -0700123
124 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700125 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700126 """
Simon Glass951a2db2011-07-17 15:58:58 -0700127 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glassa3f29ec2011-07-17 09:36:49 -0700128 payload_size = os.stat(payload).st_size
129
Simon Glass5b5fd642011-08-17 12:24:01 -0700130 script, replace_me = self._GetFlashScript(payload_size, update, verify)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700131 data = self._tools.ReadFile(uboot)
132 fdt.PutString('/config/bootcmd', script)
133 fdt_data = self._tools.ReadFile(fdt.fname)
134
135 # Work out where to place the payload in memory. This is a chicken-and-egg
136 # problem (although in case you haven't heard, it was the chicken that
137 # came first), so we resolve it by replacing the string after
138 # fdt.PutString has done its job.
139 #
140 # Correction: Technically, the egg came first. Whatever genetic mutation
141 # created the new species would have been present in the egg, but not the
142 # parent (since if it was in the parent, it would have been present in the
143 # parent when it was an egg).
144 #
145 # Question: ok so who laid the egg then?
146 payload_offset = len(data) + len(fdt_data)
147 load_address = self._text_base + payload_offset,
148 new_str = '%08x' % load_address
149 if len(replace_me) is not len(new_str):
150 raise ValueError("Internal error: replacement string '%s' length does "
151 "not match new string '%s'" % (replace_me, new_str))
152 if len(re.findall(replace_me, fdt_data)) != 1:
153 raise ValueError("Internal error: replacement string '%s' already "
154 "exists in the fdt (%d matches)" % (replace_me, matches))
155 fdt_data = re.sub(replace_me, new_str, fdt_data)
156
157 # Now put it together.
158 data += fdt_data
159 data += "\0" * (payload_offset - len(data))
160 data += self._tools.ReadFile(payload)
Simon Glass951a2db2011-07-17 15:58:58 -0700161 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700162 self._tools.WriteFile(flasher, data)
163
164 # Tell the user about a few things.
165 self._tools.OutputSize('U-Boot', uboot)
166 self._tools.OutputSize('Payload', payload)
167 self._tools.OutputSize('Flasher', flasher)
168 return flasher
169
170 def FlashImage(self, uboot, bct, payload):
171 """Flash the image to SPI flash.
172
173 This creates a special Flasher binary, with the image to be flashed as
174 a payload. This is then sent to the board using the nvflash utility.
175
176 Args:
177 uboot: Full path to u-boot.bin.
178 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
179 payload: Full path to payload.
180
181 Returns:
182 True if ok, False if failed.
183 """
Simon Glass5b5fd642011-08-17 12:24:01 -0700184 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700185
186 self._out.Progress('Uploading flasher image')
187 args = [
188 'nvflash',
189 '--bct', bct,
190 '--setbct',
191 '--bl', flasher,
192 '--go',
193 '--setentry', "%#x" % self._text_base, "%#x" % self._text_base
194 ]
195
196 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
197 last_err = None
198 for tries in range(10):
199 try:
200 # TODO(sjg): Make sudo an argument to Run()
201 # TODO(sjg): Use Chromite library so we can monitor output
202 self._tools.Run('sudo', args)
203 self._out.Notice('Flasher downloaded - please see serial output '
204 'for progress.')
205 return True
206
207 except CmdError as err:
208 if not self._out.stdout_is_tty:
209 return False
210
211 # Only show the error output once unless it changes.
212 err = str(err)
213 if not 'USB device not found' in err:
214 raise CmdError('nvflash failed: %s' % err)
215
216 if err != last_err:
217 self._out.Notice(err)
218 last_err = err
219 self._out.Progress('Please connect USB A-A cable and do a '
220 'recovery-reset', True)
221 time.sleep(1)
222
223 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700224
Simon Glass5b5fd642011-08-17 12:24:01 -0700225def DoWriteFirmware(output, tools, fdt, flasher, bct_fname, image_fname,
226 update=True, verify=False):
Simon Glass710dedc2011-08-09 14:08:52 -0700227 """A simple function to write firmware to the board.
228
229 This creates a WriteFirmware object and uses it to write the firmware image
230 to the board.
231
232 Args:
233 output: cros_output object to use.
234 tools: Tools object to use.
235 fdt: Fdt object to use as our device tree.
236 flasher: U-Boot binary to use as the flasher.
237 bct_fname: Bct file to use for the flasher.
238 image_fname: Filename of image to write.
Simon Glass5b5fd642011-08-17 12:24:01 -0700239 update: Use faster update algorithm rather then full device erase
240 verify: Verify the write by doing a readback and CRC
Simon Glass710dedc2011-08-09 14:08:52 -0700241 """
242 write = WriteFirmware(tools, fdt, output)
Simon Glass5b5fd642011-08-17 12:24:01 -0700243 write.update = update
244 write.verify = verify
Simon Glass710dedc2011-08-09 14:08:52 -0700245 if write.FlashImage(flasher, bct_fname, image_fname):
246 output.Progress('Image uploaded - please wait for flashing to '
247 'complete')
248 else:
249 raise CmdError('Image upload failed - please check board connection')