blob: 14c838a9006b60291580baabc0eaee9b071cdf91 [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 Glass02d124a2012-03-02 14:47:20 -080048 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
Doug Anderson37ae2292011-09-15 17:41:57 -070054 def _GetFlashScript(self, payload_size, update, verify, is_nand):
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
Doug Anderson37ae2292011-09-15 17:41:57 -070065 is_nand: True if we're using NAND (instead of SPI Flash)
Simon Glassa3f29ec2011-07-17 09:36:49 -070066
67 Returns:
68 A tuple containing:
69 The script, as a string ready to use as a U-Boot boot command, with an
70 embedded marker for the load address.
71 The marker string, which the caller should replace with the correct
72 load address as 8 hex digits, without changing its length.
73 """
74 replace_me = 'zsHEXYla'
75 cmds = [
76 'setenv address 0x%s' % replace_me,
77 'setenv firmware_size %#x' % payload_size,
78 'setenv length %#x' % RoundUp(payload_size, 4096),
Simon Glass5b5fd642011-08-17 12:24:01 -070079 'setenv _crc "crc32 ${address} ${firmware_size}"',
Simon Glass5b5fd642011-08-17 12:24:01 -070080 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -070081 ]
82 if is_nand:
83 cmds.extend([
84 'setenv _init "echo Init NAND; nand info"',
85 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
86 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
87 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
88 ])
89 # Don't support update for NAND yet. NAND is fast anyway...
90 update = False
91 else:
92 cmds.extend([
93 'setenv _init "echo Init SPI; sf probe 0"',
94 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
95 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
96 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
97 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
98 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -070099
Doug Anderson37ae2292011-09-15 17:41:57 -0700100 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700101 'echo Firmware loaded to ${address}, size ${firmware_size}, '
102 'length ${length}',
103 'run _crc',
104 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700105 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700106 if update:
107 cmds += ['time run _update']
108 else:
109 cmds += ['run _erase', 'run _write']
110 if verify:
111 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700112 'run _clear',
113 'run _read',
114 'run _crc',
115 'echo If the two CRCs above are equal, flash was successful.'
Simon Glass5b5fd642011-08-17 12:24:01 -0700116 ]
117 else:
118 cmds += ['echo Skipping verify']
Simon Glassa3f29ec2011-07-17 09:36:49 -0700119 script = '; '.join(cmds)
120 return script, replace_me
121
Doug Anderson37ae2292011-09-15 17:41:57 -0700122 def PrepareFlasher(self, uboot, payload, update, verify, is_nand):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700123 """Get a flasher ready for sending to the board.
124
125 The flasher is an executable image consisting of:
126
127 - U-Boot (u-boot.bin);
128 - a special FDT to tell it what to do in the form of a run command;
129 - (we could add some empty space here, in case U-Boot is not built to
130 be relocatable);
131 - the payload (which is a full flash image, or signed U-Boot + fdt).
132
133 Args:
134 uboot: Full path to u-boot.bin.
135 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700136 update: Use faster update algorithm rather then full device erase
137 verify: Verify the write by doing a readback and CRC
Doug Anderson37ae2292011-09-15 17:41:57 -0700138 is_nand: True if we're using NAND (instead of SPI Flash)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700139
140 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700141 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700142 """
Simon Glass951a2db2011-07-17 15:58:58 -0700143 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glassa3f29ec2011-07-17 09:36:49 -0700144 payload_size = os.stat(payload).st_size
145
Doug Anderson37ae2292011-09-15 17:41:57 -0700146 script, replace_me = self._GetFlashScript(payload_size, update, verify,
147 is_nand)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700148 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800149 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700150 fdt_data = self._tools.ReadFile(fdt.fname)
151
152 # Work out where to place the payload in memory. This is a chicken-and-egg
153 # problem (although in case you haven't heard, it was the chicken that
154 # came first), so we resolve it by replacing the string after
155 # fdt.PutString has done its job.
156 #
157 # Correction: Technically, the egg came first. Whatever genetic mutation
158 # created the new species would have been present in the egg, but not the
159 # parent (since if it was in the parent, it would have been present in the
160 # parent when it was an egg).
161 #
162 # Question: ok so who laid the egg then?
163 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700164
165 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
166 alignment = 0x1000
167 payload_offset = (payload_offset + alignment - 1) & ~(alignment-1)
168
Simon Glass2c4b3e52011-11-15 14:45:43 -0800169 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700170 new_str = '%08x' % load_address
171 if len(replace_me) is not len(new_str):
172 raise ValueError("Internal error: replacement string '%s' length does "
173 "not match new string '%s'" % (replace_me, new_str))
174 if len(re.findall(replace_me, fdt_data)) != 1:
175 raise ValueError("Internal error: replacement string '%s' already "
176 "exists in the fdt (%d matches)" % (replace_me, matches))
177 fdt_data = re.sub(replace_me, new_str, fdt_data)
178
179 # Now put it together.
180 data += fdt_data
181 data += "\0" * (payload_offset - len(data))
182 data += self._tools.ReadFile(payload)
Simon Glass951a2db2011-07-17 15:58:58 -0700183 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700184 self._tools.WriteFile(flasher, data)
185
186 # Tell the user about a few things.
187 self._tools.OutputSize('U-Boot', uboot)
188 self._tools.OutputSize('Payload', payload)
189 self._tools.OutputSize('Flasher', flasher)
190 return flasher
191
192 def FlashImage(self, uboot, bct, payload):
193 """Flash the image to SPI flash.
194
195 This creates a special Flasher binary, with the image to be flashed as
196 a payload. This is then sent to the board using the nvflash utility.
197
198 Args:
199 uboot: Full path to u-boot.bin.
200 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
201 payload: Full path to payload.
202
203 Returns:
204 True if ok, False if failed.
205 """
Doug Anderson37ae2292011-09-15 17:41:57 -0700206 is_nand = "NvBootDevType_Nand" in self._tools.Run('bct_dump', [bct])
207
208 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
209 is_nand)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700210
211 self._out.Progress('Uploading flasher image')
212 args = [
213 'nvflash',
214 '--bct', bct,
215 '--setbct',
216 '--bl', flasher,
217 '--go',
Simon Glass2c4b3e52011-11-15 14:45:43 -0800218 '--setentry', "%#x" % self.text_base, "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700219 ]
220
221 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
222 last_err = None
223 for tries in range(10):
224 try:
225 # TODO(sjg): Make sudo an argument to Run()
226 # TODO(sjg): Use Chromite library so we can monitor output
227 self._tools.Run('sudo', args)
228 self._out.Notice('Flasher downloaded - please see serial output '
229 'for progress.')
230 return True
231
232 except CmdError as err:
233 if not self._out.stdout_is_tty:
234 return False
235
236 # Only show the error output once unless it changes.
237 err = str(err)
238 if not 'USB device not found' in err:
239 raise CmdError('nvflash failed: %s' % err)
240
241 if err != last_err:
242 self._out.Notice(err)
243 last_err = err
244 self._out.Progress('Please connect USB A-A cable and do a '
245 'recovery-reset', True)
246 time.sleep(1)
247
248 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700249
Simon Glass5b5fd642011-08-17 12:24:01 -0700250def DoWriteFirmware(output, tools, fdt, flasher, bct_fname, image_fname,
Simon Glass2c4b3e52011-11-15 14:45:43 -0800251 text_base=None, update=True, verify=False):
Simon Glass710dedc2011-08-09 14:08:52 -0700252 """A simple function to write firmware to the board.
253
254 This creates a WriteFirmware object and uses it to write the firmware image
255 to the board.
256
257 Args:
258 output: cros_output object to use.
259 tools: Tools object to use.
260 fdt: Fdt object to use as our device tree.
261 flasher: U-Boot binary to use as the flasher.
262 bct_fname: Bct file to use for the flasher.
263 image_fname: Filename of image to write.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800264 text_base: U-Boot text base (base of executable image), None for default.
265 update: Use faster update algorithm rather then full device erase.
266 verify: Verify the write by doing a readback and CRC.
Simon Glass710dedc2011-08-09 14:08:52 -0700267 """
268 write = WriteFirmware(tools, fdt, output)
Simon Glass2c4b3e52011-11-15 14:45:43 -0800269 if text_base:
270 write.text_base = text_base
Simon Glass5b5fd642011-08-17 12:24:01 -0700271 write.update = update
272 write.verify = verify
Simon Glass710dedc2011-08-09 14:08:52 -0700273 if write.FlashImage(flasher, bct_fname, image_fname):
274 output.Progress('Image uploaded - please wait for flashing to '
275 'complete')
276 else:
277 raise CmdError('Image upload failed - please check board connection')