A/B update又叫无缝升级,是Android提出的一种新的升级方式。可以简单理解为,内存中有两套系统(假设为A和B),你正在使用A,B在更新,你仍然可以使用,等B升级好了,再切换为B。
优点:
缺点:
与传统OTA方式相比,A/B的变换:
slot A
slot B
)misc
分区的信息,决定启动Android系统还是Recovery系统bootloader
boot
system
vendor
userdata
cache
recovery
misc
bootloader
boot_a
boot_b
system_a
system_b
vendor_a
vendor_b
userdata
misc
Ps:这一节参考Android A/B System OTA分析
slot A或slot B都有三个属性:
active
bootloader选择active属性的运行,排他属性,slot A或slot B有且仅有一个为activebootable
表示该分区是一个完整可以启动的系统successful
表明该分区在当前或上一次启动中可以正确运行Common example scenarios
Normal case
系统当前运行在slot B,slot B当前具有active
bootable
successful
三个属性Update in progress
系统当前运行在slot B, slot B检测到slot A要升级,将slot A设为unbootable
,准备升级Update applied, reboot pending
slot A升级完成后,具有bootable
(此时还不知道是否升级成功),而且此时reboot后从slot A启动,所以slot A具有active
,slot B没有active
System rebooted into new update
成功启动后,当前运行系统为slot A,具有active
bootable
successful
三个属性,slot B仍然具有bootable
successful
这一部分是bootloader中的一个部分,每个芯片厂商不同,这一部分可以分为:
boot_control
HAL(hardware/libhardware/include/hardware/boot_control.h)boot_control
test tool(system/extras/bootctl)如图是bootloader的flow,首先会判断有效的slot有几个,如果只有一个,就从该slot启动;如果有多个,则从高优先级的slot启动。如果没有成功启动,则retry_count减1(一般初始值为3),如果retry_count为0,则标记该slot无效。
Android提供了.h,c/cpp由IC厂商自己实现
通过函数名字可以知道函数的作用
void (*init)(struct boot_control_module *module)
unsigned (*getNumberSlots)(struct boot_control_module *module)
unsigned (*getCurrentSlot)(struct boot_control_module *module)
int (*markBootSuccessful)(struct boot_control_module *module)
int (*setActiveBootSlot)(struct boot_control_module *module, unsigned slot)
int (*setSlotAsUnbootable)(struct boot_control_module *module, unsigned slot)
int (*isSlotBootable)(struct boot_control_module *module, unsigned slot)
const char* (*getSuffix)(struct boot_control_module *module, unsigned slot)
int (*isSlotMarkedSuccessful)(struct boot_control_module *module, unsigned slot)
/*
* 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.
*/
#ifndef ANDROID_INCLUDE_HARDWARE_BOOT_CONTROL_H
#define ANDROID_INCLUDE_HARDWARE_BOOT_CONTROL_H
#include
__BEGIN_DECLS
#define BOOT_CONTROL_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
/**
* The id of this module
*/
#define BOOT_CONTROL_HARDWARE_MODULE_ID "bootctrl"
/*
* The Boot Control HAL is designed to allow for managing sets of redundant
* partitions, called slots, that can be booted from independantly. Slots
* are sets of partitions whose names differ only by a given suffix.
* They are identified here by a 0 indexed number, and associated with their
* suffix, which can be appended to the base name for any particular partition
* to find the one associated with that slot. The bootloader must pass the suffix
* of the currently active slot either through a kernel command line property at
* androidboot.slot_suffix, or the device tree at /firmware/android/slot_suffix.
* The primary use of this set up is to allow for background updates while the
* device is running, and to provide a fallback in the event that the update fails.
*/
/**
* Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
* and the fields of this data structure must begin with hw_module_t
* followed by module specific information.
*/
typedef struct boot_control_module {
struct hw_module_t common;
/*
* (*init)() perform any initialization tasks needed for the HAL.
* This is called only once.
*/
void (*init)(struct boot_control_module *module);
/*
* (*getNumberSlots)() returns the number of available slots.
* For instance, a system with a single set of partitions would return
* 1, a system with A/B would return 2, A/B/C -> 3...
*/
unsigned (*getNumberSlots)(struct boot_control_module *module);
/*
* (*getCurrentSlot)() returns the value letting the system know
* whether the current slot is A or B. The meaning of A and B is
* left up to the implementer. It is assumed that if the current slot
* is A, then the block devices underlying B can be accessed directly
* without any risk of corruption.
* The returned value is always guaranteed to be strictly less than the
* value returned by getNumberSlots. Slots start at 0 and
* finish at getNumberSlots() - 1
*/
unsigned (*getCurrentSlot)(struct boot_control_module *module);
/*
* (*markBootSuccessful)() marks the current slot
* as having booted successfully
*
* Returns 0 on success, -errno on error.
*/
int (*markBootSuccessful)(struct boot_control_module *module);
/*
* (*setActiveBootSlot)() marks the slot passed in parameter as
* the active boot slot (see getCurrentSlot for an explanation
* of the "slot" parameter). This overrides any previous call to
* setSlotAsUnbootable.
* Returns 0 on success, -errno on error.
*/
int (*setActiveBootSlot)(struct boot_control_module *module, unsigned slot);
/*
* (*setSlotAsUnbootable)() marks the slot passed in parameter as
* an unbootable. This can be used while updating the contents of the slot's
* partitions, so that the system will not attempt to boot a known bad set up.
* Returns 0 on success, -errno on error.
*/
int (*setSlotAsUnbootable)(struct boot_control_module *module, unsigned slot);
/*
* (*isSlotBootable)() returns if the slot passed in parameter is
* bootable. Note that slots can be made unbootable by both the
* bootloader and by the OS using setSlotAsUnbootable.
* Returns 1 if the slot is bootable, 0 if it's not, and -errno on
* error.
*/
int (*isSlotBootable)(struct boot_control_module *module, unsigned slot);
/*
* (*getSuffix)() returns the string suffix used by partitions that
* correspond to the slot number passed in parameter. The returned string
* is expected to be statically allocated and not need to be freed.
* Returns NULL if slot does not match an existing slot.
*/
const char* (*getSuffix)(struct boot_control_module *module, unsigned slot);
/*
* (*isSlotMarkedSucessful)() returns if the slot passed in parameter has
* been marked as successful using markBootSuccessful.
* Returns 1 if the slot has been marked as successful, 0 if it's
* not the case, and -errno on error.
*/
int (*isSlotMarkedSuccessful)(struct boot_control_module *module, unsigned slot);
void* reserved[31];
} boot_control_module_t;
__END_DECLS
#endif // ANDROID_INCLUDE_HARDWARE_BOOT_CONTROL_H
实例:
这里使用bootctl工具可以看到当前的slot等等。当然每个IC厂的内部实现不一样。
前面说到ramdisk在boot.img和system.img都有一份,那么启动的时候怎么决定用哪个img呢?
可以看google的这笔patch:initramfs: Add skip_initramfs command line option.(init/initramfs.c)
__setup("skip_initramfs", skip_initramfs_param)
这里如果命令行(kernel command line)的skip_initramfs
参数。
skip_initramfs
,do_skip_initramfs
会置一,并调用default_rootfs()
。skip_initramfs
,调用unpack_to_rootfs()
。default_rootfs()
这里创建了一个简单的rootfs,只有三个节点:
总结来看:
如果系统设置了skip_initramfs
参数,就会跳过boot.img的ramdisk,加载Android系统;
如果系统没有设置skip_initramfs
参数,就会加载Recovery系统
rootwait init=/init ro
Android A/B System OTA分析
A/B (Seamless) System Updates