blob: 3ba37efb7a1490b4f1cfc0320eb041b2a6914829 [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
Simon Glass8bd05ec2012-03-22 11:09:04 -07005import binascii
Simon Glass3381a1a2012-03-22 11:13:19 -07006import glob
Simon Glassa3f29ec2011-07-17 09:36:49 -07007import os
8import re
9import time
10import tools
11from tools import CmdError
12
13def RoundUp(value, boundary):
14 """Align a value to the next power of 2 boundary.
15
16 Args:
17 value: The value to align.
18 boundary: The boundary value, e.g. 4096. Must be a power of 2.
19
20 Returns:
21 The rounded-up value.
22 """
23 return (value + boundary - 1) & ~(boundary - 1)
24
25
26class WriteFirmware:
27 """Write firmware to a Tegra 2 board using USB A-A cable.
28
29 This class handles re-reflashing a board with new firmware using the Tegra's
30 built-in boot ROM feature. This works by putting the chip into a special mode
31 where it ignores any available firmware and instead reads it from a connected
32 host machine over USB.
33
34 In our case we use that feature to send U-Boot along with a suitable payload
35 and instructions to flash it to SPI flash. The payload is itself normally a
36 full Chrome OS image consisting of U-Boot, some keys and verification
37 information, images and a map of the flash memory.
38 """
Simon Glass290a1802011-07-17 13:54:32 -070039 def __init__(self, tools, fdt, output):
Simon Glassa3f29ec2011-07-17 09:36:49 -070040 """Set up a new WriteFirmware object.
41
42 Args:
43 tools: A tools library for us to use.
44 fdt: An fdt which gives us some info that we need.
45 output: An output object to use for printing progress and messages.
Simon Glassa3f29ec2011-07-17 09:36:49 -070046 """
47 self._tools = tools
48 self._fdt = fdt
49 self._out = output
Simon Glass02d124a2012-03-02 14:47:20 -080050 self.text_base = self._fdt.GetInt('/chromeos-config', 'textbase');
Simon Glassa3f29ec2011-07-17 09:36:49 -070051
Simon Glass5b5fd642011-08-17 12:24:01 -070052 # For speed, use the 'update' algorithm and don't verify
53 self.update = True
54 self.verify = False
55
Simon Glass8bd05ec2012-03-22 11:09:04 -070056 def _GetFlashScript(self, payload_size, update, verify, boot_type, checksum,
57 bus='0'):
Simon Glassa3f29ec2011-07-17 09:36:49 -070058 """Get the U-Boot boot command needed to flash U-Boot.
59
60 We leave a marker in the string for the load address of the image,
61 since this depends on the size of this script. This can be replaced by
62 the caller provided that the marker length is unchanged.
63
64 Args:
65 payload_size: Size of payload in bytes.
Simon Glass5b5fd642011-08-17 12:24:01 -070066 update: Use faster update algorithm rather then full device erase
67 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -070068 boot_type: The source for bootdevice (nand, sdmmc, or spi)
Simon Glass8bd05ec2012-03-22 11:09:04 -070069 checksum: The checksum of the payload (an integer)
70 bus: The bus number
Simon Glassa3f29ec2011-07-17 09:36:49 -070071
72 Returns:
73 A tuple containing:
74 The script, as a string ready to use as a U-Boot boot command, with an
75 embedded marker for the load address.
76 The marker string, which the caller should replace with the correct
77 load address as 8 hex digits, without changing its length.
78 """
79 replace_me = 'zsHEXYla'
Rhyland Kleinc5c87282012-03-08 16:40:54 -080080 page_size = 4096
Simon Glass0dcbecb2012-03-22 21:38:55 -070081 if boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -080082 page_size = 512
Simon Glass0dcbecb2012-03-22 21:38:55 -070083 if boot_type != 'spi':
Rhyland Kleinc5c87282012-03-08 16:40:54 -080084 update = False
85
Simon Glassa3f29ec2011-07-17 09:36:49 -070086 cmds = [
87 'setenv address 0x%s' % replace_me,
88 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -080089 'setenv length %#x' % RoundUp(payload_size, page_size),
90 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass8bd05ec2012-03-22 11:09:04 -070091 'setenv _crc "crc32 -v ${address} ${firmware_size} %#08x"' %
92 checksum,
Simon Glass5b5fd642011-08-17 12:24:01 -070093 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -070094 ]
Simon Glass0dcbecb2012-03-22 21:38:55 -070095 if boot_type == 'nand':
Doug Anderson37ae2292011-09-15 17:41:57 -070096 cmds.extend([
97 'setenv _init "echo Init NAND; nand info"',
98 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
99 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
100 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
101 ])
Simon Glass0dcbecb2012-03-22 21:38:55 -0700102 elif boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800103 cmds.extend([
104 'setenv _init "echo Init EMMC; mmc rescan 0"',
105 'setenv _erase "echo Erase EMMC; "',
106 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
107 '${blocks} boot1"',
108 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
109 '${blocks} boot1"',
110 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700111 else:
112 cmds.extend([
Simon Glass3d9a6c62012-03-15 20:38:04 -0700113 'setenv _init "echo Init SPI; sf probe %s"' % bus,
Doug Anderson37ae2292011-09-15 17:41:57 -0700114 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
115 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
116 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
117 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
118 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700119
Doug Anderson37ae2292011-09-15 17:41:57 -0700120 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700121 'echo Firmware loaded to ${address}, size ${firmware_size}, '
122 'length ${length}',
Simon Glass8bd05ec2012-03-22 11:09:04 -0700123 'if run _crc; then',
Simon Glassa3f29ec2011-07-17 09:36:49 -0700124 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700125 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700126 if update:
127 cmds += ['time run _update']
128 else:
129 cmds += ['run _erase', 'run _write']
130 if verify:
131 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700132 'run _clear',
133 'run _read',
134 'run _crc',
Simon Glass5b5fd642011-08-17 12:24:01 -0700135 ]
136 else:
137 cmds += ['echo Skipping verify']
Simon Glass8bd05ec2012-03-22 11:09:04 -0700138 cmds.extend([
139 'else',
140 'echo',
141 'echo "** Checksum error on load: please check download tool **"',
142 'fi',
143 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700144 script = '; '.join(cmds)
145 return script, replace_me
146
Simon Glass3d9a6c62012-03-15 20:38:04 -0700147 def PrepareFlasher(self, uboot, payload, update, verify, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700148 """Get a flasher ready for sending to the board.
149
150 The flasher is an executable image consisting of:
151
152 - U-Boot (u-boot.bin);
153 - a special FDT to tell it what to do in the form of a run command;
154 - (we could add some empty space here, in case U-Boot is not built to
155 be relocatable);
156 - the payload (which is a full flash image, or signed U-Boot + fdt).
157
158 Args:
159 uboot: Full path to u-boot.bin.
160 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700161 update: Use faster update algorithm rather then full device erase
162 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -0700163 boot_type: the src for bootdevice (nand, sdmmc, or spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700164
165 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700166 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700167 """
Simon Glass951a2db2011-07-17 15:58:58 -0700168 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700169 payload_data = self._tools.ReadFile(payload)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700170 payload_size = os.stat(payload).st_size
171
Simon Glass8bd05ec2012-03-22 11:09:04 -0700172 # Make sure that the checksum is not negative
173 checksum = binascii.crc32(payload_data) & 0xffffffff
174
175 script, replace_me = self._GetFlashScript(len(payload_data), update,
176 verify, boot_type, checksum, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700177 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800178 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700179 fdt_data = self._tools.ReadFile(fdt.fname)
180
181 # Work out where to place the payload in memory. This is a chicken-and-egg
182 # problem (although in case you haven't heard, it was the chicken that
183 # came first), so we resolve it by replacing the string after
184 # fdt.PutString has done its job.
185 #
186 # Correction: Technically, the egg came first. Whatever genetic mutation
187 # created the new species would have been present in the egg, but not the
188 # parent (since if it was in the parent, it would have been present in the
189 # parent when it was an egg).
190 #
191 # Question: ok so who laid the egg then?
192 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700193
194 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
195 alignment = 0x1000
Simon Glass8bd05ec2012-03-22 11:09:04 -0700196 payload_offset = (payload_offset + alignment - 1) & ~(alignment - 1)
Doug Anderson37ae2292011-09-15 17:41:57 -0700197
Simon Glass2c4b3e52011-11-15 14:45:43 -0800198 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700199 new_str = '%08x' % load_address
200 if len(replace_me) is not len(new_str):
201 raise ValueError("Internal error: replacement string '%s' length does "
202 "not match new string '%s'" % (replace_me, new_str))
203 if len(re.findall(replace_me, fdt_data)) != 1:
204 raise ValueError("Internal error: replacement string '%s' already "
205 "exists in the fdt (%d matches)" % (replace_me, matches))
206 fdt_data = re.sub(replace_me, new_str, fdt_data)
207
208 # Now put it together.
209 data += fdt_data
210 data += "\0" * (payload_offset - len(data))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700211 data += payload_data
Simon Glass951a2db2011-07-17 15:58:58 -0700212 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700213 self._tools.WriteFile(flasher, data)
214
215 # Tell the user about a few things.
216 self._tools.OutputSize('U-Boot', uboot)
217 self._tools.OutputSize('Payload', payload)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700218 self._out.Notice('Payload checksum %08x' % checksum)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700219 self._tools.OutputSize('Flasher', flasher)
220 return flasher
221
Simon Glass27a9c142012-03-15 21:08:29 -0700222 def _NvidiaFlashImage(self, uboot, bct, payload):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700223 """Flash the image to SPI flash.
224
225 This creates a special Flasher binary, with the image to be flashed as
226 a payload. This is then sent to the board using the nvflash utility.
227
228 Args:
229 uboot: Full path to u-boot.bin.
230 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
231 payload: Full path to payload.
232
233 Returns:
234 True if ok, False if failed.
235 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800236 # Use a Regex to pull Boot type from BCT file.
237 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
238 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
239 boot_type = filter(match.match, bct_dumped)
Simon Glass0dcbecb2012-03-22 21:38:55 -0700240 boot_type = match.match(boot_type[0]).group('boot').lower()
Doug Anderson37ae2292011-09-15 17:41:57 -0700241
242 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Simon Glass3d9a6c62012-03-15 20:38:04 -0700243 boot_type, 0)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700244
245 self._out.Progress('Uploading flasher image')
246 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700247 '--bct', bct,
248 '--setbct',
249 '--bl', flasher,
250 '--go',
Simon Glass2c4b3e52011-11-15 14:45:43 -0800251 '--setentry', "%#x" % self.text_base, "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700252 ]
253
254 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
255 last_err = None
256 for tries in range(10):
257 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700258 # TODO(sjg): Use Chromite library so we can monitor output
Simon Glass86d16aa2012-03-09 15:29:05 -0800259 self._tools.Run('nvflash', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700260 self._out.Notice('Flasher downloaded - please see serial output '
261 'for progress.')
262 return True
263
264 except CmdError as err:
265 if not self._out.stdout_is_tty:
266 return False
267
268 # Only show the error output once unless it changes.
269 err = str(err)
270 if not 'USB device not found' in err:
271 raise CmdError('nvflash failed: %s' % err)
272
273 if err != last_err:
274 self._out.Notice(err)
275 last_err = err
276 self._out.Progress('Please connect USB A-A cable and do a '
277 'recovery-reset', True)
278 time.sleep(1)
279
280 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700281
Simon Glass27a9c142012-03-15 21:08:29 -0700282 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
283 """Wait until we see a device on the USB bus.
284
285 Args:
286 name: Board type name
287 vendor_id: USB vendor ID to look for
288 product_id: USB product ID to look for
289 timeout: Timeout to wait in seconds
290
291 Returns
292 True if the device was found, False if we timed out.
293 """
294 self._out.Progress('Waiting for board to appear on USB bus')
Simon Glass3381a1a2012-03-22 11:13:19 -0700295 for tries in range(timeout * 2):
Simon Glass27a9c142012-03-15 21:08:29 -0700296 try:
297 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
298 self._tools.Run('lsusb', args, sudo=True)
Simon Glass3381a1a2012-03-22 11:13:19 -0700299 self._out.Progress('Found %s board' % name)
Simon Glass27a9c142012-03-15 21:08:29 -0700300 return True
301
302 except CmdError as err:
303 pass
304
Simon Glass3381a1a2012-03-22 11:13:19 -0700305 time.sleep(.5)
Simon Glass27a9c142012-03-15 21:08:29 -0700306
Simon Glass3381a1a2012-03-22 11:13:19 -0700307 return False
Simon Glass27a9c142012-03-15 21:08:29 -0700308
309 def _ExynosFlashImage(self, uboot, bl1, bl2, payload):
310 """Flash the image to SPI flash.
311
312 This creates a special Flasher binary, with the image to be flashed as
313 a payload. This is then sent to the board using the nvflash utility.
314
315 Args:
316 uboot: Full path to u-boot.bin.
317 bl1: Full path to file containing BL1 (pre-boot).
318 bl2: Full path to file containing BL2 (SPL)
319 payload: Full path to payload.
320
321 Returns:
322 True if ok, False if failed.
323 """
324 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
325 'Spi', '1:0')
326
327 vendor_id = 0x04e8
328 product_id = 0x1234
Simon Glass3381a1a2012-03-22 11:13:19 -0700329
330 self._out.Progress('Reseting board via servo')
331 args = ['warm_reset:on', 'fw_up:on', 'pwr_button:press', 'sleep:.1',
332 'warm_reset:off']
333 # TODO(sjg) If the board is bricked a reset does not seem to bring it
334 # back to life.
335 # BUG=chromium-os:28229
336 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args
337 self._tools.Run('dut-control', args)
338 time.sleep(2)
Simon Glass27a9c142012-03-15 21:08:29 -0700339
340 self._out.Progress('Uploading flasher image')
341 download_list = [
342 ['bl1', 0x02021400, bl1],
343 ['bl2', 0x02023400, bl2],
344 ['u-boot', 0x43e00000, flasher]
345 ]
Simon Glass3381a1a2012-03-22 11:13:19 -0700346 first = True
347 try:
348 for item in download_list:
349 if not self._WaitForUSBDevice('exynos', vendor_id, product_id, 4):
350 if first:
351 raise CmdError('Could not find Exynos board on USB port')
352 raise CmdError("Stage '%s' did not complete" % item[0])
353 args = ['-a', '%#x' % item[1], '-f', item[2]]
354 first = False
355 self._out.Notice(item[2])
Simon Glass27a9c142012-03-15 21:08:29 -0700356 self._out.Progress("Uploading stage '%s'" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700357
358 # TODO(sjg): Remove this delay, once the need for it is understood.
359 time.sleep(1)
Simon Glass27a9c142012-03-15 21:08:29 -0700360 self._tools.Run('smdk-usbdl', args, sudo=True)
Simon Glass27a9c142012-03-15 21:08:29 -0700361
Simon Glass3381a1a2012-03-22 11:13:19 -0700362 finally:
363 args = ['fw_up:off', 'pwr_button:release']
364 self._tools.Run('dut-control', args)
Simon Glass27a9c142012-03-15 21:08:29 -0700365
Simon Glass3381a1a2012-03-22 11:13:19 -0700366 self._out.Notice('Flasher downloaded - please see serial output '
367 'for progress.')
Simon Glass27a9c142012-03-15 21:08:29 -0700368 return True
369
370def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glasse0b61442012-03-13 15:29:51 -0700371 text_base=None, update=True, verify=False, dest=None):
372 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700373
374 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700375 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700376
377 Args:
378 output: cros_output object to use.
379 tools: Tools object to use.
380 fdt: Fdt object to use as our device tree.
381 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700382 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700383 image_fname: Filename of image to write.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800384 text_base: U-Boot text base (base of executable image), None for default.
385 update: Use faster update algorithm rather then full device erase.
386 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700387 dest: Destination device to write firmware to (usb, sd).
Simon Glass710dedc2011-08-09 14:08:52 -0700388 """
389 write = WriteFirmware(tools, fdt, output)
Simon Glass2c4b3e52011-11-15 14:45:43 -0800390 if text_base:
391 write.text_base = text_base
Simon Glass5b5fd642011-08-17 12:24:01 -0700392 write.update = update
393 write.verify = verify
Simon Glasse0b61442012-03-13 15:29:51 -0700394 if dest == 'usb':
395 method = fdt.GetString('/chromeos-config', 'flash-method', 'tegra')
396 if method == 'tegra':
Simon Glass75759302012-03-15 20:26:53 -0700397 ok = write._NvidiaFlashImage(flasher, file_list['bct'], image_fname)
Simon Glass27a9c142012-03-15 21:08:29 -0700398 elif method == 'exynos':
399 ok = write._ExynosFlashImage(flasher, file_list['exynos-bl1'],
400 file_list['exynos-bl2'], image_fname)
Simon Glasse0b61442012-03-13 15:29:51 -0700401 else:
402 raise CmdError("Unknown flash method '%s'" % method)
403 if ok:
404 output.Progress('Image uploaded - please wait for flashing to '
405 'complete')
406 else:
407 raise CmdError('Image upload failed - please check board connection')
408 elif dest == 'sd':
409 pass
Simon Glass710dedc2011-08-09 14:08:52 -0700410 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700411 raise CmdError("Unknown destination device '%s'" % dest)