blob: 0478b261009759abfbfee4f9330502e9b70e4239 [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
50 def _GetFlashScript(self, payload_size):
51 """Get the U-Boot boot command needed to flash U-Boot.
52
53 We leave a marker in the string for the load address of the image,
54 since this depends on the size of this script. This can be replaced by
55 the caller provided that the marker length is unchanged.
56
57 Args:
58 payload_size: Size of payload in bytes.
59
60 Returns:
61 A tuple containing:
62 The script, as a string ready to use as a U-Boot boot command, with an
63 embedded marker for the load address.
64 The marker string, which the caller should replace with the correct
65 load address as 8 hex digits, without changing its length.
66 """
67 replace_me = 'zsHEXYla'
68 cmds = [
69 'setenv address 0x%s' % replace_me,
70 'setenv firmware_size %#x' % payload_size,
71 'setenv length %#x' % RoundUp(payload_size, 4096),
72 'setenv _crc "crc32 ${address} ${firmware_size}"',
73 'setenv _init "echo Initing SPI; sf probe 0"',
74 'setenv _erase "echo Erasing SPI; sf erase 0 ${length}"',
75 'setenv _write "echo Writing SPI; sf write ${address} 0 ${length}"',
76 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
77 'setenv _read "echo Reading SPI; sf read ${address} 0 ${length}"',
78
79 'echo Firmware loaded to ${address}, size ${firmware_size}, '
80 'length ${length}',
81 'run _crc',
82 'run _init',
83 'run _erase',
84 'run _write',
85 'run _clear',
86 'run _read',
87 'run _crc',
88 'echo If the two CRCs above are equal, flash was successful.'
89 ]
90 script = '; '.join(cmds)
91 return script, replace_me
92
93 def PrepareFlasher(self, uboot, payload):
94 """Get a flasher ready for sending to the board.
95
96 The flasher is an executable image consisting of:
97
98 - U-Boot (u-boot.bin);
99 - a special FDT to tell it what to do in the form of a run command;
100 - (we could add some empty space here, in case U-Boot is not built to
101 be relocatable);
102 - the payload (which is a full flash image, or signed U-Boot + fdt).
103
104 Args:
105 uboot: Full path to u-boot.bin.
106 payload: Full path to payload.
107
108 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700109 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700110 """
Simon Glass951a2db2011-07-17 15:58:58 -0700111 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glassa3f29ec2011-07-17 09:36:49 -0700112 payload_size = os.stat(payload).st_size
113
114 script, replace_me = self._GetFlashScript(payload_size)
115 data = self._tools.ReadFile(uboot)
116 fdt.PutString('/config/bootcmd', script)
117 fdt_data = self._tools.ReadFile(fdt.fname)
118
119 # Work out where to place the payload in memory. This is a chicken-and-egg
120 # problem (although in case you haven't heard, it was the chicken that
121 # came first), so we resolve it by replacing the string after
122 # fdt.PutString has done its job.
123 #
124 # Correction: Technically, the egg came first. Whatever genetic mutation
125 # created the new species would have been present in the egg, but not the
126 # parent (since if it was in the parent, it would have been present in the
127 # parent when it was an egg).
128 #
129 # Question: ok so who laid the egg then?
130 payload_offset = len(data) + len(fdt_data)
131 load_address = self._text_base + payload_offset,
132 new_str = '%08x' % load_address
133 if len(replace_me) is not len(new_str):
134 raise ValueError("Internal error: replacement string '%s' length does "
135 "not match new string '%s'" % (replace_me, new_str))
136 if len(re.findall(replace_me, fdt_data)) != 1:
137 raise ValueError("Internal error: replacement string '%s' already "
138 "exists in the fdt (%d matches)" % (replace_me, matches))
139 fdt_data = re.sub(replace_me, new_str, fdt_data)
140
141 # Now put it together.
142 data += fdt_data
143 data += "\0" * (payload_offset - len(data))
144 data += self._tools.ReadFile(payload)
Simon Glass951a2db2011-07-17 15:58:58 -0700145 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700146 self._tools.WriteFile(flasher, data)
147
148 # Tell the user about a few things.
149 self._tools.OutputSize('U-Boot', uboot)
150 self._tools.OutputSize('Payload', payload)
151 self._tools.OutputSize('Flasher', flasher)
152 return flasher
153
154 def FlashImage(self, uboot, bct, payload):
155 """Flash the image to SPI flash.
156
157 This creates a special Flasher binary, with the image to be flashed as
158 a payload. This is then sent to the board using the nvflash utility.
159
160 Args:
161 uboot: Full path to u-boot.bin.
162 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
163 payload: Full path to payload.
164
165 Returns:
166 True if ok, False if failed.
167 """
168 flasher = self.PrepareFlasher(uboot, payload)
169
170 self._out.Progress('Uploading flasher image')
171 args = [
172 'nvflash',
173 '--bct', bct,
174 '--setbct',
175 '--bl', flasher,
176 '--go',
177 '--setentry', "%#x" % self._text_base, "%#x" % self._text_base
178 ]
179
180 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
181 last_err = None
182 for tries in range(10):
183 try:
184 # TODO(sjg): Make sudo an argument to Run()
185 # TODO(sjg): Use Chromite library so we can monitor output
186 self._tools.Run('sudo', args)
187 self._out.Notice('Flasher downloaded - please see serial output '
188 'for progress.')
189 return True
190
191 except CmdError as err:
192 if not self._out.stdout_is_tty:
193 return False
194
195 # Only show the error output once unless it changes.
196 err = str(err)
197 if not 'USB device not found' in err:
198 raise CmdError('nvflash failed: %s' % err)
199
200 if err != last_err:
201 self._out.Notice(err)
202 last_err = err
203 self._out.Progress('Please connect USB A-A cable and do a '
204 'recovery-reset', True)
205 time.sleep(1)
206
207 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700208
209def DoWriteFirmware(output, tools, fdt, flasher, bct_fname, image_fname):
210 """A simple function to write firmware to the board.
211
212 This creates a WriteFirmware object and uses it to write the firmware image
213 to the board.
214
215 Args:
216 output: cros_output object to use.
217 tools: Tools object to use.
218 fdt: Fdt object to use as our device tree.
219 flasher: U-Boot binary to use as the flasher.
220 bct_fname: Bct file to use for the flasher.
221 image_fname: Filename of image to write.
222 """
223 write = WriteFirmware(tools, fdt, output)
224 if write.FlashImage(flasher, bct_fname, image_fname):
225 output.Progress('Image uploaded - please wait for flashing to '
226 'complete')
227 else:
228 raise CmdError('Image upload failed - please check board connection')