def AddConfigList(self, config_list, use_int=False): """Add a list of config items to the fdt. Normally these values are written to the fdt as strings, but integers are also supported, in which case the values will be converted to integers (if necessary) before being stored. Args: config_list: List of (config, value) tuples to add to the fdt. For each tuple: config: The fdt node to write to will be /config/<config>. value: An integer or string value to write. use_int: True to only write integer values. Raises: CmdError: if a value is required to be converted to integer but can't be. """ if config_list: for config in config_list: value = config[1] if use_int: try: value = int(value) except ValueError as str: raise CmdError( "Cannot convert config option '%s' to integer" % value) if type(value) == type(1): self.fdt.PutInteger('/config', '%s' % config[0], value) else: self.fdt.PutString('/config', '%s' % config[0], value)
def ConfigureExynosBl2(self, fdt, spl_load_size, orig_bl2, name=''): """Configure an Exynos BL2 binary for our needs. We create a new modified BL2 and return its filename. Args: fdt: Device tree containing the parameter values. spl_load_size: Size of U-Boot image that SPL must load orig_bl2: Filename of original BL2 file to modify. """ self._out.Info('Configuring BL2') bl2 = os.path.join(self._tools.outdir, 'updated-spl%s.bin' % name) data = self._tools.ReadFile(orig_bl2) self._tools.WriteFile(bl2, data) # Locate the parameter block data = self._tools.ReadFile(bl2) marker = struct.pack('<L', 0xdeadbeef) pos = data.rfind(marker) if not pos: raise CmdError("Could not find machine parameter block in '%s'" % orig_bl2) data = self._UpdateBl2Parameters(fdt, spl_load_size, data, pos) data = self._UpdateChecksum(data) self._tools.WriteFile(bl2, data) return bl2
def AddProperty(self, name, value): """Add a new property which can be used by the fdt. Args: name: Name of property value: Value of property (typically a filename) """ if not value: raise CmdError("Cannot find value for entry property '%s'" % name) self.props[name] = value
def AddEnableList(self, enable_list): """Process a list of nodes to enable/disable. Args: config_list: List of (node, value) tuples to add to the fdt. For each tuple: node: The fdt node to write to will be <node> or pointed to by /aliases/<node>. We can tell which value: 0 to disable the node, 1 to enable it """ if enable_list: for node_name, enabled in enable_list: try: enabled = int(enabled) if enabled not in (0, 1): raise ValueError except ValueError as str: raise CmdError("Invalid enable option value '%s' " "(should be 0 or 1)" % enabled) self.SetNodeEnabled(node_name, enabled)
def _BuildBlob(self, pack, fdt, blob_type): """Build the blob data for a particular blob type. Args: blob_type: The type of blob to create data for. Supported types are: coreboot A coreboot image (ROM plus U-boot and .dtb payloads). signed Nvidia T20/T30 signed image (BCT, U-Boot, .dtb). """ if blob_type == 'coreboot': coreboot = self._CreateCorebootStub(self.uboot_fname, self.coreboot_fname) pack.AddProperty('coreboot', coreboot) pack.AddProperty('image', coreboot) elif blob_type == 'legacy': pack.AddProperty('legacy', self.seabios_fname) elif blob_type == 'signed': bootstub, signed = self._CreateBootStub(self.uboot_fname, fdt, self.postload_fname) pack.AddProperty('bootstub', bootstub) pack.AddProperty('signed', signed) pack.AddProperty('image', signed) elif blob_type == 'exynos-bl1': pack.AddProperty(blob_type, self.exynos_bl1) # TODO([email protected]): Deprecate ecbin elif blob_type in ['ecrw', 'ecbin']: pack.AddProperty('ecrw', self.ecrw_fname) pack.AddProperty('ecbin', self.ecrw_fname) elif blob_type == 'ecro': # crosbug.com/p/13143 # We cannot have an fmap in the EC image since there can be only one, # which is the main fmap describing the whole image. # Ultimately the EC will not have an fmap, since with software sync # there is no flashrom involvement in updating the EC flash, and thus # no need for the fmap. # For now, mangle the fmap name to avoid problems. updated_ecro = os.path.join(self._tools.outdir, 'updated-ecro.bin') data = self._tools.ReadFile(self.ecro_fname) data = re.sub('__FMAP__', '__fMAP__', data) self._tools.WriteFile(updated_ecro, data) pack.AddProperty(blob_type, updated_ecro) elif blob_type == 'exynos-bl2': spl_payload = pack.GetBlobParams(blob_type) # TODO(sjg@chromium): Remove this later, when we remove boot+dtb # from all flash map files. if not spl_payload: spl_load_size = os.stat(pack.GetProperty('boot+dtb')).st_size prop_list = 'boot+dtb' # Do this later, when we remove boot+dtb. # raise CmdError("No parameters provided for blob type '%s'" % # blob_type) else: prop_list = spl_payload[0].split(',') spl_load_size = len( pack.ConcatPropContents(prop_list, False)[0]) self._out.Info( "BL2/SPL contains '%s', size is %d / %#x" % (', '.join(prop_list), spl_load_size, spl_load_size)) bl2 = self.ConfigureExynosBl2(fdt, spl_load_size, self.exynos_bl2) pack.AddProperty(blob_type, bl2) elif pack.GetProperty(blob_type): pass else: raise CmdError("Unknown blob type '%s' required in flash map" % blob_type)
def _UpdateBl2Parameters(self, fdt, spl_load_size, data, pos): """Update the parameters in a BL2 blob. We look at the list in the parameter block, extract the value of each from the device tree, and write that value to the parameter block. Args: fdt: Device tree containing the parameter values. spl_load_size: Size of U-Boot image that SPL must load data: The BL2 data. pos: The position of the start of the parameter block. Returns: The new contents of the parameter block, after updating. """ version, size = struct.unpack('<2L', data[pos + 4:pos + 12]) if version != 1: raise CmdError( "Cannot update machine parameter block version '%d'" % version) if size < 0 or pos + size > len(data): raise CmdError("Machine parameter block size %d is invalid: " "pos=%d, size=%d, space=%d, len=%d" % (size, pos, size, len(data) - pos, len(data))) # Move past the header and read the parameter list, which is terminated # with \0. pos += 12 param_list = struct.unpack('<%ds' % (len(data) - pos), data[pos:])[0] param_len = param_list.find('\0') param_list = param_list[:param_len] pos += (param_len + 4) & ~3 # Work through the parameters one at a time, adding each value new_data = '' upto = 0 for param in param_list: value = struct.unpack('<1L', data[pos + upto:pos + upto + 4])[0] # Use this to detect a missing value from the fdt. not_given = 'not-given-invalid-value' if param == 'm': mem_type = fdt.GetString('/dmc', 'mem-type', not_given) if mem_type == not_given: mem_type = 'ddr3' self._out.Warning("No value for memory type: using '%s'" % mem_type) mem_types = ['ddr2', 'ddr3', 'lpddr2', 'lpddr3'] if not mem_type in mem_types: raise CmdError("Unknown memory type '%s'" % mem_type) value = mem_types.index(mem_type) self._out.Info(' Memory type: %s (%d)' % (mem_type, value)) elif param == 'M': mem_manuf = fdt.GetString('/dmc', 'mem-manuf', not_given) if mem_manuf == not_given: mem_manuf = 'samsung' self._out.Warning( "No value for memory manufacturer: using '%s'" % mem_manuf) mem_manufs = ['autodetect', 'elpida', 'samsung'] if not mem_manuf in mem_manufs: raise CmdError("Unknown memory manufacturer: '%s'" % mem_manuf) value = mem_manufs.index(mem_manuf) self._out.Info(' Memory manufacturer: %s (%d)' % (mem_manuf, value)) elif param == 'f': mem_freq = fdt.GetInt('/dmc', 'clock-frequency', -1) if mem_freq == -1: mem_freq = 800000000 self._out.Warning( "No value for memory frequency: using '%s'" % mem_freq) mem_freq /= 1000000 if not mem_freq in [533, 667, 800]: self._out.Warning("Unexpected memory speed '%s'" % mem_freq) value = mem_freq self._out.Info(' Memory speed: %d' % mem_freq) elif param == 'v': value = 31 self._out.Info(' Memory interleave: %#0x' % value) elif param == 'u': value = (spl_load_size + 0xfff) & ~0xfff self._out.Info(' U-Boot size: %#0x (rounded up from %#0x)' % (value, spl_load_size)) elif param == 'b': # These values come from enum boot_mode in U-Boot's cpu.h if self.spl_source == 'straps': value = 32 elif self.spl_source == 'emmc': value = 4 elif self.spl_source == 'spi': value = 20 elif self.spl_source == 'usb': value = 33 else: raise CmdError("Invalid boot source '%s'" % self.spl_source) self._out.Info(' Boot source: %#0x' % value) else: self._out.Warning("Unknown machine parameter type '%s'" % param) self._out.Info(' Unknown value: %#0x' % value) new_data += struct.pack('<L', value) upto += 4 # Put the data into our block. data = data[:pos] + new_data + data[pos + len(new_data):] self._out.Info('BL2 configuration complete') return data
def _CreateBootStub(self, uboot, base_fdt, postload): """Create a boot stub and a signed boot stub. For postload: We add a /config/postload-text-offset entry to the signed bootstub's fdt so that U-Boot can find the postload code. The raw (unsigned) bootstub will have a value of -1 for this since we will simply append the postload code to the bootstub and it can find it there. This will be used for RW A/B firmware. For the signed case this value will specify where in the flash to find the postload code. This will be used for RO firmware. Args: uboot: Path to u-boot.bin (may be chroot-relative) base_fdt: Fdt object containing the flat device tree. postload: Path to u-boot-post.bin, or None if none. Returns: Tuple containing: Full path to bootstub (uboot + fdt(-1) + postload). Full path to signed (uboot + fdt(flash pos) + bct) + postload. Raises: CmdError if a command fails. """ bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt.bin') text_base = self.CalcTextBase('', self.fdt, uboot) uboot_data = self._tools.ReadFile(uboot) # Make a copy of the fdt for the bootstub fdt = base_fdt.Copy(os.path.join(self._tools.outdir, 'bootstub.dtb')) fdt.PutInteger('/config', 'postload-text-offset', 0xffffffff) fdt_data = self._tools.ReadFile(fdt.fname) self._tools.WriteFile(bootstub, uboot_data + fdt_data) self._tools.OutputSize('U-Boot binary', self.uboot_fname) self._tools.OutputSize('U-Boot fdt', self._fdt_fname) self._tools.OutputSize('Combined binary', bootstub) # Sign the bootstub; this is a combination of the board specific # bct and the stub u-boot image. signed = self._SignBootstub(self._tools.Filename(self.bct_fname), bootstub, text_base) signed_postload = os.path.join(self._tools.outdir, 'signed-postload.bin') data = self._tools.ReadFile(signed) if postload: # We must add postload to the bootstub since A and B will need to # be able to find it without the /config/postload-text-offset mechanism. bs_data = self._tools.ReadFile(bootstub) bs_data += self._tools.ReadFile(postload) bootstub = os.path.join(self._tools.outdir, 'u-boot-fdt-postload.bin') self._tools.WriteFile(bootstub, bs_data) self._tools.OutputSize('Combined binary with postload', bootstub) # Now that we know the file size, adjust the fdt and re-sign postload_bootstub = os.path.join(self._tools.outdir, 'postload.bin') fdt.PutInteger('/config', 'postload-text-offset', len(data)) fdt_data = self._tools.ReadFile(fdt.fname) self._tools.WriteFile(postload_bootstub, uboot_data + fdt_data) signed = self._SignBootstub(self._tools.Filename(self.bct_fname), postload_bootstub, text_base) if len(data) != os.path.getsize(signed): raise CmdError( 'Signed file size changed from %d to %d after updating ' 'fdt' % (len(data), os.path.getsize(signed))) # Re-read the signed image, and add the post-load binary. data = self._tools.ReadFile(signed) data += self._tools.ReadFile(postload) self._tools.OutputSize('Post-load binary', postload) self._tools.WriteFile(signed_postload, data) self._tools.OutputSize('Final bootstub with postload', signed_postload) return bootstub, signed_postload
def DoWriteFirmware(output, tools, fdt, flasher, file_list, image_fname, bundle, update=True, verify=False, dest=None, flash_dest=None, kernel=None, bootstub=None, servo='any', method='tegra'): """A simple function to write firmware to a device. This creates a WriteFirmware object and uses it to write the firmware image to the given destination device. Args: output: cros_output object to use. tools: Tools object to use. fdt: Fdt object to use as our device tree. flasher: U-Boot binary to use as the flasher. file_list: Dictionary containing files that we might need. image_fname: Filename of image to write. bundle: The bundle object which created the image. update: Use faster update algorithm rather then full device erase. verify: Verify the write by doing a readback and CRC. dest: Destination device to write firmware to (usb, sd). flash_dest: Destination device for flasher to program payload into. kernel: Kernel file to write after U-Boot bootstub: string, file name of the boot stub, if present servo: Describes the servo unit to use: none=none; any=any; otherwise port number of servo to use. """ write = WriteFirmware(tools, fdt, output, bundle) write.SelectServo(servo) write.update = update write.verify = verify if dest == 'usb': method = fdt.GetString('/chromeos-config', 'flash-method', method) if method == 'tegra': tools.CheckTool('tegrarcm') if flash_dest: write.text_base = bundle.CalcTextBase('flasher ', fdt, flasher) elif bootstub: write.text_base = bundle.CalcTextBase('bootstub ', fdt, bootstub) ok = write.NvidiaFlashImage(flash_dest, flasher, file_list['bct'], image_fname, bootstub) elif method == 'exynos': tools.CheckTool('lsusb', 'usbutils') tools.CheckTool('smdk-usbdl', 'smdk-dltool') ok = write.ExynosFlashImage(flash_dest, flasher, file_list['exynos-bl1'], file_list['exynos-bl2'], image_fname, kernel) else: raise CmdError("Unknown flash method '%s'" % method) if ok: output.Progress('Image uploaded - please wait for flashing to ' 'complete') else: raise CmdError('Image upload failed - please check board connection') elif dest == 'em100': # crosbug.com/31625 tools.CheckTool('em100') write.Em100FlashImage(image_fname) elif dest.startswith('sd'): write.SendToSdCard(dest[2:], flash_dest, flasher, image_fname) else: raise CmdError("Unknown destination device '%s'" % dest)
def ExynosFlashImage(self, flash_dest, flash_uboot, bl1, bl2, payload, kernel): """Flash the image to SPI flash. This creates a special Flasher binary, with the image to be flashed as a payload. This is then sent to the board using the tegrarcm utility. Args: flash_dest: Destination for flasher, or None to not create a flasher Valid options are spi, sdmmc. flash_uboot: Full path to u-boot.bin to use for flasher. bl1: Full path to file containing BL1 (pre-boot). bl2: Full path to file containing BL2 (SPL). payload: Full path to payload. kernel: Kernel to send after the payload, or None. Returns: True if ok, False if failed. """ if flash_dest: image = self.PrepareFlasher(flash_uboot, payload, self.update, self.verify, flash_dest, '1:0') else: bl1, bl2, image = self._ExtractPayloadParts(payload) vendor_id = 0x04e8 product_id = 0x1234 # Preserve dut_hub_sel state. preserved_dut_hub_sel = self._DutControl(['dut_hub_sel',] ).strip().split(':')[-1] required_dut_hub_sel = 'dut_sees_servo' args = ['warm_reset:on', 'fw_up:on', 'pwr_button:press', 'sleep:.1', 'warm_reset:off'] if preserved_dut_hub_sel != required_dut_hub_sel: # Need to set it to get the port properly powered up. args += ['dut_hub_sel:%s' % required_dut_hub_sel] # TODO(sjg) If the board is bricked a reset does not seem to bring it # back to life. # BUG=chromium-os:28229 args = ['cold_reset:on', 'sleep:.2', 'cold_reset:off'] + args self._out.Progress('Reseting board via servo') self._DutControl(args) # If we have a kernel to write, create a new image with that added. if kernel: dl_image = os.path.join(self._tools.outdir, 'image-plus-kernel.bin') data = self._tools.ReadFile(image) # Pad the original payload out to the original length data += '\0' * (os.stat(payload).st_size - len(data)) data += self._tools.ReadFile(kernel) self._tools.WriteFile(dl_image, data) else: dl_image = image self._out.Progress('Uploading image') download_list = [ # The numbers are the download addresses (in SRAM) for each piece # TODO([email protected]): Perhaps pick these up from the fdt? ['bl1', 0x02021400, bl1], ['bl2', 0x02023400, bl2], ['u-boot', 0x43e00000, dl_image] ] try: for upto in range(len(download_list)): item = download_list[upto] if not self._WaitForUSBDevice('exynos', vendor_id, product_id, 4): if upto == 0: raise CmdError('Could not find Exynos board on USB port') raise CmdError("Stage '%s' did not complete" % item[0]) self._out.Notice(item[2]) self._out.Progress("Uploading stage '%s'" % item[0]) if upto == 0: # The IROM needs roughly 200ms here to be ready for USB download time.sleep(.5) args = ['-a', '%#x' % item[1], '-f', item[2]] self._tools.Run('smdk-usbdl', args, sudo=True) if upto == 1: # Once SPL starts up we can release the power buttom args = ['fw_up:off', 'pwr_button:release'] self._DutControl(args) finally: # Make sure that the power button is released and dut_sel_hub state is # restored, whatever happens args = ['fw_up:off', 'pwr_button:release'] if preserved_dut_hub_sel != required_dut_hub_sel: args += ['dut_hub_sel:%s' % preserved_dut_hub_sel] self._DutControl(args) self._out.Notice('Image downloaded - please see serial output ' 'for progress.') return True
def NvidiaFlashImage(self, flash_dest, uboot, bct, payload, bootstub): """Flash the image to SPI flash. This creates a special Flasher binary, with the image to be flashed as a payload. This is then sent to the board using the tegrarcm utility. Args: flash_dest: Destination for flasher, or None to not create a flasher Valid options are spi, sdmmc uboot: Full path to u-boot.bin. bct: Full path to BCT file (binary chip timings file for Nvidia SOCs). payload: Full path to payload. bootstub: Full path to bootstub, which is the payload without the signing information (i.e. bootstub is u-boot.bin + the FDT) Returns: True if ok, False if failed. """ # Use a Regex to pull Boot type from BCT file. match = re.compile('DevType\[0\] = NvBootDevType_(?P<boot>([a-zA-Z])+);') bct_dumped = self._tools.Run('bct_dump', [bct]).splitlines() # TODO(sjg): The boot type is currently selected by the bct, rather than # flash_dest selecting which bct to use. This is a bit backwards. For now # we go with the bct's idea. boot_type = filter(match.match, bct_dumped) boot_type = match.match(boot_type[0]).group('boot').lower() if flash_dest: image = self.PrepareFlasher(uboot, payload, self.update, self.verify, boot_type, 0) elif bootstub: image = bootstub else: image = payload # If we don't know the textbase, extract it from the payload. if self.text_base == -1: data = self._tools.ReadFile(payload) # Skip the BCT which is the first 64KB self.text_base = self._bundle.DecodeTextBase(data[0x10000:]) self._out.Notice('TEXT_BASE is %#x' % self.text_base) self._out.Progress('Uploading flasher image') args = [ '--bct', bct, '--bootloader', image, '--loadaddr', "%#x" % self.text_base ] # TODO(sjg): Check for existence of board - but chroot has no lsusb! last_err = None for _ in range(10): try: # TODO(sjg): Use Chromite library so we can monitor output self._tools.Run('tegrarcm', args, sudo=True) self._out.Notice('Flasher downloaded - please see serial output ' 'for progress.') return True except CmdError as err: if not self._out.stdout_is_tty: return False # Only show the error output once unless it changes. err = str(err) if not 'could not open USB device' in err: raise CmdError('tegrarcm failed: %s' % err) if err != last_err: self._out.Notice(err) last_err = err self._out.Progress('Please connect USB A-A cable and do a ' 'recovery-reset', True) time.sleep(1) return False