# # Copyright (C) 2015 The Android Open-Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import common import struct # The target does not support OTA-flashing # the partition table, so blacklist it. DEFAULT_BOOTLOADER_OTA_BLACKLIST = [ 'partition' ] class BadMagicError(Exception): __str__ = "bad magic value" # # Huawei Bootloader packed image format # # typedef struct meta_header { # u32 magic; /* 0xce1ad63c */ # u16 major_version; /* (0x1)-reject images with higher major versions */ # u16 minor_version; /* (0x0)-allow images with higer minor versions */ # char img_version[64]; /* Top level version for images in this meta */ # u16 meta_hdr_sz; /* size of this header */ # u16 img_hdr_sz; /* size of img_header_entry list */ # } meta_header_t; # typedef struct img_header_entry { # char ptn_name[MAX_GPT_NAME_SIZE]; # u32 start_offset; # u32 size; # } img_header_entry_t MAGIC = 0xce1ad63c class HuaweiBootImage(object): def __init__(self, data, name=None): self.name = name self._unpack(data) def _unpack(self, data): """Unpack the data blob as a Huawei boot image and return the list of contained image objects""" num_imgs_fmt = struct.Struct("<IHH64sHH") header = data[0:num_imgs_fmt.size] info = {} ( info["magic"], info["major_version"], info["minor_version"], info["img_version"], info["meta_hdr_size"], info["img_hdr_size"], ) = num_imgs_fmt.unpack(header) img_info_format = "<72sLL" img_info_size = struct.calcsize(img_info_format) num = info["img_hdr_size"] / img_info_size size = num_imgs_fmt.size imgs = [ struct.unpack( img_info_format, data[size + i * img_info_size:size + (i + 1) * img_info_size]) for i in range(num) ] if info["magic"] != MAGIC: raise BadMagicError img_objs = {} for name, start, end in imgs: if TruncToNull(name): img = common.File(TruncToNull(name), data[start:start + end]) img_objs[img.name] = img self.unpacked_images = img_objs def GetUnpackedImage(self, name): return self.unpacked_images.get(name) def FindRadio(zipfile): try: return zipfile.read("RADIO/radio.img") except KeyError: return None def FullOTA_InstallEnd(info): try: bootloader_img = info.input_zip.read("RADIO/bootloader.img") except KeyError: print "no bootloader.img in target_files; skipping install" else: WriteBootloader(info, bootloader_img) radio_img = FindRadio(info.input_zip) if radio_img: WriteRadio(info, radio_img) else: print "no radio.img in target_files; skipping install" def IncrementalOTA_VerifyEnd(info): target_radio_img = FindRadio(info.target_zip) source_radio_img = FindRadio(info.source_zip) if not target_radio_img or not source_radio_img: return target_modem_img = HuaweiBootImage(target_radio_img).GetUnpackedImage("modem") if not target_modem_img: return source_modem_img = HuaweiBootImage(source_radio_img).GetUnpackedImage("modem") if not source_modem_img: return if target_modem_img.sha1 != source_modem_img.sha1: info.script.CacheFreeSpaceCheck(len(source_modem_img.data)) radio_type, radio_device = common.GetTypeAndDevice("/modem", info.info_dict) info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % ( radio_type, radio_device, len(source_modem_img.data), source_modem_img.sha1, len(target_modem_img.data), target_modem_img.sha1)) def IncrementalOTA_InstallEnd(info): try: target_bootloader_img = info.target_zip.read("RADIO/bootloader.img") try: source_bootloader_img = info.source_zip.read("RADIO/bootloader.img") except KeyError: source_bootloader_img = None if source_bootloader_img == target_bootloader_img: print "bootloader unchanged; skipping" elif source_bootloader_img == None: print "no bootloader in source target_files; installing complete image" WriteBootloader(info, target_bootloader_img) else: tf = common.File("bootloader.img", target_bootloader_img) sf = common.File("bootloader.img", source_bootloader_img) WriteIncrementalBootloader(info, tf, sf) except KeyError: print "no bootloader.img in target target_files; skipping install" target_radio_image = FindRadio(info.target_zip) if not target_radio_image: # failed to read TARGET radio image: don't include any radio in update. print "no radio.img in target target_files; skipping install" else: tf = common.File("radio.img", target_radio_image) source_radio_image = FindRadio(info.source_zip) if not source_radio_image: # failed to read SOURCE radio image: include the whole target # radio image. print "no radio image in source target_files; installing complete image" WriteRadio(info, tf.data) else: sf = common.File("radio.img", source_radio_image) if tf.size == sf.size and tf.sha1 == sf.sha1: print "radio image unchanged; skipping" else: WriteIncrementalRadio(info, tf, sf) def WriteIncrementalBootloader(info, target_imagefile, source_imagefile): try: tm = HuaweiBootImage(target_imagefile.data, "bootloader") except BadMagicError: raise ValueError("bootloader.img bad magic value") try: sm = HuaweiBootImage(source_imagefile.data, "bootloader") except BadMagicError: print "source bootloader is not a Huawei boot img; installing complete img." return WriteBootloader(info, target_imagefile.data) # blacklist any partitions that match the source image blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST for ti in tm.unpacked_images.values(): if ti not in blacklist: si = sm.GetUnpackedImage(ti.name) if not si: continue if ti.size == si.size and ti.sha1 == si.sha1: print "target bootloader partition img %s matches source; skipping" % ( ti.name) blacklist.append(ti.name) # If there are any images to then write them whitelist = [ i.name for i in tm.unpacked_images.values() if i.name not in blacklist ] if len(whitelist): # Install the bootloader, skipping any matching partitions WriteBootloader(info, target_imagefile.data, blacklist) def WriteIncrementalRadio(info, target_imagefile, source_imagefile): try: target_radio_img = HuaweiBootImage(target_imagefile.data, "radio") except BadMagicError: print "Magic number mismatch in target radio image" raise ValueError("radio.img bad magic value") try: source_radio_img = HuaweiBootImage(source_imagefile.data, "radio") except BadMagicError: print "Magic number mismatch in source radio image" source_radio_img = None write_full_modem = True if source_radio_img: target_modem_img = target_radio_img.GetUnpackedImage("modem") if target_modem_img: source_modem_img = source_radio_img.GetUnpackedImage("modem") if source_modem_img: WriteIncrementalModemPartition(info, target_modem_img, source_modem_img) write_full_modem = False # Write the full images, skipping modem if so directed. # # NOTE: Some target flex radio images are zero-filled, and must # be flashed to trigger the flex update "magic". Do not # skip installing target partition images that are identical # to its corresponding source partition image. blacklist = [] if not write_full_modem: blacklist.append("modem") WriteHuaweiBootPartitionImages(info, target_radio_img, blacklist) def WriteIncrementalModemPartition(info, target_modem_image, source_modem_image): tf = target_modem_image sf = source_modem_image pad_tf = False pad_sf = False blocksize = 4096 partial_tf = len(tf.data) % blocksize partial_sf = len(sf.data) % blocksize if partial_tf: pad_tf = True if partial_sf: pad_sf = True b = common.BlockDifference("modem", common.DataImage(tf.data,False, pad_tf), common.DataImage(sf.data,False, pad_sf)) b.WriteScript(info.script, info.output_zip) def WriteRadio(info, radio_img): info.script.Print("Writing radio...") try: huawei_boot_image = HuaweiBootImage(radio_img, "radio") except BadMagicError: raise ValueError("radio.img bad magic value") WriteHuaweiBootPartitionImages(info, huawei_boot_image) def WriteHuaweiBootPartitionImages(info, huawei_boot_image, blacklist=[]): WriteGroupedImages(info, huawei_boot_image.name, huawei_boot_image.unpacked_images.values(), blacklist) def WriteGroupedImages(info, group_name, images, blacklist=[]): """Write a group of partition images to the OTA package, and add the corresponding flash instructions to the recovery script. Skip any images that do not have a corresponding entry in recovery.fstab.""" for i in images: if i.name not in blacklist: WritePartitionImage(info, i, group_name) def WritePartitionImage(info, image, group_name=None): filename = "%s.img" % image.name if group_name: filename = "%s.%s" % (group_name, filename) try: info.script.Print("writing partition image %s" % image.name) _, device = common.GetTypeAndDevice("/" + image.name, info.info_dict) except KeyError: print "skipping flash of %s; not in recovery.fstab" % image.name return common.ZipWriteStr(info.output_zip, filename, image.data) info.script.AppendExtra('package_extract_file("%s", "%s");' % (filename, device)) def WriteBootloader(info, bootloader, blacklist=DEFAULT_BOOTLOADER_OTA_BLACKLIST): info.script.Print("Writing bootloader...") try: huawei_boot_image = HuaweiBootImage(bootloader,"bootloader") except BadMagicError: raise ValueError("bootloader.img bad magic value") common.ZipWriteStr(info.output_zip, "bootloader-flag.txt", "updating-bootloader" + "\0" * 13) common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32) _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict) info.script.AppendExtra( 'package_extract_file("bootloader-flag.txt", "%s");' % misc_device) # OTA does not support partition changes, so # do not bundle the partition image in the OTA package. WriteHuaweiBootPartitionImages(info, huawei_boot_image, blacklist) info.script.AppendExtra( 'package_extract_file("bootloader-flag-clear.txt", "%s");' % misc_device) def TruncToNull(s): if '\0' in s: return s[:s.index('\0')] else: return s