blob: d6c893e467f5a7d057fa0e34e083bf69aeab91f6 [file] [log] [blame]
Ed Tamabb43752014-06-10 15:09:35 -07001import common
2import struct
3
Andrew Wheeler38266c42014-06-13 17:07:31 -05004# The target does not support OTA-flashing
5# the partition table, so blacklist it.
6DEFAULT_BOOTLOADER_OTA_BLACKLIST = [ 'partition' ]
7
Andrew Wheelereb1b7a82014-06-06 13:25:12 -05008class 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"
Andrew Wheelerae640352014-06-12 15:10:16 -050030class MotobootImage(object):
Andrew Wheelereb1b7a82014-06-06 13:25:12 -050031
Andrew Wheelerae640352014-06-12 15:10:16 -050032 def __init__(self, data, name = None):
Andrew Wheelereb1b7a82014-06-06 13:25:12 -050033
Andrew Wheelerae640352014-06-12 15:10:16 -050034 self.name = name
35 self._unpack(data)
Andrew Wheelereb1b7a82014-06-06 13:25:12 -050036
Andrew Wheelerae640352014-06-12 15:10:16 -050037 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])
Andrew Wheelereb1b7a82014-06-06 13:25:12 -050043
Andrew Wheelerae640352014-06-12 15:10:16 -050044 img_info_format = "<24sLL"
45 img_info_size = struct.calcsize(img_info_format)
Andrew Wheelereb1b7a82014-06-06 13:25:12 -050046
Andrew Wheelerae640352014-06-12 15:10:16 -050047 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) ]
Andrew Wheelereb1b7a82014-06-06 13:25:12 -050048
Andrew Wheelerae640352014-06-12 15:10:16 -050049 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
Andrew Wheelereb1b7a82014-06-06 13:25:12 -050072
73
Ed Tamabb43752014-06-10 15:09:35 -070074def FindRadio(zipfile):
75 try:
76 return zipfile.read("RADIO/radio.img")
77 except KeyError:
78 return None
79
Ed Tamabb43752014-06-10 15:09:35 -070080def 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
Ed Tamabb43752014-06-10 15:09:35 -070094def IncrementalOTA_VerifyEnd(info):
95 target_radio_img = FindRadio(info.target_zip)
96 source_radio_img = FindRadio(info.source_zip)
97 if not target_radio_img or not source_radio_img: return
Andrew Wheelerae640352014-06-12 15:10:16 -050098 target_modem_img = MotobootImage(target_radio_img).GetUnpackedImage("modem")
99 if not target_modem_img: return
100 source_modem_img = MotobootImage(source_radio_img).GetUnpackedImage("modem")
101 if not source_modem_img: return
102 if target_modem_img.sha1 != source_modem_img.sha1:
103 info.script.CacheFreeSpaceCheck(len(source_modem_img.data))
104 radio_type, radio_device = common.GetTypeAndDevice("/modem", info.info_dict)
Ed Tamabb43752014-06-10 15:09:35 -0700105 info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % (
106 radio_type, radio_device,
Andrew Wheelerae640352014-06-12 15:10:16 -0500107 len(source_modem_img.data), source_modem_img.sha1,
108 len(target_modem_img.data), target_modem_img.sha1))
Ed Tamabb43752014-06-10 15:09:35 -0700109
Ed Tamabb43752014-06-10 15:09:35 -0700110def IncrementalOTA_InstallEnd(info):
111 try:
112 target_bootloader_img = info.target_zip.read("RADIO/bootloader.img")
113 try:
114 source_bootloader_img = info.source_zip.read("RADIO/bootloader.img")
115 except KeyError:
116 source_bootloader_img = None
117
118 if source_bootloader_img == target_bootloader_img:
119 print "bootloader unchanged; skipping"
Andrew Wheeler38266c42014-06-13 17:07:31 -0500120 elif source_bootloader_img == None:
121 print "no bootloader.img in source target_files; installing complete image"
Ed Tamabb43752014-06-10 15:09:35 -0700122 WriteBootloader(info, target_bootloader_img)
Andrew Wheeler38266c42014-06-13 17:07:31 -0500123 else:
124 tf = common.File("bootloader.img", target_bootloader_img)
125 sf = common.File("bootloader.img", source_bootloader_img)
126 WriteIncrementalBootloader(info, tf, sf)
Ed Tamabb43752014-06-10 15:09:35 -0700127 except KeyError:
128 print "no bootloader.img in target target_files; skipping install"
129
Ed Tamabb43752014-06-10 15:09:35 -0700130 tf = FindRadio(info.target_zip)
131 if not tf:
132 # failed to read TARGET radio image: don't include any radio in update.
133 print "no radio.img in target target_files; skipping install"
134 else:
135 tf = common.File("radio.img", tf)
136
137 sf = FindRadio(info.source_zip)
138 if not sf:
139 # failed to read SOURCE radio image: include the whole target
140 # radio image.
Andrew Wheelerae640352014-06-12 15:10:16 -0500141 print "no radio image in source target_files; installing complete image"
Ed Tamabb43752014-06-10 15:09:35 -0700142 WriteRadio(info, tf.data)
143 else:
144 sf = common.File("radio.img", sf)
145
Andrew Wheeler38266c42014-06-13 17:07:31 -0500146 if tf.size == sf.size and tf.sha1 == sf.sha1:
Ed Tamabb43752014-06-10 15:09:35 -0700147 print "radio image unchanged; skipping"
148 else:
Andrew Wheelerae640352014-06-12 15:10:16 -0500149 WriteIncrementalRadio(info, tf, sf)
Ed Tamabb43752014-06-10 15:09:35 -0700150
Andrew Wheeler38266c42014-06-13 17:07:31 -0500151def WriteIncrementalBootloader(info, target_imagefile, source_imagefile):
152 try:
153 tm = MotobootImage(target_imagefile.data, "bootloader")
154 except BadMagicError:
155 assert False, "bootloader.img bad magic value"
156 try:
157 sm = MotobootImage(source_imagefile.data, "bootloader")
158 except BadMagicError:
159 print "source bootloader image is not a motoboot image. Installing complete image."
160 return WriteBootloader(info, target_imagefile.data)
161
162 # blacklist any partitions that match the source image
163 blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST
164 for ti in tm.unpacked_images:
165 if ti not in blacklist:
166 si = sm.GetUnpackedImage(ti.name)
167 if not si:
168 continue
169 if ti.size == si.size and ti.sha1 == si.sha1:
170 print "target bootloader partition image %s matches source; skipping" % ti.name
171 blacklist.append(ti.name)
172
173 # If there are any images to then write them
174 whitelist = [ i.name for i in tm.unpacked_images if i.name not in blacklist ]
175 if len(whitelist):
176 # Install the bootloader, skipping any matching partitions
177 WriteBootloader(info, target_imagefile.data, blacklist)
178
Andrew Wheelerae640352014-06-12 15:10:16 -0500179def WriteIncrementalRadio(info, target_imagefile, source_imagefile):
180 try:
181 target_radio_img = MotobootImage(target_imagefile.data, "radio")
182 except BadMagicError:
183 assert False, "radio.img bad magic value"
184
185 try:
186 source_radio_img = MotobootImage(source_imagefile.data, "radio")
187 except BadMagicError:
188 source_radio_img = None
189
190 write_full_modem = True
191 if source_radio_img:
192 target_modem_img = target_radio_img.GetUnpackedImage("modem")
193 if target_modem_img:
194 source_modem_img = source_radio_img.GetUnpackedImage("modem")
195 if source_modem_img:
196 WriteIncrementalModemPartition(info, target_modem_img, source_modem_img)
197 write_full_modem = False
198
199 # Write the full images, skipping modem if so directed.
Andrew Wheeler38266c42014-06-13 17:07:31 -0500200 #
201 # NOTE: Some target flex radio images are zero-filled, and must
202 # be flashed to trigger the flex update "magic". Do not
203 # skip installing target partition images that are identical
204 # to its corresponding source partition image.
Andrew Wheelerae640352014-06-12 15:10:16 -0500205 blacklist = []
206 if not write_full_modem:
207 blacklist.append('modem')
208 WriteMotobootPartitionImages(info, target_radio_img, blacklist)
209
210def WriteIncrementalModemPartition(info, target_modem_image, source_modem_image):
211 tf = target_modem_image
212 sf = source_modem_image
213
Doug Zongkerc0a12ca2014-08-26 10:54:40 -0700214 b = common.BlockDifference("modem", common.DataImage(tf.data),
215 common.DataImage(sf.data))
Andrew Wheelerae640352014-06-12 15:10:16 -0500216
Doug Zongkerc0a12ca2014-08-26 10:54:40 -0700217 b.WriteScript(info.script, info.output_zip)
218
Ed Tamabb43752014-06-10 15:09:35 -0700219
220def WriteRadio(info, radio_img):
221 info.script.Print("Writing radio...")
Ed Tamabb43752014-06-10 15:09:35 -0700222
Ed Tamabb43752014-06-10 15:09:35 -0700223 try:
Andrew Wheelerae640352014-06-12 15:10:16 -0500224 motoboot_image = MotobootImage(radio_img, "radio")
Andrew Wheelereb1b7a82014-06-06 13:25:12 -0500225 except BadMagicError:
226 assert False, "radio.img bad magic value"
Ed Tamabb43752014-06-10 15:09:35 -0700227
Andrew Wheelerae640352014-06-12 15:10:16 -0500228 WriteMotobootPartitionImages(info, motoboot_image)
Ed Tamabb43752014-06-10 15:09:35 -0700229
Andrew Wheelerae640352014-06-12 15:10:16 -0500230def WriteMotobootPartitionImages(info, motoboot_image, blacklist = []):
231 WriteGroupedImages(info, motoboot_image.name, motoboot_image.unpacked_images, blacklist)
232
233def WriteGroupedImages(info, group_name, images, blacklist = []):
Andrew Wheelereb1b7a82014-06-06 13:25:12 -0500234 """ Write a group of partition images to the OTA package,
235 and add the corresponding flash instructions to the recovery
236 script. Skip any images that do not have a corresponding
237 entry in recovery.fstab."""
Ed Tamabb43752014-06-10 15:09:35 -0700238
Andrew Wheelereb1b7a82014-06-06 13:25:12 -0500239 for i in images:
Andrew Wheelerae640352014-06-12 15:10:16 -0500240 if i.name not in blacklist:
241 WritePartitionImage(info, i, group_name)
Andrew Wheelereb1b7a82014-06-06 13:25:12 -0500242
Andrew Wheelerae640352014-06-12 15:10:16 -0500243def WritePartitionImage(info, image, group_name = None):
244
245 filename = "%s.img" % image.name
246 if group_name:
247 filename = "%s.%s" % (group_name,filename)
248
249 try:
250 _, device = common.GetTypeAndDevice("/"+image.name, info.info_dict)
251 except KeyError:
252 print "skipping flash of %s; not in recovery.fstab" % (image.name,)
253 return
254
255 common.ZipWriteStr(info.output_zip, filename, image.data)
256
257 info.script.AppendExtra('package_extract_file("%s", "%s");' %
258 (filename, device))
Ed Tamabb43752014-06-10 15:09:35 -0700259
Andrew Wheeler38266c42014-06-13 17:07:31 -0500260def WriteBootloader(info, bootloader, blacklist = DEFAULT_BOOTLOADER_OTA_BLACKLIST):
Ed Tamabb43752014-06-10 15:09:35 -0700261 info.script.Print("Writing bootloader...")
262
Andrew Wheelereb1b7a82014-06-06 13:25:12 -0500263 try:
Andrew Wheelerae640352014-06-12 15:10:16 -0500264 motoboot_image = MotobootImage(bootloader,"bootloader")
Andrew Wheelereb1b7a82014-06-06 13:25:12 -0500265 except BadMagicError:
266 assert False, "bootloader.img bad magic value"
Ed Tamabb43752014-06-10 15:09:35 -0700267
268 common.ZipWriteStr(info.output_zip, "bootloader-flag.txt",
269 "updating-bootloader" + "\0" * 13)
270 common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32)
271
272 _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict)
273
274 info.script.AppendExtra(
275 'package_extract_file("bootloader-flag.txt", "%s");' %
276 (misc_device,))
277
Andrew Wheelereb1b7a82014-06-06 13:25:12 -0500278 # OTA does not support partition changes, so
279 # do not bundle the partition image in the OTA package.
Andrew Wheelerae640352014-06-12 15:10:16 -0500280 WriteMotobootPartitionImages(info, motoboot_image, blacklist)
Ed Tamabb43752014-06-10 15:09:35 -0700281
282 info.script.AppendExtra(
283 'package_extract_file("bootloader-flag-clear.txt", "%s");' %
284 (misc_device,))
285
Ed Tamabb43752014-06-10 15:09:35 -0700286def trunc_to_null(s):
287 if '\0' in s:
288 return s[:s.index('\0')]
289 else:
290 return s