在8.0之后MTK平台将tee lk preloder 等分区设置为双分区,升级的时候会将两个分区都进行升级,我们对这一部分做一个简单的分析和了解
一、升级包语句
ota_from_target_files def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file): target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts) source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts) ...... ...... # Do device-specific installation (eg, write radio image). # 执行驱动部分的升级部分,基本上所有平台都会基于该方法进行自己驱动部分升级的定制 device_specific.IncrementalOTA_InstallEnd()
releasetools.py def IncrementalOTA_InstallEnd(self): script = self.script source_version = self.source_version source_zip = self.source_zip target_zip = self.target_zip target_version = self.target_version output_zip = self.output_zip script = self.script metadata = self.metadata info_dict= self.info_dict tgt_info_dict = common.LoadInfoDict(target_zip) # add OTA information 添加ota信息 AddOTA_Items(target_zip, output_zip, 0) # Check bootloader path by property in porp.defaut 检查bootloader的地址 check_bootloader_path(target_zip) # add extra images to upgrade 添加额外升级的image AddOTAImage_Items(source_zip, target_zip, output_zip, tgt_info_dict, script)
def AddOTA_Items(input_zip, output_zip, isFullOTA):
//根据是否是整包 如果是整包type.txt中写入为1 如果是差分,type.txt写入为0
common.ZipWriteStr(output_zip, "type.txt", str(isFullOTA))
ota_scatter = input_zip.read("OTA/ota_scatter.txt")
//将中间包里的scatter.txt文件放入升级包中,作为升级之后校验分区表
common.ZipWriteStr(output_zip, "scatter.txt", ota_scatter)
def check_bootloader_path(input_zip):
"""check bootloader path by prop.default."""
//根据device类型,确定preloader的节点
prop_default = input_zip.read("RECOVERY/RAMDISK/prop.default")
if "ro.vendor.mtk_ufs_support=1" in prop_default:
print "device type is ufs, modify bootloader path"
part_dev_map["preloader"] = "/dev/block/sda"
part_dev_map["preloader2"] ="/dev/block/sdb"
def AddOTAImage_Items(source_zip, target_zip, output_zip, info_dict, script):
try:
//首先从中间包中查询ota_update_list 这里面存储了需要升级的分区和img文件
//odmdtbo.img odmdtbo
//tee.img tee1 tee2
//这个文件的生成在之前文档MTK驱动部分升级中分析过
output = target_zip.read("OTA/ota_update_list.txt")
except:
print "update_img_list not found"
return
//定义存储类型为EMMC
storage_type="EMMC"
td_pair = common.GetTypeAndDevice("/boot", info_dict)
if not td_pair:
return
storage_type = td_pair[0]
isBackupImgExist = 0
isFirstRun = 0
part_list = []
//通用img升级的列表
general_img_list = []
//加载部分img升级的列表
loader_img_list = []
//最后升级的img裂变
last_update_img_list = []
//以空格进行分割
for line in output.split("\n"):
if not line: continue
//对行进行分割
columns = line.split()
try:
//过去IMAGE下的打包进需要升级的img驱动文件
img_read = target_zip.read("IMAGES/%s" % columns[0])
except:
print "read image %s fail, remove from update list" % columns[0]
continue
//单分区比如 odmdtbo.img odmdtbo
if len(columns) == 2:
//如果在post_update_img_list = ["vbmeta"]中
if columns[1] in post_update_img_list:
print "Move update %s.img to the last" % columns[1]
common.ZipWriteStr(output_zip, columns[0], img_read)
//加入到last_update_img_list类表中
last_update_img_list.append(columns[1])
//如果是在incremental_update_raw_img_list = ["md1img" , "md1dsp"]中
//将使用差分升级,并放入到general_img_list列表中
elif source_zip is not None and columns[1] in incremental_update_raw_img_list:
print "%s uses incremental update" % columns[1]
general_img_list.append(columns[:2])
else:
//其他单分区也放入general_img_list
general_img_list.append(columns[:2])
common.ZipWriteStr(output_zip, columns[0], img_read)
//双分区 比如 tee.img tee1 tee2
elif len(columns) == 3:
//加入到loader_img_list列表中
loader_img_list.append(columns[:3])
common.ZipWriteStr(output_zip, columns[0], img_read)
else:
print "incorrect format in ota_update_list.txt"
return
script.AppendExtra('show_mtupdate_stage("%s");' % mtStageFile)
//单分区文件加入升级包文件和升级脚本
for img_name, mount_point in general_img_list:
if general_img_list.index([img_name, mount_point]) == 0:
script.AppendExtra('ifelse (\nless_than_int(get_mtupdate_stage("%s"), "1") ,\n(' % mtStageFile)
script.AppendExtra('ui_print("start to update general image");');
//针对incremental_update_raw_img_list 将使用差分升级
if source_zip is not None and mount_point in incremental_update_raw_img_list:
print ("Handle update incremental raw %s.img img_name %s" % (mount_point,img_name))
Inremental_raw_img(source_zip, target_zip,output_zip, mount_point, img_name, info_dict, script)
else:
//其他img整个放入差分包
WriteRawImage2(script, mount_point, img_name, info_dict)
if len(general_img_list) > 0:
SwitchStage(script, "1")
script.AppendExtra('),\nui_print("general images are already updated");\n);')
//双分区加入升级包文件和升级脚本
if len(loader_img_list) > 0:
//首先写入2分区 并且通过SwitchActive写入脚本 变更当前活动分区
for img_name, mount_point, backup_mount_point in loader_img_list:
if loader_img_list.index([img_name, mount_point, backup_mount_point]) == 0:
script.AppendExtra('ifelse (\nless_than_int(get_mtupdate_stage("%s"), "3") ,\n(' % mtStageFile)
script.AppendExtra('if less_than_int(get_mtupdate_stage("%s"), "2") then\n' % mtStageFile)
script.AppendExtra('ui_print("start to update alt loader image");');
WriteRawImage2(script, backup_mount_point, img_name, info_dict)
SwitchStage(script, "2")
script.AppendExtra('endif;\n')
for img_name, mount_point, backup_mount_point in loader_img_list:
SwitchActive(script, mount_point, backup_mount_point)
SwitchStage(script, "3")
script.AppendExtra('),\nui_print("alt loder images are already updated");\n);')
//写入1分区 并且通过SwitchActive写入脚本 变更当前活动分区
for img_name, mount_point, backup_mount_point in loader_img_list:
if loader_img_list.index([img_name, mount_point, backup_mount_point]) == 0:
script.AppendExtra('ifelse (\nless_than_int(get_mtupdate_stage("%s"), "5") ,\n(' % mtStageFile)
script.AppendExtra('if less_than_int(get_mtupdate_stage("%s"), "4") then\n' % mtStageFile)
script.AppendExtra('ui_print("start to update main loader image");');
WriteRawImage2(script, mount_point, img_name, info_dict)
SwitchStage(script, "4")
script.AppendExtra('endif;\n')
for img_name, mount_point, backup_mount_point in loader_img_list:
SwitchActive(script, backup_mount_point, mount_point)
script.AppendExtra('),\nui_print("main loader images are already updated");\n);')
script.AppendExtra('delete("%s");' % mtStageFile)
for mount_point in last_update_img_list:
WriteRawImage2(script, mount_point, mount_point+".img", info_dict)
script.AppendExtra('set_ota_result_for_dm_verity();')
//写入升级语句
def WriteRawImage2(script, partition, fn, info_dict, mapfn=None):
"""Write the given package file into the given MTD partition."""
//首先处理preloader的升级
if partition in part_dev_map.keys():
partition_type = "EMMC"
if script.fstab:
try:
p = script.fstab["/boot"]
except:
print "%s not exists in fstab, try fstab of info_dict" % mount_point
p = info_dict["fstab"]["/boot"]
partition_type = common.PARTITION_TYPES[p.fs_type]
args = {'device': p.device, 'fn': fn}
if partition_type == "MTD":
script.AppendExtra(
'write_raw_image(package_extract_file("%(fn)s"), "/dev/preloader");'
% args)
else:
if p.fs_type.upper() == "EMMC":
script.AppendExtra(
('assert(set_emmc_writable("%(force_ro)s"),\n'
' package_extract_file("%(fn)s", "%(partition)s"));')
% {'partition': part_dev_map[partition], 'fn': fn, 'force_ro': force_ro_dev_map[partition]})
else:
raise ValueError(
"Preloader don't know how to write \"%s\" partitions" % p.fs_type)
else:
//处理其他image
mount_point = "/"+partition
if script.fstab:
try:
p = script.fstab[mount_point]
except:
print "%s not exists in fstab, try fstab of info_dict" % mount_point
p = info_dict["fstab"][mount_point]
partition_type = common.PARTITION_TYPES[p.fs_type]
args = {'device': p.device, 'fn': fn}
if partition_type == "EMMC" or partition_type == "MTD":
if mapfn:
args["map"] = mapfn
script.AppendExtra(
'package_extract_file("%(fn)s", "%(device)s", "%(map)s");' % args)
else:
script.AppendExtra(
'package_extract_file("%(fn)s", "%(device)s");' % args)
else:
raise ValueError(
"don't know how to write \"%s\" partitions" % p.fs_type)
//选取活跃分区
def SwitchActive(script, from_part, to_part):
"""switch current active partition."""
partition_type = "EMMC"
if script.fstab:
try:
p = script.fstab["/boot"]
except:
print "%s not exists in fstab, try fstab of info_dict" % mount_point
p = info_dict["fstab"]["/boot"]
partition_type = common.PARTITION_TYPES[p.fs_type]
//如果是EMMC 写入语句 从1分区到2分区和从2分区到1分区
if partition_type == "EMMC":
script.AppendExtra(('switch_active("%(partition)s", "%(to_part)s");')
% {'partition':from_part.replace("bootloader","lk"), 'to_part':to_part.replace("bootloader","lk")})
if partition_type == "MTD":
script.AppendExtra(('switch_active("%(partition)s", "%(to_part)s");')
% {'partition':from_part.replace("bootloader","uboot"), 'to_part':to_part.replace("bootloader","uboot")})
生成差分包中updater-scrypt相关内容如下:
以下是升级驱动相关的所有语句
ui_print("start to update general image");
ui_print("Patching md1dsp image...");
show_progress(0.100000, 10);
//通用部分升级,除md1dsp.img md1img.img 使用查分外,其他使用整包升级到对应分区节点
apply_patch("EMMC:/dev/block/platform/bootdevice/by-name/md1dsp:6574480:54b9873602bd776655e6fe128b2e498294d27951:6574480:8b7922a855c149bcdba64c8f3bfaf36486410c36",
"-", 8b7922a855c149bcdba64c8f3bfaf36486410c36, 6574480,
54b9873602bd776655e6fe128b2e498294d27951,
package_extract_file("patch/md1dsp.img.p")) ||
abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/bootdevice/by-name/md1dsp:6574480:54b9873602bd776655e6fe128b2e498294d27951:6574480:8b7922a855c149bcdba64c8f3bfaf36486410c36");
package_extract_file("dtbo.img", "/dev/block/platform/bootdevice/by-name/dtbo");
package_extract_file("mcupmfw.img", "/dev/block/platform/bootdevice/by-name/mcupmfw");
ui_print("Patching md1img image...");
show_progress(0.100000, 10);
apply_patch("EMMC:/dev/block/platform/bootdevice/by-name/md1img:18290944:2e698b9873440c730f060c659221a27c5183ea6e:18290944:5469c6d12bc44244b4d2877f92295b9ad7dc649c",
"-", 5469c6d12bc44244b4d2877f92295b9ad7dc649c, 18290944,
2e698b9873440c730f060c659221a27c5183ea6e,
package_extract_file("patch/md1img.img.p")) ||
abort("E3008: Failed to apply patch to EMMC:/dev/block/platform/bootdevice/by-name/md1img:18290944:2e698b9873440c730f060c659221a27c5183ea6e:18290944:5469c6d12bc44244b4d2877f92295b9ad7dc649c");
package_extract_file("spmfw.img", "/dev/block/platform/bootdevice/by-name/spmfw");
set_mtupdate_stage("/cache/recovery/last_mtupdate_stage", "1");
),
ui_print("general images are already updated");
);
ifelse (
less_than_int(get_mtupdate_stage("/cache/recovery/last_mtupdate_stage"), "3") ,
(
if less_than_int(get_mtupdate_stage("/cache/recovery/last_mtupdate_stage"), "2") then
//升级双分区部分
ui_print("start to update alt loader image");
//将新版本tee写入到2分区
package_extract_file("tee.img", "/dev/block/platform/bootdevice/by-name/tee2");
set_mtupdate_stage("/cache/recovery/last_mtupdate_stage", "2");
endif;
//选取活动分区从1到2
switch_active("tee1", "tee2");
set_mtupdate_stage("/cache/recovery/last_mtupdate_stage", "3");
),
ui_print("alt loder images are already updated");
);
ifelse (
less_than_int(get_mtupdate_stage("/cache/recovery/last_mtupdate_stage"), "5") ,
(
if less_than_int(get_mtupdate_stage("/cache/recovery/last_mtupdate_stage"), "4") then
//再次升级双分区
ui_print("start to update main loader image");
//将新版本tee写入1分区
package_extract_file("tee.img", "/dev/block/platform/bootdevice/by-name/tee1");
set_mtupdate_stage("/cache/recovery/last_mtupdate_stage", "4");
endif;
//选取激活分区从2到1
switch_active("tee2", "tee1");
),
ui_print("main loader images are already updated");
);
delete("/cache/recovery/last_mtupdate_stage");
二、写入部分代码
语句是制作好了,具体写入我们要看下update-binary 也就是updater是如何执行的
这里重点关注switch_active方法
mt_install.cpp
void mt_RegisterInstallFunctions(void)
{
mt_init_partition_type();
RegisterFunction("get_mtupdate_stage", mtGetUpdateStageFn);
RegisterFunction("set_mtupdate_stage", mtSetUpdateStageFn);
RegisterFunction("show_mtupdate_stage", mtShowUpdateStageFn);
//选择活动分区真正执行的地方
RegisterFunction("switch_active", mtSwitchActiveFn);
RegisterFunction("delete", mtDeleteFn);
RegisterFunction("set_emmc_writable", mtSetEmmcWR);
RegisterFunction("set_ota_result_for_dm_verity", mtSetOTAResultForDMVerity);
}
mt_install.cpp
Value* mtSwitchActiveFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
if (argv.size() != 2)
return ErrorAbort(state, kArgsParsingFailure,"%s() expects 2 arg, got %zu", name, argv.size());
std::vector<std::string> args;
if (!ReadArgs(state, argv, &args))
return NULL;
//定义传入的第一个参数为from_partition
const std::string& from_partition = args[0];
//定义传入的第二个参数为to_partition
const std::string& to_partition = args[1];
//更新活跃分区
mt_update_active_part(state, from_partition.c_str(), to_partition.c_str());
printf("Switch %s active to %s\n", from_partition.c_str(), to_partition.c_str());
return StringValue(from_partition);
}
mt_install.cpp
static int mt_update_active_part(State* state, const char* from_partition, const char *to_partition)
{
if (((mt_get_phone_type() == FS_TYPE_MTD) && (!strncasecmp(from_partition, "preloader", strlen("preloader")))) // preloader on NAND not use active bit
) {
if (!strcasecmp(from_partition, "preloader")) // only do erase when main partition to alt partition
return mt_update_erase_part(state, from_partition);
} else if((mt_get_phone_type() == FS_TYPE_UFS) && !strncasecmp(from_partition, "preloader", strlen("preloader"))) { // preloader on UFS
unsigned int bootpart = 0;
if (!strcmp(to_partition, "preloader"))
bootpart = 1;
else
bootpart = 2;
return ufs_set_active_boot_part(bootpart);
//优先处理preloader的情况
} else if ((mt_get_phone_type() == FS_TYPE_EMMC) && !strncasecmp(from_partition, "preloader", strlen("preloader"))) { // preloader on EMMC just switch register
struct msdc_ioctl st_ioctl_arg;
unsigned int bootpart = 0;
int fd = open("/dev/misc-sd", O_RDWR);
if (fd >= 0) {
memset(&st_ioctl_arg,0,sizeof(struct msdc_ioctl));
st_ioctl_arg.host_num = 0;
st_ioctl_arg.opcode = MSDC_SET_BOOTPART;
st_ioctl_arg.total_size = 1;
if (!strcmp(to_partition, "preloader"))
bootpart = EMMC_BOOT1_EN;
else
bootpart = EMMC_BOOT2_EN;
st_ioctl_arg.buffer = &bootpart;
int ret = ioctl(fd, MSDC_SET_BOOTPART, &st_ioctl_arg);
if (ret < 0)
printf("set boot_part fail: %s\n", strerror(errno));
printf("switch bootpart to = %d, ret = %d\n", bootpart, ret);
close(fd);
} else {
uiPrintf(state, "set boot part fail, can not open misc-sd\n");
}
} else if ((mt_get_phone_type() == FS_TYPE_MNTL) || (mt_get_phone_type() == FS_TYPE_MTD)) {
// need to set to_partition active bit to 1 and then set from_partition active bit to 0
int ret = mt_pmt_update_active_part(to_partition, 1) | mt_pmt_update_active_part(from_partition, 0);
return ret;
//更新gpt分区标志位
} else if (support_gpt()) {
// need to set to_partition active bit to 1 and then set from_partition active bit to 0
int ret = mt_gpt_update_active_part(to_partition, 1) | mt_gpt_update_active_part(from_partition, 0);
return ret;
} else {
// TODO: pmt type active bit switch
}
return 1;
}
mt_gpt.cpp
int mt_gpt_update_active_part(const char* partition_name, int is_active)
{
char *part_entry = NULL;
GuidPartitionTableHeader_t pgpt_header;
GuidPartitionEntry_t *pe;
int sector_size = 0;
unsigned int i = 0;
// initial path
set_blk_device_path();
set_hw_sector_size_path();
// get sector size
if (mt_get_sector_size(§or_size) != 0) {
printf("Get sector size fail!\n");
return 1;
}
// read partition entry
if (mt_gpt_get_part_entry(&pgpt_header, &part_entry, sector_size) > 0) {
printf("Get partition entry fail!\n");
return 1;
}
// Set active bit for corresponding partition entry
pe = (GuidPartitionEntry_t *)part_entry;
for (i = 0; i < pgpt_header.NumberOfPartitionEntries; i++, pe++) {
unsigned int j;
char name[37];
#ifdef GPT_DEBUG
printf("Partition Entry %d\n", i + 1);
printf("\tFirst LBA=%ju\n", pe->StartingLBA);
printf("\tLast LBA=%ju\n", pe->EndingLBA);
#endif
for (j = 0; j < 72 / sizeof(efi_char16_t); j++) {
name[j] = (uint16_t)pe->PartitionName[j];
}
name[j] = 0;
#ifdef GPT_DEBUG
printf("\tName=%s\n", name);
#endif
if (!strcmp(name, partition_name))
break;
}
if (i >= pgpt_header.NumberOfPartitionEntries) {
printf("partition %s not found!\n", partition_name);
if (part_entry)
free(part_entry);
return 1;
}
printf("set %s active bit to %d\n", partition_name, is_active);
printf("Original active: %d, new active: %d\n", (unsigned int)pe->Attributes.LegacyBIOSBootable, is_active);
pe->Attributes.LegacyBIOSBootable = is_active;
// Write partition entry to gpt 将对应分区写入gpt分区标志位
mt_gpt_update_part_entry(&pgpt_header, &part_entry, sector_size);
// resource release
if (part_entry)
free(part_entry);
return 0;
}
mt_gpt.cpp 将当前活跃分区写入到gpt
static int mt_gpt_update_part_entry(GuidPartitionTableHeader_t *pgpt_header, char** part_entry, int sector_size)
{
int fd = 0;
int write_len = 0;
GuidPartitionTableHeader_t sgpt_header;
if (pgpt_header == NULL) {
printf("invalid gpt partition table header!\n");
return 1;
}
fd = open(blk_device, O_RDWR | O_SYNC);
if (fd < 0) {
printf("open %s fail\n", blk_device);
return 1;
}
uint64_t len = (uint64_t)pgpt_header->NumberOfPartitionEntries * (uint64_t)pgpt_header->SizeOfPartitionEntry;
pgpt_header->PartitionEntryArrayCRC32 = efi_crc32(*part_entry, len);
#ifdef GPT_DEBUG
printf("PE CRC changed=0x%08x\n", pgpt_header->PartitionEntryArrayCRC32);
#endif
pgpt_header->HeaderCRC32 = 0;
pgpt_header->HeaderCRC32 = efi_crc32(pgpt_header, pgpt_header->HeaderSize);
#ifdef GPT_DEBUG
printf("PGTP Header CRC changed=0x%08x\n", pgpt_header->HeaderCRC32);
#endif
//SGPT
if (lseek64(fd, pgpt_header->AlternateLBA * sector_size, SEEK_SET) == -1) {
printf("leesk %ju fail\n", pgpt_header->AlternateLBA * sector_size);
close(fd);
return 1;
}
if (read(fd, &sgpt_header, sizeof(sgpt_header)) != sizeof(sgpt_header)) {
printf("read SGPT header fail\n");
close(fd);
return 1;
}
#ifdef GPT_DEBUG
printf("SGPT:\n");
printf("Header CRC=0x%08x\n", sgpt_header.HeaderCRC32);
printf("Current LBA=%ju\n", sgpt_header.MyLBA);
printf("Backup LBA=%ju\n", sgpt_header.AlternateLBA);
printf("First usable LBA=%ju\n", sgpt_header.FirstUsableLBA);
printf("Last usable LBA=%ju\n", sgpt_header.LastUsableLBA);
printf("Starting PE LBA=%ju\n", sgpt_header.PartitionEntryLBA);
printf("Number of PE=%d\n", sgpt_header.NumberOfPartitionEntries);
printf("Size of PE=%d\n", sgpt_header.SizeOfPartitionEntry);
printf("PE CRC=0x%08x\n", sgpt_header.PartitionEntryArrayCRC32);
#endif
sgpt_header.PartitionEntryArrayCRC32 = pgpt_header->PartitionEntryArrayCRC32;
sgpt_header.HeaderCRC32 = 0;
sgpt_header.HeaderCRC32 = efi_crc32(&sgpt_header, sgpt_header.HeaderSize);
#ifdef GPT_DEBUG
printf("SGTP Header CRC changed=0x%08x\n", sgpt_header.HeaderCRC32);
#endif
//update PGPT header
if (lseek64(fd, pgpt_header->MyLBA * sector_size, SEEK_SET) == -1) {
printf("leesk %ju fail\n", pgpt_header->MyLBA * sector_size);
close(fd);
return 1;
}
if ((write_len = write(fd, pgpt_header, pgpt_header->HeaderSize)) != (int)pgpt_header->HeaderSize) {
printf("write PGPT fail %d (%s)\n", write_len, strerror(errno));
close(fd);
return 1;
}
//update PPE
if (lseek64(fd, pgpt_header->PartitionEntryLBA * sector_size, SEEK_SET) == -1) {
printf("leesk %ju fail\n", pgpt_header->PartitionEntryLBA * sector_size);
close(fd);
return 1;
}
if ((write_len = write(fd, *part_entry, (ssize_t)len)) != (ssize_t)len) {
printf("write PPE fail %d (%s)\n", write_len, strerror(errno));
close(fd);
return 1;
}
//update SPE
if (lseek64(fd, sgpt_header.PartitionEntryLBA * sector_size, SEEK_SET) == -1) {
printf("leesk %ju fail\n", sgpt_header.PartitionEntryLBA * sector_size);
close(fd);
return 1;
}
if ((write_len = write(fd, *part_entry, (ssize_t)len)) != (ssize_t)len) {
printf("write SPE fail %d (%s)\n", write_len, strerror(errno));
close(fd);
return 1;
}
//update SGPT header
if (lseek64(fd, sgpt_header.MyLBA * sector_size, SEEK_SET) == -1) {
printf("leesk %ju fail\n", sgpt_header.MyLBA * sector_size);
close(fd);
return 1;
}
if ((write_len = write(fd, &sgpt_header, sgpt_header.HeaderSize)) != (int)sgpt_header.HeaderSize) {
printf("write SGPT fail %d (%s)\n", write_len, strerror(errno));
close(fd);
return 1;
}
close(fd);
sync();
return 0;
}
三、总结
1、对于单分区部分 直接进行写入
2、双分区部分 首先写入2分区
3、将2分区标志位写入PGPT分区
4、再次写入1分区
5、将1分区标志位写入PGPT分区
6、可以是出去安全考虑,双分区大部分为tee lk preloader,开机引导加载重要固件,采用双分区,一旦写入出现问题,PGPT分区会选择正常的活跃分区进行启动

本文详细解析了MTK平台在8.0版本后引入的双分区升级机制,包括ota_from_target_files函数如何处理双分区如tee和preloader的升级流程,以及升级脚本的生成过程。特别关注了双分区的切换逻辑,从升级2分区到更改PGPT分区标志位,再到升级1分区并更新标志位,确保了系统在遇到写入问题时能选择正常分区启动。
545

被折叠的 条评论
为什么被折叠?



