Home | History | Annotate | Download | only in bullhead
      1 #
      2 # Copyright (C) 2015 The Android Open-Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 #
     16 
     17 import common
     18 import struct
     19 
     20 
     21 def FindRadio(zipfile):
     22   try:
     23     return zipfile.read("RADIO/radio.img")
     24   except KeyError:
     25     return None
     26 
     27 
     28 def FullOTA_InstallEnd(info):
     29   try:
     30     bootloader_img = info.input_zip.read("RADIO/bootloader.img")
     31   except KeyError:
     32     print "no bootloader.img in target_files; skipping install"
     33   else:
     34     WriteBootloader(info, bootloader_img)
     35 
     36   radio_img = FindRadio(info.input_zip)
     37   if radio_img:
     38     WriteRadio(info, radio_img)
     39   else:
     40     print "no radio.img in target_files; skipping install"
     41 
     42 
     43 def IncrementalOTA_VerifyEnd(info):
     44   target_radio_img = FindRadio(info.target_zip)
     45   if common.OPTIONS.full_radio:
     46     if not target_radio_img:
     47       assert False, "full radio option specified but no radio img found"
     48     else:
     49       return
     50   source_radio_img = FindRadio(info.source_zip)
     51   if not target_radio_img or not source_radio_img:
     52     return
     53   if source_radio_img != target_radio_img:
     54     info.script.CacheFreeSpaceCheck(len(source_radio_img))
     55     radio_type, radio_device = common.GetTypeAndDevice("/radio", info.info_dict)
     56     info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % (
     57         radio_type, radio_device,
     58         len(source_radio_img), common.sha1(source_radio_img).hexdigest(),
     59         len(target_radio_img), common.sha1(target_radio_img).hexdigest()))
     60 
     61 
     62 def IncrementalOTA_InstallEnd(info):
     63   try:
     64     target_bootloader_img = info.target_zip.read("RADIO/bootloader.img")
     65     try:
     66       source_bootloader_img = info.source_zip.read("RADIO/bootloader.img")
     67     except KeyError:
     68       source_bootloader_img = None
     69 
     70     if source_bootloader_img == target_bootloader_img:
     71       print "bootloader unchanged; skipping"
     72     else:
     73       WriteBootloader(info, target_bootloader_img)
     74   except KeyError:
     75     print "no bootloader.img in target target_files; skipping install"
     76 
     77   tf = FindRadio(info.target_zip)
     78   if not tf:
     79     # failed to read TARGET radio image: don't include any radio in update.
     80     print "no radio.img in target target_files; skipping install"
     81     # we have checked the existence of the radio image in
     82     # IncrementalOTA_VerifyEnd(), so it won't reach here.
     83     assert common.OPTIONS.full_radio == False
     84   else:
     85     tf = common.File("radio.img", tf)
     86 
     87     sf = FindRadio(info.source_zip)
     88     if not sf or common.OPTIONS.full_radio:
     89       # failed to read SOURCE radio image or one has specified the option to
     90       # include the whole target radio image.
     91       print("no radio image in source target_files or full_radio specified; "
     92             "installing complete image")
     93       WriteRadio(info, tf.data)
     94     else:
     95       sf = common.File("radio.img", sf)
     96 
     97       if tf.sha1 == sf.sha1:
     98         print "radio image unchanged; skipping"
     99       else:
    100         diff = common.Difference(tf, sf, diff_program="bsdiff")
    101         common.ComputeDifferences([diff])
    102         _, _, d = diff.GetPatch()
    103         if d is None or len(d) > tf.size * common.OPTIONS.patch_threshold:
    104           # computing difference failed, or difference is nearly as
    105           # big as the target:  simply send the target.
    106           WriteRadio(info, tf.data)
    107         else:
    108           common.ZipWriteStr(info.output_zip, "radio.img.p", d)
    109           info.script.Print("Patching radio...")
    110           radio_type, radio_device = common.GetTypeAndDevice(
    111               "/radio", info.info_dict)
    112           info.script.ApplyPatch(
    113               "%s:%s:%d:%s:%d:%s" % (radio_type, radio_device,
    114                                      sf.size, sf.sha1, tf.size, tf.sha1),
    115               "-", tf.size, tf.sha1, sf.sha1, "radio.img.p")
    116 
    117 
    118 def WriteRadio(info, radio_img):
    119   info.script.Print("Writing radio...")
    120   common.ZipWriteStr(info.output_zip, "radio.img", radio_img)
    121   _, device = common.GetTypeAndDevice("/radio", info.info_dict)
    122   info.script.AppendExtra(
    123       'package_extract_file("radio.img", "%s");' % (device,))
    124 
    125 
    126 # /* msm8992 bootloader.img format */
    127 #
    128 # #define BOOTLDR_MAGIC "BOOTLDR!"
    129 # #define BOOTLDR_MAGIC_SIZE 8
    130 #
    131 # struct bootloader_images_header {
    132 #         char magic[BOOTLDR_MAGIC_SIZE];
    133 #         unsigned int num_images;
    134 #         unsigned int start_offset;
    135 #         unsigned int bootldr_size;
    136 #         struct {
    137 #                 char name[64];
    138 #                 unsigned int size;
    139 #         } img_info[];
    140 # };
    141 #
    142 def ParseBootloaderHeader(bootloader):
    143   header_fmt = "<8sIII"
    144   header_size = struct.calcsize(header_fmt)
    145   magic, num_images, start_offset, bootloader_size = struct.unpack(
    146       header_fmt, bootloader[:header_size])
    147   assert magic == "BOOTLDR!", "bootloader.img bad magic value"
    148 
    149   img_info_fmt = "<64sI"
    150   img_info_size = struct.calcsize(img_info_fmt)
    151 
    152   imgs = [struct.unpack(img_info_fmt,
    153                         bootloader[header_size+i*img_info_size:
    154                                    header_size+(i+1)*img_info_size])
    155           for i in range(num_images)]
    156 
    157   p = start_offset
    158   img_dict = {}
    159   for name, size in imgs:
    160     img_dict[trunc_to_null(name)] = p, size
    161     p += size
    162   assert p - start_offset == bootloader_size, "bootloader.img corrupted"
    163 
    164   return img_dict
    165 
    166 
    167 # bullhead's bootloader.img contains 11 separate images.
    168 # Each goes to its own partition:
    169 #    sbl1, tz, rpm, aboot, sdi, imgdata, pmic, hyp, sec, keymaster, cmnlib
    170 #
    171 # bullhead also has 8 backup partitions:
    172 #    sbl1, tz, rpm, aboot, pmic, hyp, keymaster, cmnlib
    173 #
    174 release_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib"
    175 debug_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib"
    176 release_nobackup_partitions = "sdi imgdata sec"
    177 debug_nobackup_partitions = "sdi imgdata sec"
    178 
    179 
    180 def WriteBootloader(info, bootloader):
    181   info.script.Print("Writing bootloader...")
    182 
    183   img_dict = ParseBootloaderHeader(bootloader)
    184 
    185   common.ZipWriteStr(info.output_zip, "bootloader-flag.txt",
    186                      "updating-bootloader" + "\0" * 13)
    187   common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32)
    188 
    189   _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict)
    190 
    191   info.script.AppendExtra(
    192       'package_extract_file("bootloader-flag.txt", "%s");' % (misc_device,))
    193 
    194   # failed sbl updates, may render the handset unusable/unrestorable.
    195   # Hence adopt below strategy for updates,enabling restore at all times.
    196   # 1. Flash backup partitions
    197   # 2. patch secondary pte's to swap primary/backup, and enable secondary gpt
    198   # 3. Flash psuedo-backup partions, effectively flashing primary partitions
    199   # 4. restore secondary pte's and restore primary gpt
    200   # 5. Flash all other non backup partitions
    201   #
    202   # Depending on the build fingerprint, we can decide which partitions
    203   # to update.
    204   fp = info.info_dict["build.prop"]["ro.build.fingerprint"]
    205   if "release-keys" in fp:
    206     to_bkp_flash = release_backup_partitions.split()
    207     to_flash = release_nobackup_partitions.split()
    208   else:
    209     to_bkp_flash = debug_backup_partitions.split()
    210     to_flash = debug_nobackup_partitions.split()
    211 
    212   # Write the images to separate files in the OTA package
    213   # and flash backup partitions
    214   for i in to_bkp_flash:
    215     try:
    216       _, device = common.GetTypeAndDevice("/"+i+"bak", info.info_dict)
    217     except KeyError:
    218       print "skipping flash of %s; not in recovery.fstab" % (i,)
    219       continue
    220     common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,),
    221                        bootloader[img_dict[i][0]:
    222                                   img_dict[i][0]+img_dict[i][1]])
    223 
    224     info.script.AppendExtra(
    225         'package_extract_file("bootloader.%s.img", "%s");' % (i, device))
    226 
    227   target_device = info.info_dict["build.prop"]["ro.product.device"]
    228   # swap ptes in secondary and force secondary gpt
    229   info.script.AppendExtra("lge_"+target_device+"_update_gpt();")
    230 
    231   # flash again after swap, effectively flashing primary
    232   # pte's are not re-read, hence primary is psuedo-secondary
    233   for i in to_bkp_flash:
    234     try:
    235       _, device = common.GetTypeAndDevice("/"+i, info.info_dict)
    236     except KeyError:
    237       print "skipping flash of %s; not in recovery.fstab" % (i,)
    238       continue
    239     info.script.AppendExtra(
    240         'package_extract_file("bootloader.%s.img", "%s");' % (i, device))
    241 
    242   # restore secondary gpt for correct mappings and enable primary gpt
    243   info.script.AppendExtra("lge_"+target_device+"_recover_gpt();")
    244 
    245   # Write the images to separate files in the OTA package
    246   for i in to_flash:
    247     try:
    248       _, device = common.GetTypeAndDevice("/"+i, info.info_dict)
    249     except KeyError:
    250       print "skipping flash of %s; not in recovery.fstab" % (i,)
    251       continue
    252     common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,),
    253                        bootloader[img_dict[i][0]:
    254                                   img_dict[i][0]+img_dict[i][1]])
    255 
    256     info.script.AppendExtra(
    257         'package_extract_file("bootloader.%s.img", "%s");' % (i, device))
    258 
    259   info.script.AppendExtra(
    260       'package_extract_file("bootloader-flag-clear.txt", "%s");' %
    261       (misc_device,))
    262 
    263 
    264 def trunc_to_null(s):
    265   if '\0' in s:
    266     return s[:s.index('\0')]
    267   else:
    268     return s
    269