blob: 4d981709ed7c0baef2385aef90e904cc0f3b9faf [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
11import tools
12from tools import CmdError
13
14def RoundUp(value, boundary):
15 """Align a value to the next power of 2 boundary.
16
17 Args:
18 value: The value to align.
19 boundary: The boundary value, e.g. 4096. Must be a power of 2.
20
21 Returns:
22 The rounded-up value.
23 """
24 return (value + boundary - 1) & ~(boundary - 1)
25
26
27class WriteFirmware:
28 """Write firmware to a Tegra 2 board using USB A-A cable.
29
30 This class handles re-reflashing a board with new firmware using the Tegra's
31 built-in boot ROM feature. This works by putting the chip into a special mode
32 where it ignores any available firmware and instead reads it from a connected
33 host machine over USB.
34
35 In our case we use that feature to send U-Boot along with a suitable payload
36 and instructions to flash it to SPI flash. The payload is itself normally a
37 full Chrome OS image consisting of U-Boot, some keys and verification
38 information, images and a map of the flash memory.
Simon Glass6a616c12012-09-24 18:13:46 -070039
40 Private attributes:
41 _servo_port: Port number to use to talk to servo with dut-control.
42 Special values are:
43 None: servo is not available.
44 0: any servo will do.
45
Simon Glassa3f29ec2011-07-17 09:36:49 -070046 """
Simon Glass0191a882012-05-23 13:15:06 -070047 def __init__(self, tools, fdt, output, bundle):
Simon Glassa3f29ec2011-07-17 09:36:49 -070048 """Set up a new WriteFirmware object.
49
50 Args:
51 tools: A tools library for us to use.
52 fdt: An fdt which gives us some info that we need.
53 output: An output object to use for printing progress and messages.
Simon Glass0191a882012-05-23 13:15:06 -070054 bundle: A BundleFirmware object which created the image.
Simon Glassa3f29ec2011-07-17 09:36:49 -070055 """
56 self._tools = tools
57 self._fdt = fdt
58 self._out = output
Simon Glass0191a882012-05-23 13:15:06 -070059 self._bundle = bundle
Simon Glass60a40af2012-06-07 11:54:17 -070060 self.text_base = self._fdt.GetInt('/chromeos-config', 'textbase', -1);
Simon Glassa3f29ec2011-07-17 09:36:49 -070061
Simon Glass5b5fd642011-08-17 12:24:01 -070062 # For speed, use the 'update' algorithm and don't verify
63 self.update = True
64 self.verify = False
65
Simon Glass6a616c12012-09-24 18:13:46 -070066 # Use default servo port
67 self._servo_port = 0
68
69 def SelectServo(self, servo):
70 """Select the servo to use for writing firmware.
71
72 Args:
73 servo: String containing description of servo to use:
74 'none' : Don't use servo, generate an error on any attempt.
75 'any' : Use any available servo.
76 '<port>': Use servo with that port number.
77 """
78 if servo == 'none':
79 self._servo_port = None
80 elif servo == 'any':
81 self._servo_port = 0
82 else:
83 self._servo_port = int(servo)
84 self._out.Notice('Servo port %s' % str(self._servo_port))
85
Simon Glass8bd05ec2012-03-22 11:09:04 -070086 def _GetFlashScript(self, payload_size, update, verify, boot_type, checksum,
87 bus='0'):
Simon Glassa3f29ec2011-07-17 09:36:49 -070088 """Get the U-Boot boot command needed to flash U-Boot.
89
90 We leave a marker in the string for the load address of the image,
91 since this depends on the size of this script. This can be replaced by
92 the caller provided that the marker length is unchanged.
93
94 Args:
95 payload_size: Size of payload in bytes.
Simon Glass5b5fd642011-08-17 12:24:01 -070096 update: Use faster update algorithm rather then full device erase
97 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -070098 boot_type: The source for bootdevice (nand, sdmmc, or spi)
Simon Glass8bd05ec2012-03-22 11:09:04 -070099 checksum: The checksum of the payload (an integer)
100 bus: The bus number
Simon Glassa3f29ec2011-07-17 09:36:49 -0700101
102 Returns:
103 A tuple containing:
104 The script, as a string ready to use as a U-Boot boot command, with an
105 embedded marker for the load address.
106 The marker string, which the caller should replace with the correct
107 load address as 8 hex digits, without changing its length.
108 """
109 replace_me = 'zsHEXYla'
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800110 page_size = 4096
Simon Glass0dcbecb2012-03-22 21:38:55 -0700111 if boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800112 page_size = 512
Simon Glass0dcbecb2012-03-22 21:38:55 -0700113 if boot_type != 'spi':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800114 update = False
115
Simon Glassa3f29ec2011-07-17 09:36:49 -0700116 cmds = [
117 'setenv address 0x%s' % replace_me,
118 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800119 'setenv length %#x' % RoundUp(payload_size, page_size),
120 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass8bd05ec2012-03-22 11:09:04 -0700121 'setenv _crc "crc32 -v ${address} ${firmware_size} %#08x"' %
122 checksum,
Simon Glass5b5fd642011-08-17 12:24:01 -0700123 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -0700124 ]
Simon Glass0dcbecb2012-03-22 21:38:55 -0700125 if boot_type == 'nand':
Doug Anderson37ae2292011-09-15 17:41:57 -0700126 cmds.extend([
127 'setenv _init "echo Init NAND; nand info"',
128 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
129 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
130 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
131 ])
Simon Glass0dcbecb2012-03-22 21:38:55 -0700132 elif boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800133 cmds.extend([
134 'setenv _init "echo Init EMMC; mmc rescan 0"',
135 'setenv _erase "echo Erase EMMC; "',
136 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
137 '${blocks} boot1"',
138 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
139 '${blocks} boot1"',
140 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700141 else:
142 cmds.extend([
Simon Glass3d9a6c62012-03-15 20:38:04 -0700143 'setenv _init "echo Init SPI; sf probe %s"' % bus,
Doug Anderson37ae2292011-09-15 17:41:57 -0700144 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
145 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
146 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
147 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
148 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700149
Doug Anderson37ae2292011-09-15 17:41:57 -0700150 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700151 'echo Firmware loaded to ${address}, size ${firmware_size}, '
152 'length ${length}',
Simon Glass8bd05ec2012-03-22 11:09:04 -0700153 'if run _crc; then',
Simon Glassa3f29ec2011-07-17 09:36:49 -0700154 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700155 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700156 if update:
157 cmds += ['time run _update']
158 else:
159 cmds += ['run _erase', 'run _write']
160 if verify:
161 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700162 'run _clear',
163 'run _read',
164 'run _crc',
Simon Glass5b5fd642011-08-17 12:24:01 -0700165 ]
166 else:
167 cmds += ['echo Skipping verify']
Simon Glass8bd05ec2012-03-22 11:09:04 -0700168 cmds.extend([
169 'else',
170 'echo',
171 'echo "** Checksum error on load: please check download tool **"',
172 'fi',
173 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700174 script = '; '.join(cmds)
175 return script, replace_me
176
Simon Glass3d9a6c62012-03-15 20:38:04 -0700177 def PrepareFlasher(self, uboot, payload, update, verify, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700178 """Get a flasher ready for sending to the board.
179
180 The flasher is an executable image consisting of:
181
182 - U-Boot (u-boot.bin);
183 - a special FDT to tell it what to do in the form of a run command;
184 - (we could add some empty space here, in case U-Boot is not built to
185 be relocatable);
186 - the payload (which is a full flash image, or signed U-Boot + fdt).
187
188 Args:
189 uboot: Full path to u-boot.bin.
190 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700191 update: Use faster update algorithm rather then full device erase
192 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -0700193 boot_type: the src for bootdevice (nand, sdmmc, or spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700194
195 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700196 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700197 """
Simon Glass951a2db2011-07-17 15:58:58 -0700198 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700199 payload_data = self._tools.ReadFile(payload)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700200 payload_size = os.stat(payload).st_size
201
Simon Glass8bd05ec2012-03-22 11:09:04 -0700202 # Make sure that the checksum is not negative
203 checksum = binascii.crc32(payload_data) & 0xffffffff
204
205 script, replace_me = self._GetFlashScript(len(payload_data), update,
206 verify, boot_type, checksum, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700207 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800208 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700209 fdt_data = self._tools.ReadFile(fdt.fname)
210
211 # Work out where to place the payload in memory. This is a chicken-and-egg
212 # problem (although in case you haven't heard, it was the chicken that
213 # came first), so we resolve it by replacing the string after
214 # fdt.PutString has done its job.
215 #
216 # Correction: Technically, the egg came first. Whatever genetic mutation
217 # created the new species would have been present in the egg, but not the
218 # parent (since if it was in the parent, it would have been present in the
219 # parent when it was an egg).
220 #
221 # Question: ok so who laid the egg then?
222 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700223
224 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
225 alignment = 0x1000
Simon Glass8bd05ec2012-03-22 11:09:04 -0700226 payload_offset = (payload_offset + alignment - 1) & ~(alignment - 1)
Doug Anderson37ae2292011-09-15 17:41:57 -0700227
Simon Glass2c4b3e52011-11-15 14:45:43 -0800228 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700229 new_str = '%08x' % load_address
230 if len(replace_me) is not len(new_str):
231 raise ValueError("Internal error: replacement string '%s' length does "
232 "not match new string '%s'" % (replace_me, new_str))
233 if len(re.findall(replace_me, fdt_data)) != 1:
234 raise ValueError("Internal error: replacement string '%s' already "
235 "exists in the fdt (%d matches)" % (replace_me, matches))
236 fdt_data = re.sub(replace_me, new_str, fdt_data)
237
238 # Now put it together.
239 data += fdt_data
240 data += "\0" * (payload_offset - len(data))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700241 data += payload_data
Simon Glass951a2db2011-07-17 15:58:58 -0700242 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700243 self._tools.WriteFile(flasher, data)
244
245 # Tell the user about a few things.
246 self._tools.OutputSize('U-Boot', uboot)
247 self._tools.OutputSize('Payload', payload)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700248 self._out.Notice('Payload checksum %08x' % checksum)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700249 self._tools.OutputSize('Flasher', flasher)
250 return flasher
251
Simon Glass89ecf712012-06-07 12:20:15 -0700252 def _NvidiaFlashImage(self, flash_dest, uboot, bct, payload, bootstub):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700253 """Flash the image to SPI flash.
254
255 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000256 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700257
258 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700259 flash_dest: Destination for flasher, or None to not create a flasher
260 Valid options are spi, sdmmc
Simon Glassa3f29ec2011-07-17 09:36:49 -0700261 uboot: Full path to u-boot.bin.
262 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
263 payload: Full path to payload.
Simon Glass89ecf712012-06-07 12:20:15 -0700264 bootstub: Full path to bootstub, which is the payload without the
265 signing information (i.e. bootstub is u-boot.bin + the FDT)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700266
267 Returns:
268 True if ok, False if failed.
269 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800270 # Use a Regex to pull Boot type from BCT file.
271 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
272 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700273
274 # TODO(sjg): The boot type is currently selected by the bct, rather than
275 # flash_dest selecting which bct to use. This is a bit backwards. For now
276 # we go with the bct's idea.
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800277 boot_type = filter(match.match, bct_dumped)
Simon Glass0dcbecb2012-03-22 21:38:55 -0700278 boot_type = match.match(boot_type[0]).group('boot').lower()
Doug Anderson37ae2292011-09-15 17:41:57 -0700279
Simon Glass89ecf712012-06-07 12:20:15 -0700280 if flash_dest:
281 image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
282 boot_type, 0)
Simon Glasse1824db2012-07-11 17:38:40 +0200283 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700284 image = bootstub
Simon Glassa3f29ec2011-07-17 09:36:49 -0700285
Simon Glasse1824db2012-07-11 17:38:40 +0200286 else:
287 image = payload
288 # If we don't know the textbase, extract it from the payload.
289 if self.text_base == -1:
290 data = self._tools.ReadFile(payload)
291 # Skip the BCT which is the first 64KB
292 self.text_base = self._bundle.DecodeTextBase(data[0x10000:])
293
Simon Glass89ecf712012-06-07 12:20:15 -0700294 self._out.Notice('TEXT_BASE is %#x' % self.text_base)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700295 self._out.Progress('Uploading flasher image')
296 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700297 '--bct', bct,
Allen Martinb3aa2672012-03-23 15:55:25 +0000298 '--bootloader', image,
299 '--loadaddr', "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700300 ]
301
302 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
303 last_err = None
304 for tries in range(10):
305 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700306 # TODO(sjg): Use Chromite library so we can monitor output
Allen Martinb3aa2672012-03-23 15:55:25 +0000307 self._tools.Run('tegrarcm', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700308 self._out.Notice('Flasher downloaded - please see serial output '
309 'for progress.')
310 return True
311
312 except CmdError as err:
313 if not self._out.stdout_is_tty:
314 return False
315
316 # Only show the error output once unless it changes.
317 err = str(err)
Simon Glass22190ff2012-07-11 14:52:26 +0200318 if not 'could not open USB device' in err:
Allen Martinb3aa2672012-03-23 15:55:25 +0000319 raise CmdError('tegrarcm failed: %s' % err)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700320
321 if err != last_err:
322 self._out.Notice(err)
323 last_err = err
324 self._out.Progress('Please connect USB A-A cable and do a '
325 'recovery-reset', True)
326 time.sleep(1)
327
328 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700329
Simon Glass27a9c142012-03-15 21:08:29 -0700330 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
331 """Wait until we see a device on the USB bus.
332
333 Args:
334 name: Board type name
335 vendor_id: USB vendor ID to look for
336 product_id: USB product ID to look for
337 timeout: Timeout to wait in seconds
338
339 Returns
340 True if the device was found, False if we timed out.
341 """
342 self._out.Progress('Waiting for board to appear on USB bus')
Simon Glass4968a472012-05-23 13:52:19 -0700343 start_time = time.time()
344 while time.time() - start_time < timeout:
Simon Glass27a9c142012-03-15 21:08:29 -0700345 try:
346 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
347 self._tools.Run('lsusb', args, sudo=True)
Simon Glass3381a1a2012-03-22 11:13:19 -0700348 self._out.Progress('Found %s board' % name)
Simon Glass27a9c142012-03-15 21:08:29 -0700349 return True
350
351 except CmdError as err:
352 pass
353
Simon Glass3381a1a2012-03-22 11:13:19 -0700354 return False
Simon Glass27a9c142012-03-15 21:08:29 -0700355
Simon Glass6a616c12012-09-24 18:13:46 -0700356 def _DutControl(self, args):
357 """Run dut-control with supplied arguments.
358
359 The correct servo will be used based on self._servo_port.
360
361 Args:
362 args: List of arguments to dut-control.
363
364 Raises:
365 IOError if no servo access is permitted.
366 """
367 if self._servo_port is None:
368 raise IOError('No servo access available, please use --servo')
369 if self._servo_port:
370 args.extend(['-p', '%s' % self._servo_port])
371 self._tools.Run('dut-control', args)
372
Simon Glass3c5b35b2012-05-23 13:22:23 -0700373 def _ExtractPayloadParts(self, payload):
374 """Extract the BL1, BL2 and U-Boot parts from a payload.
375
376 An exynos image consists of 3 parts: BL1, BL2 and U-Boot/FDT.
377
378 This pulls out the various parts, puts them into files and returns
379 these files.
380
381 Args:
382 payload: Full path to payload.
383
384 Returns:
385 (bl1, bl2, image) where:
386 bl1 is the filename of the extracted BL1
387 bl2 is the filename of the extracted BL2
388 image is the filename of the extracted U-Boot image
389 """
390 # Pull out the parts from the payload
391 bl1 = os.path.join(self._tools.outdir, 'bl1.bin')
392 bl2 = os.path.join(self._tools.outdir, 'bl2.bin')
393 image = os.path.join(self._tools.outdir, 'u-boot-from-image.bin')
394 data = self._tools.ReadFile(payload)
395
396 # The BL1 is always 8KB - extract that part into a new file
397 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glasse86f3132013-01-08 16:10:43 -0800398 bl1_size = 0x2000
399 self._tools.WriteFile(bl1, data[:bl1_size])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700400
Simon Glasse86f3132013-01-08 16:10:43 -0800401 # Try to detect the BL2 size. We look for 0xea000014 which is the
402 # 'B reset' instruction at the start of U-Boot.
403 first_instr = struct.pack('<L', 0xea000014)
404 uboot_offset = data.find(first_instr, bl1_size + 0x3800)
405 if uboot_offset == -1:
406 raise ValueError('Could not locate start of U-Boot')
407 bl2_size = uboot_offset - bl1_size - 0x800 # 2KB gap after BL2
Simon Glass3c5b35b2012-05-23 13:22:23 -0700408
Simon Glasse86f3132013-01-08 16:10:43 -0800409 # Sanity check: At present we only allow 14KB and 30KB for SPL
410 allowed = [14, 30]
411 if (bl2_size >> 10) not in allowed:
412 raise ValueError('BL2 size is %dK - only %s supported' %
413 (bl2_size >> 10, ', '.join([str(size) for size in allowed])))
414 self._out.Notice('BL2 size is %dKB' % (bl2_size >> 10))
415
416 # The BL2 (U-Boot SPL) follows BL1. After that there is a 2KB gap
417 bl2_end = uboot_offset - 0x800
418 self._tools.WriteFile(bl2, data[0x2000:bl2_end])
419
420 # U-Boot itself starts after the gap
421 self._tools.WriteFile(image, data[uboot_offset:])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700422 return bl1, bl2, image
423
Simon Glassde9c8072012-07-02 22:29:02 -0700424 def _ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload,
425 kernel):
Simon Glass27a9c142012-03-15 21:08:29 -0700426 """Flash the image to SPI flash.
427
428 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000429 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glass27a9c142012-03-15 21:08:29 -0700430
431 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700432 flash_dest: Destination for flasher, or None to not create a flasher
433 Valid options are spi, sdmmc.
434 flash_uboot: Full path to u-boot.bin to use for flasher.
Simon Glass27a9c142012-03-15 21:08:29 -0700435 bl1: Full path to file containing BL1 (pre-boot).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700436 bl2: Full path to file containing BL2 (SPL).
Simon Glass27a9c142012-03-15 21:08:29 -0700437 payload: Full path to payload.
Simon Glassde9c8072012-07-02 22:29:02 -0700438 kernel: Kernel to send after the payload, or None.
Simon Glass27a9c142012-03-15 21:08:29 -0700439
440 Returns:
441 True if ok, False if failed.
442 """
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700443 if flash_dest:
444 image = self.PrepareFlasher(flash_uboot, payload, self.update,
445 self.verify, flash_dest, '1:0')
446 else:
Simon Glass3c5b35b2012-05-23 13:22:23 -0700447 bl1, bl2, image = self._ExtractPayloadParts(payload)
Simon Glass27a9c142012-03-15 21:08:29 -0700448
449 vendor_id = 0x04e8
450 product_id = 0x1234
Simon Glass3381a1a2012-03-22 11:13:19 -0700451
452 self._out.Progress('Reseting board via servo')
453 args = ['warm_reset:on', 'fw_up:on', 'pwr_button:press', 'sleep:.1',
454 'warm_reset:off']
455 # TODO(sjg) If the board is bricked a reset does not seem to bring it
456 # back to life.
457 # BUG=chromium-os:28229
458 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args
Simon Glass6a616c12012-09-24 18:13:46 -0700459 self._DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700460
Simon Glassde9c8072012-07-02 22:29:02 -0700461 # If we have a kernel to write, create a new image with that added.
462 if kernel:
463 dl_image = os.path.join(self._tools.outdir, 'image-plus-kernel.bin')
464 data = self._tools.ReadFile(image)
465
466 # Pad the original payload out to the original length
467 data += '\0' * (os.stat(payload).st_size - len(data))
468 data += self._tools.ReadFile(kernel)
469 self._tools.WriteFile(dl_image, data)
470 else:
471 dl_image = image
472
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700473 self._out.Progress('Uploading image')
Simon Glass27a9c142012-03-15 21:08:29 -0700474 download_list = [
Simon Glass3c5b35b2012-05-23 13:22:23 -0700475 # The numbers are the download addresses (in SRAM) for each piece
476 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glass27a9c142012-03-15 21:08:29 -0700477 ['bl1', 0x02021400, bl1],
478 ['bl2', 0x02023400, bl2],
Simon Glassde9c8072012-07-02 22:29:02 -0700479 ['u-boot', 0x43e00000, dl_image]
Simon Glass27a9c142012-03-15 21:08:29 -0700480 ]
Simon Glass3381a1a2012-03-22 11:13:19 -0700481 try:
Simon Glass4968a472012-05-23 13:52:19 -0700482 for upto in range(len(download_list)):
483 item = download_list[upto]
Simon Glass3381a1a2012-03-22 11:13:19 -0700484 if not self._WaitForUSBDevice('exynos', vendor_id, product_id, 4):
Simon Glass4968a472012-05-23 13:52:19 -0700485 if upto == 0:
Simon Glass3381a1a2012-03-22 11:13:19 -0700486 raise CmdError('Could not find Exynos board on USB port')
487 raise CmdError("Stage '%s' did not complete" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700488 self._out.Notice(item[2])
Simon Glass27a9c142012-03-15 21:08:29 -0700489 self._out.Progress("Uploading stage '%s'" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700490
Simon Glass4968a472012-05-23 13:52:19 -0700491 if upto == 0:
492 # The IROM needs roughly 200ms here to be ready for USB download
493 time.sleep(.5)
494
Simon Glass79e3dd02012-08-28 20:08:50 -0700495 args = ['-a', '%#x' % item[1], '-f', item[2]]
496 self._tools.Run('smdk-usbdl', args, sudo=True)
Simon Glass4968a472012-05-23 13:52:19 -0700497 if upto == 1:
498 # Once SPL starts up we can release the power buttom
499 args = ['fw_up:off', 'pwr_button:release']
Simon Glass6a616c12012-09-24 18:13:46 -0700500 self._DutControl(args)
Simon Glass4968a472012-05-23 13:52:19 -0700501
Simon Glass3381a1a2012-03-22 11:13:19 -0700502 finally:
Simon Glass4968a472012-05-23 13:52:19 -0700503 # Make sure that the power button is released, whatever happens
Simon Glass3381a1a2012-03-22 11:13:19 -0700504 args = ['fw_up:off', 'pwr_button:release']
Simon Glass6a616c12012-09-24 18:13:46 -0700505 self._DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700506
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700507 self._out.Notice('Image downloaded - please see serial output '
Simon Glass3381a1a2012-03-22 11:13:19 -0700508 'for progress.')
Simon Glass27a9c142012-03-15 21:08:29 -0700509 return True
510
Simon Glass0c2ba482012-03-22 21:57:51 -0700511 def _GetDiskInfo(self, disk, item):
512 """Returns information about a SCSI disk device.
513
514 Args:
515 disk: a block device name in sys/block, like '/sys/block/sdf'.
516 item: the item of disk information that is required.
517
518 Returns:
519 The information obtained, as a string, or '[Unknown]' if not found
520 """
521 dev_path = os.path.join(disk, 'device')
522
523 # Search upwards and through symlinks looking for the item.
524 while os.path.isdir(dev_path) and dev_path != '/sys':
525 fname = os.path.join(dev_path, item)
526 if os.path.exists(fname):
527 with open(fname, 'r') as fd:
528 return fd.readline().rstrip()
529
530 # Move up a level and follow any symlink.
531 new_path = os.path.join(dev_path, '..')
532 if os.path.islink(new_path):
533 new_path = os.path.abspath(os.readlink(os.path.dirname(dev_path)))
534 dev_path = new_path
535 return '[Unknown]'
536
537 def _GetDiskCapacity(self, device):
538 """Returns the disk capacity in GB, or 0 if not known.
539
540 Args:
541 device: Device to check, like '/dev/sdf'.
542
543 Returns:
544 Capacity of device in GB, or 0 if not known.
545 """
546 args = ['-l', device]
547 stdout = self._tools.Run('fdisk', args, sudo=True)
548 if stdout:
549 # Seach for the line with capacity information.
550 re_capacity = re.compile('Disk .*: (\d+) \w+,')
551 lines = filter(re_capacity.match, stdout.splitlines())
552 if len(lines):
553 m = re_capacity.match(lines[0])
554
555 # We get something like 7859 MB, so turn into bytes, then GB
556 return int(m.group(1)) * 1024 * 1024 / 1e9
557 return 0
558
559 def _ListUsbDisks(self):
560 """Return a list of available removable USB disks.
561
562 Returns:
563 List of USB devices, each element is itself a list containing:
564 device ('/dev/sdx')
565 manufacturer name
566 product name
567 capacity in GB (an integer)
568 full description (all of the above concatenated).
569 """
570 disk_list = []
571 for disk in glob.glob('/sys/block/sd*'):
572 with open(disk + '/removable', 'r') as fd:
573 if int(fd.readline()) == 1:
574 device = '/dev/%s' % disk.split('/')[-1]
575 manuf = self._GetDiskInfo(disk, 'manufacturer')
576 product = self._GetDiskInfo(disk, 'product')
577 capacity = self._GetDiskCapacity(device)
578 if capacity:
579 desc = '%s: %s %s %d GB' % (device, manuf, product, capacity)
580 disk_list.append([device, manuf, product, capacity, desc])
581 return disk_list
582
583 def WriteToSd(self, flash_dest, disk, uboot, payload):
584 if flash_dest:
Simon Glass559b6612012-05-23 13:28:45 -0700585 raw_image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Simon Glass0c2ba482012-03-22 21:57:51 -0700586 flash_dest, '1:0')
Simon Glass559b6612012-05-23 13:28:45 -0700587 bl1, bl2, payload_data = self._ExtractPayloadParts(payload)
588 spl_load_size = os.stat(raw_image).st_size
589 bl2 = self._bundle.ConfigureExynosBl2(self._fdt, spl_load_size, bl2,
590 'flasher')
591
592 data = self._tools.ReadFile(bl1) + self._tools.ReadFile(bl2)
593
594 # Pad BL2 out to the required size.
595 # We require that it be 24KB, but data will only contain 8KB + 14KB.
596 # Add the extra padding to bring it to 24KB.
597 data += '\0' * (0x6000 - len(data))
598 data += self._tools.ReadFile(raw_image)
599 image = os.path.join(self._tools.outdir, 'flasher-with-bl.bin')
600 self._tools.WriteFile(image, data)
Simon Glass0c2ba482012-03-22 21:57:51 -0700601 self._out.Progress('Writing flasher to %s' % disk)
602 else:
603 image = payload
604 self._out.Progress('Writing image to %s' % disk)
605
606 args = ['if=%s' % image, 'of=%s' % disk, 'bs=512', 'seek=1']
607 self._tools.Run('dd', args, sudo=True)
608
609 def SendToSdCard(self, dest, flash_dest, uboot, payload):
610 """Write a flasher to an SD card.
611
612 Args:
613 dest: Destination in one of these forms:
614 ':<full description of device>'
615 ':.' selects the only available device, fails if more than one option
616 ':<device>' select deivce
617
618 Examples:
619 ':/dev/sdd: Generic Flash Card Reader/Writer 8 GB'
620 ':.'
621 ':/dev/sdd'
622
623 flash_dest: Destination for flasher, or None to not create a flasher:
624 Valid options are spi, sdmmc.
625 uboot: Full path to u-boot.bin.
626 payload: Full path to payload.
627 """
628 disk = None
629 disks = self._ListUsbDisks()
630 if dest[:1] == ':':
631 name = dest[1:]
632
633 # A '.' just means to use the only available disk.
634 if name == '.' and len(disks) == 1:
635 disk = disks[0][0]
636 for disk_info in disks:
637 # Use the full name or the device name.
638 if disk_info[4] == name or disk_info[1] == name:
639 disk = disk_info[0]
640
641 if disk:
642 self.WriteToSd(flash_dest, disk, uboot, payload)
643 else:
644 self._out.Error("Please specify destination -w 'sd:<disk_description>':")
645 self._out.Error(' - description can be . for the only disk, SCSI '
646 'device letter')
647 self._out.Error(' or the full description listed here')
648 msg = 'Found %d available disks.' % len(disks)
649 if not disks:
650 msg += ' Please insert an SD card and try again.'
651 self._out.UserOutput(msg)
652
653 # List available disks as a convenience.
654 for disk in disks:
655 self._out.UserOutput(' %s' % disk[4])
656
Simon Glass9eb8c722012-06-07 13:34:31 -0700657 def _Em100FlashImage(self, image_fname):
658 """Send an image to an attached EM100 device.
659
660 This is a Dediprog EM100 SPI flash emulation device. We set up servo2
661 to do the SPI emulation, then write the image, then boot the board.
662 All going well, this is enough to get U-Boot running.
663
664 Args:
665 image_fname: Filename of image to send
666 """
667 args = ['spi2_vref:off', 'spi2_buf_en:off', 'spi2_buf_on_flex_en:off']
668 args.append('spi_hold:on')
Simon Glass6a616c12012-09-24 18:13:46 -0700669 self._DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700670
671 # TODO(sjg@chromium.org): This is for link. We could make this
672 # configurable from the fdt.
673 args = ['-c', 'W25Q64CV', '-d', self._tools.Filename(image_fname), '-r']
674 self._out.Progress('Writing image to em100')
675 self._tools.Run('em100', args, sudo=True)
676
677 self._out.Progress('Resetting board')
678 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off', 'sleep:.5']
679 args.extend(['pwr_button:press', 'sleep:.2', 'pwr_button:release'])
Simon Glass6a616c12012-09-24 18:13:46 -0700680 self._DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700681
Simon Glass0c2ba482012-03-22 21:57:51 -0700682
Simon Glass27a9c142012-03-15 21:08:29 -0700683def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glass60a40af2012-06-07 11:54:17 -0700684 bundle, update=True, verify=False, dest=None,
Simon Glass4b8b3bd2012-11-06 12:31:01 -0800685 flash_dest=None, kernel=None, props={}, servo='any',
686 method='tegra'):
Simon Glasse0b61442012-03-13 15:29:51 -0700687 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700688
689 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700690 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700691
692 Args:
693 output: cros_output object to use.
694 tools: Tools object to use.
695 fdt: Fdt object to use as our device tree.
696 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700697 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700698 image_fname: Filename of image to write.
Simon Glass0191a882012-05-23 13:15:06 -0700699 bundle: The bundle object which created the image.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800700 update: Use faster update algorithm rather then full device erase.
701 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700702 dest: Destination device to write firmware to (usb, sd).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700703 flash_dest: Destination device for flasher to program payload into.
Simon Glassde9c8072012-07-02 22:29:02 -0700704 kernel: Kernel file to write after U-Boot
Simon Glass794217e2012-06-07 11:40:37 -0700705 props: A dictionary containing properties from the PackFirmware object
Simon Glass6a616c12012-09-24 18:13:46 -0700706 servo: Describes the servo unit to use: none=none; any=any; otherwise
707 port number of servo to use.
Simon Glass710dedc2011-08-09 14:08:52 -0700708 """
Simon Glass0191a882012-05-23 13:15:06 -0700709 write = WriteFirmware(tools, fdt, output, bundle)
Simon Glass6a616c12012-09-24 18:13:46 -0700710 write.SelectServo(servo)
Simon Glass5b5fd642011-08-17 12:24:01 -0700711 write.update = update
712 write.verify = verify
Simon Glasse0b61442012-03-13 15:29:51 -0700713 if dest == 'usb':
Simon Glass4b8b3bd2012-11-06 12:31:01 -0800714 method = fdt.GetString('/chromeos-config', 'flash-method', method)
Simon Glasse0b61442012-03-13 15:29:51 -0700715 if method == 'tegra':
Allen Martinb3aa2672012-03-23 15:55:25 +0000716 tools.CheckTool('tegrarcm')
Simon Glasse1824db2012-07-11 17:38:40 +0200717 bootstub = props.get('bootstub')
Simon Glass89ecf712012-06-07 12:20:15 -0700718 if flash_dest:
719 write.text_base = bundle.CalcTextBase('flasher ', fdt, flasher)
Simon Glasse1824db2012-07-11 17:38:40 +0200720 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700721 write.text_base = bundle.CalcTextBase('bootstub ', fdt, bootstub)
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700722 ok = write._NvidiaFlashImage(flash_dest, flasher, file_list['bct'],
Simon Glass89ecf712012-06-07 12:20:15 -0700723 image_fname, bootstub)
Simon Glass27a9c142012-03-15 21:08:29 -0700724 elif method == 'exynos':
Simon Glass2c389062012-04-09 13:09:01 -0700725 tools.CheckTool('lsusb', 'usbutils')
726 tools.CheckTool('smdk-usbdl', 'smdk-dltool')
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700727 ok = write._ExynosFlashImage(flash_dest, flasher,
Simon Glassde9c8072012-07-02 22:29:02 -0700728 file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname,
729 kernel)
Simon Glasse0b61442012-03-13 15:29:51 -0700730 else:
731 raise CmdError("Unknown flash method '%s'" % method)
732 if ok:
733 output.Progress('Image uploaded - please wait for flashing to '
734 'complete')
735 else:
736 raise CmdError('Image upload failed - please check board connection')
Simon Glass9eb8c722012-06-07 13:34:31 -0700737 elif dest == 'em100':
738 # crosbug.com/31625
739 tools.CheckTool('em100')
740 write._Em100FlashImage(image_fname)
Simon Glass0c2ba482012-03-22 21:57:51 -0700741 elif dest.startswith('sd'):
742 write.SendToSdCard(dest[2:], flash_dest, flasher, image_fname)
Simon Glass710dedc2011-08-09 14:08:52 -0700743 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700744 raise CmdError("Unknown destination device '%s'" % dest)