blob: fce66e80f10bf8a57cecd8e4146a95eafcb36158 [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
Rhyland Kleinc5c87282012-03-08 16:40:54 -080054 def _GetFlashScript(self, payload_size, update, verify, boot_type):
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
Rhyland Kleinc5c87282012-03-08 16:40:54 -080065 boot_type: the src for bootdevice (Nand, Sdmmc, or Spi)
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'
Rhyland Kleinc5c87282012-03-08 16:40:54 -080075 page_size = 4096
76 if boot_type == 'Sdmmc':
77 page_size = 512
78 if boot_type != 'Spi':
79 update = False
80
Simon Glassa3f29ec2011-07-17 09:36:49 -070081 cmds = [
82 'setenv address 0x%s' % replace_me,
83 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -080084 'setenv length %#x' % RoundUp(payload_size, page_size),
85 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass5b5fd642011-08-17 12:24:01 -070086 'setenv _crc "crc32 ${address} ${firmware_size}"',
Simon Glass5b5fd642011-08-17 12:24:01 -070087 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -070088 ]
Rhyland Kleinc5c87282012-03-08 16:40:54 -080089 if boot_type == 'Nand':
Doug Anderson37ae2292011-09-15 17:41:57 -070090 cmds.extend([
91 'setenv _init "echo Init NAND; nand info"',
92 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
93 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
94 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
95 ])
Rhyland Kleinc5c87282012-03-08 16:40:54 -080096 elif boot_type == 'Sdmmc':
97 cmds.extend([
98 'setenv _init "echo Init EMMC; mmc rescan 0"',
99 'setenv _erase "echo Erase EMMC; "',
100 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
101 '${blocks} boot1"',
102 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
103 '${blocks} boot1"',
104 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700105 else:
106 cmds.extend([
107 'setenv _init "echo Init SPI; sf probe 0"',
108 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
109 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
110 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
111 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
112 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700113
Doug Anderson37ae2292011-09-15 17:41:57 -0700114 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700115 'echo Firmware loaded to ${address}, size ${firmware_size}, '
116 'length ${length}',
117 'run _crc',
118 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700119 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700120 if update:
121 cmds += ['time run _update']
122 else:
123 cmds += ['run _erase', 'run _write']
124 if verify:
125 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700126 'run _clear',
127 'run _read',
128 'run _crc',
129 'echo If the two CRCs above are equal, flash was successful.'
Simon Glass5b5fd642011-08-17 12:24:01 -0700130 ]
131 else:
132 cmds += ['echo Skipping verify']
Simon Glassa3f29ec2011-07-17 09:36:49 -0700133 script = '; '.join(cmds)
134 return script, replace_me
135
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800136 def PrepareFlasher(self, uboot, payload, update, verify, boot_type):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700137 """Get a flasher ready for sending to the board.
138
139 The flasher is an executable image consisting of:
140
141 - U-Boot (u-boot.bin);
142 - a special FDT to tell it what to do in the form of a run command;
143 - (we could add some empty space here, in case U-Boot is not built to
144 be relocatable);
145 - the payload (which is a full flash image, or signed U-Boot + fdt).
146
147 Args:
148 uboot: Full path to u-boot.bin.
149 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700150 update: Use faster update algorithm rather then full device erase
151 verify: Verify the write by doing a readback and CRC
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800152 boot_type: the src for bootdevice (Nand, Sdmmc, or Spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700153
154 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700155 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700156 """
Simon Glass951a2db2011-07-17 15:58:58 -0700157 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glassa3f29ec2011-07-17 09:36:49 -0700158 payload_size = os.stat(payload).st_size
159
Doug Anderson37ae2292011-09-15 17:41:57 -0700160 script, replace_me = self._GetFlashScript(payload_size, update, verify,
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800161 boot_type)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700162 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800163 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700164 fdt_data = self._tools.ReadFile(fdt.fname)
165
166 # Work out where to place the payload in memory. This is a chicken-and-egg
167 # problem (although in case you haven't heard, it was the chicken that
168 # came first), so we resolve it by replacing the string after
169 # fdt.PutString has done its job.
170 #
171 # Correction: Technically, the egg came first. Whatever genetic mutation
172 # created the new species would have been present in the egg, but not the
173 # parent (since if it was in the parent, it would have been present in the
174 # parent when it was an egg).
175 #
176 # Question: ok so who laid the egg then?
177 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700178
179 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
180 alignment = 0x1000
181 payload_offset = (payload_offset + alignment - 1) & ~(alignment-1)
182
Simon Glass2c4b3e52011-11-15 14:45:43 -0800183 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700184 new_str = '%08x' % load_address
185 if len(replace_me) is not len(new_str):
186 raise ValueError("Internal error: replacement string '%s' length does "
187 "not match new string '%s'" % (replace_me, new_str))
188 if len(re.findall(replace_me, fdt_data)) != 1:
189 raise ValueError("Internal error: replacement string '%s' already "
190 "exists in the fdt (%d matches)" % (replace_me, matches))
191 fdt_data = re.sub(replace_me, new_str, fdt_data)
192
193 # Now put it together.
194 data += fdt_data
195 data += "\0" * (payload_offset - len(data))
196 data += self._tools.ReadFile(payload)
Simon Glass951a2db2011-07-17 15:58:58 -0700197 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700198 self._tools.WriteFile(flasher, data)
199
200 # Tell the user about a few things.
201 self._tools.OutputSize('U-Boot', uboot)
202 self._tools.OutputSize('Payload', payload)
203 self._tools.OutputSize('Flasher', flasher)
204 return flasher
205
206 def FlashImage(self, uboot, bct, payload):
207 """Flash the image to SPI flash.
208
209 This creates a special Flasher binary, with the image to be flashed as
210 a payload. This is then sent to the board using the nvflash utility.
211
212 Args:
213 uboot: Full path to u-boot.bin.
214 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
215 payload: Full path to payload.
216
217 Returns:
218 True if ok, False if failed.
219 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800220 # Use a Regex to pull Boot type from BCT file.
221 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
222 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
223 boot_type = filter(match.match, bct_dumped)
224 boot_type = match.match(boot_type[0]).group('boot')
Doug Anderson37ae2292011-09-15 17:41:57 -0700225
226 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800227 boot_type)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700228
229 self._out.Progress('Uploading flasher image')
230 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700231 '--bct', bct,
232 '--setbct',
233 '--bl', flasher,
234 '--go',
Simon Glass2c4b3e52011-11-15 14:45:43 -0800235 '--setentry', "%#x" % self.text_base, "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700236 ]
237
238 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
239 last_err = None
240 for tries in range(10):
241 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700242 # TODO(sjg): Use Chromite library so we can monitor output
Simon Glass86d16aa2012-03-09 15:29:05 -0800243 self._tools.Run('nvflash', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700244 self._out.Notice('Flasher downloaded - please see serial output '
245 'for progress.')
246 return True
247
248 except CmdError as err:
249 if not self._out.stdout_is_tty:
250 return False
251
252 # Only show the error output once unless it changes.
253 err = str(err)
254 if not 'USB device not found' in err:
255 raise CmdError('nvflash failed: %s' % err)
256
257 if err != last_err:
258 self._out.Notice(err)
259 last_err = err
260 self._out.Progress('Please connect USB A-A cable and do a '
261 'recovery-reset', True)
262 time.sleep(1)
263
264 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700265
Simon Glass5b5fd642011-08-17 12:24:01 -0700266def DoWriteFirmware(output, tools, fdt, flasher, bct_fname, image_fname,
Simon Glass2c4b3e52011-11-15 14:45:43 -0800267 text_base=None, update=True, verify=False):
Simon Glass710dedc2011-08-09 14:08:52 -0700268 """A simple function to write firmware to the board.
269
270 This creates a WriteFirmware object and uses it to write the firmware image
271 to the board.
272
273 Args:
274 output: cros_output object to use.
275 tools: Tools object to use.
276 fdt: Fdt object to use as our device tree.
277 flasher: U-Boot binary to use as the flasher.
278 bct_fname: Bct file to use for the flasher.
279 image_fname: Filename of image to write.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800280 text_base: U-Boot text base (base of executable image), None for default.
281 update: Use faster update algorithm rather then full device erase.
282 verify: Verify the write by doing a readback and CRC.
Simon Glass710dedc2011-08-09 14:08:52 -0700283 """
284 write = WriteFirmware(tools, fdt, output)
Simon Glass2c4b3e52011-11-15 14:45:43 -0800285 if text_base:
286 write.text_base = text_base
Simon Glass5b5fd642011-08-17 12:24:01 -0700287 write.update = update
288 write.verify = verify
Simon Glass710dedc2011-08-09 14:08:52 -0700289 if write.FlashImage(flasher, bct_fname, image_fname):
290 output.Progress('Image uploaded - please wait for flashing to '
291 'complete')
292 else:
293 raise CmdError('Image upload failed - please check board connection')