blob: fd8b60a80f5870bbc1ba097ce476cdf455b9dc22 [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
Simon Glasse86f3132013-01-08 16:10:43 -08009import struct
Simon Glassa3f29ec2011-07-17 09:36:49 -070010import time
Simon Glassa3f29ec2011-07-17 09:36:49 -070011from 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.
Simon Glass6a616c12012-09-24 18:13:46 -070038
39 Private attributes:
40 _servo_port: Port number to use to talk to servo with dut-control.
41 Special values are:
42 None: servo is not available.
43 0: any servo will do.
44
Simon Glassa3f29ec2011-07-17 09:36:49 -070045 """
Vadim Bendebury2b719452013-02-12 17:00:06 -080046
47 _WRITE_SUCCESS_MESSAGE = 'Image Programmed Successfully'
48 _WRITE_FAILURE_MESSAGE = '** Readback checksum error, programming failed!! **'
49
50 def __init__(self, tools, fdt, output, bundle, update, verify):
Simon Glassa3f29ec2011-07-17 09:36:49 -070051 """Set up a new WriteFirmware object.
52
53 Args:
54 tools: A tools library for us to use.
55 fdt: An fdt which gives us some info that we need.
56 output: An output object to use for printing progress and messages.
Simon Glass0191a882012-05-23 13:15:06 -070057 bundle: A BundleFirmware object which created the image.
Vadim Bendebury2b719452013-02-12 17:00:06 -080058 update: Use faster update algorithm rather then full device erase.
59 verify: Verify the write by doing a readback and CRC.
Simon Glassa3f29ec2011-07-17 09:36:49 -070060 """
61 self._tools = tools
62 self._fdt = fdt
63 self._out = output
Simon Glass0191a882012-05-23 13:15:06 -070064 self._bundle = bundle
Vadim Bendebury19a77122013-01-24 17:38:00 -080065 self.text_base = self._fdt.GetInt('/chromeos-config', 'textbase', -1)
Simon Glassa3f29ec2011-07-17 09:36:49 -070066
Simon Glass5b5fd642011-08-17 12:24:01 -070067 # For speed, use the 'update' algorithm and don't verify
Vadim Bendebury2b719452013-02-12 17:00:06 -080068 self.update = update
69 self.verify = verify
Simon Glass5b5fd642011-08-17 12:24:01 -070070
Simon Glass6a616c12012-09-24 18:13:46 -070071 # Use default servo port
72 self._servo_port = 0
73
74 def SelectServo(self, servo):
75 """Select the servo to use for writing firmware.
76
77 Args:
78 servo: String containing description of servo to use:
79 'none' : Don't use servo, generate an error on any attempt.
80 'any' : Use any available servo.
81 '<port>': Use servo with that port number.
82 """
83 if servo == 'none':
84 self._servo_port = None
85 elif servo == 'any':
86 self._servo_port = 0
87 else:
88 self._servo_port = int(servo)
89 self._out.Notice('Servo port %s' % str(self._servo_port))
90
Vadim Bendebury2b719452013-02-12 17:00:06 -080091 def _GetFlashScript(self, payload_size, boot_type, checksum, bus='0'):
Simon Glassa3f29ec2011-07-17 09:36:49 -070092 """Get the U-Boot boot command needed to flash U-Boot.
93
94 We leave a marker in the string for the load address of the image,
95 since this depends on the size of this script. This can be replaced by
96 the caller provided that the marker length is unchanged.
97
98 Args:
99 payload_size: Size of payload in bytes.
Simon Glass0dcbecb2012-03-22 21:38:55 -0700100 boot_type: The source for bootdevice (nand, sdmmc, or spi)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700101 checksum: The checksum of the payload (an integer)
102 bus: The bus number
Simon Glassa3f29ec2011-07-17 09:36:49 -0700103
104 Returns:
105 A tuple containing:
106 The script, as a string ready to use as a U-Boot boot command, with an
107 embedded marker for the load address.
108 The marker string, which the caller should replace with the correct
109 load address as 8 hex digits, without changing its length.
110 """
111 replace_me = 'zsHEXYla'
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800112 page_size = 4096
Simon Glass0dcbecb2012-03-22 21:38:55 -0700113 if boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800114 page_size = 512
Vadim Bendebury2b719452013-02-12 17:00:06 -0800115 update = self.update and boot_type == 'spi'
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800116
Simon Glassa3f29ec2011-07-17 09:36:49 -0700117 cmds = [
118 'setenv address 0x%s' % replace_me,
119 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800120 'setenv length %#x' % RoundUp(payload_size, page_size),
121 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass8bd05ec2012-03-22 11:09:04 -0700122 'setenv _crc "crc32 -v ${address} ${firmware_size} %#08x"' %
123 checksum,
Simon Glass5b5fd642011-08-17 12:24:01 -0700124 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -0700125 ]
Simon Glass0dcbecb2012-03-22 21:38:55 -0700126 if boot_type == 'nand':
Doug Anderson37ae2292011-09-15 17:41:57 -0700127 cmds.extend([
128 'setenv _init "echo Init NAND; nand info"',
129 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
130 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
131 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
132 ])
Simon Glass0dcbecb2012-03-22 21:38:55 -0700133 elif boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800134 cmds.extend([
135 'setenv _init "echo Init EMMC; mmc rescan 0"',
136 'setenv _erase "echo Erase EMMC; "',
137 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
138 '${blocks} boot1"',
139 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
140 '${blocks} boot1"',
141 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700142 else:
143 cmds.extend([
Simon Glass3d9a6c62012-03-15 20:38:04 -0700144 'setenv _init "echo Init SPI; sf probe %s"' % bus,
Doug Anderson37ae2292011-09-15 17:41:57 -0700145 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
146 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
147 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
148 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
149 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700150
Doug Anderson37ae2292011-09-15 17:41:57 -0700151 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700152 'echo Firmware loaded to ${address}, size ${firmware_size}, '
153 'length ${length}',
Simon Glass8bd05ec2012-03-22 11:09:04 -0700154 'if run _crc; then',
Simon Glassa3f29ec2011-07-17 09:36:49 -0700155 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700156 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700157 if update:
158 cmds += ['time run _update']
159 else:
160 cmds += ['run _erase', 'run _write']
Vadim Bendebury2b719452013-02-12 17:00:06 -0800161 if self.verify:
Simon Glass5b5fd642011-08-17 12:24:01 -0700162 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700163 'run _clear',
164 'run _read',
Vadim Bendeburyd6b0a372013-02-06 16:36:28 -0800165 'if run _crc; then',
Vadim Bendebury2b719452013-02-12 17:00:06 -0800166 'echo "%s"' % self._WRITE_SUCCESS_MESSAGE,
Vadim Bendeburyd6b0a372013-02-06 16:36:28 -0800167 'else',
168 'echo',
Vadim Bendebury2b719452013-02-12 17:00:06 -0800169 'echo "%s"' % self._WRITE_FAILURE_MESSAGE,
Vadim Bendeburyd6b0a372013-02-06 16:36:28 -0800170 'echo',
171 'fi',
Simon Glass5b5fd642011-08-17 12:24:01 -0700172 ]
173 else:
174 cmds += ['echo Skipping verify']
Simon Glass8bd05ec2012-03-22 11:09:04 -0700175 cmds.extend([
176 'else',
177 'echo',
178 'echo "** Checksum error on load: please check download tool **"',
179 'fi',
180 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700181 script = '; '.join(cmds)
182 return script, replace_me
183
Vadim Bendebury2b719452013-02-12 17:00:06 -0800184 def _PrepareFlasher(self, uboot, payload, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700185 """Get a flasher ready for sending to the board.
186
187 The flasher is an executable image consisting of:
188
189 - U-Boot (u-boot.bin);
190 - a special FDT to tell it what to do in the form of a run command;
191 - (we could add some empty space here, in case U-Boot is not built to
192 be relocatable);
193 - the payload (which is a full flash image, or signed U-Boot + fdt).
194
195 Args:
196 uboot: Full path to u-boot.bin.
197 payload: Full path to payload.
Simon Glass0dcbecb2012-03-22 21:38:55 -0700198 boot_type: the src for bootdevice (nand, sdmmc, or spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700199
200 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700201 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700202 """
Simon Glass951a2db2011-07-17 15:58:58 -0700203 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700204 payload_data = self._tools.ReadFile(payload)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700205
Simon Glass8bd05ec2012-03-22 11:09:04 -0700206 # Make sure that the checksum is not negative
207 checksum = binascii.crc32(payload_data) & 0xffffffff
208
Vadim Bendebury2b719452013-02-12 17:00:06 -0800209 script, replace_me = self._GetFlashScript(len(payload_data), boot_type,
210 checksum, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700211 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800212 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700213 fdt_data = self._tools.ReadFile(fdt.fname)
214
215 # Work out where to place the payload in memory. This is a chicken-and-egg
216 # problem (although in case you haven't heard, it was the chicken that
217 # came first), so we resolve it by replacing the string after
218 # fdt.PutString has done its job.
219 #
220 # Correction: Technically, the egg came first. Whatever genetic mutation
221 # created the new species would have been present in the egg, but not the
222 # parent (since if it was in the parent, it would have been present in the
223 # parent when it was an egg).
224 #
225 # Question: ok so who laid the egg then?
226 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700227
228 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
229 alignment = 0x1000
Simon Glass8bd05ec2012-03-22 11:09:04 -0700230 payload_offset = (payload_offset + alignment - 1) & ~(alignment - 1)
Doug Anderson37ae2292011-09-15 17:41:57 -0700231
Simon Glass2c4b3e52011-11-15 14:45:43 -0800232 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700233 new_str = '%08x' % load_address
234 if len(replace_me) is not len(new_str):
235 raise ValueError("Internal error: replacement string '%s' length does "
236 "not match new string '%s'" % (replace_me, new_str))
Vadim Bendebury19a77122013-01-24 17:38:00 -0800237 matches = len(re.findall(replace_me, fdt_data))
238 if matches != 1:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700239 raise ValueError("Internal error: replacement string '%s' already "
240 "exists in the fdt (%d matches)" % (replace_me, matches))
241 fdt_data = re.sub(replace_me, new_str, fdt_data)
242
243 # Now put it together.
244 data += fdt_data
245 data += "\0" * (payload_offset - len(data))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700246 data += payload_data
Simon Glass951a2db2011-07-17 15:58:58 -0700247 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700248 self._tools.WriteFile(flasher, data)
249
250 # Tell the user about a few things.
251 self._tools.OutputSize('U-Boot', uboot)
252 self._tools.OutputSize('Payload', payload)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700253 self._out.Notice('Payload checksum %08x' % checksum)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700254 self._tools.OutputSize('Flasher', flasher)
255 return flasher
256
Vadim Bendebury19a77122013-01-24 17:38:00 -0800257 def NvidiaFlashImage(self, flash_dest, uboot, bct, payload, bootstub):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700258 """Flash the image to SPI flash.
259
260 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000261 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700262
263 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700264 flash_dest: Destination for flasher, or None to not create a flasher
265 Valid options are spi, sdmmc
Simon Glassa3f29ec2011-07-17 09:36:49 -0700266 uboot: Full path to u-boot.bin.
267 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
268 payload: Full path to payload.
Simon Glass89ecf712012-06-07 12:20:15 -0700269 bootstub: Full path to bootstub, which is the payload without the
270 signing information (i.e. bootstub is u-boot.bin + the FDT)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700271
272 Returns:
273 True if ok, False if failed.
274 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800275 # Use a Regex to pull Boot type from BCT file.
276 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
277 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700278
279 # TODO(sjg): The boot type is currently selected by the bct, rather than
280 # flash_dest selecting which bct to use. This is a bit backwards. For now
281 # we go with the bct's idea.
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800282 boot_type = filter(match.match, bct_dumped)
Simon Glass0dcbecb2012-03-22 21:38:55 -0700283 boot_type = match.match(boot_type[0]).group('boot').lower()
Doug Anderson37ae2292011-09-15 17:41:57 -0700284
Simon Glass89ecf712012-06-07 12:20:15 -0700285 if flash_dest:
Vadim Bendebury2b719452013-02-12 17:00:06 -0800286 image = self._PrepareFlasher(uboot, payload, boot_type, 0)
Simon Glasse1824db2012-07-11 17:38:40 +0200287 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700288 image = bootstub
Simon Glassa3f29ec2011-07-17 09:36:49 -0700289
Simon Glasse1824db2012-07-11 17:38:40 +0200290 else:
291 image = payload
292 # If we don't know the textbase, extract it from the payload.
293 if self.text_base == -1:
294 data = self._tools.ReadFile(payload)
295 # Skip the BCT which is the first 64KB
296 self.text_base = self._bundle.DecodeTextBase(data[0x10000:])
297
Simon Glass89ecf712012-06-07 12:20:15 -0700298 self._out.Notice('TEXT_BASE is %#x' % self.text_base)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700299 self._out.Progress('Uploading flasher image')
300 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700301 '--bct', bct,
Allen Martinb3aa2672012-03-23 15:55:25 +0000302 '--bootloader', image,
303 '--loadaddr', "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700304 ]
305
306 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
307 last_err = None
Vadim Bendebury19a77122013-01-24 17:38:00 -0800308 for _ in range(10):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700309 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700310 # TODO(sjg): Use Chromite library so we can monitor output
Allen Martinb3aa2672012-03-23 15:55:25 +0000311 self._tools.Run('tegrarcm', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700312 self._out.Notice('Flasher downloaded - please see serial output '
313 'for progress.')
314 return True
315
316 except CmdError as err:
317 if not self._out.stdout_is_tty:
318 return False
319
320 # Only show the error output once unless it changes.
321 err = str(err)
Simon Glass22190ff2012-07-11 14:52:26 +0200322 if not 'could not open USB device' in err:
Allen Martinb3aa2672012-03-23 15:55:25 +0000323 raise CmdError('tegrarcm failed: %s' % err)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700324
325 if err != last_err:
326 self._out.Notice(err)
327 last_err = err
328 self._out.Progress('Please connect USB A-A cable and do a '
329 'recovery-reset', True)
330 time.sleep(1)
331
332 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700333
Simon Glass27a9c142012-03-15 21:08:29 -0700334 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
335 """Wait until we see a device on the USB bus.
336
337 Args:
338 name: Board type name
339 vendor_id: USB vendor ID to look for
340 product_id: USB product ID to look for
341 timeout: Timeout to wait in seconds
342
343 Returns
344 True if the device was found, False if we timed out.
345 """
346 self._out.Progress('Waiting for board to appear on USB bus')
Simon Glass4968a472012-05-23 13:52:19 -0700347 start_time = time.time()
348 while time.time() - start_time < timeout:
Simon Glass27a9c142012-03-15 21:08:29 -0700349 try:
350 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
351 self._tools.Run('lsusb', args, sudo=True)
Simon Glass3381a1a2012-03-22 11:13:19 -0700352 self._out.Progress('Found %s board' % name)
Simon Glass27a9c142012-03-15 21:08:29 -0700353 return True
354
Vadim Bendebury19a77122013-01-24 17:38:00 -0800355 except CmdError:
Simon Glass27a9c142012-03-15 21:08:29 -0700356 pass
357
Simon Glass3381a1a2012-03-22 11:13:19 -0700358 return False
Simon Glass27a9c142012-03-15 21:08:29 -0700359
Vadim Bendebury2b719452013-02-12 17:00:06 -0800360 def DutControl(self, args):
Simon Glass6a616c12012-09-24 18:13:46 -0700361 """Run dut-control with supplied arguments.
362
Simon Glass2065a742013-02-09 13:39:26 -0800363 The correct servo will be used based on self._servo_port. If servo use is
364 disabled, this function does nothing.
Simon Glass6a616c12012-09-24 18:13:46 -0700365
366 Args:
367 args: List of arguments to dut-control.
368
Simon Glass2065a742013-02-09 13:39:26 -0800369 Returns:
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800370 a string, stdout generated by running the command
Simon Glass6a616c12012-09-24 18:13:46 -0700371 """
372 if self._servo_port is None:
Simon Glass2065a742013-02-09 13:39:26 -0800373 return '' # User has requested not to use servo
374 elif self._servo_port:
Simon Glass6a616c12012-09-24 18:13:46 -0700375 args.extend(['-p', '%s' % self._servo_port])
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800376 return self._tools.Run('dut-control', args)
Simon Glass6a616c12012-09-24 18:13:46 -0700377
Vadim Bendebury2b719452013-02-12 17:00:06 -0800378 def VerifySuccess(self):
379 """Verify flash programming operation success.
380
381 The DUT is presumed to be programming flash with verification enabled and
382 console capture mode on. This function scans console output for the
383 success or failure strings.
384
385 Raises:
386 CmdError if neither of the strings show up in the allotted time (2
387 minutes) or console goes silent for more than 10 seconds, or an
388 unexpected output is seen.
389 """
390
391 _SOFT_DEADLINE_LIMIT = 10
392 _HARD_DEADLINE_LIMIT = 120
393 string_leftover = ''
394 soft_deadline = time.time() + _SOFT_DEADLINE_LIMIT
395 hard_deadline = soft_deadline + _HARD_DEADLINE_LIMIT - _SOFT_DEADLINE_LIMIT
396
397 while True:
398 now = time.time()
399 if now > hard_deadline:
400 raise CmdError('Target console flooded, programming failed')
401 if now > soft_deadline:
402 raise CmdError('Target console dead, programming failed')
403 stream = self.DutControl(['cpu_uart_stream',])
404 match = re.search("^cpu_uart_stream:'(.*)'\n", stream)
405 if not match:
406 raise CmdError('Unexpected console output: \n%s\n' % stream)
407
408 text = string_leftover + match.group(1)
409 strings = text.split('\\r')
410 string_leftover = strings.pop()
411 if strings:
412 soft_deadline = now + _SOFT_DEADLINE_LIMIT
413 for string in strings:
414 if self._WRITE_SUCCESS_MESSAGE in string:
415 return True
416 if self._WRITE_FAILURE_MESSAGE in string:
417 return False
418
Simon Glass3c5b35b2012-05-23 13:22:23 -0700419 def _ExtractPayloadParts(self, payload):
420 """Extract the BL1, BL2 and U-Boot parts from a payload.
421
422 An exynos image consists of 3 parts: BL1, BL2 and U-Boot/FDT.
423
424 This pulls out the various parts, puts them into files and returns
425 these files.
426
427 Args:
428 payload: Full path to payload.
429
430 Returns:
431 (bl1, bl2, image) where:
432 bl1 is the filename of the extracted BL1
433 bl2 is the filename of the extracted BL2
434 image is the filename of the extracted U-Boot image
435 """
436 # Pull out the parts from the payload
437 bl1 = os.path.join(self._tools.outdir, 'bl1.bin')
438 bl2 = os.path.join(self._tools.outdir, 'bl2.bin')
439 image = os.path.join(self._tools.outdir, 'u-boot-from-image.bin')
440 data = self._tools.ReadFile(payload)
441
442 # The BL1 is always 8KB - extract that part into a new file
443 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glasse86f3132013-01-08 16:10:43 -0800444 bl1_size = 0x2000
445 self._tools.WriteFile(bl1, data[:bl1_size])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700446
Simon Glasse86f3132013-01-08 16:10:43 -0800447 # Try to detect the BL2 size. We look for 0xea000014 which is the
Tom Wai-Hong Tamf67046f2013-02-06 09:23:26 +0800448 # 'B reset' instruction at the start of U-Boot. When U-Boot is LZO
449 # compressed, we look for a LZO magic instead.
Simon Glasse86f3132013-01-08 16:10:43 -0800450 first_instr = struct.pack('<L', 0xea000014)
Tom Wai-Hong Tamf67046f2013-02-06 09:23:26 +0800451 lzo_magic = struct.pack('>B3s', 0x89, 'LZO')
452 first_instr_offset = data.find(first_instr, bl1_size + 0x3800)
453 lzo_magic_offset = data.find(lzo_magic, bl1_size + 0x3800)
454 uboot_offset = min(first_instr_offset, lzo_magic_offset)
455 if uboot_offset == -1:
456 uboot_offset = max(first_instr_offset, lzo_magic_offset)
Simon Glasse86f3132013-01-08 16:10:43 -0800457 if uboot_offset == -1:
Vadim Bendebury19a77122013-01-24 17:38:00 -0800458 raise ValueError('Could not locate start of U-Boot')
Simon Glasse86f3132013-01-08 16:10:43 -0800459 bl2_size = uboot_offset - bl1_size - 0x800 # 2KB gap after BL2
Simon Glass3c5b35b2012-05-23 13:22:23 -0700460
Simon Glasse86f3132013-01-08 16:10:43 -0800461 # Sanity check: At present we only allow 14KB and 30KB for SPL
462 allowed = [14, 30]
463 if (bl2_size >> 10) not in allowed:
Vadim Bendebury19a77122013-01-24 17:38:00 -0800464 raise ValueError('BL2 size is %dK - only %s supported' %
465 (bl2_size >> 10, ', '.join(
466 [str(size) for size in allowed])))
Simon Glasse86f3132013-01-08 16:10:43 -0800467 self._out.Notice('BL2 size is %dKB' % (bl2_size >> 10))
468
469 # The BL2 (U-Boot SPL) follows BL1. After that there is a 2KB gap
470 bl2_end = uboot_offset - 0x800
471 self._tools.WriteFile(bl2, data[0x2000:bl2_end])
472
Simon Glassbc0d8d42013-02-09 11:59:36 -0800473 # U-Boot itself starts at 24KB, after the gap. As a hack, truncate it
474 # to an assumed maximum size.
475 # TODO(sjg@chromium.org): Get a proper flash map here so we know how
476 # large it is
Simon Glasse86f3132013-01-08 16:10:43 -0800477 # U-Boot itself starts after the gap
Simon Glassbc0d8d42013-02-09 11:59:36 -0800478 self._tools.WriteFile(image, data[uboot_offset:uboot_offset + 0xa0000])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700479 return bl1, bl2, image
480
Vadim Bendebury19a77122013-01-24 17:38:00 -0800481 def ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload,
Simon Glassde9c8072012-07-02 22:29:02 -0700482 kernel):
Simon Glass27a9c142012-03-15 21:08:29 -0700483 """Flash the image to SPI flash.
484
485 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000486 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glass27a9c142012-03-15 21:08:29 -0700487
488 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700489 flash_dest: Destination for flasher, or None to not create a flasher
490 Valid options are spi, sdmmc.
491 flash_uboot: Full path to u-boot.bin to use for flasher.
Simon Glass27a9c142012-03-15 21:08:29 -0700492 bl1: Full path to file containing BL1 (pre-boot).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700493 bl2: Full path to file containing BL2 (SPL).
Simon Glass27a9c142012-03-15 21:08:29 -0700494 payload: Full path to payload.
Simon Glassde9c8072012-07-02 22:29:02 -0700495 kernel: Kernel to send after the payload, or None.
Simon Glass27a9c142012-03-15 21:08:29 -0700496
497 Returns:
498 True if ok, False if failed.
499 """
Simon Glassbc0d8d42013-02-09 11:59:36 -0800500 tools = self._tools
501 payload_bl1, payload_bl2, payload_image = (
502 self._ExtractPayloadParts(payload))
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700503 if flash_dest:
Simon Glassbc0d8d42013-02-09 11:59:36 -0800504 # If we don't have some bits, get them from the image
505 if not flash_uboot or not os.path.exists(tools.Filename(flash_uboot)):
506 self._out.Warning('Extracting U-Boot from payload')
507 flash_uboot = payload_image
508 if not bl1 or not os.path.exists(tools.Filename(bl1)):
509 self._out.Warning('Extracting BL1 from payload')
510 bl1 = payload_bl1
511 if not bl2 or not os.path.exists(tools.Filename(bl2)):
512 self._out.Warning('Extracting BL2 from payload')
513 bl2 = payload_bl2
Vadim Bendebury2b719452013-02-12 17:00:06 -0800514 image = self._PrepareFlasher(flash_uboot, payload, flash_dest, '1:0')
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700515 else:
Simon Glassbc0d8d42013-02-09 11:59:36 -0800516 bl1, bl2, image = payload_bl1, payload_bl2, payload_image
Simon Glass27a9c142012-03-15 21:08:29 -0700517
518 vendor_id = 0x04e8
519 product_id = 0x1234
Simon Glass3381a1a2012-03-22 11:13:19 -0700520
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800521 # Preserve dut_hub_sel state.
Vadim Bendebury2b719452013-02-12 17:00:06 -0800522 preserved_dut_hub_sel = self.DutControl(['dut_hub_sel',]
523 ).strip().split(':')[-1]
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800524 required_dut_hub_sel = 'dut_sees_servo'
Simon Glass3381a1a2012-03-22 11:13:19 -0700525 args = ['warm_reset:on', 'fw_up:on', 'pwr_button:press', 'sleep:.1',
526 'warm_reset:off']
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800527 if preserved_dut_hub_sel != required_dut_hub_sel:
528 # Need to set it to get the port properly powered up.
529 args += ['dut_hub_sel:%s' % required_dut_hub_sel]
Simon Glass3381a1a2012-03-22 11:13:19 -0700530 # TODO(sjg) If the board is bricked a reset does not seem to bring it
531 # back to life.
532 # BUG=chromium-os:28229
533 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800534 self._out.Progress('Reseting board via servo')
Vadim Bendebury2b719452013-02-12 17:00:06 -0800535 self.DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700536
Simon Glassde9c8072012-07-02 22:29:02 -0700537 # If we have a kernel to write, create a new image with that added.
538 if kernel:
539 dl_image = os.path.join(self._tools.outdir, 'image-plus-kernel.bin')
540 data = self._tools.ReadFile(image)
541
542 # Pad the original payload out to the original length
543 data += '\0' * (os.stat(payload).st_size - len(data))
544 data += self._tools.ReadFile(kernel)
545 self._tools.WriteFile(dl_image, data)
546 else:
547 dl_image = image
548
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700549 self._out.Progress('Uploading image')
Simon Glass27a9c142012-03-15 21:08:29 -0700550 download_list = [
Simon Glass3c5b35b2012-05-23 13:22:23 -0700551 # The numbers are the download addresses (in SRAM) for each piece
552 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glass27a9c142012-03-15 21:08:29 -0700553 ['bl1', 0x02021400, bl1],
554 ['bl2', 0x02023400, bl2],
Simon Glassde9c8072012-07-02 22:29:02 -0700555 ['u-boot', 0x43e00000, dl_image]
Simon Glass27a9c142012-03-15 21:08:29 -0700556 ]
Simon Glass3381a1a2012-03-22 11:13:19 -0700557 try:
Simon Glass4968a472012-05-23 13:52:19 -0700558 for upto in range(len(download_list)):
559 item = download_list[upto]
Simon Glass3381a1a2012-03-22 11:13:19 -0700560 if not self._WaitForUSBDevice('exynos', vendor_id, product_id, 4):
Simon Glass4968a472012-05-23 13:52:19 -0700561 if upto == 0:
Simon Glass3381a1a2012-03-22 11:13:19 -0700562 raise CmdError('Could not find Exynos board on USB port')
563 raise CmdError("Stage '%s' did not complete" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700564 self._out.Notice(item[2])
Simon Glass27a9c142012-03-15 21:08:29 -0700565 self._out.Progress("Uploading stage '%s'" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700566
Simon Glass4968a472012-05-23 13:52:19 -0700567 if upto == 0:
568 # The IROM needs roughly 200ms here to be ready for USB download
569 time.sleep(.5)
570
Simon Glass79e3dd02012-08-28 20:08:50 -0700571 args = ['-a', '%#x' % item[1], '-f', item[2]]
572 self._tools.Run('smdk-usbdl', args, sudo=True)
Simon Glass4968a472012-05-23 13:52:19 -0700573 if upto == 1:
574 # Once SPL starts up we can release the power buttom
575 args = ['fw_up:off', 'pwr_button:release']
Vadim Bendebury2b719452013-02-12 17:00:06 -0800576 self.DutControl(args)
Simon Glass4968a472012-05-23 13:52:19 -0700577
Simon Glass3381a1a2012-03-22 11:13:19 -0700578 finally:
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800579 # Make sure that the power button is released and dut_sel_hub state is
580 # restored, whatever happens
Simon Glass3381a1a2012-03-22 11:13:19 -0700581 args = ['fw_up:off', 'pwr_button:release']
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800582 if preserved_dut_hub_sel != required_dut_hub_sel:
583 args += ['dut_hub_sel:%s' % preserved_dut_hub_sel]
Vadim Bendebury2b719452013-02-12 17:00:06 -0800584 self.DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700585
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700586 self._out.Notice('Image downloaded - please see serial output '
Simon Glass3381a1a2012-03-22 11:13:19 -0700587 'for progress.')
Simon Glass27a9c142012-03-15 21:08:29 -0700588 return True
589
Simon Glass0c2ba482012-03-22 21:57:51 -0700590 def _GetDiskInfo(self, disk, item):
591 """Returns information about a SCSI disk device.
592
593 Args:
594 disk: a block device name in sys/block, like '/sys/block/sdf'.
595 item: the item of disk information that is required.
596
597 Returns:
598 The information obtained, as a string, or '[Unknown]' if not found
599 """
600 dev_path = os.path.join(disk, 'device')
601
602 # Search upwards and through symlinks looking for the item.
603 while os.path.isdir(dev_path) and dev_path != '/sys':
604 fname = os.path.join(dev_path, item)
605 if os.path.exists(fname):
606 with open(fname, 'r') as fd:
607 return fd.readline().rstrip()
608
609 # Move up a level and follow any symlink.
610 new_path = os.path.join(dev_path, '..')
611 if os.path.islink(new_path):
612 new_path = os.path.abspath(os.readlink(os.path.dirname(dev_path)))
613 dev_path = new_path
614 return '[Unknown]'
615
616 def _GetDiskCapacity(self, device):
617 """Returns the disk capacity in GB, or 0 if not known.
618
619 Args:
620 device: Device to check, like '/dev/sdf'.
621
622 Returns:
623 Capacity of device in GB, or 0 if not known.
624 """
625 args = ['-l', device]
626 stdout = self._tools.Run('fdisk', args, sudo=True)
627 if stdout:
628 # Seach for the line with capacity information.
629 re_capacity = re.compile('Disk .*: (\d+) \w+,')
630 lines = filter(re_capacity.match, stdout.splitlines())
631 if len(lines):
632 m = re_capacity.match(lines[0])
633
634 # We get something like 7859 MB, so turn into bytes, then GB
635 return int(m.group(1)) * 1024 * 1024 / 1e9
636 return 0
637
638 def _ListUsbDisks(self):
639 """Return a list of available removable USB disks.
640
641 Returns:
642 List of USB devices, each element is itself a list containing:
643 device ('/dev/sdx')
644 manufacturer name
645 product name
646 capacity in GB (an integer)
647 full description (all of the above concatenated).
648 """
649 disk_list = []
650 for disk in glob.glob('/sys/block/sd*'):
651 with open(disk + '/removable', 'r') as fd:
652 if int(fd.readline()) == 1:
653 device = '/dev/%s' % disk.split('/')[-1]
654 manuf = self._GetDiskInfo(disk, 'manufacturer')
655 product = self._GetDiskInfo(disk, 'product')
656 capacity = self._GetDiskCapacity(device)
657 if capacity:
658 desc = '%s: %s %s %d GB' % (device, manuf, product, capacity)
659 disk_list.append([device, manuf, product, capacity, desc])
660 return disk_list
661
662 def WriteToSd(self, flash_dest, disk, uboot, payload):
663 if flash_dest:
Vadim Bendebury2b719452013-02-12 17:00:06 -0800664 raw_image = self._PrepareFlasher(uboot, payload, flash_dest, '1:0')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800665 bl1, bl2, _ = self._ExtractPayloadParts(payload)
Simon Glass559b6612012-05-23 13:28:45 -0700666 spl_load_size = os.stat(raw_image).st_size
667 bl2 = self._bundle.ConfigureExynosBl2(self._fdt, spl_load_size, bl2,
668 'flasher')
669
670 data = self._tools.ReadFile(bl1) + self._tools.ReadFile(bl2)
671
672 # Pad BL2 out to the required size.
673 # We require that it be 24KB, but data will only contain 8KB + 14KB.
674 # Add the extra padding to bring it to 24KB.
675 data += '\0' * (0x6000 - len(data))
676 data += self._tools.ReadFile(raw_image)
677 image = os.path.join(self._tools.outdir, 'flasher-with-bl.bin')
678 self._tools.WriteFile(image, data)
Simon Glass0c2ba482012-03-22 21:57:51 -0700679 self._out.Progress('Writing flasher to %s' % disk)
680 else:
681 image = payload
682 self._out.Progress('Writing image to %s' % disk)
683
684 args = ['if=%s' % image, 'of=%s' % disk, 'bs=512', 'seek=1']
685 self._tools.Run('dd', args, sudo=True)
686
687 def SendToSdCard(self, dest, flash_dest, uboot, payload):
688 """Write a flasher to an SD card.
689
690 Args:
691 dest: Destination in one of these forms:
692 ':<full description of device>'
693 ':.' selects the only available device, fails if more than one option
694 ':<device>' select deivce
695
696 Examples:
697 ':/dev/sdd: Generic Flash Card Reader/Writer 8 GB'
698 ':.'
699 ':/dev/sdd'
700
701 flash_dest: Destination for flasher, or None to not create a flasher:
702 Valid options are spi, sdmmc.
703 uboot: Full path to u-boot.bin.
704 payload: Full path to payload.
705 """
706 disk = None
707 disks = self._ListUsbDisks()
708 if dest[:1] == ':':
709 name = dest[1:]
710
711 # A '.' just means to use the only available disk.
712 if name == '.' and len(disks) == 1:
713 disk = disks[0][0]
714 for disk_info in disks:
715 # Use the full name or the device name.
716 if disk_info[4] == name or disk_info[1] == name:
717 disk = disk_info[0]
718
719 if disk:
720 self.WriteToSd(flash_dest, disk, uboot, payload)
721 else:
722 self._out.Error("Please specify destination -w 'sd:<disk_description>':")
723 self._out.Error(' - description can be . for the only disk, SCSI '
724 'device letter')
725 self._out.Error(' or the full description listed here')
726 msg = 'Found %d available disks.' % len(disks)
727 if not disks:
728 msg += ' Please insert an SD card and try again.'
729 self._out.UserOutput(msg)
730
731 # List available disks as a convenience.
732 for disk in disks:
733 self._out.UserOutput(' %s' % disk[4])
734
Vadim Bendebury19a77122013-01-24 17:38:00 -0800735 def Em100FlashImage(self, image_fname):
Simon Glass9eb8c722012-06-07 13:34:31 -0700736 """Send an image to an attached EM100 device.
737
738 This is a Dediprog EM100 SPI flash emulation device. We set up servo2
739 to do the SPI emulation, then write the image, then boot the board.
740 All going well, this is enough to get U-Boot running.
741
742 Args:
743 image_fname: Filename of image to send
744 """
745 args = ['spi2_vref:off', 'spi2_buf_en:off', 'spi2_buf_on_flex_en:off']
746 args.append('spi_hold:on')
Vadim Bendebury2b719452013-02-12 17:00:06 -0800747 self.DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700748
749 # TODO(sjg@chromium.org): This is for link. We could make this
750 # configurable from the fdt.
751 args = ['-c', 'W25Q64CV', '-d', self._tools.Filename(image_fname), '-r']
752 self._out.Progress('Writing image to em100')
753 self._tools.Run('em100', args, sudo=True)
754
755 self._out.Progress('Resetting board')
756 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off', 'sleep:.5']
757 args.extend(['pwr_button:press', 'sleep:.2', 'pwr_button:release'])
Vadim Bendebury2b719452013-02-12 17:00:06 -0800758 self.DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700759
Simon Glass0c2ba482012-03-22 21:57:51 -0700760
Simon Glass27a9c142012-03-15 21:08:29 -0700761def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glass60a40af2012-06-07 11:54:17 -0700762 bundle, update=True, verify=False, dest=None,
Vadim Bendebury19a77122013-01-24 17:38:00 -0800763 flash_dest=None, kernel=None, bootstub=None, servo='any',
Simon Glass4b8b3bd2012-11-06 12:31:01 -0800764 method='tegra'):
Simon Glasse0b61442012-03-13 15:29:51 -0700765 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700766
767 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700768 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700769
770 Args:
771 output: cros_output object to use.
772 tools: Tools object to use.
773 fdt: Fdt object to use as our device tree.
774 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700775 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700776 image_fname: Filename of image to write.
Simon Glass0191a882012-05-23 13:15:06 -0700777 bundle: The bundle object which created the image.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800778 update: Use faster update algorithm rather then full device erase.
779 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700780 dest: Destination device to write firmware to (usb, sd).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700781 flash_dest: Destination device for flasher to program payload into.
Simon Glassde9c8072012-07-02 22:29:02 -0700782 kernel: Kernel file to write after U-Boot
Vadim Bendebury19a77122013-01-24 17:38:00 -0800783 bootstub: string, file name of the boot stub, if present
Simon Glass6a616c12012-09-24 18:13:46 -0700784 servo: Describes the servo unit to use: none=none; any=any; otherwise
785 port number of servo to use.
Simon Glass710dedc2011-08-09 14:08:52 -0700786 """
Vadim Bendebury2b719452013-02-12 17:00:06 -0800787 write = WriteFirmware(tools, fdt, output, bundle, update, verify)
Simon Glass6a616c12012-09-24 18:13:46 -0700788 write.SelectServo(servo)
Simon Glasse0b61442012-03-13 15:29:51 -0700789 if dest == 'usb':
Vadim Bendebury2b719452013-02-12 17:00:06 -0800790 try:
791 write.DutControl(['cpu_uart_capture:on',])
792 method = fdt.GetString('/chromeos-config', 'flash-method', method)
793 if method == 'tegra':
794 tools.CheckTool('tegrarcm')
795 if flash_dest:
796 write.text_base = bundle.CalcTextBase('flasher ', fdt, flasher)
797 elif bootstub:
798 write.text_base = bundle.CalcTextBase('bootstub ', fdt, bootstub)
799 ok = write.NvidiaFlashImage(flash_dest, flasher, file_list['bct'],
800 image_fname, bootstub)
801 elif method == 'exynos':
802 tools.CheckTool('lsusb', 'usbutils')
803 tools.CheckTool('smdk-usbdl', 'smdk-dltool')
804 ok = write.ExynosFlashImage(flash_dest, flasher,
805 file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname,
806 kernel)
807 else:
808 raise CmdError("Unknown flash method '%s'" % method)
809 if ok:
810 if verify:
811 output.Progress('Image uploaded, waiting for verification')
812 if write.VerifySuccess():
813 output.Progress('Done!')
814 else:
815 ok = False
816 else:
817 output.Progress('Image uploaded - please wait for flashing to '
818 'complete')
819 if not ok:
820 raise CmdError('Image upload failed - please check board connection')
821 finally:
822 write.DutControl(['cpu_uart_capture:off',])
Simon Glass9eb8c722012-06-07 13:34:31 -0700823 elif dest == 'em100':
824 # crosbug.com/31625
825 tools.CheckTool('em100')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800826 write.Em100FlashImage(image_fname)
Simon Glass0c2ba482012-03-22 21:57:51 -0700827 elif dest.startswith('sd'):
828 write.SendToSdCard(dest[2:], flash_dest, flasher, image_fname)
Simon Glass710dedc2011-08-09 14:08:52 -0700829 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700830 raise CmdError("Unknown destination device '%s'" % dest)