# # 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 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) if common.OPTIONS.full_radio: if not target_radio_img: assert False, "full radio option specified but no radio img found" else: return source_radio_img = FindRadio(info.source_zip) if not target_radio_img or not source_radio_img: return if source_radio_img != target_radio_img: info.script.CacheFreeSpaceCheck(len(source_radio_img)) radio_type, radio_device = common.GetTypeAndDevice("/radio", info.info_dict) info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % ( radio_type, radio_device, len(source_radio_img), common.sha1(source_radio_img).hexdigest(), len(target_radio_img), common.sha1(target_radio_img).hexdigest())) 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" else: WriteBootloader(info, target_bootloader_img) except KeyError: print "no bootloader.img in target target_files; skipping install" tf = FindRadio(info.target_zip) if not tf: # failed to read TARGET radio image: don't include any radio in update. print "no radio.img in target target_files; skipping install" # we have checked the existence of the radio image in # IncrementalOTA_VerifyEnd(), so it won't reach here. assert common.OPTIONS.full_radio == False else: tf = common.File("radio.img", tf) sf = FindRadio(info.source_zip) if not sf or common.OPTIONS.full_radio: # failed to read SOURCE radio image or one has specified the option to # include the whole target radio image. print("no radio image in source target_files or full_radio specified; " "installing complete image") WriteRadio(info, tf.data) else: sf = common.File("radio.img", sf) if tf.sha1 == sf.sha1: print "radio image unchanged; skipping" else: diff = common.Difference(tf, sf, diff_program="bsdiff") common.ComputeDifferences([diff]) _, _, d = diff.GetPatch() if d is None or len(d) > tf.size * common.OPTIONS.patch_threshold: # computing difference failed, or difference is nearly as # big as the target: simply send the target. WriteRadio(info, tf.data) else: common.ZipWriteStr(info.output_zip, "radio.img.p", d) info.script.Print("Patching radio...") radio_type, radio_device = common.GetTypeAndDevice( "/radio", info.info_dict) info.script.ApplyPatch( "%s:%s:%d:%s:%d:%s" % (radio_type, radio_device, sf.size, sf.sha1, tf.size, tf.sha1), "-", tf.size, tf.sha1, sf.sha1, "radio.img.p") def WriteRadio(info, radio_img): info.script.Print("Writing radio...") common.ZipWriteStr(info.output_zip, "radio.img", radio_img) _, device = common.GetTypeAndDevice("/radio", info.info_dict) info.script.AppendExtra( 'package_extract_file("radio.img", "%s");' % (device,)) # /* msm8992 bootloader.img format */ # # #define BOOTLDR_MAGIC "BOOTLDR!" # #define BOOTLDR_MAGIC_SIZE 8 # # struct bootloader_images_header { # char magic[BOOTLDR_MAGIC_SIZE]; # unsigned int num_images; # unsigned int start_offset; # unsigned int bootldr_size; # struct { # char name[64]; # unsigned int size; # } img_info[]; # }; # def ParseBootloaderHeader(bootloader): header_fmt = "<8sIII" header_size = struct.calcsize(header_fmt) magic, num_images, start_offset, bootloader_size = struct.unpack( header_fmt, bootloader[:header_size]) assert magic == "BOOTLDR!", "bootloader.img bad magic value" img_info_fmt = "<64sI" img_info_size = struct.calcsize(img_info_fmt) imgs = [struct.unpack(img_info_fmt, bootloader[header_size+i*img_info_size: header_size+(i+1)*img_info_size]) for i in range(num_images)] p = start_offset img_dict = {} for name, size in imgs: img_dict[trunc_to_null(name)] = p, size p += size assert p - start_offset == bootloader_size, "bootloader.img corrupted" return img_dict # bullhead's bootloader.img contains 11 separate images. # Each goes to its own partition: # sbl1, tz, rpm, aboot, sdi, imgdata, pmic, hyp, sec, keymaster, cmnlib # # bullhead also has 8 backup partitions: # sbl1, tz, rpm, aboot, pmic, hyp, keymaster, cmnlib # release_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib" debug_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib" release_nobackup_partitions = "sdi imgdata sec" debug_nobackup_partitions = "sdi imgdata sec" def WriteBootloader(info, bootloader): info.script.Print("Writing bootloader...") img_dict = ParseBootloaderHeader(bootloader) 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,)) # failed sbl updates, may render the handset unusable/unrestorable. # Hence adopt below strategy for updates,enabling restore at all times. # 1. Flash backup partitions # 2. patch secondary pte's to swap primary/backup, and enable secondary gpt # 3. Flash psuedo-backup partions, effectively flashing primary partitions # 4. restore secondary pte's and restore primary gpt # 5. Flash all other non backup partitions # # Depending on the build fingerprint, we can decide which partitions # to update. fp = info.info_dict["build.prop"]["ro.build.fingerprint"] if "release-keys" in fp: to_bkp_flash = release_backup_partitions.split() to_flash = release_nobackup_partitions.split() else: to_bkp_flash = debug_backup_partitions.split() to_flash = debug_nobackup_partitions.split() # Write the images to separate files in the OTA package # and flash backup partitions for i in to_bkp_flash: try: _, device = common.GetTypeAndDevice("/"+i+"bak", info.info_dict) except KeyError: print "skipping flash of %s; not in recovery.fstab" % (i,) continue common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,), bootloader[img_dict[i][0]: img_dict[i][0]+img_dict[i][1]]) info.script.AppendExtra( 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) target_device = info.info_dict["build.prop"]["ro.product.device"] # swap ptes in secondary and force secondary gpt info.script.AppendExtra("lge_"+target_device+"_update_gpt();") # flash again after swap, effectively flashing primary # pte's are not re-read, hence primary is psuedo-secondary for i in to_bkp_flash: try: _, device = common.GetTypeAndDevice("/"+i, info.info_dict) except KeyError: print "skipping flash of %s; not in recovery.fstab" % (i,) continue info.script.AppendExtra( 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) # restore secondary gpt for correct mappings and enable primary gpt info.script.AppendExtra("lge_"+target_device+"_recover_gpt();") # Write the images to separate files in the OTA package for i in to_flash: try: _, device = common.GetTypeAndDevice("/"+i, info.info_dict) except KeyError: print "skipping flash of %s; not in recovery.fstab" % (i,) continue common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,), bootloader[img_dict[i][0]: img_dict[i][0]+img_dict[i][1]]) info.script.AppendExtra( 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) info.script.AppendExtra( 'package_extract_file("bootloader-flag-clear.txt", "%s");' % (misc_device,)) def trunc_to_null(s): if '\0' in s: return s[:s.index('\0')] else: return s