blob: ee09efcb4fe20f249ad4e28c6d2fe3a99e84c794 [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
5import os
6import re
7import time
8import tools
9from tools import CmdError
10
11def RoundUp(value, boundary):
12 """Align a value to the next power of 2 boundary.
13
14 Args:
15 value: The value to align.
16 boundary: The boundary value, e.g. 4096. Must be a power of 2.
17
18 Returns:
19 The rounded-up value.
20 """
21 return (value + boundary - 1) & ~(boundary - 1)
22
23
24class WriteFirmware:
25 """Write firmware to a Tegra 2 board using USB A-A cable.
26
27 This class handles re-reflashing a board with new firmware using the Tegra's
28 built-in boot ROM feature. This works by putting the chip into a special mode
29 where it ignores any available firmware and instead reads it from a connected
30 host machine over USB.
31
32 In our case we use that feature to send U-Boot along with a suitable payload
33 and instructions to flash it to SPI flash. The payload is itself normally a
34 full Chrome OS image consisting of U-Boot, some keys and verification
35 information, images and a map of the flash memory.
36 """
Simon Glass290a1802011-07-17 13:54:32 -070037 def __init__(self, tools, fdt, output):
Simon Glassa3f29ec2011-07-17 09:36:49 -070038 """Set up a new WriteFirmware object.
39
40 Args:
41 tools: A tools library for us to use.
42 fdt: An fdt which gives us some info that we need.
43 output: An output object to use for printing progress and messages.
Simon Glassa3f29ec2011-07-17 09:36:49 -070044 """
45 self._tools = tools
46 self._fdt = fdt
47 self._out = output
Simon Glass02d124a2012-03-02 14:47:20 -080048 self.text_base = self._fdt.GetInt('/chromeos-config', 'textbase');
Simon Glassa3f29ec2011-07-17 09:36:49 -070049
Simon Glass5b5fd642011-08-17 12:24:01 -070050 # For speed, use the 'update' algorithm and don't verify
51 self.update = True
52 self.verify = False
53
Simon Glass3d9a6c62012-03-15 20:38:04 -070054 def _GetFlashScript(self, payload_size, update, verify, boot_type, bus=0):
Simon Glassa3f29ec2011-07-17 09:36:49 -070055 """Get the U-Boot boot command needed to flash U-Boot.
56
57 We leave a marker in the string for the load address of the image,
58 since this depends on the size of this script. This can be replaced by
59 the caller provided that the marker length is unchanged.
60
61 Args:
62 payload_size: Size of payload in bytes.
Simon Glass5b5fd642011-08-17 12:24:01 -070063 update: Use faster update algorithm rather then full device erase
64 verify: Verify the write by doing a readback and CRC
Rhyland Kleinc5c87282012-03-08 16:40:54 -080065 boot_type: the src for bootdevice (Nand, Sdmmc, or Spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -070066
67 Returns:
68 A tuple containing:
69 The script, as a string ready to use as a U-Boot boot command, with an
70 embedded marker for the load address.
71 The marker string, which the caller should replace with the correct
72 load address as 8 hex digits, without changing its length.
73 """
74 replace_me = 'zsHEXYla'
Rhyland Kleinc5c87282012-03-08 16:40:54 -080075 page_size = 4096
76 if boot_type == 'Sdmmc':
77 page_size = 512
78 if boot_type != 'Spi':
79 update = False
80
Simon Glassa3f29ec2011-07-17 09:36:49 -070081 cmds = [
82 'setenv address 0x%s' % replace_me,
83 'setenv firmware_size %#x' % payload_size,
Rhyland Kleinc5c87282012-03-08 16:40:54 -080084 'setenv length %#x' % RoundUp(payload_size, page_size),
85 'setenv blocks %#x' % (RoundUp(payload_size, page_size) / page_size),
Simon Glass5b5fd642011-08-17 12:24:01 -070086 'setenv _crc "crc32 ${address} ${firmware_size}"',
Simon Glass5b5fd642011-08-17 12:24:01 -070087 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"',
Doug Anderson37ae2292011-09-15 17:41:57 -070088 ]
Rhyland Kleinc5c87282012-03-08 16:40:54 -080089 if boot_type == 'Nand':
Doug Anderson37ae2292011-09-15 17:41:57 -070090 cmds.extend([
91 'setenv _init "echo Init NAND; nand info"',
92 'setenv _erase "echo Erase NAND; nand erase 0 ${length}"',
93 'setenv _write "echo Write NAND; nand write ${address} 0 ${length}"',
94 'setenv _read "echo Read NAND; nand read ${address} 0 ${length}"',
95 ])
Rhyland Kleinc5c87282012-03-08 16:40:54 -080096 elif boot_type == 'Sdmmc':
97 cmds.extend([
98 'setenv _init "echo Init EMMC; mmc rescan 0"',
99 'setenv _erase "echo Erase EMMC; "',
100 'setenv _write "echo Write EMMC; mmc write 0 ${address} 0 ' \
101 '${blocks} boot1"',
102 'setenv _read "echo Read EMMC; mmc read 0 ${address} 0 ' \
103 '${blocks} boot1"',
104 ])
Doug Anderson37ae2292011-09-15 17:41:57 -0700105 else:
106 cmds.extend([
Simon Glass3d9a6c62012-03-15 20:38:04 -0700107 'setenv _init "echo Init SPI; sf probe %s"' % bus,
Doug Anderson37ae2292011-09-15 17:41:57 -0700108 'setenv _erase "echo Erase SPI; sf erase 0 ${length}"',
109 'setenv _write "echo Write SPI; sf write ${address} 0 ${length}"',
110 'setenv _read "echo Read SPI; sf read ${address} 0 ${length}"',
111 'setenv _update "echo Update SPI; sf update ${address} 0 ${length}"',
112 ])
Simon Glassa3f29ec2011-07-17 09:36:49 -0700113
Doug Anderson37ae2292011-09-15 17:41:57 -0700114 cmds.extend([
Simon Glassa3f29ec2011-07-17 09:36:49 -0700115 'echo Firmware loaded to ${address}, size ${firmware_size}, '
116 'length ${length}',
117 'run _crc',
118 'run _init',
Doug Anderson37ae2292011-09-15 17:41:57 -0700119 ])
Simon Glass5b5fd642011-08-17 12:24:01 -0700120 if update:
121 cmds += ['time run _update']
122 else:
123 cmds += ['run _erase', 'run _write']
124 if verify:
125 cmds += [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700126 'run _clear',
127 'run _read',
128 'run _crc',
129 'echo If the two CRCs above are equal, flash was successful.'
Simon Glass5b5fd642011-08-17 12:24:01 -0700130 ]
131 else:
132 cmds += ['echo Skipping verify']
Simon Glassa3f29ec2011-07-17 09:36:49 -0700133 script = '; '.join(cmds)
134 return script, replace_me
135
Simon Glass3d9a6c62012-03-15 20:38:04 -0700136 def PrepareFlasher(self, uboot, payload, update, verify, boot_type, bus):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700137 """Get a flasher ready for sending to the board.
138
139 The flasher is an executable image consisting of:
140
141 - U-Boot (u-boot.bin);
142 - a special FDT to tell it what to do in the form of a run command;
143 - (we could add some empty space here, in case U-Boot is not built to
144 be relocatable);
145 - the payload (which is a full flash image, or signed U-Boot + fdt).
146
147 Args:
148 uboot: Full path to u-boot.bin.
149 payload: Full path to payload.
Simon Glass5b5fd642011-08-17 12:24:01 -0700150 update: Use faster update algorithm rather then full device erase
151 verify: Verify the write by doing a readback and CRC
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800152 boot_type: the src for bootdevice (Nand, Sdmmc, or Spi)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700153
154 Returns:
Simon Glass290a1802011-07-17 13:54:32 -0700155 Filename of the flasher binary created.
Simon Glassa3f29ec2011-07-17 09:36:49 -0700156 """
Simon Glass951a2db2011-07-17 15:58:58 -0700157 fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb'))
Simon Glassa3f29ec2011-07-17 09:36:49 -0700158 payload_size = os.stat(payload).st_size
159
Doug Anderson37ae2292011-09-15 17:41:57 -0700160 script, replace_me = self._GetFlashScript(payload_size, update, verify,
Simon Glass3d9a6c62012-03-15 20:38:04 -0700161 boot_type, bus)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700162 data = self._tools.ReadFile(uboot)
Simon Glass02d124a2012-03-02 14:47:20 -0800163 fdt.PutString('/config', 'bootcmd', script)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700164 fdt_data = self._tools.ReadFile(fdt.fname)
165
166 # Work out where to place the payload in memory. This is a chicken-and-egg
167 # problem (although in case you haven't heard, it was the chicken that
168 # came first), so we resolve it by replacing the string after
169 # fdt.PutString has done its job.
170 #
171 # Correction: Technically, the egg came first. Whatever genetic mutation
172 # created the new species would have been present in the egg, but not the
173 # parent (since if it was in the parent, it would have been present in the
174 # parent when it was an egg).
175 #
176 # Question: ok so who laid the egg then?
177 payload_offset = len(data) + len(fdt_data)
Doug Anderson37ae2292011-09-15 17:41:57 -0700178
179 # NAND driver expects 4-byte alignment. Just go whole hog and do 4K.
180 alignment = 0x1000
181 payload_offset = (payload_offset + alignment - 1) & ~(alignment-1)
182
Simon Glass2c4b3e52011-11-15 14:45:43 -0800183 load_address = self.text_base + payload_offset,
Simon Glassa3f29ec2011-07-17 09:36:49 -0700184 new_str = '%08x' % load_address
185 if len(replace_me) is not len(new_str):
186 raise ValueError("Internal error: replacement string '%s' length does "
187 "not match new string '%s'" % (replace_me, new_str))
188 if len(re.findall(replace_me, fdt_data)) != 1:
189 raise ValueError("Internal error: replacement string '%s' already "
190 "exists in the fdt (%d matches)" % (replace_me, matches))
191 fdt_data = re.sub(replace_me, new_str, fdt_data)
192
193 # Now put it together.
194 data += fdt_data
195 data += "\0" * (payload_offset - len(data))
196 data += self._tools.ReadFile(payload)
Simon Glass951a2db2011-07-17 15:58:58 -0700197 flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin')
Simon Glassa3f29ec2011-07-17 09:36:49 -0700198 self._tools.WriteFile(flasher, data)
199
200 # Tell the user about a few things.
201 self._tools.OutputSize('U-Boot', uboot)
202 self._tools.OutputSize('Payload', payload)
203 self._tools.OutputSize('Flasher', flasher)
204 return flasher
205
Simon Glass27a9c142012-03-15 21:08:29 -0700206 def _NvidiaFlashImage(self, uboot, bct, payload):
Simon Glassa3f29ec2011-07-17 09:36:49 -0700207 """Flash the image to SPI flash.
208
209 This creates a special Flasher binary, with the image to be flashed as
210 a payload. This is then sent to the board using the nvflash utility.
211
212 Args:
213 uboot: Full path to u-boot.bin.
214 bct: Full path to BCT file (binary chip timings file for Nvidia SOCs).
215 payload: Full path to payload.
216
217 Returns:
218 True if ok, False if failed.
219 """
Rhyland Kleinc5c87282012-03-08 16:40:54 -0800220 # Use a Regex to pull Boot type from BCT file.
221 match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);')
222 bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines()
223 boot_type = filter(match.match, bct_dumped)
224 boot_type = match.match(boot_type[0]).group('boot')
Doug Anderson37ae2292011-09-15 17:41:57 -0700225
226 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
Simon Glass3d9a6c62012-03-15 20:38:04 -0700227 boot_type, 0)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700228
229 self._out.Progress('Uploading flasher image')
230 args = [
Simon Glassa3f29ec2011-07-17 09:36:49 -0700231 '--bct', bct,
232 '--setbct',
233 '--bl', flasher,
234 '--go',
Simon Glass2c4b3e52011-11-15 14:45:43 -0800235 '--setentry', "%#x" % self.text_base, "%#x" % self.text_base
Simon Glassa3f29ec2011-07-17 09:36:49 -0700236 ]
237
238 # TODO(sjg): Check for existence of board - but chroot has no lsusb!
239 last_err = None
240 for tries in range(10):
241 try:
Simon Glassa3f29ec2011-07-17 09:36:49 -0700242 # TODO(sjg): Use Chromite library so we can monitor output
Simon Glass86d16aa2012-03-09 15:29:05 -0800243 self._tools.Run('nvflash', args, sudo=True)
Simon Glassa3f29ec2011-07-17 09:36:49 -0700244 self._out.Notice('Flasher downloaded - please see serial output '
245 'for progress.')
246 return True
247
248 except CmdError as err:
249 if not self._out.stdout_is_tty:
250 return False
251
252 # Only show the error output once unless it changes.
253 err = str(err)
254 if not 'USB device not found' in err:
255 raise CmdError('nvflash failed: %s' % err)
256
257 if err != last_err:
258 self._out.Notice(err)
259 last_err = err
260 self._out.Progress('Please connect USB A-A cable and do a '
261 'recovery-reset', True)
262 time.sleep(1)
263
264 return False
Simon Glass710dedc2011-08-09 14:08:52 -0700265
Simon Glass27a9c142012-03-15 21:08:29 -0700266 def _WaitForUSBDevice(self, name, vendor_id, product_id, timeout=10):
267 """Wait until we see a device on the USB bus.
268
269 Args:
270 name: Board type name
271 vendor_id: USB vendor ID to look for
272 product_id: USB product ID to look for
273 timeout: Timeout to wait in seconds
274
275 Returns
276 True if the device was found, False if we timed out.
277 """
278 self._out.Progress('Waiting for board to appear on USB bus')
279 for tries in range(timeout):
280 try:
281 args = ['-d', '%04x:%04x' % (vendor_id, product_id)]
282 self._tools.Run('lsusb', args, sudo=True)
283 self._out.Notice('Flasher downloaded - please see serial output '
284 'for progress.')
285 return True
286
287 except CmdError as err:
288 pass
289
290 time.sleep(1)
291
292 self._out.Progress('Found %s board' % name)
293 return True
294
295 def _ExynosFlashImage(self, uboot, bl1, bl2, payload):
296 """Flash the image to SPI flash.
297
298 This creates a special Flasher binary, with the image to be flashed as
299 a payload. This is then sent to the board using the nvflash utility.
300
301 Args:
302 uboot: Full path to u-boot.bin.
303 bl1: Full path to file containing BL1 (pre-boot).
304 bl2: Full path to file containing BL2 (SPL)
305 payload: Full path to payload.
306
307 Returns:
308 True if ok, False if failed.
309 """
310 flasher = self.PrepareFlasher(uboot, payload, self.update, self.verify,
311 'Spi', '1:0')
312
313 vendor_id = 0x04e8
314 product_id = 0x1234
315 if not self._WaitForUSBDevice('exynos', vendor_id, product_id):
316 raise CmdError('Could not find USB device %04x:%04x' % (vendor_id,
317 product_id))
318
319 self._out.Progress('Uploading flasher image')
320 download_list = [
321 ['bl1', 0x02021400, bl1],
322 ['bl2', 0x02023400, bl2],
323 ['u-boot', 0x43e00000, flasher]
324 ]
325 for item in download_list:
326 print item
327 args = ['-a', '%#x' % item[1], '-f', item[2]]
328 bad = False
329 try:
330 self._out.Progress("Uploading stage '%s'" % item[0])
331 self._tools.Run('smdk-usbdl', args, sudo=True)
332 except CmdError as err:
333 bad = True
334
335 if bad or not self._WaitForUSBDevice('exynos', vendor_id, product_id, 1):
336 raise CmdError("Stage '%s' did not complete" % item[0])
337 time.sleep(.7)
338
339 return True
340
341def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname,
Simon Glasse0b61442012-03-13 15:29:51 -0700342 text_base=None, update=True, verify=False, dest=None):
343 """A simple function to write firmware to a device.
Simon Glass710dedc2011-08-09 14:08:52 -0700344
345 This creates a WriteFirmware object and uses it to write the firmware image
Simon Glasse0b61442012-03-13 15:29:51 -0700346 to the given destination device.
Simon Glass710dedc2011-08-09 14:08:52 -0700347
348 Args:
349 output: cros_output object to use.
350 tools: Tools object to use.
351 fdt: Fdt object to use as our device tree.
352 flasher: U-Boot binary to use as the flasher.
Simon Glass75759302012-03-15 20:26:53 -0700353 file_list: Dictionary containing files that we might need.
Simon Glass710dedc2011-08-09 14:08:52 -0700354 image_fname: Filename of image to write.
Simon Glass2c4b3e52011-11-15 14:45:43 -0800355 text_base: U-Boot text base (base of executable image), None for default.
356 update: Use faster update algorithm rather then full device erase.
357 verify: Verify the write by doing a readback and CRC.
Simon Glasse0b61442012-03-13 15:29:51 -0700358 dest: Destination device to write firmware to (usb, sd).
Simon Glass710dedc2011-08-09 14:08:52 -0700359 """
360 write = WriteFirmware(tools, fdt, output)
Simon Glass2c4b3e52011-11-15 14:45:43 -0800361 if text_base:
362 write.text_base = text_base
Simon Glass5b5fd642011-08-17 12:24:01 -0700363 write.update = update
364 write.verify = verify
Simon Glasse0b61442012-03-13 15:29:51 -0700365 if dest == 'usb':
366 method = fdt.GetString('/chromeos-config', 'flash-method', 'tegra')
367 if method == 'tegra':
Simon Glass75759302012-03-15 20:26:53 -0700368 ok = write._NvidiaFlashImage(flasher, file_list['bct'], image_fname)
Simon Glass27a9c142012-03-15 21:08:29 -0700369 elif method == 'exynos':
370 ok = write._ExynosFlashImage(flasher, file_list['exynos-bl1'],
371 file_list['exynos-bl2'], image_fname)
Simon Glasse0b61442012-03-13 15:29:51 -0700372 else:
373 raise CmdError("Unknown flash method '%s'" % method)
374 if ok:
375 output.Progress('Image uploaded - please wait for flashing to '
376 'complete')
377 else:
378 raise CmdError('Image upload failed - please check board connection')
379 elif dest == 'sd':
380 pass
Simon Glass710dedc2011-08-09 14:08:52 -0700381 else:
Simon Glasse0b61442012-03-13 15:29:51 -0700382 raise CmdError("Unknown destination device '%s'" % dest)