You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
9.3 KiB
268 lines
9.3 KiB
# |
|
# Copyright (C) 2015 The Android Open-Source Project |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
# you may not use this file except in compliance with the License. |
|
# You may obtain a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
# See the License for the specific language governing permissions and |
|
# limitations under the License. |
|
# |
|
|
|
import common |
|
import struct |
|
|
|
|
|
def FindRadio(zipfile): |
|
try: |
|
return zipfile.read("RADIO/radio.img") |
|
except KeyError: |
|
return None |
|
|
|
|
|
def FullOTA_InstallEnd(info): |
|
try: |
|
bootloader_img = info.input_zip.read("RADIO/bootloader.img") |
|
except KeyError: |
|
print "no bootloader.img in target_files; skipping install" |
|
else: |
|
WriteBootloader(info, bootloader_img) |
|
|
|
radio_img = FindRadio(info.input_zip) |
|
if radio_img: |
|
WriteRadio(info, radio_img) |
|
else: |
|
print "no radio.img in target_files; skipping install" |
|
|
|
|
|
def IncrementalOTA_VerifyEnd(info): |
|
target_radio_img = FindRadio(info.target_zip) |
|
if common.OPTIONS.full_radio: |
|
if not target_radio_img: |
|
assert False, "full radio option specified but no radio img found" |
|
else: |
|
return |
|
source_radio_img = FindRadio(info.source_zip) |
|
if not target_radio_img or not source_radio_img: |
|
return |
|
if source_radio_img != target_radio_img: |
|
info.script.CacheFreeSpaceCheck(len(source_radio_img)) |
|
radio_type, radio_device = common.GetTypeAndDevice("/radio", info.info_dict) |
|
info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % ( |
|
radio_type, radio_device, |
|
len(source_radio_img), common.sha1(source_radio_img).hexdigest(), |
|
len(target_radio_img), common.sha1(target_radio_img).hexdigest())) |
|
|
|
|
|
def IncrementalOTA_InstallEnd(info): |
|
try: |
|
target_bootloader_img = info.target_zip.read("RADIO/bootloader.img") |
|
try: |
|
source_bootloader_img = info.source_zip.read("RADIO/bootloader.img") |
|
except KeyError: |
|
source_bootloader_img = None |
|
|
|
if source_bootloader_img == target_bootloader_img: |
|
print "bootloader unchanged; skipping" |
|
else: |
|
WriteBootloader(info, target_bootloader_img) |
|
except KeyError: |
|
print "no bootloader.img in target target_files; skipping install" |
|
|
|
tf = FindRadio(info.target_zip) |
|
if not tf: |
|
# failed to read TARGET radio image: don't include any radio in update. |
|
print "no radio.img in target target_files; skipping install" |
|
# we have checked the existence of the radio image in |
|
# IncrementalOTA_VerifyEnd(), so it won't reach here. |
|
assert common.OPTIONS.full_radio == False |
|
else: |
|
tf = common.File("radio.img", tf) |
|
|
|
sf = FindRadio(info.source_zip) |
|
if not sf or common.OPTIONS.full_radio: |
|
# failed to read SOURCE radio image or one has specified the option to |
|
# include the whole target radio image. |
|
print("no radio image in source target_files or full_radio specified; " |
|
"installing complete image") |
|
WriteRadio(info, tf.data) |
|
else: |
|
sf = common.File("radio.img", sf) |
|
|
|
if tf.sha1 == sf.sha1: |
|
print "radio image unchanged; skipping" |
|
else: |
|
diff = common.Difference(tf, sf, diff_program="bsdiff") |
|
common.ComputeDifferences([diff]) |
|
_, _, d = diff.GetPatch() |
|
if d is None or len(d) > tf.size * common.OPTIONS.patch_threshold: |
|
# computing difference failed, or difference is nearly as |
|
# big as the target: simply send the target. |
|
WriteRadio(info, tf.data) |
|
else: |
|
common.ZipWriteStr(info.output_zip, "radio.img.p", d) |
|
info.script.Print("Patching radio...") |
|
radio_type, radio_device = common.GetTypeAndDevice( |
|
"/radio", info.info_dict) |
|
info.script.ApplyPatch( |
|
"%s:%s:%d:%s:%d:%s" % (radio_type, radio_device, |
|
sf.size, sf.sha1, tf.size, tf.sha1), |
|
"-", tf.size, tf.sha1, sf.sha1, "radio.img.p") |
|
|
|
|
|
def WriteRadio(info, radio_img): |
|
info.script.Print("Writing radio...") |
|
common.ZipWriteStr(info.output_zip, "radio.img", radio_img) |
|
_, device = common.GetTypeAndDevice("/radio", info.info_dict) |
|
info.script.AppendExtra( |
|
'package_extract_file("radio.img", "%s");' % (device,)) |
|
|
|
|
|
# /* msm8992 bootloader.img format */ |
|
# |
|
# #define BOOTLDR_MAGIC "BOOTLDR!" |
|
# #define BOOTLDR_MAGIC_SIZE 8 |
|
# |
|
# struct bootloader_images_header { |
|
# char magic[BOOTLDR_MAGIC_SIZE]; |
|
# unsigned int num_images; |
|
# unsigned int start_offset; |
|
# unsigned int bootldr_size; |
|
# struct { |
|
# char name[64]; |
|
# unsigned int size; |
|
# } img_info[]; |
|
# }; |
|
# |
|
def ParseBootloaderHeader(bootloader): |
|
header_fmt = "<8sIII" |
|
header_size = struct.calcsize(header_fmt) |
|
magic, num_images, start_offset, bootloader_size = struct.unpack( |
|
header_fmt, bootloader[:header_size]) |
|
assert magic == "BOOTLDR!", "bootloader.img bad magic value" |
|
|
|
img_info_fmt = "<64sI" |
|
img_info_size = struct.calcsize(img_info_fmt) |
|
|
|
imgs = [struct.unpack(img_info_fmt, |
|
bootloader[header_size+i*img_info_size: |
|
header_size+(i+1)*img_info_size]) |
|
for i in range(num_images)] |
|
|
|
p = start_offset |
|
img_dict = {} |
|
for name, size in imgs: |
|
img_dict[trunc_to_null(name)] = p, size |
|
p += size |
|
assert p - start_offset == bootloader_size, "bootloader.img corrupted" |
|
|
|
return img_dict |
|
|
|
|
|
# bullhead's bootloader.img contains 11 separate images. |
|
# Each goes to its own partition: |
|
# sbl1, tz, rpm, aboot, sdi, imgdata, pmic, hyp, sec, keymaster, cmnlib |
|
# |
|
# bullhead also has 8 backup partitions: |
|
# sbl1, tz, rpm, aboot, pmic, hyp, keymaster, cmnlib |
|
# |
|
release_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib" |
|
debug_backup_partitions = "sbl1 tz rpm aboot pmic hyp keymaster cmnlib" |
|
release_nobackup_partitions = "sdi imgdata sec" |
|
debug_nobackup_partitions = "sdi imgdata sec" |
|
|
|
|
|
def WriteBootloader(info, bootloader): |
|
info.script.Print("Writing bootloader...") |
|
|
|
img_dict = ParseBootloaderHeader(bootloader) |
|
|
|
common.ZipWriteStr(info.output_zip, "bootloader-flag.txt", |
|
"updating-bootloader" + "\0" * 13) |
|
common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32) |
|
|
|
_, misc_device = common.GetTypeAndDevice("/misc", info.info_dict) |
|
|
|
info.script.AppendExtra( |
|
'package_extract_file("bootloader-flag.txt", "%s");' % (misc_device,)) |
|
|
|
# failed sbl updates, may render the handset unusable/unrestorable. |
|
# Hence adopt below strategy for updates,enabling restore at all times. |
|
# 1. Flash backup partitions |
|
# 2. patch secondary pte's to swap primary/backup, and enable secondary gpt |
|
# 3. Flash psuedo-backup partions, effectively flashing primary partitions |
|
# 4. restore secondary pte's and restore primary gpt |
|
# 5. Flash all other non backup partitions |
|
# |
|
# Depending on the build fingerprint, we can decide which partitions |
|
# to update. |
|
fp = info.info_dict["build.prop"]["ro.build.fingerprint"] |
|
if "release-keys" in fp: |
|
to_bkp_flash = release_backup_partitions.split() |
|
to_flash = release_nobackup_partitions.split() |
|
else: |
|
to_bkp_flash = debug_backup_partitions.split() |
|
to_flash = debug_nobackup_partitions.split() |
|
|
|
# Write the images to separate files in the OTA package |
|
# and flash backup partitions |
|
for i in to_bkp_flash: |
|
try: |
|
_, device = common.GetTypeAndDevice("/"+i+"bak", info.info_dict) |
|
except KeyError: |
|
print "skipping flash of %s; not in recovery.fstab" % (i,) |
|
continue |
|
common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,), |
|
bootloader[img_dict[i][0]: |
|
img_dict[i][0]+img_dict[i][1]]) |
|
|
|
info.script.AppendExtra( |
|
'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) |
|
|
|
target_device = info.info_dict["build.prop"]["ro.product.device"] |
|
# swap ptes in secondary and force secondary gpt |
|
info.script.AppendExtra("lge_"+target_device+"_update_gpt();") |
|
|
|
# flash again after swap, effectively flashing primary |
|
# pte's are not re-read, hence primary is psuedo-secondary |
|
for i in to_bkp_flash: |
|
try: |
|
_, device = common.GetTypeAndDevice("/"+i, info.info_dict) |
|
except KeyError: |
|
print "skipping flash of %s; not in recovery.fstab" % (i,) |
|
continue |
|
info.script.AppendExtra( |
|
'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) |
|
|
|
# restore secondary gpt for correct mappings and enable primary gpt |
|
info.script.AppendExtra("lge_"+target_device+"_recover_gpt();") |
|
|
|
# Write the images to separate files in the OTA package |
|
for i in to_flash: |
|
try: |
|
_, device = common.GetTypeAndDevice("/"+i, info.info_dict) |
|
except KeyError: |
|
print "skipping flash of %s; not in recovery.fstab" % (i,) |
|
continue |
|
common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i,), |
|
bootloader[img_dict[i][0]: |
|
img_dict[i][0]+img_dict[i][1]]) |
|
|
|
info.script.AppendExtra( |
|
'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) |
|
|
|
info.script.AppendExtra( |
|
'package_extract_file("bootloader-flag-clear.txt", "%s");' % |
|
(misc_device,)) |
|
|
|
|
|
def trunc_to_null(s): |
|
if '\0' in s: |
|
return s[:s.index('\0')] |
|
else: |
|
return s
|
|
|