Home | History | Annotate | Download | only in marlin
      1 # Copyright (C) 2009 The Android Open Source Project
      2 # Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
      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 """Emit commands needed for QCOM devices during OTA installation
     17 (installing the radio image)."""
     18 
     19 import common
     20 import re
     21 
     22 
     23 bootImages = {}
     24 binImages = {}
     25 fwImages = {}
     26 
     27 
     28 # Parse filesmap file containing firmware residing places
     29 def LoadFilesMap(zip, name="RADIO/filesmap"):
     30   try:
     31     data = zip.read(name)
     32   except KeyError:
     33     print "Warning: could not find %s in %s." % (name, zip)
     34     data = ""
     35   d = {}
     36   for line in data.split("\n"):
     37     line = line.strip()
     38     if not line or line.startswith("#"):
     39       continue
     40     pieces = line.split()
     41     if not (len(pieces) == 2):
     42       raise ValueError("malformed filesmap line: \"%s\"" % (line,))
     43     d[pieces[0]] = pieces[1]
     44   return d
     45 
     46 
     47 # Read firmware images from target files zip
     48 def GetRadioFiles(z):
     49   out = {}
     50   for info in z.infolist():
     51     f = info.filename
     52     if f.startswith("RADIO/") and (f.__len__() > len("RADIO/")):
     53       fn = f[6:]
     54       if fn.startswith("filesmap"):
     55         continue
     56       data = z.read(f)
     57       out[fn] = common.File(f, data)
     58   return out
     59 
     60 
     61 # Get firmware residing place from filesmap
     62 def GetFileDestination(fn, filesmap):
     63   # if file is encoded disregard the .enc extention
     64   if fn.endswith('.enc'):
     65     fn = fn[:-4]
     66 
     67   # get backup destination as well if present
     68   backup = None
     69   if fn + ".bak" in filesmap:
     70     backup = filesmap[fn + ".bak"]
     71 
     72   # If full filename is not specified in filesmap get only the name part
     73   # and look for this token
     74   if fn not in filesmap:
     75     fn = fn.split(".")[0] + ".*"
     76     if fn not in filesmap:
     77       print "warning radio-update: '%s' not found in filesmap" % (fn)
     78       return None, backup
     79   return filesmap[fn], backup
     80 
     81 
     82 # Separate image types as each type needs different handling
     83 def SplitFwTypes(files):
     84   boot = {}
     85   bin = {}
     86   fw = {}
     87 
     88   for f in files:
     89     extIdx = -1
     90     dotSeparated = f.split(".")
     91     while True:
     92       if dotSeparated[extIdx] != 'p' and dotSeparated[extIdx] != 'enc':
     93         break
     94       extIdx -= 1
     95 
     96     if dotSeparated[extIdx] == 'mbn' or dotSeparated[extIdx] == 'elf':
     97       boot[f] = files[f]
     98     elif dotSeparated[extIdx] == 'bin':
     99       bin[f] = files[f]
    100     else:
    101       fw[f] = files[f]
    102   return boot, bin, fw
    103 
    104 
    105 # Prepare radio-update files and verify them
    106 def OTA_VerifyEnd(info, api_version, target_zip, source_zip=None):
    107   if api_version < 3:
    108     print "warning radio-update: no support for api_version less than 3"
    109     return False
    110 
    111   print "Loading radio filesmap..."
    112   filesmap = LoadFilesMap(target_zip)
    113   if filesmap == {}:
    114     print "warning radio-update: no or invalid filesmap file found"
    115     return False
    116 
    117   print "Loading radio target..."
    118   tgt_files = GetRadioFiles(target_zip)
    119   if tgt_files == {}:
    120     print "warning radio-update: no radio images in input target_files"
    121     return False
    122 
    123   src_files = None
    124   if source_zip is not None:
    125     print "Loading radio source..."
    126     src_files = GetRadioFiles(source_zip)
    127 
    128   update_list = {}
    129   largest_source_size = 0
    130 
    131   print "Preparing radio-update files..."
    132   for fn in tgt_files:
    133     dest, destBak = GetFileDestination(fn, filesmap)
    134     if dest is None:
    135       continue
    136 
    137     tf = tgt_files[fn]
    138     sf = None
    139     if src_files is not None:
    140       sf = src_files.get(fn, None)
    141 
    142     full = sf is None or fn.endswith('.enc')
    143     if not full:
    144       # no difference - skip this file
    145       if tf.sha1 == sf.sha1:
    146         continue
    147       d = common.Difference(tf, sf)
    148       _, _, d = d.ComputePatch()
    149       # no difference - skip this file
    150       if d is None:
    151         continue
    152       # if patch is almost as big as the file - don't bother patching
    153       full = len(d) > tf.size * common.OPTIONS.patch_threshold
    154       if not full:
    155         f = "patch/firmware-update/" + fn + ".p"
    156         common.ZipWriteStr(info.output_zip, f, d)
    157         update_list[f] = (dest, destBak, tf, sf)
    158         largest_source_size = max(largest_source_size, sf.size)
    159     if full:
    160       f = "firmware-update/" + fn
    161       common.ZipWriteStr(info.output_zip, f, tf.data)
    162       update_list[f] = (dest, destBak, None, None)
    163 
    164   global bootImages
    165   global binImages
    166   global fwImages
    167   bootImages, binImages, fwImages = SplitFwTypes(update_list)
    168 
    169   # If there are incremental patches verify them
    170   if largest_source_size != 0:
    171     info.script.Comment("---- radio update verification ----")
    172     info.script.Print("Verifying radio-update...")
    173 
    174     for f in bootImages:
    175       dest, destBak, tf, sf = bootImages[f]
    176       # Not incremental
    177       if sf is None:
    178         continue
    179       info.script.PatchCheck("EMMC:%s:%d:%s:%d:%s" %
    180               (dest, sf.size, sf.sha1, tf.size, tf.sha1))
    181       if destBak is not None:
    182         info.script.PatchCheck("EMMC:%s:%d:%s:%d:%s" %
    183                 (destBak, sf.size, sf.sha1, tf.size, tf.sha1))
    184     for f in binImages:
    185       dest, destBak, tf, sf = binImages[f]
    186       # Not incremental
    187       if sf is None:
    188         continue
    189       info.script.PatchCheck("EMMC:%s:%d:%s:%d:%s" %
    190               (dest, sf.size, sf.sha1, tf.size, tf.sha1))
    191 
    192     last_mounted = ""
    193     for f in fwImages:
    194       dest, destBak, tf, sf = fwImages[f]
    195       # Not incremental
    196       if sf is None:
    197         continue
    198       # Get the filename without the path and the patch (.p) extention
    199       f = f.split("/")[-1][:-2]
    200       # Parse filesmap destination paths for "/dev/" pattern in the beginng.
    201       # This would mean that the file must be written to block device -
    202       # fs mount needed
    203       if dest.startswith("/dev/"):
    204         if last_mounted != dest:
    205           info.script.AppendExtra('unmount("/firmware");')
    206           info.script.AppendExtra('mount("vfat", "EMMC", "%s", "/firmware");' %
    207                                     (dest))
    208           last_mounted = dest
    209         dest = "/firmware/image/" + f
    210       else:
    211         dest = dest + "/" + f
    212       info.script.PatchCheck(dest, tf.sha1, sf.sha1)
    213 
    214     info.script.CacheFreeSpaceCheck(largest_source_size)
    215   return True
    216 
    217 
    218 def FullOTA_Assertions(info):
    219   #TODO: Implement device specific asserstions.
    220   return
    221 
    222 
    223 def IncrementalOTA_Assertions(info):
    224   #TODO: Implement device specific asserstions.
    225   return
    226 
    227 
    228 def IncrementalOTA_VerifyEnd(info):
    229  OTA_VerifyEnd(info, info.target_version, info.target_zip, info.source_zip)
    230  return
    231 
    232 
    233 # This function handles only non-HLOS whole partition images
    234 def InstallRawImage(script, f, dest, tf, sf):
    235   if f.endswith('.p'):
    236     script.ApplyPatch("EMMC:%s:%d:%s:%d:%s" %
    237                         (dest, sf.size, sf.sha1, tf.size, tf.sha1),
    238                         "-", tf.size, tf.sha1, sf.sha1, f)
    239   elif f.endswith('.enc'):
    240     # Get the filename without the path
    241     fn = f.split("/")[-1]
    242     script.AppendExtra('package_extract_file("%s", "/tmp/%s");' % (f, fn))
    243     script.AppendExtra('msm.decrypt("/tmp/%s", "%s");' % (fn, dest))
    244   else:
    245     script.AppendExtra('package_extract_file("%s", "%s");' % (f, dest))
    246   return
    247 
    248 
    249 # This function handles only non-HLOS boot images - files list must contain
    250 # only such images (aboot, tz, etc)
    251 def InstallBootImages(script, files):
    252   bakExists = False
    253   # update main partitions
    254   script.AppendExtra('ifelse(msm.boot_update("main"), (')
    255   for f in files:
    256     dest, destBak, tf, sf = files[f]
    257     if destBak is not None:
    258       bakExists = True
    259     InstallRawImage(script, f, dest, tf, sf)
    260   script.AppendExtra('), "");')
    261 
    262   # update backup partitions
    263   if bakExists:
    264     script.AppendExtra('ifelse(msm.boot_update("backup"), (')
    265     for f in files:
    266       dest, destBak, tf, sf = files[f]
    267       if destBak is not None:
    268         InstallRawImage(script, f, destBak, tf, sf)
    269     script.AppendExtra('), "");')
    270   # just finalize primary update stage
    271   else:
    272     script.AppendExtra('msm.boot_update("backup");')
    273 
    274   # finalize partitions update
    275   script.AppendExtra('msm.boot_update("finalize");')
    276   return
    277 
    278 
    279 # This function handles only non-HLOS bin images
    280 def InstallBinImages(script, files):
    281   for f in files:
    282     dest, _, tf, sf = files[f]
    283     InstallRawImage(script, f, dest, tf, sf)
    284   return
    285 
    286 
    287 # This function handles only non-HLOS firmware files that are not whole
    288 # partition images (modem, dsp, etc)
    289 def InstallFwImages(script, files):
    290   last_mounted = ""
    291 
    292   for f in files:
    293     dest, _, tf, sf = files[f]
    294     # Get the filename without the path
    295     fn = f.split("/")[-1]
    296     # Parse filesmap destination paths for "/dev/" pattern in the beginng.
    297     # This would mean that the file must be written to block device -
    298     # fs mount needed
    299     if dest.startswith("/dev/"):
    300       if last_mounted != dest:
    301         script.AppendExtra('unmount("/firmware");')
    302         script.AppendExtra('mount("vfat", "EMMC", "%s", "/firmware");' %
    303                             (dest))
    304         last_mounted = dest
    305       dest = "/firmware/image/" + fn
    306     else:
    307       dest = dest + "/" + fn
    308 
    309     if f.endswith('.p'):
    310       script.ApplyPatch(dest[:-2], "-", tf.size, tf.sha1, sf.sha1, f)
    311     elif f.endswith('.enc'):
    312       script.AppendExtra('package_extract_file("%s", "/tmp/%s");' % (f, fn))
    313       script.AppendExtra('msm.decrypt("/tmp/%s", "%s");' % (fn, dest[:-4]))
    314     else:
    315       script.AppendExtra('package_extract_file("%s", "%s");' % (f, dest))
    316 
    317   if last_mounted != "":
    318     script.AppendExtra('unmount("/firmware");')
    319   return
    320 
    321 
    322 def OTA_InstallEnd(info):
    323   print "Applying radio-update script modifications..."
    324   info.script.Comment("---- radio update tasks ----")
    325   info.script.Print("Patching firmware images...")
    326 
    327   if bootImages != {}:
    328     InstallBootImages(info.script, bootImages)
    329   if binImages != {}:
    330     InstallBinImages(info.script, binImages)
    331   if fwImages != {}:
    332     InstallFwImages(info.script, fwImages)
    333   return
    334 
    335 
    336 def FullOTA_InstallEnd_MMC(info):
    337   if OTA_VerifyEnd(info, info.input_version, info.input_zip):
    338     OTA_InstallEnd(info)
    339   return
    340 
    341 
    342 def FullOTA_InstallEnd_MTD(info):
    343   print "warning radio-update: radio update for NAND devices not supported"
    344   return
    345 
    346 
    347 def FullOTA_InstallEnd(info):
    348   FullOTA_InstallEnd_MMC(info)
    349   return
    350 
    351 def IncrementalOTA_InstallEnd_MMC(info):
    352   OTA_InstallEnd(info)
    353   return
    354 
    355 
    356 def IncrementalOTA_InstallEnd_MTD(info):
    357   print "warning radio-update: radio update for NAND devices not supported"
    358   return
    359 
    360 def IncrementalOTA_InstallEnd(info):
    361   IncrementalOTA_InstallEnd_MMC(info)
    362   return
    363