1import common 2import struct 3 4# The target does not support OTA-flashing 5# the partition table, so blacklist it. 6DEFAULT_BOOTLOADER_OTA_BLACKLIST = [ 'partition' ] 7 8class BadMagicError(Exception): 9 __str__ = "bad magic value" 10 11# 12# Motoboot packed image format 13# 14# #define BOOTLDR_MAGIC "MBOOTV1" 15# #define HEADER_SIZE 1024 16# #define SECTOR_SIZE 512 17# struct packed_images_header { 18# unsigned int num_images; 19# struct { 20# char name[24]; 21# unsigned int start; // start offset = HEADER_SIZE + start * SECTOR_SIZE 22# unsigned int end; // end offset = HEADER_SIZE + (end + 1) * SECTOR_SIZE - 1 23# } img_info[20]; 24# char magic[8]; // set to BOOTLDR_MAGIC 25# }; 26HEADER_SIZE = 1024 27SECTOR_SIZE = 512 28NUM_MAX_IMAGES = 20 29MAGIC = "MBOOTV1\0" 30class MotobootImage(object): 31 32 def __init__(self, data, name = None): 33 34 self.name = name 35 self._unpack(data) 36 37 def _unpack(self, data): 38 """ Unpack the data blob as a motoboot image and return the list 39 of contained image objects""" 40 num_imgs_fmt = "<L" 41 num_imgs_size = struct.calcsize(num_imgs_fmt) 42 num_imgs, = struct.unpack(num_imgs_fmt, data[:num_imgs_size]) 43 44 img_info_format = "<24sLL" 45 img_info_size = struct.calcsize(img_info_format) 46 47 imgs = [ struct.unpack(img_info_format, data[num_imgs_size + i*img_info_size:num_imgs_size + (i+1)*img_info_size]) for i in range(num_imgs) ] 48 49 magic_format = "<8s" 50 magic_size = struct.calcsize(magic_format) 51 magic, = struct.unpack(magic_format, data[num_imgs_size + NUM_MAX_IMAGES*img_info_size:num_imgs_size + NUM_MAX_IMAGES*img_info_size + magic_size]) 52 if magic != MAGIC: 53 raise BadMagicError 54 55 img_objs = [] 56 for name, start, end in imgs: 57 start_offset = HEADER_SIZE + start * SECTOR_SIZE 58 end_offset = HEADER_SIZE + (end + 1) * SECTOR_SIZE - 1 59 img = common.File(trunc_to_null(name), data[start_offset:end_offset+1]) 60 img_objs.append(img) 61 62 self.unpacked_images = img_objs 63 64 def GetUnpackedImage(self, name): 65 66 found_image = None 67 for image in self.unpacked_images: 68 if image.name == name: 69 found_image = image 70 break 71 return found_image 72 73 74def FindRadio(zipfile): 75 try: 76 return zipfile.read("RADIO/radio.img") 77 except KeyError: 78 return None 79 80def FullOTA_InstallEnd(info): 81 try: 82 bootloader_img = info.input_zip.read("RADIO/bootloader.img") 83 except KeyError: 84 print "no bootloader.img in target_files; skipping install" 85 else: 86 WriteBootloader(info, bootloader_img) 87 88 radio_img = FindRadio(info.input_zip) 89 if radio_img: 90 WriteRadio(info, radio_img) 91 else: 92 print "no radio.img in target_files; skipping install" 93 94def IncrementalOTA_VerifyEnd(info): 95 target_radio_img = FindRadio(info.target_zip) 96 if common.OPTIONS.full_radio: 97 if not target_radio_img: 98 assert False, "full radio option specified but no radio img found" 99 else: 100 return 101 source_radio_img = FindRadio(info.source_zip) 102 if not target_radio_img or not source_radio_img: 103 return 104 target_modem_img = MotobootImage(target_radio_img).GetUnpackedImage("modem") 105 if not target_modem_img: return 106 source_modem_img = MotobootImage(source_radio_img).GetUnpackedImage("modem") 107 if not source_modem_img: return 108 if target_modem_img.sha1 != source_modem_img.sha1: 109 info.script.CacheFreeSpaceCheck(len(source_modem_img.data)) 110 radio_type, radio_device = common.GetTypeAndDevice("/modem", info.info_dict) 111 info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % ( 112 radio_type, radio_device, 113 len(source_modem_img.data), source_modem_img.sha1, 114 len(target_modem_img.data), target_modem_img.sha1)) 115 116def IncrementalOTA_InstallEnd(info): 117 try: 118 target_bootloader_img = info.target_zip.read("RADIO/bootloader.img") 119 try: 120 source_bootloader_img = info.source_zip.read("RADIO/bootloader.img") 121 except KeyError: 122 source_bootloader_img = None 123 124 if source_bootloader_img == target_bootloader_img: 125 print "bootloader unchanged; skipping" 126 elif source_bootloader_img == None: 127 print "no bootloader.img in source target_files; installing complete image" 128 WriteBootloader(info, target_bootloader_img) 129 else: 130 tf = common.File("bootloader.img", target_bootloader_img) 131 sf = common.File("bootloader.img", source_bootloader_img) 132 WriteIncrementalBootloader(info, tf, sf) 133 except KeyError: 134 print "no bootloader.img in target target_files; skipping install" 135 136 tf = FindRadio(info.target_zip) 137 if not tf: 138 # failed to read TARGET radio image: don't include any radio in update. 139 print "no radio.img in target target_files; skipping install" 140 # we have checked the existence of the radio image in 141 # IncrementalOTA_VerifyEnd(), so it won't reach here. 142 assert common.OPTIONS.full_radio == False 143 else: 144 tf = common.File("radio.img", tf) 145 146 sf = FindRadio(info.source_zip) 147 if not sf or common.OPTIONS.full_radio: 148 # failed to read SOURCE radio image or one has specified the option to 149 # include the whole target radio image. 150 print("no radio image in source target_files or full_radio specified; " 151 "installing complete image") 152 WriteRadio(info, tf.data) 153 else: 154 sf = common.File("radio.img", sf) 155 156 if tf.size == sf.size and tf.sha1 == sf.sha1: 157 print "radio image unchanged; skipping" 158 else: 159 WriteIncrementalRadio(info, tf, sf) 160 161def WriteIncrementalBootloader(info, target_imagefile, source_imagefile): 162 try: 163 tm = MotobootImage(target_imagefile.data, "bootloader") 164 except BadMagicError: 165 assert False, "bootloader.img bad magic value" 166 try: 167 sm = MotobootImage(source_imagefile.data, "bootloader") 168 except BadMagicError: 169 print "source bootloader image is not a motoboot image. Installing complete image." 170 return WriteBootloader(info, target_imagefile.data) 171 172 # blacklist any partitions that match the source image 173 blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST 174 for ti in tm.unpacked_images: 175 if ti not in blacklist: 176 si = sm.GetUnpackedImage(ti.name) 177 if not si: 178 continue 179 if ti.size == si.size and ti.sha1 == si.sha1: 180 print "target bootloader partition image %s matches source; skipping" % ti.name 181 blacklist.append(ti.name) 182 183 # If there are any images to then write them 184 whitelist = [ i.name for i in tm.unpacked_images if i.name not in blacklist ] 185 if len(whitelist): 186 # Install the bootloader, skipping any matching partitions 187 WriteBootloader(info, target_imagefile.data, blacklist) 188 189def WriteIncrementalRadio(info, target_imagefile, source_imagefile): 190 try: 191 target_radio_img = MotobootImage(target_imagefile.data, "radio") 192 except BadMagicError: 193 assert False, "radio.img bad magic value" 194 195 try: 196 source_radio_img = MotobootImage(source_imagefile.data, "radio") 197 except BadMagicError: 198 source_radio_img = None 199 200 write_full_modem = True 201 if source_radio_img: 202 target_modem_img = target_radio_img.GetUnpackedImage("modem") 203 if target_modem_img: 204 source_modem_img = source_radio_img.GetUnpackedImage("modem") 205 if source_modem_img: 206 WriteIncrementalModemPartition(info, target_modem_img, source_modem_img) 207 write_full_modem = False 208 209 # Write the full images, skipping modem if so directed. 210 # 211 # NOTE: Some target flex radio images are zero-filled, and must 212 # be flashed to trigger the flex update "magic". Do not 213 # skip installing target partition images that are identical 214 # to its corresponding source partition image. 215 blacklist = [] 216 if not write_full_modem: 217 blacklist.append('modem') 218 WriteMotobootPartitionImages(info, target_radio_img, blacklist) 219 220def WriteIncrementalModemPartition(info, target_modem_image, source_modem_image): 221 tf = target_modem_image 222 sf = source_modem_image 223 224 b = common.BlockDifference("modem", common.DataImage(tf.data), 225 common.DataImage(sf.data)) 226 227 b.WriteScript(info.script, info.output_zip) 228 229 230def WriteRadio(info, radio_img): 231 info.script.Print("Writing radio...") 232 233 try: 234 motoboot_image = MotobootImage(radio_img, "radio") 235 except BadMagicError: 236 assert False, "radio.img bad magic value" 237 238 WriteMotobootPartitionImages(info, motoboot_image) 239 240def WriteMotobootPartitionImages(info, motoboot_image, blacklist = []): 241 WriteGroupedImages(info, motoboot_image.name, motoboot_image.unpacked_images, blacklist) 242 243def WriteGroupedImages(info, group_name, images, blacklist = []): 244 """ Write a group of partition images to the OTA package, 245 and add the corresponding flash instructions to the recovery 246 script. Skip any images that do not have a corresponding 247 entry in recovery.fstab.""" 248 249 for i in images: 250 if i.name not in blacklist: 251 WritePartitionImage(info, i, group_name) 252 253def WritePartitionImage(info, image, group_name = None): 254 255 filename = "%s.img" % image.name 256 if group_name: 257 filename = "%s.%s" % (group_name,filename) 258 259 try: 260 _, device = common.GetTypeAndDevice("/"+image.name, info.info_dict) 261 except KeyError: 262 print "skipping flash of %s; not in recovery.fstab" % (image.name,) 263 return 264 265 common.ZipWriteStr(info.output_zip, filename, image.data) 266 267 info.script.AppendExtra('package_extract_file("%s", "%s");' % 268 (filename, device)) 269 270def WriteBootloader(info, bootloader, blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST): 271 info.script.Print("Writing bootloader...") 272 273 try: 274 motoboot_image = MotobootImage(bootloader,"bootloader") 275 except BadMagicError: 276 assert False, "bootloader.img bad magic value" 277 278 common.ZipWriteStr(info.output_zip, "bootloader-flag.txt", 279 "updating-bootloader" + "\0" * 13) 280 common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32) 281 282 _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict) 283 284 info.script.AppendExtra( 285 'package_extract_file("bootloader-flag.txt", "%s");' % 286 (misc_device,)) 287 288 # OTA does not support partition changes, so 289 # do not bundle the partition image in the OTA package. 290 WriteMotobootPartitionImages(info, motoboot_image, blacklist) 291 292 info.script.AppendExtra( 293 'package_extract_file("bootloader-flag-clear.txt", "%s");' % 294 (misc_device,)) 295 296def trunc_to_null(s): 297 if '\0' in s: 298 return s[:s.index('\0')] 299 else: 300 return s 301