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 17import common 18import struct 19 20# The target does not support OTA-flashing 21# the partition table, so blacklist it. 22DEFAULT_BOOTLOADER_OTA_BLACKLIST = ['partition'] 23 24 25class BadMagicError(Exception): 26 __str__ = "bad magic value" 27 28# 29# Huawei Bootloader packed image format 30# 31# typedef struct meta_header { 32# u32 magic; /* 0xce1ad63c */ 33# u16 major_version; /* (0x1)-reject images with higher major versions */ 34# u16 minor_version; /* (0x0)-allow images with higer minor versions */ 35# char img_version[64]; /* Top level version for images in this meta */ 36# u16 meta_hdr_sz; /* size of this header */ 37# u16 img_hdr_sz; /* size of img_header_entry list */ 38# } meta_header_t; 39 40# typedef struct img_header_entry { 41# char ptn_name[MAX_GPT_NAME_SIZE]; 42# u32 start_offset; 43# u32 size; 44# } img_header_entry_t 45 46 47MAGIC = 0xce1ad63c 48 49 50class HuaweiBootImage(object): 51 52 def __init__(self, data, name=None): 53 self.name = name 54 self.unpacked_images = None 55 self._unpack(data) 56 57 def _unpack(self, data): 58 """Unpack the data blob as a Huawei boot image and return the list 59 of contained image objects""" 60 num_imgs_fmt = struct.Struct("<IHH64sHH") 61 header = data[0:num_imgs_fmt.size] 62 info = {} 63 (info["magic"], info["major_version"], 64 info["minor_version"], info["img_version"], 65 info["meta_hdr_size"], info["img_hdr_size"]) = num_imgs_fmt.unpack(header) 66 67 img_info_format = "<72sLL" 68 img_info_size = struct.calcsize(img_info_format) 69 num = info["img_hdr_size"] / img_info_size 70 size = num_imgs_fmt.size 71 imgs = [ 72 struct.unpack( 73 img_info_format, 74 data[size + i * img_info_size:size + (i + 1) * img_info_size]) 75 for i in range(num) 76 ] 77 78 if info["magic"] != MAGIC: 79 raise BadMagicError 80 81 img_objs = {} 82 for name, start, end in imgs: 83 if TruncToNull(name): 84 img = common.File(TruncToNull(name), data[start:start + end]) 85 img_objs[img.name] = img 86 87 self.unpacked_images = img_objs 88 89 def GetUnpackedImage(self, name): 90 return self.unpacked_images.get(name) 91 92 93def FindRadio(zipfile): 94 try: 95 return zipfile.read("RADIO/radio.img") 96 except KeyError: 97 return None 98 99 100def FullOTA_InstallEnd(info): 101 try: 102 bootloader_img = info.input_zip.read("RADIO/bootloader.img") 103 except KeyError: 104 print "no bootloader.img in target_files; skipping install" 105 else: 106 WriteBootloader(info, bootloader_img) 107 108 radio_img = FindRadio(info.input_zip) 109 if radio_img: 110 WriteRadio(info, radio_img) 111 else: 112 print "no radio.img in target_files; skipping install" 113 114 115def IncrementalOTA_VerifyEnd(info): 116 target_radio_img = FindRadio(info.target_zip) 117 source_radio_img = FindRadio(info.source_zip) 118 if not target_radio_img or not source_radio_img: 119 return 120 target_modem_img = HuaweiBootImage(target_radio_img).GetUnpackedImage("modem") 121 if not target_modem_img: 122 return 123 source_modem_img = HuaweiBootImage(source_radio_img).GetUnpackedImage("modem") 124 if not source_modem_img: 125 return 126 if target_modem_img.sha1 != source_modem_img.sha1: 127 info.script.CacheFreeSpaceCheck(len(source_modem_img.data)) 128 radio_type, radio_device = common.GetTypeAndDevice("/modem", info.info_dict) 129 info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % ( 130 radio_type, radio_device, 131 len(source_modem_img.data), source_modem_img.sha1, 132 len(target_modem_img.data), target_modem_img.sha1)) 133 134 135def IncrementalOTA_InstallEnd(info): 136 try: 137 target_bootloader_img = info.target_zip.read("RADIO/bootloader.img") 138 try: 139 source_bootloader_img = info.source_zip.read("RADIO/bootloader.img") 140 except KeyError: 141 source_bootloader_img = None 142 143 if source_bootloader_img == target_bootloader_img: 144 print "bootloader unchanged; skipping" 145 elif source_bootloader_img == None: 146 print "no bootloader in source target_files; installing complete image" 147 WriteBootloader(info, target_bootloader_img) 148 else: 149 tf = common.File("bootloader.img", target_bootloader_img) 150 sf = common.File("bootloader.img", source_bootloader_img) 151 WriteIncrementalBootloader(info, tf, sf) 152 except KeyError: 153 print "no bootloader.img in target target_files; skipping install" 154 155 target_radio_image = FindRadio(info.target_zip) 156 if not target_radio_image: 157 # failed to read TARGET radio image: don't include any radio in update. 158 print "no radio.img in target target_files; skipping install" 159 else: 160 tf = common.File("radio.img", target_radio_image) 161 162 source_radio_image = FindRadio(info.source_zip) 163 if not source_radio_image: 164 # failed to read SOURCE radio image: include the whole target 165 # radio image. 166 print "no radio image in source target_files; installing complete image" 167 WriteRadio(info, tf.data) 168 else: 169 sf = common.File("radio.img", source_radio_image) 170 171 if tf.size == sf.size and tf.sha1 == sf.sha1: 172 print "radio image unchanged; skipping" 173 else: 174 WriteIncrementalRadio(info, tf, sf) 175 176 177def WriteIncrementalBootloader(info, target_imagefile, source_imagefile): 178 try: 179 tm = HuaweiBootImage(target_imagefile.data, "bootloader") 180 except BadMagicError: 181 raise ValueError("bootloader.img bad magic value") 182 try: 183 sm = HuaweiBootImage(source_imagefile.data, "bootloader") 184 except BadMagicError: 185 print "source bootloader is not a Huawei boot img; installing complete img." 186 return WriteBootloader(info, target_imagefile.data) 187 188 # blacklist any partitions that match the source image 189 blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST 190 for ti in tm.unpacked_images.values(): 191 if ti not in blacklist: 192 si = sm.GetUnpackedImage(ti.name) 193 if not si: 194 continue 195 if ti.size == si.size and ti.sha1 == si.sha1: 196 print "target bootloader partition img %s matches source; skipping" % ( 197 ti.name) 198 blacklist.append(ti.name) 199 200 # If there are any images to then write them 201 whitelist = [i.name for i in tm.unpacked_images.values() 202 if i.name not in blacklist] 203 if len(whitelist): 204 # Install the bootloader, skipping any matching partitions 205 WriteBootloader(info, target_imagefile.data, blacklist) 206 207 208def WriteIncrementalRadio(info, target_imagefile, source_imagefile): 209 try: 210 target_radio_img = HuaweiBootImage(target_imagefile.data, "radio") 211 except BadMagicError: 212 print "Magic number mismatch in target radio image" 213 raise ValueError("radio.img bad magic value") 214 215 try: 216 source_radio_img = HuaweiBootImage(source_imagefile.data, "radio") 217 except BadMagicError: 218 print "Magic number mismatch in source radio image" 219 source_radio_img = None 220 221 write_full_modem = True 222 if source_radio_img: 223 target_modem_img = target_radio_img.GetUnpackedImage("modem") 224 if target_modem_img: 225 source_modem_img = source_radio_img.GetUnpackedImage("modem") 226 if source_modem_img: 227 WriteIncrementalModemPartition(info, target_modem_img, source_modem_img) 228 write_full_modem = False 229 230 # Write the full images, skipping modem if so directed. 231 # 232 # NOTE: Some target flex radio images are zero-filled, and must 233 # be flashed to trigger the flex update "magic". Do not 234 # skip installing target partition images that are identical 235 # to its corresponding source partition image. 236 blacklist = [] 237 if not write_full_modem: 238 blacklist.append("modem") 239 WriteHuaweiBootPartitionImages(info, target_radio_img, blacklist) 240 241 242def WriteIncrementalModemPartition(info, target_modem_image, 243 source_modem_image): 244 tf = target_modem_image 245 sf = source_modem_image 246 pad_tf = False 247 pad_sf = False 248 blocksize = 4096 249 250 partial_tf = len(tf.data) % blocksize 251 partial_sf = len(sf.data) % blocksize 252 253 if partial_tf: 254 pad_tf = True 255 if partial_sf: 256 pad_sf = True 257 b = common.BlockDifference("modem", common.DataImage(tf.data, False, pad_tf), 258 common.DataImage(sf.data, False, pad_sf)) 259 b.WriteScript(info.script, info.output_zip) 260 261 262def WriteRadio(info, radio_img): 263 info.script.Print("Writing radio...") 264 265 try: 266 huawei_boot_image = HuaweiBootImage(radio_img, "radio") 267 except BadMagicError: 268 raise ValueError("radio.img bad magic value") 269 270 WriteHuaweiBootPartitionImages(info, huawei_boot_image) 271 272 273def WriteHuaweiBootPartitionImages(info, huawei_boot_image, blacklist=None): 274 if blacklist is None: 275 blacklist = [] 276 WriteGroupedImages(info, huawei_boot_image.name, 277 huawei_boot_image.unpacked_images.values(), blacklist) 278 279 280def WriteGroupedImages(info, group_name, images, blacklist=None): 281 """Write a group of partition images to the OTA package, 282 and add the corresponding flash instructions to the recovery 283 script. Skip any images that do not have a corresponding 284 entry in recovery.fstab.""" 285 if blacklist is None: 286 blacklist = [] 287 for i in images: 288 if i.name not in blacklist: 289 WritePartitionImage(info, i, group_name) 290 291 292def WritePartitionImage(info, image, group_name=None): 293 filename = "%s.img" % image.name 294 if group_name: 295 filename = "%s.%s" % (group_name, filename) 296 297 try: 298 info.script.Print("writing partition image %s" % image.name) 299 _, device = common.GetTypeAndDevice("/" + image.name, info.info_dict) 300 except KeyError: 301 print "skipping flash of %s; not in recovery.fstab" % image.name 302 return 303 304 common.ZipWriteStr(info.output_zip, filename, image.data) 305 306 info.script.AppendExtra('package_extract_file("%s", "%s");' % 307 (filename, device)) 308 309 310def WriteBootloader(info, bootloader, blacklist=None): 311 if blacklist is None: 312 blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST 313 info.script.Print("Writing bootloader...") 314 try: 315 huawei_boot_image = HuaweiBootImage(bootloader, "bootloader") 316 except BadMagicError: 317 raise ValueError("bootloader.img bad magic value") 318 319 common.ZipWriteStr(info.output_zip, "bootloader-flag.txt", 320 "updating-bootloader" + "\0" * 13) 321 common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32) 322 323 _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict) 324 325 info.script.AppendExtra( 326 'package_extract_file("bootloader-flag.txt", "%s");' % misc_device) 327 328 # OTA does not support partition changes, so 329 # do not bundle the partition image in the OTA package. 330 WriteHuaweiBootPartitionImages(info, huawei_boot_image, blacklist) 331 332 info.script.AppendExtra( 333 'package_extract_file("bootloader-flag-clear.txt", "%s");' % misc_device) 334 335 336def TruncToNull(s): 337 if '\0' in s: 338 return s[:s.index('\0')] 339 else: 340 return s 341