• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19import common
20import re
21
22
23bootImages = {}
24binImages = {}
25fwImages = {}
26
27
28# Parse filesmap file containing firmware residing places
29def 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
48def 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
62def 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
83def 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
106def 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
218def FullOTA_Assertions(info):
219  #TODO: Implement device specific asserstions.
220  return
221
222
223def IncrementalOTA_Assertions(info):
224  #TODO: Implement device specific asserstions.
225  return
226
227
228def 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
234def 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)
251def 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
280def 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)
289def 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
322def 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
336def FullOTA_InstallEnd_MMC(info):
337  if OTA_VerifyEnd(info, info.input_version, info.input_zip):
338    OTA_InstallEnd(info)
339  return
340
341
342def FullOTA_InstallEnd_MTD(info):
343  print "warning radio-update: radio update for NAND devices not supported"
344  return
345
346
347def FullOTA_InstallEnd(info):
348  FullOTA_InstallEnd_MMC(info)
349  return
350
351def IncrementalOTA_InstallEnd_MMC(info):
352  OTA_InstallEnd(info)
353  return
354
355
356def IncrementalOTA_InstallEnd_MTD(info):
357  print "warning radio-update: radio update for NAND devices not supported"
358  return
359
360def IncrementalOTA_InstallEnd(info):
361  IncrementalOTA_InstallEnd_MMC(info)
362  return
363