Home | History | Annotate | Download | only in shamu
      1 import common
      2 import struct
      3 
      4 # The target does not support OTA-flashing
      5 # the partition table, so blacklist it.
      6 DEFAULT_BOOTLOADER_OTA_BLACKLIST = [ 'partition' ]
      7 
      8 class BadMagicError(Exception):
      9     __str__ = "bad magic value"
     10 
     11 #
     12 # Motoboot packed image format
     13 #
     14 # #define BOOTLDR_MAGIC "MBOOTV1"
     15 # #define HEADER_SIZE 1024
     16 # #define SECTOR_SIZE 512
     17 # struct packed_images_header {
     18 #         unsigned int num_images;
     19 #         struct {
     20 #                 char name[24];
     21 #                 unsigned int start;  // start offset = HEADER_SIZE + start * SECTOR_SIZE
     22 #                 unsigned int end;    // end offset = HEADER_SIZE + (end + 1) * SECTOR_SIZE - 1
     23 #         } img_info[20];
     24 #         char magic[8];  // set to BOOTLDR_MAGIC
     25 # };
     26 HEADER_SIZE = 1024
     27 SECTOR_SIZE = 512
     28 NUM_MAX_IMAGES = 20
     29 MAGIC = "MBOOTV1\0"
     30 class MotobootImage(object):
     31 
     32   def __init__(self, data, name = None):
     33 
     34     self.name = name
     35     self._unpack(data)
     36 
     37   def _unpack(self, data):
     38     """ Unpack the data blob as a motoboot image and return the list
     39     of contained image objects"""
     40     num_imgs_fmt = "<L"
     41     num_imgs_size = struct.calcsize(num_imgs_fmt)
     42     num_imgs, = struct.unpack(num_imgs_fmt, data[:num_imgs_size])
     43 
     44     img_info_format = "<24sLL"
     45     img_info_size = struct.calcsize(img_info_format)
     46 
     47     imgs = [ struct.unpack(img_info_format, data[num_imgs_size + i*img_info_size:num_imgs_size + (i+1)*img_info_size]) for i in range(num_imgs) ]
     48 
     49     magic_format = "<8s"
     50     magic_size = struct.calcsize(magic_format)
     51     magic, = struct.unpack(magic_format, data[num_imgs_size + NUM_MAX_IMAGES*img_info_size:num_imgs_size + NUM_MAX_IMAGES*img_info_size + magic_size])
     52     if magic != MAGIC:
     53       raise BadMagicError
     54 
     55     img_objs = []
     56     for name, start, end in imgs:
     57       start_offset = HEADER_SIZE + start * SECTOR_SIZE
     58       end_offset = HEADER_SIZE + (end + 1) * SECTOR_SIZE - 1
     59       img = common.File(trunc_to_null(name), data[start_offset:end_offset+1])
     60       img_objs.append(img)
     61 
     62     self.unpacked_images = img_objs
     63 
     64   def GetUnpackedImage(self, name):
     65 
     66     found_image = None
     67     for image in self.unpacked_images:
     68       if image.name == name:
     69           found_image = image
     70           break
     71     return found_image
     72 
     73 
     74 def FindRadio(zipfile):
     75   try:
     76     return zipfile.read("RADIO/radio.img")
     77   except KeyError:
     78     return None
     79 
     80 def FullOTA_InstallEnd(info):
     81   try:
     82     bootloader_img = info.input_zip.read("RADIO/bootloader.img")
     83   except KeyError:
     84     print "no bootloader.img in target_files; skipping install"
     85   else:
     86     WriteBootloader(info, bootloader_img)
     87 
     88   radio_img = FindRadio(info.input_zip)
     89   if radio_img:
     90     WriteRadio(info, radio_img)
     91   else:
     92     print "no radio.img in target_files; skipping install"
     93 
     94 def IncrementalOTA_VerifyEnd(info):
     95   target_radio_img = FindRadio(info.target_zip)
     96   if common.OPTIONS.full_radio:
     97     if not target_radio_img:
     98       assert False, "full radio option specified but no radio img found"
     99     else:
    100       return
    101   source_radio_img = FindRadio(info.source_zip)
    102   if not target_radio_img or not source_radio_img:
    103     return
    104   target_modem_img = MotobootImage(target_radio_img).GetUnpackedImage("modem")
    105   if not target_modem_img: return
    106   source_modem_img = MotobootImage(source_radio_img).GetUnpackedImage("modem")
    107   if not source_modem_img: return
    108   if target_modem_img.sha1 != source_modem_img.sha1:
    109     info.script.CacheFreeSpaceCheck(len(source_modem_img.data))
    110     radio_type, radio_device = common.GetTypeAndDevice("/modem", info.info_dict)
    111     info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % (
    112         radio_type, radio_device,
    113         len(source_modem_img.data), source_modem_img.sha1,
    114         len(target_modem_img.data), target_modem_img.sha1))
    115 
    116 def IncrementalOTA_InstallEnd(info):
    117   try:
    118     target_bootloader_img = info.target_zip.read("RADIO/bootloader.img")
    119     try:
    120       source_bootloader_img = info.source_zip.read("RADIO/bootloader.img")
    121     except KeyError:
    122       source_bootloader_img = None
    123 
    124     if source_bootloader_img == target_bootloader_img:
    125       print "bootloader unchanged; skipping"
    126     elif source_bootloader_img == None:
    127       print "no bootloader.img in source target_files; installing complete image"
    128       WriteBootloader(info, target_bootloader_img)
    129     else:
    130       tf = common.File("bootloader.img", target_bootloader_img)
    131       sf = common.File("bootloader.img", source_bootloader_img)
    132       WriteIncrementalBootloader(info, tf, sf)
    133   except KeyError:
    134     print "no bootloader.img in target target_files; skipping install"
    135 
    136   tf = FindRadio(info.target_zip)
    137   if not tf:
    138     # failed to read TARGET radio image: don't include any radio in update.
    139     print "no radio.img in target target_files; skipping install"
    140     # we have checked the existence of the radio image in
    141     # IncrementalOTA_VerifyEnd(), so it won't reach here.
    142     assert common.OPTIONS.full_radio == False
    143   else:
    144     tf = common.File("radio.img", tf)
    145 
    146     sf = FindRadio(info.source_zip)
    147     if not sf or common.OPTIONS.full_radio:
    148       # failed to read SOURCE radio image or one has specified the option to
    149       # include the whole target radio image.
    150       print("no radio image in source target_files or full_radio specified; "
    151             "installing complete image")
    152       WriteRadio(info, tf.data)
    153     else:
    154       sf = common.File("radio.img", sf)
    155 
    156       if tf.size == sf.size and tf.sha1 == sf.sha1:
    157         print "radio image unchanged; skipping"
    158       else:
    159         WriteIncrementalRadio(info, tf, sf)
    160 
    161 def WriteIncrementalBootloader(info, target_imagefile, source_imagefile):
    162   try:
    163     tm = MotobootImage(target_imagefile.data, "bootloader")
    164   except BadMagicError:
    165     assert False, "bootloader.img bad magic value"
    166   try:
    167     sm = MotobootImage(source_imagefile.data, "bootloader")
    168   except BadMagicError:
    169     print "source bootloader image is not a motoboot image. Installing complete image."
    170     return WriteBootloader(info, target_imagefile.data)
    171 
    172   # blacklist any partitions that match the source image
    173   blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST
    174   for ti in tm.unpacked_images:
    175     if ti not in blacklist:
    176       si = sm.GetUnpackedImage(ti.name)
    177       if not si:
    178         continue
    179       if ti.size == si.size and ti.sha1 == si.sha1:
    180         print "target bootloader partition image %s matches source; skipping" % ti.name
    181         blacklist.append(ti.name)
    182 
    183   # If there are any images to then write them
    184   whitelist = [ i.name for i in tm.unpacked_images if i.name not in blacklist ]
    185   if len(whitelist):
    186     # Install the bootloader, skipping any matching partitions
    187     WriteBootloader(info, target_imagefile.data, blacklist)
    188 
    189 def WriteIncrementalRadio(info, target_imagefile, source_imagefile):
    190   try:
    191     target_radio_img = MotobootImage(target_imagefile.data, "radio")
    192   except BadMagicError:
    193     assert False, "radio.img bad magic value"
    194 
    195   try:
    196     source_radio_img = MotobootImage(source_imagefile.data, "radio")
    197   except BadMagicError:
    198     source_radio_img = None
    199 
    200   write_full_modem = True
    201   if source_radio_img:
    202     target_modem_img = target_radio_img.GetUnpackedImage("modem")
    203     if target_modem_img:
    204       source_modem_img = source_radio_img.GetUnpackedImage("modem")
    205       if source_modem_img:
    206         WriteIncrementalModemPartition(info, target_modem_img, source_modem_img)
    207         write_full_modem = False
    208 
    209   # Write the full images, skipping modem if so directed.
    210   #
    211   # NOTE: Some target flex radio images are zero-filled, and must
    212   #       be flashed to trigger the flex update "magic".  Do not
    213   #       skip installing target partition images that are identical
    214   #       to its corresponding source partition image.
    215   blacklist = []
    216   if not write_full_modem:
    217     blacklist.append('modem')
    218   WriteMotobootPartitionImages(info, target_radio_img, blacklist)
    219 
    220 def WriteIncrementalModemPartition(info, target_modem_image, source_modem_image):
    221   tf = target_modem_image
    222   sf = source_modem_image
    223 
    224   b = common.BlockDifference("modem", common.DataImage(tf.data),
    225                              common.DataImage(sf.data))
    226 
    227   b.WriteScript(info.script, info.output_zip)
    228 
    229 
    230 def WriteRadio(info, radio_img):
    231   info.script.Print("Writing radio...")
    232 
    233   try:
    234     motoboot_image = MotobootImage(radio_img, "radio")
    235   except BadMagicError:
    236       assert False, "radio.img bad magic value"
    237 
    238   WriteMotobootPartitionImages(info, motoboot_image)
    239 
    240 def WriteMotobootPartitionImages(info, motoboot_image, blacklist = []):
    241   WriteGroupedImages(info, motoboot_image.name, motoboot_image.unpacked_images, blacklist)
    242 
    243 def WriteGroupedImages(info, group_name, images, blacklist = []):
    244   """ Write a group of partition images to the OTA package,
    245   and add the corresponding flash instructions to the recovery
    246   script.  Skip any images that do not have a corresponding
    247   entry in recovery.fstab."""
    248 
    249   for i in images:
    250     if i.name not in blacklist:
    251       WritePartitionImage(info, i, group_name)
    252 
    253 def WritePartitionImage(info, image, group_name = None):
    254 
    255   filename = "%s.img" % image.name
    256   if group_name:
    257     filename = "%s.%s" % (group_name,filename)
    258 
    259   try:
    260     _, device = common.GetTypeAndDevice("/"+image.name, info.info_dict)
    261   except KeyError:
    262     print "skipping flash of %s; not in recovery.fstab" % (image.name,)
    263     return
    264 
    265   common.ZipWriteStr(info.output_zip, filename, image.data)
    266 
    267   info.script.AppendExtra('package_extract_file("%s", "%s");' %
    268                           (filename, device))
    269 
    270 def WriteBootloader(info, bootloader, blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST):
    271   info.script.Print("Writing bootloader...")
    272 
    273   try:
    274     motoboot_image = MotobootImage(bootloader,"bootloader")
    275   except BadMagicError:
    276       assert False, "bootloader.img bad magic value"
    277 
    278   common.ZipWriteStr(info.output_zip, "bootloader-flag.txt",
    279                      "updating-bootloader" + "\0" * 13)
    280   common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32)
    281 
    282   _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict)
    283 
    284   info.script.AppendExtra(
    285       'package_extract_file("bootloader-flag.txt", "%s");' %
    286       (misc_device,))
    287 
    288   # OTA does not support partition changes, so
    289   # do not bundle the partition image in the OTA package.
    290   WriteMotobootPartitionImages(info, motoboot_image, blacklist)
    291 
    292   info.script.AppendExtra(
    293       'package_extract_file("bootloader-flag-clear.txt", "%s");' %
    294       (misc_device,))
    295 
    296 def trunc_to_null(s):
    297   if '\0' in s:
    298     return s[:s.index('\0')]
    299   else:
    300     return s
    301