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