Home | History | Annotate | Download | only in angler
      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 # The target does not support OTA-flashing
     21 # the partition table, so blacklist it.
     22 DEFAULT_BOOTLOADER_OTA_BLACKLIST = ['partition']
     23 
     24 
     25 class BadMagicError(Exception):
     26   __str__ = "bad magic value"
     27 
     28 #
     29 # Huawei Bootloader packed image format
     30 #
     31 # typedef struct meta_header {
     32 #  u32   magic;             /* 0xce1ad63c */
     33 #  u16   major_version;     /* (0x1)-reject images with higher major versions */
     34 #  u16   minor_version;     /* (0x0)-allow images with higer minor versions */
     35 #  char  img_version[64];   /* Top level version for images in this meta */
     36 #  u16   meta_hdr_sz;       /* size of this header */
     37 #  u16   img_hdr_sz;        /* size of img_header_entry list */
     38 # } meta_header_t;
     39 
     40 # typedef struct img_header_entry {
     41 #  char   ptn_name[MAX_GPT_NAME_SIZE];
     42 #  u32    start_offset;
     43 #  u32    size;
     44 # } img_header_entry_t
     45 
     46 
     47 MAGIC = 0xce1ad63c
     48 
     49 
     50 class HuaweiBootImage(object):
     51 
     52   def __init__(self, data, name=None):
     53     self.name = name
     54     self.unpacked_images = None
     55     self._unpack(data)
     56 
     57   def _unpack(self, data):
     58     """Unpack the data blob as a Huawei boot image and return the list
     59     of contained image objects"""
     60     num_imgs_fmt = struct.Struct("<IHH64sHH")
     61     header = data[0:num_imgs_fmt.size]
     62     info = {}
     63     (info["magic"], info["major_version"],
     64      info["minor_version"], info["img_version"],
     65      info["meta_hdr_size"], info["img_hdr_size"]) = num_imgs_fmt.unpack(header)
     66 
     67     img_info_format = "<72sLL"
     68     img_info_size = struct.calcsize(img_info_format)
     69     num = info["img_hdr_size"] / img_info_size
     70     size = num_imgs_fmt.size
     71     imgs = [
     72          struct.unpack(
     73              img_info_format,
     74              data[size + i * img_info_size:size + (i + 1) * img_info_size])
     75          for i in range(num)
     76     ]
     77 
     78     if info["magic"] != MAGIC:
     79       raise BadMagicError
     80 
     81     img_objs = {}
     82     for name, start, end in imgs:
     83       if TruncToNull(name):
     84         img = common.File(TruncToNull(name), data[start:start + end])
     85         img_objs[img.name] = img
     86 
     87     self.unpacked_images = img_objs
     88 
     89   def GetUnpackedImage(self, name):
     90     return self.unpacked_images.get(name)
     91 
     92 
     93 def FindRadio(zipfile):
     94   try:
     95     return zipfile.read("RADIO/radio.img")
     96   except KeyError:
     97     return None
     98 
     99 
    100 def FullOTA_InstallEnd(info):
    101   try:
    102     bootloader_img = info.input_zip.read("RADIO/bootloader.img")
    103   except KeyError:
    104     print "no bootloader.img in target_files; skipping install"
    105   else:
    106     WriteBootloader(info, bootloader_img)
    107 
    108   radio_img = FindRadio(info.input_zip)
    109   if radio_img:
    110     WriteRadio(info, radio_img)
    111   else:
    112     print "no radio.img in target_files; skipping install"
    113 
    114 
    115 def IncrementalOTA_VerifyEnd(info):
    116   target_radio_img = FindRadio(info.target_zip)
    117   source_radio_img = FindRadio(info.source_zip)
    118   if not target_radio_img or not source_radio_img:
    119     return
    120   target_modem_img = HuaweiBootImage(target_radio_img).GetUnpackedImage("modem")
    121   if not target_modem_img:
    122     return
    123   source_modem_img = HuaweiBootImage(source_radio_img).GetUnpackedImage("modem")
    124   if not source_modem_img:
    125     return
    126   if target_modem_img.sha1 != source_modem_img.sha1:
    127     info.script.CacheFreeSpaceCheck(len(source_modem_img.data))
    128     radio_type, radio_device = common.GetTypeAndDevice("/modem", info.info_dict)
    129     info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % (
    130         radio_type, radio_device,
    131         len(source_modem_img.data), source_modem_img.sha1,
    132         len(target_modem_img.data), target_modem_img.sha1))
    133 
    134 
    135 def IncrementalOTA_InstallEnd(info):
    136   try:
    137     target_bootloader_img = info.target_zip.read("RADIO/bootloader.img")
    138     try:
    139       source_bootloader_img = info.source_zip.read("RADIO/bootloader.img")
    140     except KeyError:
    141       source_bootloader_img = None
    142 
    143     if source_bootloader_img == target_bootloader_img:
    144       print "bootloader unchanged; skipping"
    145     elif source_bootloader_img == None:
    146       print "no bootloader in source target_files; installing complete image"
    147       WriteBootloader(info, target_bootloader_img)
    148     else:
    149       tf = common.File("bootloader.img", target_bootloader_img)
    150       sf = common.File("bootloader.img", source_bootloader_img)
    151       WriteIncrementalBootloader(info, tf, sf)
    152   except KeyError:
    153     print "no bootloader.img in target target_files; skipping install"
    154 
    155   target_radio_image = FindRadio(info.target_zip)
    156   if not target_radio_image:
    157     # failed to read TARGET radio image: don't include any radio in update.
    158     print "no radio.img in target target_files; skipping install"
    159   else:
    160     tf = common.File("radio.img", target_radio_image)
    161 
    162     source_radio_image = FindRadio(info.source_zip)
    163     if not source_radio_image:
    164       # failed to read SOURCE radio image: include the whole target
    165       # radio image.
    166       print "no radio image in source target_files; installing complete image"
    167       WriteRadio(info, tf.data)
    168     else:
    169       sf = common.File("radio.img", source_radio_image)
    170 
    171       if tf.size == sf.size and tf.sha1 == sf.sha1:
    172         print "radio image unchanged; skipping"
    173       else:
    174         WriteIncrementalRadio(info, tf, sf)
    175 
    176 
    177 def WriteIncrementalBootloader(info, target_imagefile, source_imagefile):
    178   try:
    179     tm = HuaweiBootImage(target_imagefile.data, "bootloader")
    180   except BadMagicError:
    181     raise ValueError("bootloader.img bad magic value")
    182   try:
    183     sm = HuaweiBootImage(source_imagefile.data, "bootloader")
    184   except BadMagicError:
    185     print "source bootloader is not a Huawei boot img; installing complete img."
    186     return WriteBootloader(info, target_imagefile.data)
    187 
    188   # blacklist any partitions that match the source image
    189   blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST
    190   for ti in tm.unpacked_images.values():
    191     if ti not in blacklist:
    192       si = sm.GetUnpackedImage(ti.name)
    193       if not si:
    194         continue
    195       if ti.size == si.size and ti.sha1 == si.sha1:
    196         print "target bootloader partition img %s matches source; skipping" % (
    197             ti.name)
    198         blacklist.append(ti.name)
    199 
    200   # If there are any images to then write them
    201   whitelist = [i.name for i in tm.unpacked_images.values()
    202                if i.name not in blacklist]
    203   if len(whitelist):
    204     # Install the bootloader, skipping any matching partitions
    205     WriteBootloader(info, target_imagefile.data, blacklist)
    206 
    207 
    208 def WriteIncrementalRadio(info, target_imagefile, source_imagefile):
    209   try:
    210     target_radio_img = HuaweiBootImage(target_imagefile.data, "radio")
    211   except BadMagicError:
    212     print "Magic number mismatch in target radio image"
    213     raise ValueError("radio.img bad magic value")
    214 
    215   try:
    216     source_radio_img = HuaweiBootImage(source_imagefile.data, "radio")
    217   except BadMagicError:
    218     print "Magic number mismatch in source radio image"
    219     source_radio_img = None
    220 
    221   write_full_modem = True
    222   if source_radio_img:
    223     target_modem_img = target_radio_img.GetUnpackedImage("modem")
    224     if target_modem_img:
    225       source_modem_img = source_radio_img.GetUnpackedImage("modem")
    226       if source_modem_img:
    227         WriteIncrementalModemPartition(info, target_modem_img, source_modem_img)
    228         write_full_modem = False
    229 
    230   # Write the full images, skipping modem if so directed.
    231   #
    232   # NOTE: Some target flex radio images are zero-filled, and must
    233   #       be flashed to trigger the flex update "magic".  Do not
    234   #       skip installing target partition images that are identical
    235   #       to its corresponding source partition image.
    236   blacklist = []
    237   if not write_full_modem:
    238     blacklist.append("modem")
    239   WriteHuaweiBootPartitionImages(info, target_radio_img, blacklist)
    240 
    241 
    242 def WriteIncrementalModemPartition(info, target_modem_image,
    243                                    source_modem_image):
    244   tf = target_modem_image
    245   sf = source_modem_image
    246   pad_tf = False
    247   pad_sf = False
    248   blocksize = 4096
    249 
    250   partial_tf = len(tf.data) % blocksize
    251   partial_sf = len(sf.data) % blocksize
    252 
    253   if partial_tf:
    254     pad_tf = True
    255   if partial_sf:
    256     pad_sf = True
    257   b = common.BlockDifference("modem", common.DataImage(tf.data, False, pad_tf),
    258                              common.DataImage(sf.data, False, pad_sf))
    259   b.WriteScript(info.script, info.output_zip)
    260 
    261 
    262 def WriteRadio(info, radio_img):
    263   info.script.Print("Writing radio...")
    264 
    265   try:
    266     huawei_boot_image = HuaweiBootImage(radio_img, "radio")
    267   except BadMagicError:
    268     raise ValueError("radio.img bad magic value")
    269 
    270   WriteHuaweiBootPartitionImages(info, huawei_boot_image)
    271 
    272 
    273 def WriteHuaweiBootPartitionImages(info, huawei_boot_image, blacklist=None):
    274   if blacklist is None:
    275     blacklist = []
    276   WriteGroupedImages(info, huawei_boot_image.name,
    277                      huawei_boot_image.unpacked_images.values(), blacklist)
    278 
    279 
    280 def WriteGroupedImages(info, group_name, images, blacklist=None):
    281   """Write a group of partition images to the OTA package,
    282   and add the corresponding flash instructions to the recovery
    283   script.  Skip any images that do not have a corresponding
    284   entry in recovery.fstab."""
    285   if blacklist is None:
    286     blacklist = []
    287   for i in images:
    288     if i.name not in blacklist:
    289       WritePartitionImage(info, i, group_name)
    290 
    291 
    292 def WritePartitionImage(info, image, group_name=None):
    293   filename = "%s.img" % image.name
    294   if group_name:
    295     filename = "%s.%s" % (group_name, filename)
    296 
    297   try:
    298     info.script.Print("writing partition image %s" % image.name)
    299     _, device = common.GetTypeAndDevice("/" + image.name, info.info_dict)
    300   except KeyError:
    301     print "skipping flash of %s; not in recovery.fstab" % image.name
    302     return
    303 
    304   common.ZipWriteStr(info.output_zip, filename, image.data)
    305 
    306   info.script.AppendExtra('package_extract_file("%s", "%s");' %
    307                           (filename, device))
    308 
    309 
    310 def WriteBootloader(info, bootloader, blacklist=None):
    311   if blacklist is None:
    312     blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST
    313   info.script.Print("Writing bootloader...")
    314   try:
    315     huawei_boot_image = HuaweiBootImage(bootloader, "bootloader")
    316   except BadMagicError:
    317     raise ValueError("bootloader.img bad magic value")
    318 
    319   common.ZipWriteStr(info.output_zip, "bootloader-flag.txt",
    320                      "updating-bootloader" + "\0" * 13)
    321   common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32)
    322 
    323   _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict)
    324 
    325   info.script.AppendExtra(
    326       'package_extract_file("bootloader-flag.txt", "%s");' % misc_device)
    327 
    328   # OTA does not support partition changes, so
    329   # do not bundle the partition image in the OTA package.
    330   WriteHuaweiBootPartitionImages(info, huawei_boot_image, blacklist)
    331 
    332   info.script.AppendExtra(
    333       'package_extract_file("bootloader-flag-clear.txt", "%s");' % misc_device)
    334 
    335 
    336 def TruncToNull(s):
    337   if '\0' in s:
    338     return s[:s.index('\0')]
    339   else:
    340     return s
    341