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
21def FindRadio(zipfile):
22  try:
23    return zipfile.read("RADIO/radio.img")
24  except KeyError:
25    return None
26
27
28def 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
43def 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
62def 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
118def 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#
142def 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#
174release_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib"
175debug_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib"
176release_nobackup_partitions = "sdi imgdata sec"
177debug_nobackup_partitions = "sdi imgdata sec"
178
179
180def 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
264def trunc_to_null(s):
265  if '\0' in s:
266    return s[:s.index('\0')]
267  else:
268    return s
269