Simon Glass | a3f29ec | 2011-07-17 09:36:49 -0700 | [diff] [blame] | 1 | # 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 | |
| 5 | import os |
| 6 | import re |
| 7 | import time |
| 8 | import tools |
| 9 | from tools import CmdError |
| 10 | |
| 11 | def 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 | |
| 24 | class 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 Glass | 290a180 | 2011-07-17 13:54:32 -0700 | [diff] [blame] | 37 | def __init__(self, tools, fdt, output): |
Simon Glass | a3f29ec | 2011-07-17 09:36:49 -0700 | [diff] [blame] | 38 | """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 Glass | a3f29ec | 2011-07-17 09:36:49 -0700 | [diff] [blame] | 44 | """ |
| 45 | self._tools = tools |
| 46 | self._fdt = fdt |
| 47 | self._out = output |
Simon Glass | 290a180 | 2011-07-17 13:54:32 -0700 | [diff] [blame] | 48 | self._text_base = self._fdt.GetInt('/chromeos-config/textbase'); |
Simon Glass | a3f29ec | 2011-07-17 09:36:49 -0700 | [diff] [blame] | 49 | |
| 50 | def _GetFlashScript(self, payload_size): |
| 51 | """Get the U-Boot boot command needed to flash U-Boot. |
| 52 | |
| 53 | We leave a marker in the string for the load address of the image, |
| 54 | since this depends on the size of this script. This can be replaced by |
| 55 | the caller provided that the marker length is unchanged. |
| 56 | |
| 57 | Args: |
| 58 | payload_size: Size of payload in bytes. |
| 59 | |
| 60 | Returns: |
| 61 | A tuple containing: |
| 62 | The script, as a string ready to use as a U-Boot boot command, with an |
| 63 | embedded marker for the load address. |
| 64 | The marker string, which the caller should replace with the correct |
| 65 | load address as 8 hex digits, without changing its length. |
| 66 | """ |
| 67 | replace_me = 'zsHEXYla' |
| 68 | cmds = [ |
| 69 | 'setenv address 0x%s' % replace_me, |
| 70 | 'setenv firmware_size %#x' % payload_size, |
| 71 | 'setenv length %#x' % RoundUp(payload_size, 4096), |
| 72 | 'setenv _crc "crc32 ${address} ${firmware_size}"', |
| 73 | 'setenv _init "echo Initing SPI; sf probe 0"', |
| 74 | 'setenv _erase "echo Erasing SPI; sf erase 0 ${length}"', |
| 75 | 'setenv _write "echo Writing SPI; sf write ${address} 0 ${length}"', |
| 76 | 'setenv _clear "echo Clearing RAM; mw.b ${address} 0 ${length}"', |
| 77 | 'setenv _read "echo Reading SPI; sf read ${address} 0 ${length}"', |
| 78 | |
| 79 | 'echo Firmware loaded to ${address}, size ${firmware_size}, ' |
| 80 | 'length ${length}', |
| 81 | 'run _crc', |
| 82 | 'run _init', |
| 83 | 'run _erase', |
| 84 | 'run _write', |
| 85 | 'run _clear', |
| 86 | 'run _read', |
| 87 | 'run _crc', |
| 88 | 'echo If the two CRCs above are equal, flash was successful.' |
| 89 | ] |
| 90 | script = '; '.join(cmds) |
| 91 | return script, replace_me |
| 92 | |
| 93 | def PrepareFlasher(self, uboot, payload): |
| 94 | """Get a flasher ready for sending to the board. |
| 95 | |
| 96 | The flasher is an executable image consisting of: |
| 97 | |
| 98 | - U-Boot (u-boot.bin); |
| 99 | - a special FDT to tell it what to do in the form of a run command; |
| 100 | - (we could add some empty space here, in case U-Boot is not built to |
| 101 | be relocatable); |
| 102 | - the payload (which is a full flash image, or signed U-Boot + fdt). |
| 103 | |
| 104 | Args: |
| 105 | uboot: Full path to u-boot.bin. |
| 106 | payload: Full path to payload. |
| 107 | |
| 108 | Returns: |
Simon Glass | 290a180 | 2011-07-17 13:54:32 -0700 | [diff] [blame] | 109 | Filename of the flasher binary created. |
Simon Glass | a3f29ec | 2011-07-17 09:36:49 -0700 | [diff] [blame] | 110 | """ |
Simon Glass | 951a2db | 2011-07-17 15:58:58 -0700 | [diff] [blame] | 111 | fdt = self._fdt.Copy(os.path.join(self._tools.outdir, 'flasher.dtb')) |
Simon Glass | a3f29ec | 2011-07-17 09:36:49 -0700 | [diff] [blame] | 112 | payload_size = os.stat(payload).st_size |
| 113 | |
| 114 | script, replace_me = self._GetFlashScript(payload_size) |
| 115 | data = self._tools.ReadFile(uboot) |
| 116 | fdt.PutString('/config/bootcmd', script) |
| 117 | fdt_data = self._tools.ReadFile(fdt.fname) |
| 118 | |
| 119 | # Work out where to place the payload in memory. This is a chicken-and-egg |
| 120 | # problem (although in case you haven't heard, it was the chicken that |
| 121 | # came first), so we resolve it by replacing the string after |
| 122 | # fdt.PutString has done its job. |
| 123 | # |
| 124 | # Correction: Technically, the egg came first. Whatever genetic mutation |
| 125 | # created the new species would have been present in the egg, but not the |
| 126 | # parent (since if it was in the parent, it would have been present in the |
| 127 | # parent when it was an egg). |
| 128 | # |
| 129 | # Question: ok so who laid the egg then? |
| 130 | payload_offset = len(data) + len(fdt_data) |
| 131 | load_address = self._text_base + payload_offset, |
| 132 | new_str = '%08x' % load_address |
| 133 | if len(replace_me) is not len(new_str): |
| 134 | raise ValueError("Internal error: replacement string '%s' length does " |
| 135 | "not match new string '%s'" % (replace_me, new_str)) |
| 136 | if len(re.findall(replace_me, fdt_data)) != 1: |
| 137 | raise ValueError("Internal error: replacement string '%s' already " |
| 138 | "exists in the fdt (%d matches)" % (replace_me, matches)) |
| 139 | fdt_data = re.sub(replace_me, new_str, fdt_data) |
| 140 | |
| 141 | # Now put it together. |
| 142 | data += fdt_data |
| 143 | data += "\0" * (payload_offset - len(data)) |
| 144 | data += self._tools.ReadFile(payload) |
Simon Glass | 951a2db | 2011-07-17 15:58:58 -0700 | [diff] [blame] | 145 | flasher = os.path.join(self._tools.outdir, 'flasher-for-image.bin') |
Simon Glass | a3f29ec | 2011-07-17 09:36:49 -0700 | [diff] [blame] | 146 | self._tools.WriteFile(flasher, data) |
| 147 | |
| 148 | # Tell the user about a few things. |
| 149 | self._tools.OutputSize('U-Boot', uboot) |
| 150 | self._tools.OutputSize('Payload', payload) |
| 151 | self._tools.OutputSize('Flasher', flasher) |
| 152 | return flasher |
| 153 | |
| 154 | def FlashImage(self, uboot, bct, payload): |
| 155 | """Flash the image to SPI flash. |
| 156 | |
| 157 | This creates a special Flasher binary, with the image to be flashed as |
| 158 | a payload. This is then sent to the board using the nvflash utility. |
| 159 | |
| 160 | Args: |
| 161 | uboot: Full path to u-boot.bin. |
| 162 | bct: Full path to BCT file (binary chip timings file for Nvidia SOCs). |
| 163 | payload: Full path to payload. |
| 164 | |
| 165 | Returns: |
| 166 | True if ok, False if failed. |
| 167 | """ |
| 168 | flasher = self.PrepareFlasher(uboot, payload) |
| 169 | |
| 170 | self._out.Progress('Uploading flasher image') |
| 171 | args = [ |
| 172 | 'nvflash', |
| 173 | '--bct', bct, |
| 174 | '--setbct', |
| 175 | '--bl', flasher, |
| 176 | '--go', |
| 177 | '--setentry', "%#x" % self._text_base, "%#x" % self._text_base |
| 178 | ] |
| 179 | |
| 180 | # TODO(sjg): Check for existence of board - but chroot has no lsusb! |
| 181 | last_err = None |
| 182 | for tries in range(10): |
| 183 | try: |
| 184 | # TODO(sjg): Make sudo an argument to Run() |
| 185 | # TODO(sjg): Use Chromite library so we can monitor output |
| 186 | self._tools.Run('sudo', args) |
| 187 | self._out.Notice('Flasher downloaded - please see serial output ' |
| 188 | 'for progress.') |
| 189 | return True |
| 190 | |
| 191 | except CmdError as err: |
| 192 | if not self._out.stdout_is_tty: |
| 193 | return False |
| 194 | |
| 195 | # Only show the error output once unless it changes. |
| 196 | err = str(err) |
| 197 | if not 'USB device not found' in err: |
| 198 | raise CmdError('nvflash failed: %s' % err) |
| 199 | |
| 200 | if err != last_err: |
| 201 | self._out.Notice(err) |
| 202 | last_err = err |
| 203 | self._out.Progress('Please connect USB A-A cable and do a ' |
| 204 | 'recovery-reset', True) |
| 205 | time.sleep(1) |
| 206 | |
| 207 | return False |
Simon Glass | 710dedc | 2011-08-09 14:08:52 -0700 | [diff] [blame] | 208 | |
| 209 | def DoWriteFirmware(output, tools, fdt, flasher, bct_fname, image_fname): |
| 210 | """A simple function to write firmware to the board. |
| 211 | |
| 212 | This creates a WriteFirmware object and uses it to write the firmware image |
| 213 | to the board. |
| 214 | |
| 215 | Args: |
| 216 | output: cros_output object to use. |
| 217 | tools: Tools object to use. |
| 218 | fdt: Fdt object to use as our device tree. |
| 219 | flasher: U-Boot binary to use as the flasher. |
| 220 | bct_fname: Bct file to use for the flasher. |
| 221 | image_fname: Filename of image to write. |
| 222 | """ |
| 223 | write = WriteFirmware(tools, fdt, output) |
| 224 | if write.FlashImage(flasher, bct_fname, image_fname): |
| 225 | output.Progress('Image uploaded - please wait for flashing to ' |
| 226 | 'complete') |
| 227 | else: |
| 228 | raise CmdError('Image upload failed - please check board connection') |