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 21 def FindRadio(zipfile): 22 try: 23 return zipfile.read("RADIO/radio.img") 24 except KeyError: 25 return None 26 27 28 def FullOTA_InstallEnd(info): 29 try: 30 bootloader_img = info.input_zip.read("RADIO/bootloader.img") 31 except KeyError: 32 print "no bootloader.img in target_files; skipping install" 33 else: 34 WriteBootloader(info, bootloader_img) 35 36 radio_img = FindRadio(info.input_zip) 37 if radio_img: 38 WriteRadio(info, radio_img) 39 else: 40 print "no radio.img in target_files; skipping install" 41 42 43 def IncrementalOTA_VerifyEnd(info): 44 target_radio_img = FindRadio(info.target_zip) 45 if common.OPTIONS.full_radio: 46 if not target_radio_img: 47 assert False, "full radio option specified but no radio img found" 48 else: 49 return 50 source_radio_img = FindRadio(info.source_zip) 51 if not target_radio_img or not source_radio_img: 52 return 53 if source_radio_img != target_radio_img: 54 info.script.CacheFreeSpaceCheck(len(source_radio_img)) 55 radio_type, radio_device = common.GetTypeAndDevice("/radio", info.info_dict) 56 info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % ( 57 radio_type, radio_device, 58 len(source_radio_img), common.sha1(source_radio_img).hexdigest(), 59 len(target_radio_img), common.sha1(target_radio_img).hexdigest())) 60 61 62 def IncrementalOTA_InstallEnd(info): 63 try: 64 target_bootloader_img = info.target_zip.read("RADIO/bootloader.img") 65 try: 66 source_bootloader_img = info.source_zip.read("RADIO/bootloader.img") 67 except KeyError: 68 source_bootloader_img = None 69 70 if source_bootloader_img == target_bootloader_img: 71 print "bootloader unchanged; skipping" 72 else: 73 WriteBootloader(info, target_bootloader_img) 74 except KeyError: 75 print "no bootloader.img in target target_files; skipping install" 76 77 tf = FindRadio(info.target_zip) 78 if not tf: 79 # failed to read TARGET radio image: don't include any radio in update. 80 print "no radio.img in target target_files; skipping install" 81 # we have checked the existence of the radio image in 82 # IncrementalOTA_VerifyEnd(), so it won't reach here. 83 assert common.OPTIONS.full_radio == False 84 else: 85 tf = common.File("radio.img", tf) 86 87 sf = FindRadio(info.source_zip) 88 if not sf or common.OPTIONS.full_radio: 89 # failed to read SOURCE radio image or one has specified the option to 90 # include the whole target radio image. 91 print("no radio image in source target_files or full_radio specified; " 92 "installing complete image") 93 WriteRadio(info, tf.data) 94 else: 95 sf = common.File("radio.img", sf) 96 97 if tf.sha1 == sf.sha1: 98 print "radio image unchanged; skipping" 99 else: 100 diff = common.Difference(tf, sf, diff_program="bsdiff") 101 common.ComputeDifferences([diff]) 102 _, _, d = diff.GetPatch() 103 if d is None or len(d) > tf.size * common.OPTIONS.patch_threshold: 104 # computing difference failed, or difference is nearly as 105 # big as the target: simply send the target. 106 WriteRadio(info, tf.data) 107 else: 108 common.ZipWriteStr(info.output_zip, "radio.img.p", d) 109 info.script.Print("Patching radio...") 110 radio_type, radio_device = common.GetTypeAndDevice( 111 "/radio", info.info_dict) 112 info.script.ApplyPatch( 113 "%s:%s:%d:%s:%d:%s" % (radio_type, radio_device, 114 sf.size, sf.sha1, tf.size, tf.sha1), 115 "-", tf.size, tf.sha1, sf.sha1, "radio.img.p") 116 117 118 def WriteRadio(info, radio_img): 119 info.script.Print("Writing radio...") 120 common.ZipWriteStr(info.output_zip, "radio.img", radio_img) 121 _, device = common.GetTypeAndDevice("/radio", info.info_dict) 122 info.script.AppendExtra( 123 'package_extract_file("radio.img", "%s");' % (device,)) 124 125 126 # /* msm8992 bootloader.img format */ 127 # 128 # #define BOOTLDR_MAGIC "BOOTLDR!" 129 # #define BOOTLDR_MAGIC_SIZE 8 130 # 131 # struct bootloader_images_header { 132 # char magic[BOOTLDR_MAGIC_SIZE]; 133 # unsigned int num_images; 134 # unsigned int start_offset; 135 # unsigned int bootldr_size; 136 # struct { 137 # char name[64]; 138 # unsigned int size; 139 # } img_info[]; 140 # }; 141 # 142 def ParseBootloaderHeader(bootloader): 143 header_fmt = "<8sIII" 144 header_size = struct.calcsize(header_fmt) 145 magic, num_images, start_offset, bootloader_size = struct.unpack( 146 header_fmt, bootloader[:header_size]) 147 assert magic == "BOOTLDR!", "bootloader.img bad magic value" 148 149 img_info_fmt = "<64sI" 150 img_info_size = struct.calcsize(img_info_fmt) 151 152 imgs = [struct.unpack(img_info_fmt, 153 bootloader[header_size+i*img_info_size: 154 header_size+(i+1)*img_info_size]) 155 for i in range(num_images)] 156 157 p = start_offset 158 img_dict = {} 159 for name, size in imgs: 160 img_dict[trunc_to_null(name)] = p, size 161 p += size 162 assert p - start_offset == bootloader_size, "bootloader.img corrupted" 163 164 return img_dict 165 166 167 # bullhead's bootloader.img contains 11 separate images. 168 # Each goes to its own partition: 169 # sbl1, tz, rpm, aboot, sdi, imgdata, pmic, hyp, sec, keymaster, cmnlib 170 # 171 # bullhead also has 8 backup partitions: 172 # sbl1, tz, rpm, aboot, pmic, hyp, keymaster, cmnlib 173 # 174 release_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib" 175 debug_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib" 176 release_nobackup_partitions = "sdi imgdata sec" 177 debug_nobackup_partitions = "sdi imgdata sec" 178 179 180 def WriteBootloader(info, bootloader): 181 info.script.Print("Writing bootloader...") 182 183 img_dict = ParseBootloaderHeader(bootloader) 184 185 common.ZipWriteStr(info.output_zip, "bootloader-flag.txt", 186 "updating-bootloader" + "\0" * 13) 187 common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32) 188 189 _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict) 190 191 info.script.AppendExtra( 192 'package_extract_file("bootloader-flag.txt", "%s");' % (misc_device,)) 193 194 # failed sbl updates, may render the handset unusable/unrestorable. 195 # Hence adopt below strategy for updates,enabling restore at all times. 196 # 1. Flash backup partitions 197 # 2. patch secondary pte's to swap primary/backup, and enable secondary gpt 198 # 3. Flash psuedo-backup partions, effectively flashing primary partitions 199 # 4. restore secondary pte's and restore primary gpt 200 # 5. Flash all other non backup partitions 201 # 202 # Depending on the build fingerprint, we can decide which partitions 203 # to update. 204 fp = info.info_dict["build.prop"]["ro.build.fingerprint"] 205 if "release-keys" in fp: 206 to_bkp_flash = release_backup_partitions.split() 207 to_flash = release_nobackup_partitions.split() 208 else: 209 to_bkp_flash = debug_backup_partitions.split() 210 to_flash = debug_nobackup_partitions.split() 211 212 # Write the images to separate files in the OTA package 213 # and flash backup partitions 214 for i in to_bkp_flash: 215 try: 216 _, device = common.GetTypeAndDevice("/"+i+"bak", info.info_dict) 217 except KeyError: 218 print "skipping flash of %s; not in recovery.fstab" % (i,) 219 continue 220 common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,), 221 bootloader[img_dict[i][0]: 222 img_dict[i][0]+img_dict[i][1]]) 223 224 info.script.AppendExtra( 225 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) 226 227 target_device = info.info_dict["build.prop"]["ro.product.device"] 228 # swap ptes in secondary and force secondary gpt 229 info.script.AppendExtra("lge_"+target_device+"_update_gpt();") 230 231 # flash again after swap, effectively flashing primary 232 # pte's are not re-read, hence primary is psuedo-secondary 233 for i in to_bkp_flash: 234 try: 235 _, device = common.GetTypeAndDevice("/"+i, info.info_dict) 236 except KeyError: 237 print "skipping flash of %s; not in recovery.fstab" % (i,) 238 continue 239 info.script.AppendExtra( 240 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) 241 242 # restore secondary gpt for correct mappings and enable primary gpt 243 info.script.AppendExtra("lge_"+target_device+"_recover_gpt();") 244 245 # Write the images to separate files in the OTA package 246 for i in to_flash: 247 try: 248 _, device = common.GetTypeAndDevice("/"+i, info.info_dict) 249 except KeyError: 250 print "skipping flash of %s; not in recovery.fstab" % (i,) 251 continue 252 common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,), 253 bootloader[img_dict[i][0]: 254 img_dict[i][0]+img_dict[i][1]]) 255 256 info.script.AppendExtra( 257 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) 258 259 info.script.AppendExtra( 260 'package_extract_file("bootloader-flag-clear.txt", "%s");' % 261 (misc_device,)) 262 263 264 def trunc_to_null(s): 265 if '\0' in s: 266 return s[:s.index('\0')] 267 else: 268 return s 269