blob: 8952dccb9a0fb656d2111a9cbb4c362f15b1aa8b [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 """
Simon Glass0191a882012-05-23 13:15:06 -070046 def __init__(self, tools, fdt, output, bundle):
Simon Glassa3f29ec2011-07-17 09:36:49 -070047 """Set up a new WriteFirmware object.
48
49 Args:
50 tools: A tools library for us to use.
51 fdt: An fdt which gives us some info that we need.
52 output: An output object to use for printing progress and messages.
Simon Glass0191a882012-05-23 13:15:06 -070053 bundle: A BundleFirmware object which created the image.
Simon Glassa3f29ec2011-07-17 09:36:49 -070054 """
55 self._tools = tools
56 self._fdt = fdt
57 self._out = output
Simon Glass0191a882012-05-23 13:15:06 -070058 self._bundle = bundle
Vadim Bendebury19a77122013-01-24 17:38:00 -080059 self.text_base = self._fdt.GetInt('/chromeos-config', 'textbase', -1)
Simon Glassa3f29ec2011-07-17 09:36:49 -070060
Simon Glass5b5fd642011-08-17 12:24:01 -070061 # For speed, use the 'update' algorithm and don't verify
62 self.update = True
63 self.verify = False
64
Simon Glass6a616c12012-09-24 18:13:46 -070065 # Use default servo port
66 self._servo_port = 0
67
68 def SelectServo(self, servo):
69 """Select the servo to use for writing firmware.
70
71 Args:
72 servo: String containing description of servo to use:
73 'none' : Don't use servo, generate an error on any attempt.
74 'any' : Use any available servo.
75 '<port>': Use servo with that port number.
76 """
77 if servo == 'none':
78 self._servo_port = None
79 elif servo == 'any':
80 self._servo_port = 0
81 else:
82 self._servo_port = int(servo)
83 self._out.Notice('Servo port %s' % str(self._servo_port))
84
Simon Glass8bd05ec2012-03-22 11:09:04 -070085 def _GetFlashScript(self, payload_size, update, verify, boot_type, checksum,
86 bus='0'):
Simon Glassa3f29ec2011-07-17 09:36:49 -070087 """Get the U-Boot boot command needed to flash U-Boot.
88
89 We leave a marker in the string for the load address of the image,
90 since this depends on the size of this script. This can be replaced by
91 the caller provided that the marker length is unchanged.
92
93 Args:
94 payload_size: Size of payload in bytes.
Simon Glass5b5fd642011-08-17 12:24:01 -070095 update: Use faster update algorithm rather then full device erase
96 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -070097 boot_type: The source for bootdevice (nand, sdmmc, or spi)
Simon Glass8bd05ec2012-03-22 11:09:04 -070098 checksum: The checksum of the payload (an integer)
99 bus: The bus number
Simon Glassa3f29ec2011-07-17 09:36:49 -0700100
101 Returns:
102 A tuple containing:
103 The script, as a string ready to use as a U-Boot boot command, with an
104 embedded marker for the load address.
105 The marker string, which the caller should replace with the correct
106 load address as 8 hex digits, without changing its length.
107 """
108 replace_me = 'zsHEXYla'
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800109 page_size = 4096
Simon Glass0dcbecb2012-03-22 21:38:55 -0700110 if boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800111 page_size = 512
Simon Glass0dcbecb2012-03-22 21:38:55 -0700112 if boot_type != 'spi':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800113 update = False
114
Simon Glassa3f29ec2011-07-17 09:36:49 -0700115 cmds = [
116 'setenv address 0x%s' % replace_me,
117 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800118 'setenv length %#x' % RoundUp(payload_size, page_size),
119 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass8bd05ec2012-03-22 11:09:04 -0700120 'setenv _crc "crc32 -v ${address} ${firmware_size} %#08x"' %
121 checksum,
Simon Glass5b5fd642011-08-17 12:24:01 -0700122 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -0700123 ]
Simon Glass0dcbecb2012-03-22 21:38:55 -0700124 if boot_type == 'nand':
Doug Anderson37ae2292011-09-15 17:41:57 -0700125 cmds.extend([
126 'setenv _init "echo Init NAND; nand info"',
127 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
128 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
129 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
130 ])
Simon Glass0dcbecb2012-03-22 21:38:55 -0700131 elif boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800132 cmds.extend([
133 'setenv _init "echo Init EMMC; mmc rescan 0"',
134 'setenv _erase "echo Erase EMMC; "',
135 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
136 '${blocks} boot1"',
137 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
138 '${blocks} boot1"',
139 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700140 else:
141 cmds.extend([
Simon Glass3d9a6c62012-03-15 20:38:04 -0700142 'setenv _init "echo Init SPI; sf probe %s"' % bus,
Doug Anderson37ae2292011-09-15 17:41:57 -0700143 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
144 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
145 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
146 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
147 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700148
Doug Anderson37ae2292011-09-15 17:41:57 -0700149 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700150 'echo Firmware loaded to ${address}, size ${firmware_size}, '
151 'length ${length}',
Simon Glass8bd05ec2012-03-22 11:09:04 -0700152 'if run _crc; then',
Simon Glassa3f29ec2011-07-17 09:36:49 -0700153 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700154 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700155 if update:
156 cmds += ['time run _update']
157 else:
158 cmds += ['run _erase', 'run _write']
159 if verify:
160 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700161 'run _clear',
162 'run _read',
163 'run _crc',
Simon Glass5b5fd642011-08-17 12:24:01 -0700164 ]
165 else:
166 cmds += ['echo Skipping verify']
Simon Glass8bd05ec2012-03-22 11:09:04 -0700167 cmds.extend([
168 'else',
169 'echo',
170 'echo "** Checksum error on load: please check download tool **"',
171 'fi',
172 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700173 script = '; '.join(cmds)
174 return script, replace_me
175
Simon Glass3d9a6c62012-03-15 20:38:04 -0700176 def PrepareFlasher(self, uboot, payload, update, verify, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700177 """Get a flasher ready for sending to the board.
178
179 The flasher is an executable image consisting of:
180
181 - U-Boot (u-boot.bin);
182 - a special FDT to tell it what to do in the form of a run command;
183 - (we could add some empty space here, in case U-Boot is not built to
184 be relocatable);
185 - the payload (which is a full flash image, or signed U-Boot + fdt).
186
187 Args:
188 uboot: Full path to u-boot.bin.
189 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700190 update: Use faster update algorithm rather then full device erase
191 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -0700192 boot_type: the src for bootdevice (nand, sdmmc, or spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700193
194 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700195 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700196 """
Simon Glass951a2db2011-07-17 15:58:58 -0700197 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700198 payload_data = self._tools.ReadFile(payload)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700199
Simon Glass8bd05ec2012-03-22 11:09:04 -0700200 # Make sure that the checksum is not negative
201 checksum = binascii.crc32(payload_data) & 0xffffffff
202
203 script, replace_me = self._GetFlashScript(len(payload_data), update,
204 verify, boot_type, checksum, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700205 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800206 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700207 fdt_data = self._tools.ReadFile(fdt.fname)
208
209 # Work out where to place the payload in memory. This is a chicken-and-egg
210 # problem (although in case you haven't heard, it was the chicken that
211 # came first), so we resolve it by replacing the string after
212 # fdt.PutString has done its job.
213 #
214 # Correction: Technically, the egg came first. Whatever genetic mutation
215 # created the new species would have been present in the egg, but not the
216 # parent (since if it was in the parent, it would have been present in the
217 # parent when it was an egg).
218 #
219 # Question: ok so who laid the egg then?
220 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700221
222 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
223 alignment = 0x1000
Simon Glass8bd05ec2012-03-22 11:09:04 -0700224 payload_offset = (payload_offset + alignment - 1) & ~(alignment - 1)
Doug Anderson37ae2292011-09-15 17:41:57 -0700225
Simon Glass2c4b3e52011-11-15 14:45:43 -0800226 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700227 new_str = '%08x' % load_address
228 if len(replace_me) is not len(new_str):
229 raise ValueError("Internal error: replacement string '%s' length does "
230 "not match new string '%s'" % (replace_me, new_str))
Vadim Bendebury19a77122013-01-24 17:38:00 -0800231 matches = len(re.findall(replace_me, fdt_data))
232 if matches != 1:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700233 raise ValueError("Internal error: replacement string '%s' already "
234 "exists in the fdt (%d matches)" % (replace_me, matches))
235 fdt_data = re.sub(replace_me, new_str, fdt_data)
236
237 # Now put it together.
238 data += fdt_data
239 data += "\0" * (payload_offset - len(data))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700240 data += payload_data
Simon Glass951a2db2011-07-17 15:58:58 -0700241 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700242 self._tools.WriteFile(flasher, data)
243
244 # Tell the user about a few things.
245 self._tools.OutputSize('U-Boot', uboot)
246 self._tools.OutputSize('Payload', payload)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700247 self._out.Notice('Payload checksum %08x' % checksum)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700248 self._tools.OutputSize('Flasher', flasher)
249 return flasher
250
Vadim Bendebury19a77122013-01-24 17:38:00 -0800251 def NvidiaFlashImage(self, flash_dest, uboot, bct, payload, bootstub):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700252 """Flash the image to SPI flash.
253
254 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000255 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700256
257 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700258 flash_dest: Destination for flasher, or None to not create a flasher
259 Valid options are spi, sdmmc
Simon Glassa3f29ec2011-07-17 09:36:49 -0700260 uboot: Full path to u-boot.bin.
261 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
262 payload: Full path to payload.
Simon Glass89ecf712012-06-07 12:20:15 -0700263 bootstub: Full path to bootstub, which is the payload without the
264 signing information (i.e. bootstub is u-boot.bin + the FDT)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700265
266 Returns:
267 True if ok, False if failed.
268 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800269 # Use a Regex to pull Boot type from BCT file.
270 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
271 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700272
273 # TODO(sjg): The boot type is currently selected by the bct, rather than
274 # flash_dest selecting which bct to use. This is a bit backwards. For now
275 # we go with the bct's idea.
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800276 boot_type = filter(match.match, bct_dumped)
Simon Glass0dcbecb2012-03-22 21:38:55 -0700277 boot_type = match.match(boot_type[0]).group('boot').lower()
Doug Anderson37ae2292011-09-15 17:41:57 -0700278
Simon Glass89ecf712012-06-07 12:20:15 -0700279 if flash_dest:
280 image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
281 boot_type, 0)
Simon Glasse1824db2012-07-11 17:38:40 +0200282 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700283 image = bootstub
Simon Glassa3f29ec2011-07-17 09:36:49 -0700284
Simon Glasse1824db2012-07-11 17:38:40 +0200285 else:
286 image = payload
287 # If we don't know the textbase, extract it from the payload.
288 if self.text_base == -1:
289 data = self._tools.ReadFile(payload)
290 # Skip the BCT which is the first 64KB
291 self.text_base = self._bundle.DecodeTextBase(data[0x10000:])
292
Simon Glass89ecf712012-06-07 12:20:15 -0700293 self._out.Notice('TEXT_BASE is %#x' % self.text_base)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700294 self._out.Progress('Uploading flasher image')
295 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700296 '--bct', bct,
Allen Martinb3aa2672012-03-23 15:55:25 +0000297 '--bootloader', image,
298 '--loadaddr', "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700299 ]
300
301 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
302 last_err = None
Vadim Bendebury19a77122013-01-24 17:38:00 -0800303 for _ in range(10):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700304 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700305 # TODO(sjg): Use Chromite library so we can monitor output
Allen Martinb3aa2672012-03-23 15:55:25 +0000306 self._tools.Run('tegrarcm', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700307 self._out.Notice('Flasher downloaded - please see serial output '
308 'for progress.')
309 return True
310
311 except CmdError as err:
312 if not self._out.stdout_is_tty:
313 return False
314
315 # Only show the error output once unless it changes.
316 err = str(err)
Simon Glass22190ff2012-07-11 14:52:26 +0200317 if not 'could not open USB device' in err:
Allen Martinb3aa2672012-03-23 15:55:25 +0000318 raise CmdError('tegrarcm failed: %s' % err)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700319
320 if err != last_err:
321 self._out.Notice(err)
322 last_err = err
323 self._out.Progress('Please connect USB A-A cable and do a '
324 'recovery-reset', True)
325 time.sleep(1)
326
327 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700328
Simon Glass27a9c142012-03-15 21:08:29 -0700329 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
330 """Wait until we see a device on the USB bus.
331
332 Args:
333 name: Board type name
334 vendor_id: USB vendor ID to look for
335 product_id: USB product ID to look for
336 timeout: Timeout to wait in seconds
337
338 Returns
339 True if the device was found, False if we timed out.
340 """
341 self._out.Progress('Waiting for board to appear on USB bus')
Simon Glass4968a472012-05-23 13:52:19 -0700342 start_time = time.time()
343 while time.time() - start_time < timeout:
Simon Glass27a9c142012-03-15 21:08:29 -0700344 try:
345 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
346 self._tools.Run('lsusb', args, sudo=True)
Simon Glass3381a1a2012-03-22 11:13:19 -0700347 self._out.Progress('Found %s board' % name)
Simon Glass27a9c142012-03-15 21:08:29 -0700348 return True
349
Vadim Bendebury19a77122013-01-24 17:38:00 -0800350 except CmdError:
Simon Glass27a9c142012-03-15 21:08:29 -0700351 pass
352
Simon Glass3381a1a2012-03-22 11:13:19 -0700353 return False
Simon Glass27a9c142012-03-15 21:08:29 -0700354
Simon Glass6a616c12012-09-24 18:13:46 -0700355 def _DutControl(self, args):
356 """Run dut-control with supplied arguments.
357
358 The correct servo will be used based on self._servo_port.
359
360 Args:
361 args: List of arguments to dut-control.
362
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800363 Retruns:
364 a string, stdout generated by running the command
Simon Glass6a616c12012-09-24 18:13:46 -0700365 Raises:
366 IOError if no servo access is permitted.
367 """
368 if self._servo_port is None:
369 raise IOError('No servo access available, please use --servo')
370 if self._servo_port:
371 args.extend(['-p', '%s' % self._servo_port])
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800372 return self._tools.Run('dut-control', args)
Simon Glass6a616c12012-09-24 18:13:46 -0700373
Simon Glass3c5b35b2012-05-23 13:22:23 -0700374 def _ExtractPayloadParts(self, payload):
375 """Extract the BL1, BL2 and U-Boot parts from a payload.
376
377 An exynos image consists of 3 parts: BL1, BL2 and U-Boot/FDT.
378
379 This pulls out the various parts, puts them into files and returns
380 these files.
381
382 Args:
383 payload: Full path to payload.
384
385 Returns:
386 (bl1, bl2, image) where:
387 bl1 is the filename of the extracted BL1
388 bl2 is the filename of the extracted BL2
389 image is the filename of the extracted U-Boot image
390 """
391 # Pull out the parts from the payload
392 bl1 = os.path.join(self._tools.outdir, 'bl1.bin')
393 bl2 = os.path.join(self._tools.outdir, 'bl2.bin')
394 image = os.path.join(self._tools.outdir, 'u-boot-from-image.bin')
395 data = self._tools.ReadFile(payload)
396
397 # The BL1 is always 8KB - extract that part into a new file
398 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glasse86f3132013-01-08 16:10:43 -0800399 bl1_size = 0x2000
400 self._tools.WriteFile(bl1, data[:bl1_size])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700401
Simon Glasse86f3132013-01-08 16:10:43 -0800402 # Try to detect the BL2 size. We look for 0xea000014 which is the
Tom Wai-Hong Tamf67046f2013-02-06 09:23:26 +0800403 # 'B reset' instruction at the start of U-Boot. When U-Boot is LZO
404 # compressed, we look for a LZO magic instead.
Simon Glasse86f3132013-01-08 16:10:43 -0800405 first_instr = struct.pack('<L', 0xea000014)
Tom Wai-Hong Tamf67046f2013-02-06 09:23:26 +0800406 lzo_magic = struct.pack('>B3s', 0x89, 'LZO')
407 first_instr_offset = data.find(first_instr, bl1_size + 0x3800)
408 lzo_magic_offset = data.find(lzo_magic, bl1_size + 0x3800)
409 uboot_offset = min(first_instr_offset, lzo_magic_offset)
410 if uboot_offset == -1:
411 uboot_offset = max(first_instr_offset, lzo_magic_offset)
Simon Glasse86f3132013-01-08 16:10:43 -0800412 if uboot_offset == -1:
Vadim Bendebury19a77122013-01-24 17:38:00 -0800413 raise ValueError('Could not locate start of U-Boot')
Simon Glasse86f3132013-01-08 16:10:43 -0800414 bl2_size = uboot_offset - bl1_size - 0x800 # 2KB gap after BL2
Simon Glass3c5b35b2012-05-23 13:22:23 -0700415
Simon Glasse86f3132013-01-08 16:10:43 -0800416 # Sanity check: At present we only allow 14KB and 30KB for SPL
417 allowed = [14, 30]
418 if (bl2_size >> 10) not in allowed:
Vadim Bendebury19a77122013-01-24 17:38:00 -0800419 raise ValueError('BL2 size is %dK - only %s supported' %
420 (bl2_size >> 10, ', '.join(
421 [str(size) for size in allowed])))
Simon Glasse86f3132013-01-08 16:10:43 -0800422 self._out.Notice('BL2 size is %dKB' % (bl2_size >> 10))
423
424 # The BL2 (U-Boot SPL) follows BL1. After that there is a 2KB gap
425 bl2_end = uboot_offset - 0x800
426 self._tools.WriteFile(bl2, data[0x2000:bl2_end])
427
428 # U-Boot itself starts after the gap
429 self._tools.WriteFile(image, data[uboot_offset:])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700430 return bl1, bl2, image
431
Vadim Bendebury19a77122013-01-24 17:38:00 -0800432 def ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload,
Simon Glassde9c8072012-07-02 22:29:02 -0700433 kernel):
Simon Glass27a9c142012-03-15 21:08:29 -0700434 """Flash the image to SPI flash.
435
436 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000437 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glass27a9c142012-03-15 21:08:29 -0700438
439 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700440 flash_dest: Destination for flasher, or None to not create a flasher
441 Valid options are spi, sdmmc.
442 flash_uboot: Full path to u-boot.bin to use for flasher.
Simon Glass27a9c142012-03-15 21:08:29 -0700443 bl1: Full path to file containing BL1 (pre-boot).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700444 bl2: Full path to file containing BL2 (SPL).
Simon Glass27a9c142012-03-15 21:08:29 -0700445 payload: Full path to payload.
Simon Glassde9c8072012-07-02 22:29:02 -0700446 kernel: Kernel to send after the payload, or None.
Simon Glass27a9c142012-03-15 21:08:29 -0700447
448 Returns:
449 True if ok, False if failed.
450 """
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700451 if flash_dest:
452 image = self.PrepareFlasher(flash_uboot, payload, self.update,
453 self.verify, flash_dest, '1:0')
454 else:
Simon Glass3c5b35b2012-05-23 13:22:23 -0700455 bl1, bl2, image = self._ExtractPayloadParts(payload)
Simon Glass27a9c142012-03-15 21:08:29 -0700456
457 vendor_id = 0x04e8
458 product_id = 0x1234
Simon Glass3381a1a2012-03-22 11:13:19 -0700459
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800460 # Preserve dut_hub_sel state.
461 preserved_dut_hub_sel = self._DutControl(['dut_hub_sel',]
462 ).strip().split(':')[-1]
463 required_dut_hub_sel = 'dut_sees_servo'
Simon Glass3381a1a2012-03-22 11:13:19 -0700464 args = ['warm_reset:on', 'fw_up:on', 'pwr_button:press', 'sleep:.1',
465 'warm_reset:off']
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800466 if preserved_dut_hub_sel != required_dut_hub_sel:
467 # Need to set it to get the port properly powered up.
468 args += ['dut_hub_sel:%s' % required_dut_hub_sel]
Simon Glass3381a1a2012-03-22 11:13:19 -0700469 # TODO(sjg) If the board is bricked a reset does not seem to bring it
470 # back to life.
471 # BUG=chromium-os:28229
472 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800473 self._out.Progress('Reseting board via servo')
Simon Glass6a616c12012-09-24 18:13:46 -0700474 self._DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700475
Simon Glassde9c8072012-07-02 22:29:02 -0700476 # If we have a kernel to write, create a new image with that added.
477 if kernel:
478 dl_image = os.path.join(self._tools.outdir, 'image-plus-kernel.bin')
479 data = self._tools.ReadFile(image)
480
481 # Pad the original payload out to the original length
482 data += '\0' * (os.stat(payload).st_size - len(data))
483 data += self._tools.ReadFile(kernel)
484 self._tools.WriteFile(dl_image, data)
485 else:
486 dl_image = image
487
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700488 self._out.Progress('Uploading image')
Simon Glass27a9c142012-03-15 21:08:29 -0700489 download_list = [
Simon Glass3c5b35b2012-05-23 13:22:23 -0700490 # The numbers are the download addresses (in SRAM) for each piece
491 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glass27a9c142012-03-15 21:08:29 -0700492 ['bl1', 0x02021400, bl1],
493 ['bl2', 0x02023400, bl2],
Simon Glassde9c8072012-07-02 22:29:02 -0700494 ['u-boot', 0x43e00000, dl_image]
Simon Glass27a9c142012-03-15 21:08:29 -0700495 ]
Simon Glass3381a1a2012-03-22 11:13:19 -0700496 try:
Simon Glass4968a472012-05-23 13:52:19 -0700497 for upto in range(len(download_list)):
498 item = download_list[upto]
Simon Glass3381a1a2012-03-22 11:13:19 -0700499 if not self._WaitForUSBDevice('exynos', vendor_id, product_id, 4):
Simon Glass4968a472012-05-23 13:52:19 -0700500 if upto == 0:
Simon Glass3381a1a2012-03-22 11:13:19 -0700501 raise CmdError('Could not find Exynos board on USB port')
502 raise CmdError("Stage '%s' did not complete" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700503 self._out.Notice(item[2])
Simon Glass27a9c142012-03-15 21:08:29 -0700504 self._out.Progress("Uploading stage '%s'" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700505
Simon Glass4968a472012-05-23 13:52:19 -0700506 if upto == 0:
507 # The IROM needs roughly 200ms here to be ready for USB download
508 time.sleep(.5)
509
Simon Glass79e3dd02012-08-28 20:08:50 -0700510 args = ['-a', '%#x' % item[1], '-f', item[2]]
511 self._tools.Run('smdk-usbdl', args, sudo=True)
Simon Glass4968a472012-05-23 13:52:19 -0700512 if upto == 1:
513 # Once SPL starts up we can release the power buttom
514 args = ['fw_up:off', 'pwr_button:release']
Simon Glass6a616c12012-09-24 18:13:46 -0700515 self._DutControl(args)
Simon Glass4968a472012-05-23 13:52:19 -0700516
Simon Glass3381a1a2012-03-22 11:13:19 -0700517 finally:
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800518 # Make sure that the power button is released and dut_sel_hub state is
519 # restored, whatever happens
Simon Glass3381a1a2012-03-22 11:13:19 -0700520 args = ['fw_up:off', 'pwr_button:release']
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800521 if preserved_dut_hub_sel != required_dut_hub_sel:
522 args += ['dut_hub_sel:%s' % preserved_dut_hub_sel]
Simon Glass6a616c12012-09-24 18:13:46 -0700523 self._DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700524
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700525 self._out.Notice('Image downloaded - please see serial output '
Simon Glass3381a1a2012-03-22 11:13:19 -0700526 'for progress.')
Simon Glass27a9c142012-03-15 21:08:29 -0700527 return True
528
Simon Glass0c2ba482012-03-22 21:57:51 -0700529 def _GetDiskInfo(self, disk, item):
530 """Returns information about a SCSI disk device.
531
532 Args:
533 disk: a block device name in sys/block, like '/sys/block/sdf'.
534 item: the item of disk information that is required.
535
536 Returns:
537 The information obtained, as a string, or '[Unknown]' if not found
538 """
539 dev_path = os.path.join(disk, 'device')
540
541 # Search upwards and through symlinks looking for the item.
542 while os.path.isdir(dev_path) and dev_path != '/sys':
543 fname = os.path.join(dev_path, item)
544 if os.path.exists(fname):
545 with open(fname, 'r') as fd:
546 return fd.readline().rstrip()
547
548 # Move up a level and follow any symlink.
549 new_path = os.path.join(dev_path, '..')
550 if os.path.islink(new_path):
551 new_path = os.path.abspath(os.readlink(os.path.dirname(dev_path)))
552 dev_path = new_path
553 return '[Unknown]'
554
555 def _GetDiskCapacity(self, device):
556 """Returns the disk capacity in GB, or 0 if not known.
557
558 Args:
559 device: Device to check, like '/dev/sdf'.
560
561 Returns:
562 Capacity of device in GB, or 0 if not known.
563 """
564 args = ['-l', device]
565 stdout = self._tools.Run('fdisk', args, sudo=True)
566 if stdout:
567 # Seach for the line with capacity information.
568 re_capacity = re.compile('Disk .*: (\d+) \w+,')
569 lines = filter(re_capacity.match, stdout.splitlines())
570 if len(lines):
571 m = re_capacity.match(lines[0])
572
573 # We get something like 7859 MB, so turn into bytes, then GB
574 return int(m.group(1)) * 1024 * 1024 / 1e9
575 return 0
576
577 def _ListUsbDisks(self):
578 """Return a list of available removable USB disks.
579
580 Returns:
581 List of USB devices, each element is itself a list containing:
582 device ('/dev/sdx')
583 manufacturer name
584 product name
585 capacity in GB (an integer)
586 full description (all of the above concatenated).
587 """
588 disk_list = []
589 for disk in glob.glob('/sys/block/sd*'):
590 with open(disk + '/removable', 'r') as fd:
591 if int(fd.readline()) == 1:
592 device = '/dev/%s' % disk.split('/')[-1]
593 manuf = self._GetDiskInfo(disk, 'manufacturer')
594 product = self._GetDiskInfo(disk, 'product')
595 capacity = self._GetDiskCapacity(device)
596 if capacity:
597 desc = '%s: %s %s %d GB' % (device, manuf, product, capacity)
598 disk_list.append([device, manuf, product, capacity, desc])
599 return disk_list
600
601 def WriteToSd(self, flash_dest, disk, uboot, payload):
602 if flash_dest:
Simon Glass559b6612012-05-23 13:28:45 -0700603 raw_image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Simon Glass0c2ba482012-03-22 21:57:51 -0700604 flash_dest, '1:0')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800605 bl1, bl2, _ = self._ExtractPayloadParts(payload)
Simon Glass559b6612012-05-23 13:28:45 -0700606 spl_load_size = os.stat(raw_image).st_size
607 bl2 = self._bundle.ConfigureExynosBl2(self._fdt, spl_load_size, bl2,
608 'flasher')
609
610 data = self._tools.ReadFile(bl1) + self._tools.ReadFile(bl2)
611
612 # Pad BL2 out to the required size.
613 # We require that it be 24KB, but data will only contain 8KB + 14KB.
614 # Add the extra padding to bring it to 24KB.
615 data += '\0' * (0x6000 - len(data))
616 data += self._tools.ReadFile(raw_image)
617 image = os.path.join(self._tools.outdir, 'flasher-with-bl.bin')
618 self._tools.WriteFile(image, data)
Simon Glass0c2ba482012-03-22 21:57:51 -0700619 self._out.Progress('Writing flasher to %s' % disk)
620 else:
621 image = payload
622 self._out.Progress('Writing image to %s' % disk)
623
624 args = ['if=%s' % image, 'of=%s' % disk, 'bs=512', 'seek=1']
625 self._tools.Run('dd', args, sudo=True)
626
627 def SendToSdCard(self, dest, flash_dest, uboot, payload):
628 """Write a flasher to an SD card.
629
630 Args:
631 dest: Destination in one of these forms:
632 ':<full description of device>'
633 ':.' selects the only available device, fails if more than one option
634 ':<device>' select deivce
635
636 Examples:
637 ':/dev/sdd: Generic Flash Card Reader/Writer 8 GB'
638 ':.'
639 ':/dev/sdd'
640
641 flash_dest: Destination for flasher, or None to not create a flasher:
642 Valid options are spi, sdmmc.
643 uboot: Full path to u-boot.bin.
644 payload: Full path to payload.
645 """
646 disk = None
647 disks = self._ListUsbDisks()
648 if dest[:1] == ':':
649 name = dest[1:]
650
651 # A '.' just means to use the only available disk.
652 if name == '.' and len(disks) == 1:
653 disk = disks[0][0]
654 for disk_info in disks:
655 # Use the full name or the device name.
656 if disk_info[4] == name or disk_info[1] == name:
657 disk = disk_info[0]
658
659 if disk:
660 self.WriteToSd(flash_dest, disk, uboot, payload)
661 else:
662 self._out.Error("Please specify destination -w 'sd:<disk_description>':")
663 self._out.Error(' - description can be . for the only disk, SCSI '
664 'device letter')
665 self._out.Error(' or the full description listed here')
666 msg = 'Found %d available disks.' % len(disks)
667 if not disks:
668 msg += ' Please insert an SD card and try again.'
669 self._out.UserOutput(msg)
670
671 # List available disks as a convenience.
672 for disk in disks:
673 self._out.UserOutput(' %s' % disk[4])
674
Vadim Bendebury19a77122013-01-24 17:38:00 -0800675 def Em100FlashImage(self, image_fname):
Simon Glass9eb8c722012-06-07 13:34:31 -0700676 """Send an image to an attached EM100 device.
677
678 This is a Dediprog EM100 SPI flash emulation device. We set up servo2
679 to do the SPI emulation, then write the image, then boot the board.
680 All going well, this is enough to get U-Boot running.
681
682 Args:
683 image_fname: Filename of image to send
684 """
685 args = ['spi2_vref:off', 'spi2_buf_en:off', 'spi2_buf_on_flex_en:off']
686 args.append('spi_hold:on')
Simon Glass6a616c12012-09-24 18:13:46 -0700687 self._DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700688
689 # TODO(sjg@chromium.org): This is for link. We could make this
690 # configurable from the fdt.
691 args = ['-c', 'W25Q64CV', '-d', self._tools.Filename(image_fname), '-r']
692 self._out.Progress('Writing image to em100')
693 self._tools.Run('em100', args, sudo=True)
694
695 self._out.Progress('Resetting board')
696 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off', 'sleep:.5']
697 args.extend(['pwr_button:press', 'sleep:.2', 'pwr_button:release'])
Simon Glass6a616c12012-09-24 18:13:46 -0700698 self._DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700699
Simon Glass0c2ba482012-03-22 21:57:51 -0700700
Simon Glass27a9c142012-03-15 21:08:29 -0700701def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glass60a40af2012-06-07 11:54:17 -0700702 bundle, update=True, verify=False, dest=None,
Vadim Bendebury19a77122013-01-24 17:38:00 -0800703 flash_dest=None, kernel=None, bootstub=None, servo='any',
Simon Glass4b8b3bd2012-11-06 12:31:01 -0800704 method='tegra'):
Simon Glasse0b61442012-03-13 15:29:51 -0700705 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700706
707 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700708 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700709
710 Args:
711 output: cros_output object to use.
712 tools: Tools object to use.
713 fdt: Fdt object to use as our device tree.
714 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700715 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700716 image_fname: Filename of image to write.
Simon Glass0191a882012-05-23 13:15:06 -0700717 bundle: The bundle object which created the image.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800718 update: Use faster update algorithm rather then full device erase.
719 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700720 dest: Destination device to write firmware to (usb, sd).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700721 flash_dest: Destination device for flasher to program payload into.
Simon Glassde9c8072012-07-02 22:29:02 -0700722 kernel: Kernel file to write after U-Boot
Vadim Bendebury19a77122013-01-24 17:38:00 -0800723 bootstub: string, file name of the boot stub, if present
Simon Glass6a616c12012-09-24 18:13:46 -0700724 servo: Describes the servo unit to use: none=none; any=any; otherwise
725 port number of servo to use.
Simon Glass710dedc2011-08-09 14:08:52 -0700726 """
Simon Glass0191a882012-05-23 13:15:06 -0700727 write = WriteFirmware(tools, fdt, output, bundle)
Simon Glass6a616c12012-09-24 18:13:46 -0700728 write.SelectServo(servo)
Simon Glass5b5fd642011-08-17 12:24:01 -0700729 write.update = update
730 write.verify = verify
Simon Glasse0b61442012-03-13 15:29:51 -0700731 if dest == 'usb':
Simon Glass4b8b3bd2012-11-06 12:31:01 -0800732 method = fdt.GetString('/chromeos-config', 'flash-method', method)
Simon Glasse0b61442012-03-13 15:29:51 -0700733 if method == 'tegra':
Allen Martinb3aa2672012-03-23 15:55:25 +0000734 tools.CheckTool('tegrarcm')
Simon Glass89ecf712012-06-07 12:20:15 -0700735 if flash_dest:
736 write.text_base = bundle.CalcTextBase('flasher ', fdt, flasher)
Simon Glasse1824db2012-07-11 17:38:40 +0200737 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700738 write.text_base = bundle.CalcTextBase('bootstub ', fdt, bootstub)
Vadim Bendebury19a77122013-01-24 17:38:00 -0800739 ok = write.NvidiaFlashImage(flash_dest, flasher, file_list['bct'],
Simon Glass89ecf712012-06-07 12:20:15 -0700740 image_fname, bootstub)
Simon Glass27a9c142012-03-15 21:08:29 -0700741 elif method == 'exynos':
Simon Glass2c389062012-04-09 13:09:01 -0700742 tools.CheckTool('lsusb', 'usbutils')
743 tools.CheckTool('smdk-usbdl', 'smdk-dltool')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800744 ok = write.ExynosFlashImage(flash_dest, flasher,
Simon Glassde9c8072012-07-02 22:29:02 -0700745 file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname,
746 kernel)
Simon Glasse0b61442012-03-13 15:29:51 -0700747 else:
748 raise CmdError("Unknown flash method '%s'" % method)
749 if ok:
750 output.Progress('Image uploaded - please wait for flashing to '
751 'complete')
752 else:
753 raise CmdError('Image upload failed - please check board connection')
Simon Glass9eb8c722012-06-07 13:34:31 -0700754 elif dest == 'em100':
755 # crosbug.com/31625
756 tools.CheckTool('em100')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800757 write.Em100FlashImage(image_fname)
Simon Glass0c2ba482012-03-22 21:57:51 -0700758 elif dest.startswith('sd'):
759 write.SendToSdCard(dest[2:], flash_dest, flasher, image_fname)
Simon Glass710dedc2011-08-09 14:08:52 -0700760 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700761 raise CmdError("Unknown destination device '%s'" % dest)