blob: fcd1dba4c2adb8038f29f9e805c06fec540578b5 [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 Glassa3f29ec2011-07-17 09:36:49 -07006import os
7import re
8import time
9import tools
10from tools import CmdError
11
12def RoundUp(value, boundary):
13 """Align a value to the next power of 2 boundary.
14
15 Args:
16 value: The value to align.
17 boundary: The boundary value, e.g. 4096. Must be a power of 2.
18
19 Returns:
20 The rounded-up value.
21 """
22 return (value + boundary - 1) & ~(boundary - 1)
23
24
25class WriteFirmware:
26 """Write firmware to a Tegra 2 board using USB A-A cable.
27
28 This class handles re-reflashing a board with new firmware using the Tegra's
29 built-in boot ROM feature. This works by putting the chip into a special mode
30 where it ignores any available firmware and instead reads it from a connected
31 host machine over USB.
32
33 In our case we use that feature to send U-Boot along with a suitable payload
34 and instructions to flash it to SPI flash. The payload is itself normally a
35 full Chrome OS image consisting of U-Boot, some keys and verification
36 information, images and a map of the flash memory.
37 """
Simon Glass290a1802011-07-17 13:54:32 -070038 def __init__(self, tools, fdt, output):
Simon Glassa3f29ec2011-07-17 09:36:49 -070039 """Set up a new WriteFirmware object.
40
41 Args:
42 tools: A tools library for us to use.
43 fdt: An fdt which gives us some info that we need.
44 output: An output object to use for printing progress and messages.
Simon Glassa3f29ec2011-07-17 09:36:49 -070045 """
46 self._tools = tools
47 self._fdt = fdt
48 self._out = output
Simon Glass02d124a2012-03-02 14:47:20 -080049 self.text_base = self._fdt.GetInt('/chromeos-config', 'textbase');
Simon Glassa3f29ec2011-07-17 09:36:49 -070050
Simon Glass5b5fd642011-08-17 12:24:01 -070051 # For speed, use the 'update' algorithm and don't verify
52 self.update = True
53 self.verify = False
54
Simon Glass8bd05ec2012-03-22 11:09:04 -070055 def _GetFlashScript(self, payload_size, update, verify, boot_type, checksum,
56 bus='0'):
Simon Glassa3f29ec2011-07-17 09:36:49 -070057 """Get the U-Boot boot command needed to flash U-Boot.
58
59 We leave a marker in the string for the load address of the image,
60 since this depends on the size of this script. This can be replaced by
61 the caller provided that the marker length is unchanged.
62
63 Args:
64 payload_size: Size of payload in bytes.
Simon Glass5b5fd642011-08-17 12:24:01 -070065 update: Use faster update algorithm rather then full device erase
66 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -070067 boot_type: The source for bootdevice (nand, sdmmc, or spi)
Simon Glass8bd05ec2012-03-22 11:09:04 -070068 checksum: The checksum of the payload (an integer)
69 bus: The bus number
Simon Glassa3f29ec2011-07-17 09:36:49 -070070
71 Returns:
72 A tuple containing:
73 The script, as a string ready to use as a U-Boot boot command, with an
74 embedded marker for the load address.
75 The marker string, which the caller should replace with the correct
76 load address as 8 hex digits, without changing its length.
77 """
78 replace_me = 'zsHEXYla'
Rhyland Kleinc5c87282012-03-08 16:40:54 -080079 page_size = 4096
Simon Glass0dcbecb2012-03-22 21:38:55 -070080 if boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -080081 page_size = 512
Simon Glass0dcbecb2012-03-22 21:38:55 -070082 if boot_type != 'spi':
Rhyland Kleinc5c87282012-03-08 16:40:54 -080083 update = False
84
Simon Glassa3f29ec2011-07-17 09:36:49 -070085 cmds = [
86 'setenv address 0x%s' % replace_me,
87 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -080088 'setenv length %#x' % RoundUp(payload_size, page_size),
89 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass8bd05ec2012-03-22 11:09:04 -070090 'setenv _crc "crc32 -v ${address} ${firmware_size} %#08x"' %
91 checksum,
Simon Glass5b5fd642011-08-17 12:24:01 -070092 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -070093 ]
Simon Glass0dcbecb2012-03-22 21:38:55 -070094 if boot_type == 'nand':
Doug Anderson37ae2292011-09-15 17:41:57 -070095 cmds.extend([
96 'setenv _init "echo Init NAND; nand info"',
97 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
98 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
99 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
100 ])
Simon Glass0dcbecb2012-03-22 21:38:55 -0700101 elif boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800102 cmds.extend([
103 'setenv _init "echo Init EMMC; mmc rescan 0"',
104 'setenv _erase "echo Erase EMMC; "',
105 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
106 '${blocks} boot1"',
107 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
108 '${blocks} boot1"',
109 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700110 else:
111 cmds.extend([
Simon Glass3d9a6c62012-03-15 20:38:04 -0700112 'setenv _init "echo Init SPI; sf probe %s"' % bus,
Doug Anderson37ae2292011-09-15 17:41:57 -0700113 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
114 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
115 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
116 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
117 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700118
Doug Anderson37ae2292011-09-15 17:41:57 -0700119 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700120 'echo Firmware loaded to ${address}, size ${firmware_size}, '
121 'length ${length}',
Simon Glass8bd05ec2012-03-22 11:09:04 -0700122 'if run _crc; then',
Simon Glassa3f29ec2011-07-17 09:36:49 -0700123 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700124 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700125 if update:
126 cmds += ['time run _update']
127 else:
128 cmds += ['run _erase', 'run _write']
129 if verify:
130 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700131 'run _clear',
132 'run _read',
133 'run _crc',
Simon Glass5b5fd642011-08-17 12:24:01 -0700134 ]
135 else:
136 cmds += ['echo Skipping verify']
Simon Glass8bd05ec2012-03-22 11:09:04 -0700137 cmds.extend([
138 'else',
139 'echo',
140 'echo "** Checksum error on load: please check download tool **"',
141 'fi',
142 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700143 script = '; '.join(cmds)
144 return script, replace_me
145
Simon Glass3d9a6c62012-03-15 20:38:04 -0700146 def PrepareFlasher(self, uboot, payload, update, verify, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700147 """Get a flasher ready for sending to the board.
148
149 The flasher is an executable image consisting of:
150
151 - U-Boot (u-boot.bin);
152 - a special FDT to tell it what to do in the form of a run command;
153 - (we could add some empty space here, in case U-Boot is not built to
154 be relocatable);
155 - the payload (which is a full flash image, or signed U-Boot + fdt).
156
157 Args:
158 uboot: Full path to u-boot.bin.
159 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700160 update: Use faster update algorithm rather then full device erase
161 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -0700162 boot_type: the src for bootdevice (nand, sdmmc, or spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700163
164 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700165 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700166 """
Simon Glass951a2db2011-07-17 15:58:58 -0700167 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700168 payload_data = self._tools.ReadFile(payload)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700169 payload_size = os.stat(payload).st_size
170
Simon Glass8bd05ec2012-03-22 11:09:04 -0700171 # Make sure that the checksum is not negative
172 checksum = binascii.crc32(payload_data) & 0xffffffff
173
174 script, replace_me = self._GetFlashScript(len(payload_data), update,
175 verify, boot_type, checksum, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700176 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800177 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700178 fdt_data = self._tools.ReadFile(fdt.fname)
179
180 # Work out where to place the payload in memory. This is a chicken-and-egg
181 # problem (although in case you haven't heard, it was the chicken that
182 # came first), so we resolve it by replacing the string after
183 # fdt.PutString has done its job.
184 #
185 # Correction: Technically, the egg came first. Whatever genetic mutation
186 # created the new species would have been present in the egg, but not the
187 # parent (since if it was in the parent, it would have been present in the
188 # parent when it was an egg).
189 #
190 # Question: ok so who laid the egg then?
191 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700192
193 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
194 alignment = 0x1000
Simon Glass8bd05ec2012-03-22 11:09:04 -0700195 payload_offset = (payload_offset + alignment - 1) & ~(alignment - 1)
Doug Anderson37ae2292011-09-15 17:41:57 -0700196
Simon Glass2c4b3e52011-11-15 14:45:43 -0800197 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700198 new_str = '%08x' % load_address
199 if len(replace_me) is not len(new_str):
200 raise ValueError("Internal error: replacement string '%s' length does "
201 "not match new string '%s'" % (replace_me, new_str))
202 if len(re.findall(replace_me, fdt_data)) != 1:
203 raise ValueError("Internal error: replacement string '%s' already "
204 "exists in the fdt (%d matches)" % (replace_me, matches))
205 fdt_data = re.sub(replace_me, new_str, fdt_data)
206
207 # Now put it together.
208 data += fdt_data
209 data += "\0" * (payload_offset - len(data))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700210 data += payload_data
Simon Glass951a2db2011-07-17 15:58:58 -0700211 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700212 self._tools.WriteFile(flasher, data)
213
214 # Tell the user about a few things.
215 self._tools.OutputSize('U-Boot', uboot)
216 self._tools.OutputSize('Payload', payload)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700217 self._out.Notice('Payload checksum %08x' % checksum)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700218 self._tools.OutputSize('Flasher', flasher)
219 return flasher
220
Simon Glass27a9c142012-03-15 21:08:29 -0700221 def _NvidiaFlashImage(self, uboot, bct, payload):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700222 """Flash the image to SPI flash.
223
224 This creates a special Flasher binary, with the image to be flashed as
225 a payload. This is then sent to the board using the nvflash utility.
226
227 Args:
228 uboot: Full path to u-boot.bin.
229 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
230 payload: Full path to payload.
231
232 Returns:
233 True if ok, False if failed.
234 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800235 # Use a Regex to pull Boot type from BCT file.
236 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
237 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
238 boot_type = filter(match.match, bct_dumped)
Simon Glass0dcbecb2012-03-22 21:38:55 -0700239 boot_type = match.match(boot_type[0]).group('boot').lower()
Doug Anderson37ae2292011-09-15 17:41:57 -0700240
241 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Simon Glass3d9a6c62012-03-15 20:38:04 -0700242 boot_type, 0)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700243
244 self._out.Progress('Uploading flasher image')
245 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700246 '--bct', bct,
247 '--setbct',
248 '--bl', flasher,
249 '--go',
Simon Glass2c4b3e52011-11-15 14:45:43 -0800250 '--setentry', "%#x" % self.text_base, "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700251 ]
252
253 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
254 last_err = None
255 for tries in range(10):
256 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700257 # TODO(sjg): Use Chromite library so we can monitor output
Simon Glass86d16aa2012-03-09 15:29:05 -0800258 self._tools.Run('nvflash', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700259 self._out.Notice('Flasher downloaded - please see serial output '
260 'for progress.')
261 return True
262
263 except CmdError as err:
264 if not self._out.stdout_is_tty:
265 return False
266
267 # Only show the error output once unless it changes.
268 err = str(err)
269 if not 'USB device not found' in err:
270 raise CmdError('nvflash failed: %s' % err)
271
272 if err != last_err:
273 self._out.Notice(err)
274 last_err = err
275 self._out.Progress('Please connect USB A-A cable and do a '
276 'recovery-reset', True)
277 time.sleep(1)
278
279 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700280
Simon Glass27a9c142012-03-15 21:08:29 -0700281 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
282 """Wait until we see a device on the USB bus.
283
284 Args:
285 name: Board type name
286 vendor_id: USB vendor ID to look for
287 product_id: USB product ID to look for
288 timeout: Timeout to wait in seconds
289
290 Returns
291 True if the device was found, False if we timed out.
292 """
293 self._out.Progress('Waiting for board to appear on USB bus')
294 for tries in range(timeout):
295 try:
296 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
297 self._tools.Run('lsusb', args, sudo=True)
298 self._out.Notice('Flasher downloaded - please see serial output '
299 'for progress.')
300 return True
301
302 except CmdError as err:
303 pass
304
305 time.sleep(1)
306
307 self._out.Progress('Found %s board' % name)
308 return True
309
310 def _ExynosFlashImage(self, uboot, bl1, bl2, payload):
311 """Flash the image to SPI flash.
312
313 This creates a special Flasher binary, with the image to be flashed as
314 a payload. This is then sent to the board using the nvflash utility.
315
316 Args:
317 uboot: Full path to u-boot.bin.
318 bl1: Full path to file containing BL1 (pre-boot).
319 bl2: Full path to file containing BL2 (SPL)
320 payload: Full path to payload.
321
322 Returns:
323 True if ok, False if failed.
324 """
325 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
326 'Spi', '1:0')
327
328 vendor_id = 0x04e8
329 product_id = 0x1234
330 if not self._WaitForUSBDevice('exynos', vendor_id, product_id):
331 raise CmdError('Could not find USB device %04x:%04x' % (vendor_id,
332 product_id))
333
334 self._out.Progress('Uploading flasher image')
335 download_list = [
336 ['bl1', 0x02021400, bl1],
337 ['bl2', 0x02023400, bl2],
338 ['u-boot', 0x43e00000, flasher]
339 ]
340 for item in download_list:
341 print item
342 args = ['-a', '%#x' % item[1], '-f', item[2]]
343 bad = False
344 try:
345 self._out.Progress("Uploading stage '%s'" % item[0])
346 self._tools.Run('smdk-usbdl', args, sudo=True)
347 except CmdError as err:
348 bad = True
349
350 if bad or not self._WaitForUSBDevice('exynos', vendor_id, product_id, 1):
351 raise CmdError("Stage '%s' did not complete" % item[0])
352 time.sleep(.7)
353
354 return True
355
356def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glasse0b61442012-03-13 15:29:51 -0700357 text_base=None, update=True, verify=False, dest=None):
358 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700359
360 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700361 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700362
363 Args:
364 output: cros_output object to use.
365 tools: Tools object to use.
366 fdt: Fdt object to use as our device tree.
367 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700368 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700369 image_fname: Filename of image to write.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800370 text_base: U-Boot text base (base of executable image), None for default.
371 update: Use faster update algorithm rather then full device erase.
372 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700373 dest: Destination device to write firmware to (usb, sd).
Simon Glass710dedc2011-08-09 14:08:52 -0700374 """
375 write = WriteFirmware(tools, fdt, output)
Simon Glass2c4b3e52011-11-15 14:45:43 -0800376 if text_base:
377 write.text_base = text_base
Simon Glass5b5fd642011-08-17 12:24:01 -0700378 write.update = update
379 write.verify = verify
Simon Glasse0b61442012-03-13 15:29:51 -0700380 if dest == 'usb':
381 method = fdt.GetString('/chromeos-config', 'flash-method', 'tegra')
382 if method == 'tegra':
Simon Glass75759302012-03-15 20:26:53 -0700383 ok = write._NvidiaFlashImage(flasher, file_list['bct'], image_fname)
Simon Glass27a9c142012-03-15 21:08:29 -0700384 elif method == 'exynos':
385 ok = write._ExynosFlashImage(flasher, file_list['exynos-bl1'],
386 file_list['exynos-bl2'], image_fname)
Simon Glasse0b61442012-03-13 15:29:51 -0700387 else:
388 raise CmdError("Unknown flash method '%s'" % method)
389 if ok:
390 output.Progress('Image uploaded - please wait for flashing to '
391 'complete')
392 else:
393 raise CmdError('Image upload failed - please check board connection')
394 elif dest == 'sd':
395 pass
Simon Glass710dedc2011-08-09 14:08:52 -0700396 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700397 raise CmdError("Unknown destination device '%s'" % dest)