blob: bfdbf56d10cf1d530b61e1a4dae8806349e9da6d [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',
Vadim Bendeburyd6b0a372013-02-06 16:36:28 -0800163 'if run _crc; then',
164 'echo "Image Programmed Successfully"',
165 'else',
166 'echo',
167 'echo "** Checksum error on readback, programming failed!! **"',
168 'echo',
169 'fi',
Simon Glass5b5fd642011-08-17 12:24:01 -0700170 ]
171 else:
172 cmds += ['echo Skipping verify']
Simon Glass8bd05ec2012-03-22 11:09:04 -0700173 cmds.extend([
174 'else',
175 'echo',
176 'echo "** Checksum error on load: please check download tool **"',
177 'fi',
178 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700179 script = '; '.join(cmds)
180 return script, replace_me
181
Simon Glass3d9a6c62012-03-15 20:38:04 -0700182 def PrepareFlasher(self, uboot, payload, update, verify, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700183 """Get a flasher ready for sending to the board.
184
185 The flasher is an executable image consisting of:
186
187 - U-Boot (u-boot.bin);
188 - a special FDT to tell it what to do in the form of a run command;
189 - (we could add some empty space here, in case U-Boot is not built to
190 be relocatable);
191 - the payload (which is a full flash image, or signed U-Boot + fdt).
192
193 Args:
194 uboot: Full path to u-boot.bin.
195 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700196 update: Use faster update algorithm rather then full device erase
197 verify: Verify the write by doing a readback and CRC
Simon Glass0dcbecb2012-03-22 21:38:55 -0700198 boot_type: the src for bootdevice (nand, sdmmc, or spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700199
200 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700201 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700202 """
Simon Glass951a2db2011-07-17 15:58:58 -0700203 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700204 payload_data = self._tools.ReadFile(payload)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700205
Simon Glass8bd05ec2012-03-22 11:09:04 -0700206 # Make sure that the checksum is not negative
207 checksum = binascii.crc32(payload_data) & 0xffffffff
208
209 script, replace_me = self._GetFlashScript(len(payload_data), update,
210 verify, boot_type, checksum, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700211 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800212 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700213 fdt_data = self._tools.ReadFile(fdt.fname)
214
215 # Work out where to place the payload in memory. This is a chicken-and-egg
216 # problem (although in case you haven't heard, it was the chicken that
217 # came first), so we resolve it by replacing the string after
218 # fdt.PutString has done its job.
219 #
220 # Correction: Technically, the egg came first. Whatever genetic mutation
221 # created the new species would have been present in the egg, but not the
222 # parent (since if it was in the parent, it would have been present in the
223 # parent when it was an egg).
224 #
225 # Question: ok so who laid the egg then?
226 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700227
228 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
229 alignment = 0x1000
Simon Glass8bd05ec2012-03-22 11:09:04 -0700230 payload_offset = (payload_offset + alignment - 1) & ~(alignment - 1)
Doug Anderson37ae2292011-09-15 17:41:57 -0700231
Simon Glass2c4b3e52011-11-15 14:45:43 -0800232 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700233 new_str = '%08x' % load_address
234 if len(replace_me) is not len(new_str):
235 raise ValueError("Internal error: replacement string '%s' length does "
236 "not match new string '%s'" % (replace_me, new_str))
Vadim Bendebury19a77122013-01-24 17:38:00 -0800237 matches = len(re.findall(replace_me, fdt_data))
238 if matches != 1:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700239 raise ValueError("Internal error: replacement string '%s' already "
240 "exists in the fdt (%d matches)" % (replace_me, matches))
241 fdt_data = re.sub(replace_me, new_str, fdt_data)
242
243 # Now put it together.
244 data += fdt_data
245 data += "\0" * (payload_offset - len(data))
Simon Glass8bd05ec2012-03-22 11:09:04 -0700246 data += payload_data
Simon Glass951a2db2011-07-17 15:58:58 -0700247 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700248 self._tools.WriteFile(flasher, data)
249
250 # Tell the user about a few things.
251 self._tools.OutputSize('U-Boot', uboot)
252 self._tools.OutputSize('Payload', payload)
Simon Glass8bd05ec2012-03-22 11:09:04 -0700253 self._out.Notice('Payload checksum %08x' % checksum)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700254 self._tools.OutputSize('Flasher', flasher)
255 return flasher
256
Vadim Bendebury19a77122013-01-24 17:38:00 -0800257 def NvidiaFlashImage(self, flash_dest, uboot, bct, payload, bootstub):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700258 """Flash the image to SPI flash.
259
260 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000261 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700262
263 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700264 flash_dest: Destination for flasher, or None to not create a flasher
265 Valid options are spi, sdmmc
Simon Glassa3f29ec2011-07-17 09:36:49 -0700266 uboot: Full path to u-boot.bin.
267 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
268 payload: Full path to payload.
Simon Glass89ecf712012-06-07 12:20:15 -0700269 bootstub: Full path to bootstub, which is the payload without the
270 signing information (i.e. bootstub is u-boot.bin + the FDT)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700271
272 Returns:
273 True if ok, False if failed.
274 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800275 # Use a Regex to pull Boot type from BCT file.
276 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
277 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700278
279 # TODO(sjg): The boot type is currently selected by the bct, rather than
280 # flash_dest selecting which bct to use. This is a bit backwards. For now
281 # we go with the bct's idea.
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800282 boot_type = filter(match.match, bct_dumped)
Simon Glass0dcbecb2012-03-22 21:38:55 -0700283 boot_type = match.match(boot_type[0]).group('boot').lower()
Doug Anderson37ae2292011-09-15 17:41:57 -0700284
Simon Glass89ecf712012-06-07 12:20:15 -0700285 if flash_dest:
286 image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
287 boot_type, 0)
Simon Glasse1824db2012-07-11 17:38:40 +0200288 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700289 image = bootstub
Simon Glassa3f29ec2011-07-17 09:36:49 -0700290
Simon Glasse1824db2012-07-11 17:38:40 +0200291 else:
292 image = payload
293 # If we don't know the textbase, extract it from the payload.
294 if self.text_base == -1:
295 data = self._tools.ReadFile(payload)
296 # Skip the BCT which is the first 64KB
297 self.text_base = self._bundle.DecodeTextBase(data[0x10000:])
298
Simon Glass89ecf712012-06-07 12:20:15 -0700299 self._out.Notice('TEXT_BASE is %#x' % self.text_base)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700300 self._out.Progress('Uploading flasher image')
301 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700302 '--bct', bct,
Allen Martinb3aa2672012-03-23 15:55:25 +0000303 '--bootloader', image,
304 '--loadaddr', "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700305 ]
306
307 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
308 last_err = None
Vadim Bendebury19a77122013-01-24 17:38:00 -0800309 for _ in range(10):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700310 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700311 # TODO(sjg): Use Chromite library so we can monitor output
Allen Martinb3aa2672012-03-23 15:55:25 +0000312 self._tools.Run('tegrarcm', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700313 self._out.Notice('Flasher downloaded - please see serial output '
314 'for progress.')
315 return True
316
317 except CmdError as err:
318 if not self._out.stdout_is_tty:
319 return False
320
321 # Only show the error output once unless it changes.
322 err = str(err)
Simon Glass22190ff2012-07-11 14:52:26 +0200323 if not 'could not open USB device' in err:
Allen Martinb3aa2672012-03-23 15:55:25 +0000324 raise CmdError('tegrarcm failed: %s' % err)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700325
326 if err != last_err:
327 self._out.Notice(err)
328 last_err = err
329 self._out.Progress('Please connect USB A-A cable and do a '
330 'recovery-reset', True)
331 time.sleep(1)
332
333 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700334
Simon Glass27a9c142012-03-15 21:08:29 -0700335 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
336 """Wait until we see a device on the USB bus.
337
338 Args:
339 name: Board type name
340 vendor_id: USB vendor ID to look for
341 product_id: USB product ID to look for
342 timeout: Timeout to wait in seconds
343
344 Returns
345 True if the device was found, False if we timed out.
346 """
347 self._out.Progress('Waiting for board to appear on USB bus')
Simon Glass4968a472012-05-23 13:52:19 -0700348 start_time = time.time()
349 while time.time() - start_time < timeout:
Simon Glass27a9c142012-03-15 21:08:29 -0700350 try:
351 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
352 self._tools.Run('lsusb', args, sudo=True)
Simon Glass3381a1a2012-03-22 11:13:19 -0700353 self._out.Progress('Found %s board' % name)
Simon Glass27a9c142012-03-15 21:08:29 -0700354 return True
355
Vadim Bendebury19a77122013-01-24 17:38:00 -0800356 except CmdError:
Simon Glass27a9c142012-03-15 21:08:29 -0700357 pass
358
Simon Glass3381a1a2012-03-22 11:13:19 -0700359 return False
Simon Glass27a9c142012-03-15 21:08:29 -0700360
Simon Glass6a616c12012-09-24 18:13:46 -0700361 def _DutControl(self, args):
362 """Run dut-control with supplied arguments.
363
364 The correct servo will be used based on self._servo_port.
365
366 Args:
367 args: List of arguments to dut-control.
368
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800369 Retruns:
370 a string, stdout generated by running the command
Simon Glass6a616c12012-09-24 18:13:46 -0700371 Raises:
372 IOError if no servo access is permitted.
373 """
374 if self._servo_port is None:
375 raise IOError('No servo access available, please use --servo')
376 if self._servo_port:
377 args.extend(['-p', '%s' % self._servo_port])
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800378 return self._tools.Run('dut-control', args)
Simon Glass6a616c12012-09-24 18:13:46 -0700379
Simon Glass3c5b35b2012-05-23 13:22:23 -0700380 def _ExtractPayloadParts(self, payload):
381 """Extract the BL1, BL2 and U-Boot parts from a payload.
382
383 An exynos image consists of 3 parts: BL1, BL2 and U-Boot/FDT.
384
385 This pulls out the various parts, puts them into files and returns
386 these files.
387
388 Args:
389 payload: Full path to payload.
390
391 Returns:
392 (bl1, bl2, image) where:
393 bl1 is the filename of the extracted BL1
394 bl2 is the filename of the extracted BL2
395 image is the filename of the extracted U-Boot image
396 """
397 # Pull out the parts from the payload
398 bl1 = os.path.join(self._tools.outdir, 'bl1.bin')
399 bl2 = os.path.join(self._tools.outdir, 'bl2.bin')
400 image = os.path.join(self._tools.outdir, 'u-boot-from-image.bin')
401 data = self._tools.ReadFile(payload)
402
403 # The BL1 is always 8KB - extract that part into a new file
404 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glasse86f3132013-01-08 16:10:43 -0800405 bl1_size = 0x2000
406 self._tools.WriteFile(bl1, data[:bl1_size])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700407
Simon Glasse86f3132013-01-08 16:10:43 -0800408 # Try to detect the BL2 size. We look for 0xea000014 which is the
Tom Wai-Hong Tamf67046f2013-02-06 09:23:26 +0800409 # 'B reset' instruction at the start of U-Boot. When U-Boot is LZO
410 # compressed, we look for a LZO magic instead.
Simon Glasse86f3132013-01-08 16:10:43 -0800411 first_instr = struct.pack('<L', 0xea000014)
Tom Wai-Hong Tamf67046f2013-02-06 09:23:26 +0800412 lzo_magic = struct.pack('>B3s', 0x89, 'LZO')
413 first_instr_offset = data.find(first_instr, bl1_size + 0x3800)
414 lzo_magic_offset = data.find(lzo_magic, bl1_size + 0x3800)
415 uboot_offset = min(first_instr_offset, lzo_magic_offset)
416 if uboot_offset == -1:
417 uboot_offset = max(first_instr_offset, lzo_magic_offset)
Simon Glasse86f3132013-01-08 16:10:43 -0800418 if uboot_offset == -1:
Vadim Bendebury19a77122013-01-24 17:38:00 -0800419 raise ValueError('Could not locate start of U-Boot')
Simon Glasse86f3132013-01-08 16:10:43 -0800420 bl2_size = uboot_offset - bl1_size - 0x800 # 2KB gap after BL2
Simon Glass3c5b35b2012-05-23 13:22:23 -0700421
Simon Glasse86f3132013-01-08 16:10:43 -0800422 # Sanity check: At present we only allow 14KB and 30KB for SPL
423 allowed = [14, 30]
424 if (bl2_size >> 10) not in allowed:
Vadim Bendebury19a77122013-01-24 17:38:00 -0800425 raise ValueError('BL2 size is %dK - only %s supported' %
426 (bl2_size >> 10, ', '.join(
427 [str(size) for size in allowed])))
Simon Glasse86f3132013-01-08 16:10:43 -0800428 self._out.Notice('BL2 size is %dKB' % (bl2_size >> 10))
429
430 # The BL2 (U-Boot SPL) follows BL1. After that there is a 2KB gap
431 bl2_end = uboot_offset - 0x800
432 self._tools.WriteFile(bl2, data[0x2000:bl2_end])
433
434 # U-Boot itself starts after the gap
435 self._tools.WriteFile(image, data[uboot_offset:])
Simon Glass3c5b35b2012-05-23 13:22:23 -0700436 return bl1, bl2, image
437
Vadim Bendebury19a77122013-01-24 17:38:00 -0800438 def ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload,
Simon Glassde9c8072012-07-02 22:29:02 -0700439 kernel):
Simon Glass27a9c142012-03-15 21:08:29 -0700440 """Flash the image to SPI flash.
441
442 This creates a special Flasher binary, with the image to be flashed as
Allen Martinb3aa2672012-03-23 15:55:25 +0000443 a payload. This is then sent to the board using the tegrarcm utility.
Simon Glass27a9c142012-03-15 21:08:29 -0700444
445 Args:
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700446 flash_dest: Destination for flasher, or None to not create a flasher
447 Valid options are spi, sdmmc.
448 flash_uboot: Full path to u-boot.bin to use for flasher.
Simon Glass27a9c142012-03-15 21:08:29 -0700449 bl1: Full path to file containing BL1 (pre-boot).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700450 bl2: Full path to file containing BL2 (SPL).
Simon Glass27a9c142012-03-15 21:08:29 -0700451 payload: Full path to payload.
Simon Glassde9c8072012-07-02 22:29:02 -0700452 kernel: Kernel to send after the payload, or None.
Simon Glass27a9c142012-03-15 21:08:29 -0700453
454 Returns:
455 True if ok, False if failed.
456 """
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700457 if flash_dest:
458 image = self.PrepareFlasher(flash_uboot, payload, self.update,
459 self.verify, flash_dest, '1:0')
460 else:
Simon Glass3c5b35b2012-05-23 13:22:23 -0700461 bl1, bl2, image = self._ExtractPayloadParts(payload)
Simon Glass27a9c142012-03-15 21:08:29 -0700462
463 vendor_id = 0x04e8
464 product_id = 0x1234
Simon Glass3381a1a2012-03-22 11:13:19 -0700465
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800466 # Preserve dut_hub_sel state.
467 preserved_dut_hub_sel = self._DutControl(['dut_hub_sel',]
468 ).strip().split(':')[-1]
469 required_dut_hub_sel = 'dut_sees_servo'
Simon Glass3381a1a2012-03-22 11:13:19 -0700470 args = ['warm_reset:on', 'fw_up:on', 'pwr_button:press', 'sleep:.1',
471 'warm_reset:off']
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800472 if preserved_dut_hub_sel != required_dut_hub_sel:
473 # Need to set it to get the port properly powered up.
474 args += ['dut_hub_sel:%s' % required_dut_hub_sel]
Simon Glass3381a1a2012-03-22 11:13:19 -0700475 # TODO(sjg) If the board is bricked a reset does not seem to bring it
476 # back to life.
477 # BUG=chromium-os:28229
478 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800479 self._out.Progress('Reseting board via servo')
Simon Glass6a616c12012-09-24 18:13:46 -0700480 self._DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700481
Simon Glassde9c8072012-07-02 22:29:02 -0700482 # If we have a kernel to write, create a new image with that added.
483 if kernel:
484 dl_image = os.path.join(self._tools.outdir, 'image-plus-kernel.bin')
485 data = self._tools.ReadFile(image)
486
487 # Pad the original payload out to the original length
488 data += '\0' * (os.stat(payload).st_size - len(data))
489 data += self._tools.ReadFile(kernel)
490 self._tools.WriteFile(dl_image, data)
491 else:
492 dl_image = image
493
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700494 self._out.Progress('Uploading image')
Simon Glass27a9c142012-03-15 21:08:29 -0700495 download_list = [
Simon Glass3c5b35b2012-05-23 13:22:23 -0700496 # The numbers are the download addresses (in SRAM) for each piece
497 # TODO(sjg@chromium.org): Perhaps pick these up from the fdt?
Simon Glass27a9c142012-03-15 21:08:29 -0700498 ['bl1', 0x02021400, bl1],
499 ['bl2', 0x02023400, bl2],
Simon Glassde9c8072012-07-02 22:29:02 -0700500 ['u-boot', 0x43e00000, dl_image]
Simon Glass27a9c142012-03-15 21:08:29 -0700501 ]
Simon Glass3381a1a2012-03-22 11:13:19 -0700502 try:
Simon Glass4968a472012-05-23 13:52:19 -0700503 for upto in range(len(download_list)):
504 item = download_list[upto]
Simon Glass3381a1a2012-03-22 11:13:19 -0700505 if not self._WaitForUSBDevice('exynos', vendor_id, product_id, 4):
Simon Glass4968a472012-05-23 13:52:19 -0700506 if upto == 0:
Simon Glass3381a1a2012-03-22 11:13:19 -0700507 raise CmdError('Could not find Exynos board on USB port')
508 raise CmdError("Stage '%s' did not complete" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700509 self._out.Notice(item[2])
Simon Glass27a9c142012-03-15 21:08:29 -0700510 self._out.Progress("Uploading stage '%s'" % item[0])
Simon Glass3381a1a2012-03-22 11:13:19 -0700511
Simon Glass4968a472012-05-23 13:52:19 -0700512 if upto == 0:
513 # The IROM needs roughly 200ms here to be ready for USB download
514 time.sleep(.5)
515
Simon Glass79e3dd02012-08-28 20:08:50 -0700516 args = ['-a', '%#x' % item[1], '-f', item[2]]
517 self._tools.Run('smdk-usbdl', args, sudo=True)
Simon Glass4968a472012-05-23 13:52:19 -0700518 if upto == 1:
519 # Once SPL starts up we can release the power buttom
520 args = ['fw_up:off', 'pwr_button:release']
Simon Glass6a616c12012-09-24 18:13:46 -0700521 self._DutControl(args)
Simon Glass4968a472012-05-23 13:52:19 -0700522
Simon Glass3381a1a2012-03-22 11:13:19 -0700523 finally:
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800524 # Make sure that the power button is released and dut_sel_hub state is
525 # restored, whatever happens
Simon Glass3381a1a2012-03-22 11:13:19 -0700526 args = ['fw_up:off', 'pwr_button:release']
Vadim Bendeburybfaf0552013-01-24 17:38:00 -0800527 if preserved_dut_hub_sel != required_dut_hub_sel:
528 args += ['dut_hub_sel:%s' % preserved_dut_hub_sel]
Simon Glass6a616c12012-09-24 18:13:46 -0700529 self._DutControl(args)
Simon Glass27a9c142012-03-15 21:08:29 -0700530
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700531 self._out.Notice('Image downloaded - please see serial output '
Simon Glass3381a1a2012-03-22 11:13:19 -0700532 'for progress.')
Simon Glass27a9c142012-03-15 21:08:29 -0700533 return True
534
Simon Glass0c2ba482012-03-22 21:57:51 -0700535 def _GetDiskInfo(self, disk, item):
536 """Returns information about a SCSI disk device.
537
538 Args:
539 disk: a block device name in sys/block, like '/sys/block/sdf'.
540 item: the item of disk information that is required.
541
542 Returns:
543 The information obtained, as a string, or '[Unknown]' if not found
544 """
545 dev_path = os.path.join(disk, 'device')
546
547 # Search upwards and through symlinks looking for the item.
548 while os.path.isdir(dev_path) and dev_path != '/sys':
549 fname = os.path.join(dev_path, item)
550 if os.path.exists(fname):
551 with open(fname, 'r') as fd:
552 return fd.readline().rstrip()
553
554 # Move up a level and follow any symlink.
555 new_path = os.path.join(dev_path, '..')
556 if os.path.islink(new_path):
557 new_path = os.path.abspath(os.readlink(os.path.dirname(dev_path)))
558 dev_path = new_path
559 return '[Unknown]'
560
561 def _GetDiskCapacity(self, device):
562 """Returns the disk capacity in GB, or 0 if not known.
563
564 Args:
565 device: Device to check, like '/dev/sdf'.
566
567 Returns:
568 Capacity of device in GB, or 0 if not known.
569 """
570 args = ['-l', device]
571 stdout = self._tools.Run('fdisk', args, sudo=True)
572 if stdout:
573 # Seach for the line with capacity information.
574 re_capacity = re.compile('Disk .*: (\d+) \w+,')
575 lines = filter(re_capacity.match, stdout.splitlines())
576 if len(lines):
577 m = re_capacity.match(lines[0])
578
579 # We get something like 7859 MB, so turn into bytes, then GB
580 return int(m.group(1)) * 1024 * 1024 / 1e9
581 return 0
582
583 def _ListUsbDisks(self):
584 """Return a list of available removable USB disks.
585
586 Returns:
587 List of USB devices, each element is itself a list containing:
588 device ('/dev/sdx')
589 manufacturer name
590 product name
591 capacity in GB (an integer)
592 full description (all of the above concatenated).
593 """
594 disk_list = []
595 for disk in glob.glob('/sys/block/sd*'):
596 with open(disk + '/removable', 'r') as fd:
597 if int(fd.readline()) == 1:
598 device = '/dev/%s' % disk.split('/')[-1]
599 manuf = self._GetDiskInfo(disk, 'manufacturer')
600 product = self._GetDiskInfo(disk, 'product')
601 capacity = self._GetDiskCapacity(device)
602 if capacity:
603 desc = '%s: %s %s %d GB' % (device, manuf, product, capacity)
604 disk_list.append([device, manuf, product, capacity, desc])
605 return disk_list
606
607 def WriteToSd(self, flash_dest, disk, uboot, payload):
608 if flash_dest:
Simon Glass559b6612012-05-23 13:28:45 -0700609 raw_image = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Simon Glass0c2ba482012-03-22 21:57:51 -0700610 flash_dest, '1:0')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800611 bl1, bl2, _ = self._ExtractPayloadParts(payload)
Simon Glass559b6612012-05-23 13:28:45 -0700612 spl_load_size = os.stat(raw_image).st_size
613 bl2 = self._bundle.ConfigureExynosBl2(self._fdt, spl_load_size, bl2,
614 'flasher')
615
616 data = self._tools.ReadFile(bl1) + self._tools.ReadFile(bl2)
617
618 # Pad BL2 out to the required size.
619 # We require that it be 24KB, but data will only contain 8KB + 14KB.
620 # Add the extra padding to bring it to 24KB.
621 data += '\0' * (0x6000 - len(data))
622 data += self._tools.ReadFile(raw_image)
623 image = os.path.join(self._tools.outdir, 'flasher-with-bl.bin')
624 self._tools.WriteFile(image, data)
Simon Glass0c2ba482012-03-22 21:57:51 -0700625 self._out.Progress('Writing flasher to %s' % disk)
626 else:
627 image = payload
628 self._out.Progress('Writing image to %s' % disk)
629
630 args = ['if=%s' % image, 'of=%s' % disk, 'bs=512', 'seek=1']
631 self._tools.Run('dd', args, sudo=True)
632
633 def SendToSdCard(self, dest, flash_dest, uboot, payload):
634 """Write a flasher to an SD card.
635
636 Args:
637 dest: Destination in one of these forms:
638 ':<full description of device>'
639 ':.' selects the only available device, fails if more than one option
640 ':<device>' select deivce
641
642 Examples:
643 ':/dev/sdd: Generic Flash Card Reader/Writer 8 GB'
644 ':.'
645 ':/dev/sdd'
646
647 flash_dest: Destination for flasher, or None to not create a flasher:
648 Valid options are spi, sdmmc.
649 uboot: Full path to u-boot.bin.
650 payload: Full path to payload.
651 """
652 disk = None
653 disks = self._ListUsbDisks()
654 if dest[:1] == ':':
655 name = dest[1:]
656
657 # A '.' just means to use the only available disk.
658 if name == '.' and len(disks) == 1:
659 disk = disks[0][0]
660 for disk_info in disks:
661 # Use the full name or the device name.
662 if disk_info[4] == name or disk_info[1] == name:
663 disk = disk_info[0]
664
665 if disk:
666 self.WriteToSd(flash_dest, disk, uboot, payload)
667 else:
668 self._out.Error("Please specify destination -w 'sd:<disk_description>':")
669 self._out.Error(' - description can be . for the only disk, SCSI '
670 'device letter')
671 self._out.Error(' or the full description listed here')
672 msg = 'Found %d available disks.' % len(disks)
673 if not disks:
674 msg += ' Please insert an SD card and try again.'
675 self._out.UserOutput(msg)
676
677 # List available disks as a convenience.
678 for disk in disks:
679 self._out.UserOutput(' %s' % disk[4])
680
Vadim Bendebury19a77122013-01-24 17:38:00 -0800681 def Em100FlashImage(self, image_fname):
Simon Glass9eb8c722012-06-07 13:34:31 -0700682 """Send an image to an attached EM100 device.
683
684 This is a Dediprog EM100 SPI flash emulation device. We set up servo2
685 to do the SPI emulation, then write the image, then boot the board.
686 All going well, this is enough to get U-Boot running.
687
688 Args:
689 image_fname: Filename of image to send
690 """
691 args = ['spi2_vref:off', 'spi2_buf_en:off', 'spi2_buf_on_flex_en:off']
692 args.append('spi_hold:on')
Simon Glass6a616c12012-09-24 18:13:46 -0700693 self._DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700694
695 # TODO(sjg@chromium.org): This is for link. We could make this
696 # configurable from the fdt.
697 args = ['-c', 'W25Q64CV', '-d', self._tools.Filename(image_fname), '-r']
698 self._out.Progress('Writing image to em100')
699 self._tools.Run('em100', args, sudo=True)
700
701 self._out.Progress('Resetting board')
702 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off', 'sleep:.5']
703 args.extend(['pwr_button:press', 'sleep:.2', 'pwr_button:release'])
Simon Glass6a616c12012-09-24 18:13:46 -0700704 self._DutControl(args)
Simon Glass9eb8c722012-06-07 13:34:31 -0700705
Simon Glass0c2ba482012-03-22 21:57:51 -0700706
Simon Glass27a9c142012-03-15 21:08:29 -0700707def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glass60a40af2012-06-07 11:54:17 -0700708 bundle, update=True, verify=False, dest=None,
Vadim Bendebury19a77122013-01-24 17:38:00 -0800709 flash_dest=None, kernel=None, bootstub=None, servo='any',
Simon Glass4b8b3bd2012-11-06 12:31:01 -0800710 method='tegra'):
Simon Glasse0b61442012-03-13 15:29:51 -0700711 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700712
713 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700714 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700715
716 Args:
717 output: cros_output object to use.
718 tools: Tools object to use.
719 fdt: Fdt object to use as our device tree.
720 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700721 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700722 image_fname: Filename of image to write.
Simon Glass0191a882012-05-23 13:15:06 -0700723 bundle: The bundle object which created the image.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800724 update: Use faster update algorithm rather then full device erase.
725 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700726 dest: Destination device to write firmware to (usb, sd).
Simon Glass2d1fc8b2012-03-22 21:44:50 -0700727 flash_dest: Destination device for flasher to program payload into.
Simon Glassde9c8072012-07-02 22:29:02 -0700728 kernel: Kernel file to write after U-Boot
Vadim Bendebury19a77122013-01-24 17:38:00 -0800729 bootstub: string, file name of the boot stub, if present
Simon Glass6a616c12012-09-24 18:13:46 -0700730 servo: Describes the servo unit to use: none=none; any=any; otherwise
731 port number of servo to use.
Simon Glass710dedc2011-08-09 14:08:52 -0700732 """
Simon Glass0191a882012-05-23 13:15:06 -0700733 write = WriteFirmware(tools, fdt, output, bundle)
Simon Glass6a616c12012-09-24 18:13:46 -0700734 write.SelectServo(servo)
Simon Glass5b5fd642011-08-17 12:24:01 -0700735 write.update = update
736 write.verify = verify
Simon Glasse0b61442012-03-13 15:29:51 -0700737 if dest == 'usb':
Simon Glass4b8b3bd2012-11-06 12:31:01 -0800738 method = fdt.GetString('/chromeos-config', 'flash-method', method)
Simon Glasse0b61442012-03-13 15:29:51 -0700739 if method == 'tegra':
Allen Martinb3aa2672012-03-23 15:55:25 +0000740 tools.CheckTool('tegrarcm')
Simon Glass89ecf712012-06-07 12:20:15 -0700741 if flash_dest:
742 write.text_base = bundle.CalcTextBase('flasher ', fdt, flasher)
Simon Glasse1824db2012-07-11 17:38:40 +0200743 elif bootstub:
Simon Glass89ecf712012-06-07 12:20:15 -0700744 write.text_base = bundle.CalcTextBase('bootstub ', fdt, bootstub)
Vadim Bendebury19a77122013-01-24 17:38:00 -0800745 ok = write.NvidiaFlashImage(flash_dest, flasher, file_list['bct'],
Simon Glass89ecf712012-06-07 12:20:15 -0700746 image_fname, bootstub)
Simon Glass27a9c142012-03-15 21:08:29 -0700747 elif method == 'exynos':
Simon Glass2c389062012-04-09 13:09:01 -0700748 tools.CheckTool('lsusb', 'usbutils')
749 tools.CheckTool('smdk-usbdl', 'smdk-dltool')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800750 ok = write.ExynosFlashImage(flash_dest, flasher,
Simon Glassde9c8072012-07-02 22:29:02 -0700751 file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname,
752 kernel)
Simon Glasse0b61442012-03-13 15:29:51 -0700753 else:
754 raise CmdError("Unknown flash method '%s'" % method)
755 if ok:
756 output.Progress('Image uploaded - please wait for flashing to '
757 'complete')
758 else:
759 raise CmdError('Image upload failed - please check board connection')
Simon Glass9eb8c722012-06-07 13:34:31 -0700760 elif dest == 'em100':
761 # crosbug.com/31625
762 tools.CheckTool('em100')
Vadim Bendebury19a77122013-01-24 17:38:00 -0800763 write.Em100FlashImage(image_fname)
Simon Glass0c2ba482012-03-22 21:57:51 -0700764 elif dest.startswith('sd'):
765 write.SendToSdCard(dest[2:], flash_dest, flasher, image_fname)
Simon Glass710dedc2011-08-09 14:08:52 -0700766 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700767 raise CmdError("Unknown destination device '%s'" % dest)