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