blob: b0d7aab55339b2da476f0c2bfe0bf41230016aa7 [file] [log] [blame]
Simon Glassa3f29ec2011-07-17 09:36:49 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Simon Glass8bd05ec2012-03-22 11:09:04 -07005import binascii
Simon Glass3381a1a2012-03-22 11:13:19 -07006import glob
Simon Glassa3f29ec2011-07-17 09:36:49 -07007import os
8import re
9import time
10import tools
11from tools import CmdError
12
13def RoundUp(value, boundary):
14 """Align a value to the next power of 2 boundary.
15
16 Args:
17 value: The value to align.
18 boundary: The boundary value, e.g. 4096. Must be a power of 2.
19
20 Returns:
21 The rounded-up value.
22 """
23 return (value + boundary - 1) & ~(boundary - 1)
24
25
26class WriteFirmware:
27 """Write firmware to a Tegra 2 board using USB A-A cable.
28
29 This class handles re-reflashing a board with new firmware using the Tegra's
30 built-in boot ROM feature. This works by putting the chip into a special mode
31 where it ignores any available firmware and instead reads it from a connected
32 host machine over USB.
33
34 In our case we use that feature to send U-Boot along with a suitable payload
35 and instructions to flash it to SPI flash. The payload is itself normally a
36 full Chrome OS image consisting of U-Boot, some keys and verification
37 information, images and a map of the flash memory.
38 """
Simon Glass0191a882012-05-23 13:15:06 -070039 def __init__(self, tools, fdt, output, bundle):
Simon Glassa3f29ec2011-07-17 09:36:49 -070040 """Set up a new WriteFirmware object.
41
42 Args:
43 tools: A tools library for us to use.
44 fdt: An fdt which gives us some info that we need.
45 output: An output object to use for printing progress and messages.
Simon Glass0191a882012-05-23 13:15:06 -070046 bundle: A BundleFirmware object which created the image.
Simon Glassa3f29ec2011-07-17 09:36:49 -070047 """
48 self._tools = tools
49 self._fdt = fdt
50 self._out = output
Simon Glass0191a882012-05-23 13:15:06 -070051 self._bundle = bundle
Simon Glass60a40af2012-06-07 11:54:17 -070052 self.text_base = self._fdt.GetInt('/chromeos-config', 'textbase', -1);
Simon Glassa3f29ec2011-07-17 09:36:49 -070053
Simon Glass5b5fd642011-08-17 12:24:01 -070054 # For speed, use the 'update' algorithm and don't verify
55 self.update = True
56 self.verify = False
57
Simon Glass8bd05ec2012-03-22 11:09:04 -070058 def _GetFlashScript(self, payload_size, update, verify, boot_type, checksum,
59 bus='0'):
Simon Glassa3f29ec2011-07-17 09:36:49 -070060 """Get the U-Boot boot command needed to flash U-Boot.
61
62 We leave a marker in the string for the load address of the image,
63 since this depends on the size of this script. This can be replaced by
64 the caller provided that the marker length is unchanged.
65
66 Args:
67 payload_size: Size of payload in bytes.
Simon Glass5b5fd642011-08-17 12:24:01 -070068 update: Use faster update algorithm rather then full device erase
69 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -070070 boot_type: The source for bootdevice (nand, sdmmc, or spi)
Simon Glass8bd05ec2012-03-22 11:09:04 -070071 checksum: The checksum of the payload (an integer)
72 bus: The bus number
Simon Glassa3f29ec2011-07-17 09:36:49 -070073
74 Returns:
75 A tuple containing:
76 The script, as a string ready to use as a U-Boot boot command, with an
77 embedded marker for the load address.
78 The marker string, which the caller should replace with the correct
79 load address as 8 hex digits, without changing its length.
80 """
81 replace_me = 'zsHEXYla'
Rhyland Kleinc5c87282012-03-08 16:40:54 -080082 page_size = 4096
Simon Glass0dcbecb2012-03-22 21:38:55 -070083 if boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -080084 page_size = 512
Simon Glass0dcbecb2012-03-22 21:38:55 -070085 if boot_type != 'spi':
Rhyland Kleinc5c87282012-03-08 16:40:54 -080086 update = False
87
Simon Glassa3f29ec2011-07-17 09:36:49 -070088 cmds = [
89 'setenv address 0x%s' % replace_me,
90 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -080091 'setenv length %#x' % RoundUp(payload_size, page_size),
92 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass8bd05ec2012-03-22 11:09:04 -070093 'setenv _crc "crc32 -v ${address} ${firmware_size} %#08x"' %
94 checksum,
Simon Glass5b5fd642011-08-17 12:24:01 -070095 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -070096 ]
Simon Glass0dcbecb2012-03-22 21:38:55 -070097 if boot_type == 'nand':
Doug Anderson37ae2292011-09-15 17:41:57 -070098 cmds.extend([
99 'setenv _init "echo Init NAND; nand info"',
100 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
101 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
102 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
103 ])
Simon Glass0dcbecb2012-03-22 21:38:55 -0700104 elif boot_type == 'sdmmc':
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800105 cmds.extend([
106 'setenv _init "echo Init EMMC; mmc rescan 0"',
107 'setenv _erase "echo Erase EMMC; "',
108 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
109 '${blocks} boot1"',
110 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
111 '${blocks} boot1"',
112 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700113 else:
114 cmds.extend([
Simon Glass3d9a6c62012-03-15 20:38:04 -0700115 'setenv _init "echo Init SPI; sf probe %s"' % bus,
Doug Anderson37ae2292011-09-15 17:41:57 -0700116 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
117 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
118 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
119 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
120 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700121
Doug Anderson37ae2292011-09-15 17:41:57 -0700122 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700123 'echo Firmware loaded to ${address}, size ${firmware_size}, '
124 'length ${length}',
Simon Glass8bd05ec2012-03-22 11:09:04 -0700125 'if run _crc; then',
Simon Glassa3f29ec2011-07-17 09:36:49 -0700126 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700127 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700128 if update:
129 cmds += ['time run _update']
130 else:
131 cmds += ['run _erase', 'run _write']
132 if verify:
133 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700134 'run _clear',
135 'run _read',
136 'run _crc',
Simon Glass5b5fd642011-08-17 12:24:01 -0700137 ]
138 else:
139 cmds += ['echo Skipping verify']
Simon Glass8bd05ec2012-03-22 11:09:04 -0700140 cmds.extend([
141 'else',
142 'echo',
143 'echo "** Checksum error on load: please check download tool **"',
144 'fi',
145 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700146 script = '; '.join(cmds)
147 return script, replace_me
148
Simon Glass3d9a6c62012-03-15 20:38:04 -0700149 def PrepareFlasher(self, uboot, payload, update, verify, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700150 """Get a flasher ready for sending to the board.
151
152 The flasher is an executable image consisting of:
153
154 - U-Boot (u-boot.bin);
155 - a special FDT to tell it what to do in the form of a run command;
156 - (we could add some empty space here, in case U-Boot is not built to
157 be relocatable);
158 - the payload (which is a full flash image, or signed U-Boot + fdt).
159
160 Args:
161 uboot: Full path to u-boot.bin.
162 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700163 update: Use faster update algorithm rather then full device erase
164 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -0700165 boot_type: the src for bootdevice (nand, sdmmc, or spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700166
167 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700168 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700169 """
Simon Glass951a2db2011-07-17 15:58:58 -0700170 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700171 payload_data = self._tools.ReadFile(payload)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700172 payload_size = os.stat(payload).st_size
173
Simon Glass8bd05ec2012-03-22 11:09:04 -0700174 # Make sure that the checksum is not negative
175 checksum = binascii.crc32(payload_data) & 0xffffffff
176
177 script, replace_me = self._GetFlashScript(len(payload_data), update,
178 verify, boot_type, checksum, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700179 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800180 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700181 fdt_data = self._tools.ReadFile(fdt.fname)
182
183 # Work out where to place the payload in memory. This is a chicken-and-egg
184 # problem (although in case you haven't heard, it was the chicken that
185 # came first), so we resolve it by replacing the string after
186 # fdt.PutString has done its job.
187 #
188 # Correction: Technically, the egg came first. Whatever genetic mutation
189 # created the new species would have been present in the egg, but not the
190 # parent (since if it was in the parent, it would have been present in the
191 # parent when it was an egg).
192 #
193 # Question: ok so who laid the egg then?
194 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700195
196 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
197 alignment = 0x1000
Simon Glass8bd05ec2012-03-22 11:09:04 -0700198 payload_offset = (payload_offset + alignment - 1) & ~(alignment - 1)
Doug Anderson37ae2292011-09-15 17:41:57 -0700199
Simon Glass2c4b3e52011-11-15 14:45:43 -0800200 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700201 new_str = '%08x' % load_address
202 if len(replace_me) is not len(new_str):
203 raise ValueError("Internal error: replacement string '%s' length does "
204 "not match new string '%s'" % (replace_me, new_str))
205 if len(re.findall(replace_me, fdt_data)) != 1:
206 raise ValueError("Internal error: replacement string '%s' already "
207 "exists in the fdt (%d matches)" % (replace_me, matches))
208 fdt_data = re.sub(replace_me, new_str, fdt_data)
209
210 # Now put it together.
211 data += fdt_data
212 data += "\0" * (payload_offset - len(data))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700213 data += payload_data
Simon Glass951a2db2011-07-17 15:58:58 -0700214 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700215 self._tools.WriteFile(flasher, data)
216
217 # Tell the user about a few things.
218 self._tools.OutputSize('U-Boot', uboot)
219 self._tools.OutputSize('Payload', payload)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700220 self._out.Notice('Payload checksum %08x' % checksum)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700221 self._tools.OutputSize('Flasher', flasher)
222 return flasher
223
Simon Glass89ecf712012-06-07 12:20:15 -0700224 def _NvidiaFlashImage(self, flash_dest, uboot, bct, payload, bootstub):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700225 """Flash the image to SPI flash.
226
227 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000228 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700229
230 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700231 flash_dest: Destination for flasher, or None to not create a flasher
232 Valid options are spi, sdmmc
Simon Glassa3f29ec2011-07-17 09:36:49 -0700233 uboot: Full path to u-boot.bin.
234 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
235 payload: Full path to payload.
Simon Glass89ecf712012-06-07 12:20:15 -0700236 bootstub: Full path to bootstub, which is the payload without the
237 signing information (i.e. bootstub is u-boot.bin + the FDT)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700238
239 Returns:
240 True if ok, False if failed.
241 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800242 # Use a Regex to pull Boot type from BCT file.
243 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
244 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700245
246 # TODO(sjg): The boot type is currently selected by the bct, rather than
247 # flash_dest selecting which bct to use. This is a bit backwards. For now
248 # we go with the bct's idea.
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800249 boot_type = filter(match.match, bct_dumped)
Simon Glass0dcbecb2012-03-22 21:38:55 -0700250 boot_type = match.match(boot_type[0]).group('boot').lower()
Doug Anderson37ae2292011-09-15 17:41:57 -0700251
Simon Glass89ecf712012-06-07 12:20:15 -0700252 if flash_dest:
253 image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
254 boot_type, 0)
Simon Glasse1824db2012-07-11 17:38:40 +0200255 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700256 image = bootstub
Simon Glassa3f29ec2011-07-17 09:36:49 -0700257
Simon Glasse1824db2012-07-11 17:38:40 +0200258 else:
259 image = payload
260 # If we don't know the textbase, extract it from the payload.
261 if self.text_base == -1:
262 data = self._tools.ReadFile(payload)
263 # Skip the BCT which is the first 64KB
264 self.text_base = self._bundle.DecodeTextBase(data[0x10000:])
265
Simon Glass89ecf712012-06-07 12:20:15 -0700266 self._out.Notice('TEXT_BASE is %#x' % self.text_base)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700267 self._out.Progress('Uploading flasher image')
268 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700269 '--bct', bct,
Allen Martinb3aa2672012-03-23 15:55:25 +0000270 '--bootloader', image,
271 '--loadaddr', "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700272 ]
273
274 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
275 last_err = None
276 for tries in range(10):
277 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700278 # TODO(sjg): Use Chromite library so we can monitor output
Allen Martinb3aa2672012-03-23 15:55:25 +0000279 self._tools.Run('tegrarcm', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700280 self._out.Notice('Flasher downloaded - please see serial output '
281 'for progress.')
282 return True
283
284 except CmdError as err:
285 if not self._out.stdout_is_tty:
286 return False
287
288 # Only show the error output once unless it changes.
289 err = str(err)
Simon Glass22190ff2012-07-11 14:52:26 +0200290 if not 'could not open USB device' in err:
Allen Martinb3aa2672012-03-23 15:55:25 +0000291 raise CmdError('tegrarcm failed: %s' % err)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700292
293 if err != last_err:
294 self._out.Notice(err)
295 last_err = err
296 self._out.Progress('Please connect USB A-A cable and do a '
297 'recovery-reset', True)
298 time.sleep(1)
299
300 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700301
Simon Glass27a9c142012-03-15 21:08:29 -0700302 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
303 """Wait until we see a device on the USB bus.
304
305 Args:
306 name: Board type name
307 vendor_id: USB vendor ID to look for
308 product_id: USB product ID to look for
309 timeout: Timeout to wait in seconds
310
311 Returns
312 True if the device was found, False if we timed out.
313 """
314 self._out.Progress('Waiting for board to appear on USB bus')
Simon Glass4968a472012-05-23 13:52:19 -0700315 start_time = time.time()
316 while time.time() - start_time < timeout:
Simon Glass27a9c142012-03-15 21:08:29 -0700317 try:
318 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
319 self._tools.Run('lsusb', args, sudo=True)
Simon Glass3381a1a2012-03-22 11:13:19 -0700320 self._out.Progress('Found %s board' % name)
Simon Glass27a9c142012-03-15 21:08:29 -0700321 return True
322
323 except CmdError as err:
324 pass
325
Simon Glass3381a1a2012-03-22 11:13:19 -0700326 return False
Simon Glass27a9c142012-03-15 21:08:29 -0700327
Simon Glass3c5b35b2012-05-23 13:22:23 -0700328 def _ExtractPayloadParts(self, payload):
329 """Extract the BL1, BL2 and U-Boot parts from a payload.
330
331 An exynos image consists of 3 parts: BL1, BL2 and U-Boot/FDT.
332
333 This pulls out the various parts, puts them into files and returns
334 these files.
335
336 Args:
337 payload: Full path to payload.
338
339 Returns:
340 (bl1, bl2, image) where:
341 bl1 is the filename of the extracted BL1
342 bl2 is the filename of the extracted BL2
343 image is the filename of the extracted U-Boot image
344 """
345 # Pull out the parts from the payload
346 bl1 = os.path.join(self._tools.outdir, 'bl1.bin')
347 bl2 = os.path.join(self._tools.outdir, 'bl2.bin')
348 image = os.path.join(self._tools.outdir, 'u-boot-from-image.bin')
349 data = self._tools.ReadFile(payload)
350
351 # The BL1 is always 8KB - extract that part into a new file
352 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
353 self._tools.WriteFile(bl1, data[:0x2000])
354
355 # The BL2 (U-Boot SPL) is 14KB and follows BL1. After that there is
356 # a 2KB gap
357 self._tools.WriteFile(bl2, data[0x2000:0x5800])
358
359 # U-Boot itself starts at 24KB, after the gap
360 self._tools.WriteFile(image, data[0x6000:])
361 return bl1, bl2, image
362
Simon Glassde9c8072012-07-02 22:29:02 -0700363 def _ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload,
364 kernel):
Simon Glass27a9c142012-03-15 21:08:29 -0700365 """Flash the image to SPI flash.
366
367 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000368 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glass27a9c142012-03-15 21:08:29 -0700369
370 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700371 flash_dest: Destination for flasher, or None to not create a flasher
372 Valid options are spi, sdmmc.
373 flash_uboot: Full path to u-boot.bin to use for flasher.
Simon Glass27a9c142012-03-15 21:08:29 -0700374 bl1: Full path to file containing BL1 (pre-boot).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700375 bl2: Full path to file containing BL2 (SPL).
Simon Glass27a9c142012-03-15 21:08:29 -0700376 payload: Full path to payload.
Simon Glassde9c8072012-07-02 22:29:02 -0700377 kernel: Kernel to send after the payload, or None.
Simon Glass27a9c142012-03-15 21:08:29 -0700378
379 Returns:
380 True if ok, False if failed.
381 """
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700382 if flash_dest:
383 image = self.PrepareFlasher(flash_uboot, payload, self.update,
384 self.verify, flash_dest, '1:0')
385 else:
Simon Glass3c5b35b2012-05-23 13:22:23 -0700386 bl1, bl2, image = self._ExtractPayloadParts(payload)
Simon Glass27a9c142012-03-15 21:08:29 -0700387
388 vendor_id = 0x04e8
389 product_id = 0x1234
Simon Glass3381a1a2012-03-22 11:13:19 -0700390
391 self._out.Progress('Reseting board via servo')
392 args = ['warm_reset:on', 'fw_up:on', 'pwr_button:press', 'sleep:.1',
393 'warm_reset:off']
394 # TODO(sjg) If the board is bricked a reset does not seem to bring it
395 # back to life.
396 # BUG=chromium-os:28229
397 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args
398 self._tools.Run('dut-control', args)
Simon Glass27a9c142012-03-15 21:08:29 -0700399
Simon Glassde9c8072012-07-02 22:29:02 -0700400 # If we have a kernel to write, create a new image with that added.
401 if kernel:
402 dl_image = os.path.join(self._tools.outdir, 'image-plus-kernel.bin')
403 data = self._tools.ReadFile(image)
404
405 # Pad the original payload out to the original length
406 data += '\0' * (os.stat(payload).st_size - len(data))
407 data += self._tools.ReadFile(kernel)
408 self._tools.WriteFile(dl_image, data)
409 else:
410 dl_image = image
411
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700412 self._out.Progress('Uploading image')
Simon Glass27a9c142012-03-15 21:08:29 -0700413 download_list = [
Simon Glass3c5b35b2012-05-23 13:22:23 -0700414 # The numbers are the download addresses (in SRAM) for each piece
415 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glass27a9c142012-03-15 21:08:29 -0700416 ['bl1', 0x02021400, bl1],
417 ['bl2', 0x02023400, bl2],
Simon Glassde9c8072012-07-02 22:29:02 -0700418 ['u-boot', 0x43e00000, dl_image]
Simon Glass27a9c142012-03-15 21:08:29 -0700419 ]
Simon Glass3381a1a2012-03-22 11:13:19 -0700420 try:
Simon Glass4968a472012-05-23 13:52:19 -0700421 for upto in range(len(download_list)):
422 item = download_list[upto]
Simon Glass3381a1a2012-03-22 11:13:19 -0700423 if not self._WaitForUSBDevice('exynos', vendor_id, product_id, 4):
Simon Glass4968a472012-05-23 13:52:19 -0700424 if upto == 0:
Simon Glass3381a1a2012-03-22 11:13:19 -0700425 raise CmdError('Could not find Exynos board on USB port')
426 raise CmdError("Stage '%s' did not complete" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700427 self._out.Notice(item[2])
Simon Glass27a9c142012-03-15 21:08:29 -0700428 self._out.Progress("Uploading stage '%s'" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700429
Simon Glass4968a472012-05-23 13:52:19 -0700430 if upto == 0:
431 # The IROM needs roughly 200ms here to be ready for USB download
432 time.sleep(.5)
433
434 if upto == 1:
435 # Once SPL starts up we can release the power buttom
436 args = ['fw_up:off', 'pwr_button:release']
437 self._tools.Run('dut-control', args)
438
439 args = ['-a', '%#x' % item[1], '-f', item[2]]
Simon Glass27a9c142012-03-15 21:08:29 -0700440 self._tools.Run('smdk-usbdl', args, sudo=True)
Simon Glass27a9c142012-03-15 21:08:29 -0700441
Simon Glass3381a1a2012-03-22 11:13:19 -0700442 finally:
Simon Glass4968a472012-05-23 13:52:19 -0700443 # Make sure that the power button is released, whatever happens
Simon Glass3381a1a2012-03-22 11:13:19 -0700444 args = ['fw_up:off', 'pwr_button:release']
445 self._tools.Run('dut-control', args)
Simon Glass27a9c142012-03-15 21:08:29 -0700446
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700447 self._out.Notice('Image downloaded - please see serial output '
Simon Glass3381a1a2012-03-22 11:13:19 -0700448 'for progress.')
Simon Glass27a9c142012-03-15 21:08:29 -0700449 return True
450
Simon Glass0c2ba482012-03-22 21:57:51 -0700451 def _GetDiskInfo(self, disk, item):
452 """Returns information about a SCSI disk device.
453
454 Args:
455 disk: a block device name in sys/block, like '/sys/block/sdf'.
456 item: the item of disk information that is required.
457
458 Returns:
459 The information obtained, as a string, or '[Unknown]' if not found
460 """
461 dev_path = os.path.join(disk, 'device')
462
463 # Search upwards and through symlinks looking for the item.
464 while os.path.isdir(dev_path) and dev_path != '/sys':
465 fname = os.path.join(dev_path, item)
466 if os.path.exists(fname):
467 with open(fname, 'r') as fd:
468 return fd.readline().rstrip()
469
470 # Move up a level and follow any symlink.
471 new_path = os.path.join(dev_path, '..')
472 if os.path.islink(new_path):
473 new_path = os.path.abspath(os.readlink(os.path.dirname(dev_path)))
474 dev_path = new_path
475 return '[Unknown]'
476
477 def _GetDiskCapacity(self, device):
478 """Returns the disk capacity in GB, or 0 if not known.
479
480 Args:
481 device: Device to check, like '/dev/sdf'.
482
483 Returns:
484 Capacity of device in GB, or 0 if not known.
485 """
486 args = ['-l', device]
487 stdout = self._tools.Run('fdisk', args, sudo=True)
488 if stdout:
489 # Seach for the line with capacity information.
490 re_capacity = re.compile('Disk .*: (\d+) \w+,')
491 lines = filter(re_capacity.match, stdout.splitlines())
492 if len(lines):
493 m = re_capacity.match(lines[0])
494
495 # We get something like 7859 MB, so turn into bytes, then GB
496 return int(m.group(1)) * 1024 * 1024 / 1e9
497 return 0
498
499 def _ListUsbDisks(self):
500 """Return a list of available removable USB disks.
501
502 Returns:
503 List of USB devices, each element is itself a list containing:
504 device ('/dev/sdx')
505 manufacturer name
506 product name
507 capacity in GB (an integer)
508 full description (all of the above concatenated).
509 """
510 disk_list = []
511 for disk in glob.glob('/sys/block/sd*'):
512 with open(disk + '/removable', 'r') as fd:
513 if int(fd.readline()) == 1:
514 device = '/dev/%s' % disk.split('/')[-1]
515 manuf = self._GetDiskInfo(disk, 'manufacturer')
516 product = self._GetDiskInfo(disk, 'product')
517 capacity = self._GetDiskCapacity(device)
518 if capacity:
519 desc = '%s: %s %s %d GB' % (device, manuf, product, capacity)
520 disk_list.append([device, manuf, product, capacity, desc])
521 return disk_list
522
523 def WriteToSd(self, flash_dest, disk, uboot, payload):
524 if flash_dest:
Simon Glass559b6612012-05-23 13:28:45 -0700525 raw_image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Simon Glass0c2ba482012-03-22 21:57:51 -0700526 flash_dest, '1:0')
Simon Glass559b6612012-05-23 13:28:45 -0700527 bl1, bl2, payload_data = self._ExtractPayloadParts(payload)
528 spl_load_size = os.stat(raw_image).st_size
529 bl2 = self._bundle.ConfigureExynosBl2(self._fdt, spl_load_size, bl2,
530 'flasher')
531
532 data = self._tools.ReadFile(bl1) + self._tools.ReadFile(bl2)
533
534 # Pad BL2 out to the required size.
535 # We require that it be 24KB, but data will only contain 8KB + 14KB.
536 # Add the extra padding to bring it to 24KB.
537 data += '\0' * (0x6000 - len(data))
538 data += self._tools.ReadFile(raw_image)
539 image = os.path.join(self._tools.outdir, 'flasher-with-bl.bin')
540 self._tools.WriteFile(image, data)
Simon Glass0c2ba482012-03-22 21:57:51 -0700541 self._out.Progress('Writing flasher to %s' % disk)
542 else:
543 image = payload
544 self._out.Progress('Writing image to %s' % disk)
545
546 args = ['if=%s' % image, 'of=%s' % disk, 'bs=512', 'seek=1']
547 self._tools.Run('dd', args, sudo=True)
548
549 def SendToSdCard(self, dest, flash_dest, uboot, payload):
550 """Write a flasher to an SD card.
551
552 Args:
553 dest: Destination in one of these forms:
554 ':<full description of device>'
555 ':.' selects the only available device, fails if more than one option
556 ':<device>' select deivce
557
558 Examples:
559 ':/dev/sdd: Generic Flash Card Reader/Writer 8 GB'
560 ':.'
561 ':/dev/sdd'
562
563 flash_dest: Destination for flasher, or None to not create a flasher:
564 Valid options are spi, sdmmc.
565 uboot: Full path to u-boot.bin.
566 payload: Full path to payload.
567 """
568 disk = None
569 disks = self._ListUsbDisks()
570 if dest[:1] == ':':
571 name = dest[1:]
572
573 # A '.' just means to use the only available disk.
574 if name == '.' and len(disks) == 1:
575 disk = disks[0][0]
576 for disk_info in disks:
577 # Use the full name or the device name.
578 if disk_info[4] == name or disk_info[1] == name:
579 disk = disk_info[0]
580
581 if disk:
582 self.WriteToSd(flash_dest, disk, uboot, payload)
583 else:
584 self._out.Error("Please specify destination -w 'sd:<disk_description>':")
585 self._out.Error(' - description can be . for the only disk, SCSI '
586 'device letter')
587 self._out.Error(' or the full description listed here')
588 msg = 'Found %d available disks.' % len(disks)
589 if not disks:
590 msg += ' Please insert an SD card and try again.'
591 self._out.UserOutput(msg)
592
593 # List available disks as a convenience.
594 for disk in disks:
595 self._out.UserOutput(' %s' % disk[4])
596
Simon Glass9eb8c722012-06-07 13:34:31 -0700597 def _Em100FlashImage(self, image_fname):
598 """Send an image to an attached EM100 device.
599
600 This is a Dediprog EM100 SPI flash emulation device. We set up servo2
601 to do the SPI emulation, then write the image, then boot the board.
602 All going well, this is enough to get U-Boot running.
603
604 Args:
605 image_fname: Filename of image to send
606 """
607 args = ['spi2_vref:off', 'spi2_buf_en:off', 'spi2_buf_on_flex_en:off']
608 args.append('spi_hold:on')
609 self._tools.Run('dut-control', args)
610
611 # TODO(sjg@chromium.org): This is for link. We could make this
612 # configurable from the fdt.
613 args = ['-c', 'W25Q64CV', '-d', self._tools.Filename(image_fname), '-r']
614 self._out.Progress('Writing image to em100')
615 self._tools.Run('em100', args, sudo=True)
616
617 self._out.Progress('Resetting board')
618 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off', 'sleep:.5']
619 args.extend(['pwr_button:press', 'sleep:.2', 'pwr_button:release'])
620 self._tools.Run('dut-control', args)
621
Simon Glass0c2ba482012-03-22 21:57:51 -0700622
Simon Glass27a9c142012-03-15 21:08:29 -0700623def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glass60a40af2012-06-07 11:54:17 -0700624 bundle, update=True, verify=False, dest=None,
Simon Glasse1824db2012-07-11 17:38:40 +0200625 flash_dest=None, kernel=None, props={}):
Simon Glasse0b61442012-03-13 15:29:51 -0700626 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700627
628 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700629 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700630
631 Args:
632 output: cros_output object to use.
633 tools: Tools object to use.
634 fdt: Fdt object to use as our device tree.
635 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700636 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700637 image_fname: Filename of image to write.
Simon Glass0191a882012-05-23 13:15:06 -0700638 bundle: The bundle object which created the image.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800639 update: Use faster update algorithm rather then full device erase.
640 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700641 dest: Destination device to write firmware to (usb, sd).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700642 flash_dest: Destination device for flasher to program payload into.
Simon Glassde9c8072012-07-02 22:29:02 -0700643 kernel: Kernel file to write after U-Boot
Simon Glass794217e2012-06-07 11:40:37 -0700644 props: A dictionary containing properties from the PackFirmware object
Simon Glass710dedc2011-08-09 14:08:52 -0700645 """
Simon Glass0191a882012-05-23 13:15:06 -0700646 write = WriteFirmware(tools, fdt, output, bundle)
Simon Glass5b5fd642011-08-17 12:24:01 -0700647 write.update = update
648 write.verify = verify
Simon Glasse0b61442012-03-13 15:29:51 -0700649 if dest == 'usb':
650 method = fdt.GetString('/chromeos-config', 'flash-method', 'tegra')
651 if method == 'tegra':
Allen Martinb3aa2672012-03-23 15:55:25 +0000652 tools.CheckTool('tegrarcm')
Simon Glasse1824db2012-07-11 17:38:40 +0200653 bootstub = props.get('bootstub')
Simon Glass89ecf712012-06-07 12:20:15 -0700654 if flash_dest:
655 write.text_base = bundle.CalcTextBase('flasher ', fdt, flasher)
Simon Glasse1824db2012-07-11 17:38:40 +0200656 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700657 write.text_base = bundle.CalcTextBase('bootstub ', fdt, bootstub)
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700658 ok = write._NvidiaFlashImage(flash_dest, flasher, file_list['bct'],
Simon Glass89ecf712012-06-07 12:20:15 -0700659 image_fname, bootstub)
Simon Glass27a9c142012-03-15 21:08:29 -0700660 elif method == 'exynos':
Simon Glass2c389062012-04-09 13:09:01 -0700661 tools.CheckTool('lsusb', 'usbutils')
662 tools.CheckTool('smdk-usbdl', 'smdk-dltool')
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700663 ok = write._ExynosFlashImage(flash_dest, flasher,
Simon Glassde9c8072012-07-02 22:29:02 -0700664 file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname,
665 kernel)
Simon Glasse0b61442012-03-13 15:29:51 -0700666 else:
667 raise CmdError("Unknown flash method '%s'" % method)
668 if ok:
669 output.Progress('Image uploaded - please wait for flashing to '
670 'complete')
671 else:
672 raise CmdError('Image upload failed - please check board connection')
Simon Glass9eb8c722012-06-07 13:34:31 -0700673 elif dest == 'em100':
674 # crosbug.com/31625
675 tools.CheckTool('em100')
676 write._Em100FlashImage(image_fname)
Simon Glass0c2ba482012-03-22 21:57:51 -0700677 elif dest.startswith('sd'):
678 write.SendToSdCard(dest[2:], flash_dest, flasher, image_fname)
Simon Glass710dedc2011-08-09 14:08:52 -0700679 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700680 raise CmdError("Unknown destination device '%s'" % dest)