在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分区会选择正常的活跃分区进行启动