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