3

安卓ro.serialno产生的整个流程 - 耳东Sir

 2 years ago
source link: https://www.cnblogs.com/erdongsir/p/17152900.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

关于ro.serialno这个属性,相信大家都不陌生了,应用层的Build.getSerial()Build.SERIAL等均是直接或间接的获取了这个属性值。接下来从boot到系统应用,小小的分析一下它的整个流程:

由于是APP经常使用,那我们从应用层分析到底层kernel/boot

一,framework层

好的,我们进入安卓源码目录,grep查找一下:

xxxx@server01:~/workspace/rk3128_tablet$ grep -nrw "SERIAL" frameworks/base/
frameworks/base/docs/html/about/versions/android-4.2.jd:364:address or the {@link android.os.Build#SERIAL} number), they will provide the same value for each
frameworks/base/api/test-current.txt:28614:    field public static final java.lang.String SERIAL;
frameworks/base/api/system-current.txt:31035:    field public static final java.lang.String SERIAL;
frameworks/base/api/current.txt:28540:    field public static final java.lang.String SERIAL;
frameworks/base/core/java/android/os/Build.java:102:    public static final String SERIAL = getString("ro.serialno");
frameworks/base/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/SinkActivity.java:61:    private static final String SERIAL = "0000000012345678";
frameworks/base/tests/AccessoryDisplay/sink/src/com/android/accessorydisplay/sink/SinkActivity.java:254:            sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_SERIAL, SERIAL);
xxxx@server01:~/workspace/rk3128_tablet$ 

成功的在Build.java找到了这个SERIAL属性,我们继续往下跟getString这个方法大概在871行。

.....
/**
     * Returns the version string for the radio firmware.  May return
     * null (if, for instance, the radio is not currently on).
     */
    public static String getRadioVersion() {
        return SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION, null);
    }

	private static String getString(String property) {
        return SystemProperties.get(property, UNKNOWN);
    }

	private static String[] getStringList(String property, String separator) {
        String value = SystemProperties.get(property);
        if (value.isEmpty()) {
            return new String[0];
        } else {
            return value.split(separator);
        }
    }
.....

SystemProperties大家应该很熟了

可以看出,getString是传入的"ro.serialno"这个字串去获取的属性中的值,其效果在命令行上相当于getprop ro.serialno

好的,framework分析到这。

二,系统层

我们从第一个程序init开始,源码路径:

your_pro/system/core/init/init.cpp

根据关键字ro.serialno找到了地方,大概在464行:

static void export_kernel_boot_props() {
    char cmdline[1024];
    char* s1;
    char* s2;
    char* s3;
    char* s4;

    struct {
        const char *src_prop;
        const char *dst_prop;
        const char *default_value;
    } prop_map[] = {
        { "ro.boot.serialno",   "ro.serialno",   "", },//就是这了,根据ro.boot.serialno的值设置ro.serialno的值
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
    };

    //if storagemedia is emmc, so we will wait emmc init finish
    for (int i = 0; i < EMMC_RETRY_COUNT; i++) {
        proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
        s1 = strstr(cmdline, STORAGE_MEDIA);
        s2 = strstr(cmdline, "androidboot.mode=emmc");
	s3 = strstr(cmdline, "storagemedia=nvme");
	s4 = strstr(cmdline, "androidboot.mode=nvme");

        if ((s1 == NULL) && (s3 == NULL)) {
            //storagemedia is unknow
            break;
        }

        if ((s1 > 0) && (s2 > 0)) {
            ERROR("OK,EMMC DRIVERS INIT OK\n");
            property_set("ro.boot.mode", "emmc");
            break;
        } else if ((s3 > 0) && (s4 > 0)) {
	    ERROR("OK,NVME DRIVERS INIT OK\n");
	    property_set("ro.boot.mode", "nvme");
	    break;
	} else {
            ERROR("OK,EMMC DRIVERS NOT READY, RERRY=%d\n", i);
            usleep(10000);
        }
    }

    for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {//这里这里
        std::string value = property_get(prop_map[i].src_prop);
        property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
    }

    /* save a copy for init's usage during boot */
    std::string bootmode_value = property_get("ro.bootmode");
    if (!bootmode_value.empty())
        strlcpy(bootmode, bootmode_value.c_str(), sizeof(bootmode));

    /* if this was given on kernel command line, override what we read
     * before (e.g. from /proc/cpuinfo), if anything */
    std::string hardware_value = property_get("ro.boot.hardware");
    if (!hardware_value.empty())
        strlcpy(hardware, hardware_value.c_str(), sizeof(hardware));
    property_set("ro.hardware", hardware);

    symlink_fstab();
}

以上代码针对于ro.serialno的大致意思就是根据ro.boot.serialno的值设它。

但是,ro.boot.serialno在哪还不知道呢,我们继续。

好的,分析开始

从mian开始,找到第一阶段需要执行的代码

int main(int argc, char** argv) {
....

	if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

        property_init();

        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        process_kernel_dt();
        process_kernel_cmdline();//根据函数名字就大概知道,这是处理内核cmdline的函数

        //add by xzj to set ro.rk.soc read from /proc/cpuinfo if not set
        set_soc_if_need();

        // Propagate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();//这里就是将处理完cmdline的相关的boot属性输出,我们上面已经分析过这个函数了
    }

....
}

先看process_kernel_cmdline函数:

这里做了两个动作,改cmdline的权限和设置import_kernel_nv这个回调函数

static void process_kernel_cmdline() {
    // Don't expose the raw commandline to unprivileged processes.
    chmod("/proc/cmdline", 0440);

    // The first pass does the common stuff, and finds if we are in qemu.
    // The second pass is only necessary for qemu to export all kernel params
    // as properties.
    import_kernel_cmdline(false, import_kernel_nv);
    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}

回调函数import_kernel_nv将传入的cmdline中的条目解析并且设置property

static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
    if (key.empty()) return;
    if (for_emulator) {
        // In the emulator, export any kernel option with the "ro.kernel." prefix.
        property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
        return;
    }

    if (key == "qemu") {
        strlcpy(qemu, value.c_str(), sizeof(qemu));
    } else if (android::base::StartsWith(key, "androidboot.")) {
        property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
                     value.c_str());
    }
}

再看看import_kernel_cmdline做了什么动作?

这里从/proc/cmdline读出数据,然后以空格“ ”分开数据,for循环调用传入的回调函数指针fn,也就是import_kernel_nv函数,再将分开的数据传参入回调函数。

void import_kernel_cmdline(bool in_qemu,
                           std::function<void(const std::string&, const std::string&, bool)> fn) {
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);

    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
        std::vector<std::string> pieces = android::base::Split(entry, "=");
        if (pieces.size() == 2) {
            fn(pieces[0], pieces[1], in_qemu);
        }
    }
}

这里小小的总结下:

从上面的步骤跟踪下来,发现整体流程是将从boot传给kernelcmdline中的androidboot.serialno赋给ro.boot.serialno,然后再根据ro.boot.*相关的属性去设置export_kernel_boot_props函数中prop_map这个数组对应的ro. 属性。

举个栗子,此处serialno的流程就该为:

boot- > kernel cmdline -> androidboot.serialno -> ro.boot.serialno -> ro.serialno -> 然后再被prop调用

到这里,只有kernel cmdline之前的流程不知道了,具体boot是怎么将一堆东西传给/proc/cmdline的呢?

好的,安排它~

三,u-Boot层

继续进uboot目录搜索一下:

xxx@server01:~/workspace/rk3128_tablet$ grep -nrw "androidboot.serialno" u-boot/
匹配到二进制文件 u-boot/u-boot.bin
匹配到二进制文件 u-boot/common/cmd_bootrk.o
匹配到二进制文件 u-boot/common/built-in.o
匹配到二进制文件 u-boot/uboot.img
匹配到二进制文件 u-boot/u-boot
u-boot/include/fastboot.h:81:#define FASTBOOT_SERIALNO_BOOTARG "androidboot.serialno"
xxx@server01:~/workspace/rk3128_tablet$ 

找到一个FASTBOOT_SERIALNO_BOOTARG,继续搜它,看谁用了

xtw-cl@server01:~/workspace/pnd_rk3128_tablet$ grep -nrw "FASTBOOT_SERIALNO_BOOTARG" u-boot/
u-boot/common/cmd_bootrk.c:583:         if (!strstr(command_line, FASTBOOT_SERIALNO_BOOTARG)) {
u-boot/common/cmd_bootrk.c:585:                                 "%s %s=%s", command_line, FASTBOOT_SERIALNO_BOOTARG, sn);
u-boot/include/fastboot.h:81:#define FASTBOOT_SERIALNO_BOOTARG "androidboot.serialno"
xtw-cl@server01:~/workspace/pnd_rk3128_tablet$

找到了,u-boot/common/cmd_bootrk.c文件

好的,开始分析源码:

static void rk_commandline_setenv(const char *boot_name, rk_boot_img_hdr *hdr, bool charge)
{
....

	snprintf(command_line, sizeof(command_line),
			 "%s SecureBootCheckOk=%d", command_line, SecureBootCheckOK);

	char *sn = getenv("fbt_sn#");
	if (sn != NULL) {
		/* append serial number if it wasn't in device_info already */
		if (!strstr(command_line, FASTBOOT_SERIALNO_BOOTARG)) {
			snprintf(command_line, sizeof(command_line),
					"%s %s=%s", command_line, FASTBOOT_SERIALNO_BOOTARG, sn);
		}
	}

	command_line[sizeof(command_line) - 1] = 0;

	setenv("bootargs", command_line);
#endif /* CONFIG_CMDLINE_TAG */
}

从源码可得知,androidboot.serialno的这个sn参数是通过getenv("fbt_sn#")获取到的,好的,继续搜索fbt_sn#看看是哪里设置的这个环境变量

xxx@server01:~/workspace/rk3128_tablet$ grep -nrw "fbt_sn#" u-boot/
匹配到二进制文件 u-boot/u-boot.bin
u-boot/common/cmd_bootrk.c:580: char *sn = getenv("fbt_sn#");
匹配到二进制文件 u-boot/common/cmd_fastboot.o
匹配到二进制文件 u-boot/common/cmd_bootrk.o
u-boot/common/cmd_fastboot.c:662:       //setenv("fbt_sn#", serial_number);
u-boot/common/cmd_fastboot.c:668:       char *sn = getenv("fbt_sn#");
匹配到二进制文件 u-boot/common/built-in.o
u-boot/board/rockchip/rk33xx/rk33xx.c:226:              setenv("fbt_sn#", tmp_buf);
u-boot/board/rockchip/rk32xx/rk32xx.c:220:              setenv("fbt_sn#", tmp_buf);
匹配到二进制文件 u-boot/board/rockchip/rk32xx/rk32xx.o
匹配到二进制文件 u-boot/board/rockchip/rk32xx/built-in.o
匹配到二进制文件 u-boot/uboot.img
匹配到二进制文件 u-boot/u-boot
xxx@server01:~/workspace/rk3128_tablet$ 

可以得知,设setenv的有两个,但是我们生成的二进制文件是rk32xx.o,所以我们分析rk32xx.c这个源码。

#ifdef CONFIG_BOARD_LATE_INIT
extern char bootloader_ver[24];
int board_late_init(void)
{
	debug("board_late_init\n");
	
    ....

	char tmp_buf[32];
	/* rk sn size 30bytes, zero buff */
	memset(tmp_buf, 0, 32);
	if (rkidb_get_sn(tmp_buf)) {
		setenv("fbt_sn#", tmp_buf);
	}

	debug("fbt preboot\n");
	board_fbt_preboot();

	return 0;
}
#endif

从上面可以看出设进fbt_sn#属性名字的tmp_buf是从rkidb_get_sn函数获取的,so继续。

顺便提一句,board_late_init会在环境初始化函数中调用,而它会被启动的更底层的汇编程序调用,这里不展开讲

搜一下这个rkidb_get_sn函数

xxxx@server01:~/workspace/rk3128_tablet$ grep -nrw "rkidb_get_sn" u-boot/
u-boot/board/rockchip/rk33xx/rk33xx.c:225:      if (rkidb_get_sn(tmp_buf)) {
u-boot/board/rockchip/rk32xx/rk32xx.c:219:      if (rkidb_get_sn(tmp_buf)) {
匹配到二进制文件 u-boot/board/rockchip/rk32xx/rk32xx.o
匹配到二进制文件 u-boot/board/rockchip/rk32xx/built-in.o
u-boot/board/rockchip/common/rkloader/idblock.c:565:int rkidb_get_sn(char* buf)
u-boot/board/rockchip/common/rkloader/idblock.su:7:idblock.c:565:5:rkidb_get_sn 16      static
u-boot/board/rockchip/common/rkloader/idblock.h:252:int rkidb_get_sn(char *buf);
匹配到二进制文件 u-boot/board/rockchip/common/rkloader/idblock.o
匹配到二进制文件 u-boot/board/rockchip/common/built-in.o
u-boot/u-boot.map:1468: .text.rkidb_get_sn
u-boot/u-boot.map:1470:                0x0000000060008bc4                rkidb_get_sn
u-boot/u-boot.map:4608: .rel.text.rkidb_get_sn
u-boot/System.map:219:60008bc4 T rkidb_get_sn
匹配到二进制文件 u-boot/u-boot
xxxx@server01:~/workspace/rk3128_tablet$

实现在u-boot/board/rockchip/common/rkloader/idblock.c文件,打开它

int  (char* buf)
{
	int size;
	Sector3Info *pSec3;
	uint8 *pidbbuf = (uint8 *)gIdDataBuf;

	pSec3 = (Sector3Info *)(pidbbuf + IDBLOCK_SIZE * IDBLOCK_SN);

	size = pSec3->snSize;
	if (size <= 0 || size > SN_MAX_SIZE) {
		PRINT_E("empty serial no.\n");
		return false;
	}
	strncpy(buf, (char *)pSec3->sn, size);
	buf[size] = '\0';
	PRINT_E("sn: %s\n", buf);
	return true;
}

可以看出是通过ID Block去读的,通过地址偏移取值拿到的,那我们继续找寻哪里给这个gIdDataBuf赋的值。

搜索一下,根据搜索出的信息去筛选

xxxxx@server01:~/workspace/rk3128_tablet$ grep -nrw "gIdDataBuf" u-boot/
匹配到二进制文件 u-boot/board/rockchip/common/storage/storage.o
u-boot/board/rockchip/common/storage/storage.h:197:EXT uint32 gIdDataBuf[512] __attribute__((aligned(ARCH_DMA_MINALIGN)));
u-boot/board/rockchip/common/SecureBoot/SecureBoot.c:133:       FlashSramLoadStore(&gIdDataBuf[384], 1536, 1, 512);  // idblk sn info
匹配到二进制文件 u-boot/board/rockchip/common/SecureBoot/SecureBoot.o
匹配到二进制文件 u-boot/board/rockchip/common/mediaboot/sdmmcBoot.o
u-boot/board/rockchip/common/mediaboot/sdmmcBoot.c:120:         ret1 = SDM_Read(ChipSel, SD_CARD_BOOT_PART_OFFSET, 4, gIdDataBuf);
u-boot/board/rockchip/common/mediaboot/sdmmcBoot.c:123:                 if (gIdDataBuf[0] == 0xFCDC8C3B) {
匹配到二进制文件 u-boot/board/rockchip/common/mediaboot/sdmmcBoot.c
u-boot/board/rockchip/common/mediaboot/UMSBoot.c:307:                   __UMSReadLBA(usb_stor_curr_dev, UMS_BOOT_PART_OFFSET, gIdDataBuf, 4);
u-boot/board/rockchip/common/mediaboot/UMSBoot.c:308:                   if (gIdDataBuf[0] == 0xFCDC8C3B) {
u-boot/board/rockchip/common/mediaboot/UMSBoot.c:309:                           if (0 == gIdDataBuf[128+104/4]) {
u-boot/board/rockchip/common/mediaboot/UMSBoot.c:313:                           } else if (1 == gIdDataBuf[128+104/4]) {
u-boot/board/rockchip/common/mediaboot/sdhciBoot.c:53:  block_mmc_read(SDHCI_EMMC_DEV_ID, SD_CARD_BOOT_PART_OFFSET, 4, gIdDataBuf);
u-boot/board/rockchip/common/rkloader/idblock.c:30:extern uint32 gIdDataBuf[512];
u-boot/board/rockchip/common/rkloader/idblock.c:505:            pdst = (uint8 *)gIdDataBuf;
u-boot/board/rockchip/common/rkloader/idblock.c:512:    GetIdblockDataNoRc4((char *)&gIdDataBuf[128 * 2], 512);
u-boot/board/rockchip/common/rkloader/idblock.c:513:    GetIdblockDataNoRc4((char *)&gIdDataBuf[128 * 3], 512);
u-boot/board/rockchip/common/rkloader/idblock.c:532:    if (gIdDataBuf[0] == 0xFCDC8C3B) {
u-boot/board/rockchip/common/rkloader/idblock.c:533:            memcpy((char *)&idb0_info, gIdDataBuf, 512);
u-boot/board/rockchip/common/rkloader/idblock.c:545:    uint8 *buf = (uint8 *)&gIdDataBuf[0];
u-boot/board/rockchip/common/rkloader/idblock.c:569:    uint8 *pidbbuf = (uint8 *)gIdDataBuf;
u-boot/board/rockchip/common/rkloader/idblock.c:588:    uint8 *pidbbuf = (uint8 *)gIdDataBuf;
u-boot/board/rockchip/common/rkloader/idblock.c:609:    uint8 *pidbbuf = (uint8 *)gIdDataBuf;
匹配到二进制文件 u-boot/board/rockchip/common/rkloader/idblock.o
匹配到二进制文件 u-boot/board/rockchip/common/built-in.o
u-boot/u-boot.map:6203: .bss.gIdDataBuf
u-boot/u-boot.map:6205:                0x000000006009b5c0                gIdDataBuf
u-boot/System.map:1464:6009b5c0 B gIdDataBuf
匹配到二进制文件 u-boot/u-boot
xxxxx@server01:~/workspace/rk3128_tablet$

我们这里的目的是需要知道哪里给gIdDataBuf其赋值,所以我们直接查看有编译到产出.o文件的并且有可能是直接给它赋值的文件及函数位置。

文件位置:u-boot/board/rockchip/common/mediaboot/sdmmcBoot.c

从名字就可以大概看出,这是操作sdmmc的,也就是eMMCSD卡的地方,好的继续看函数。

uint32 SdmmcInit(uint32 ChipSel)
{
	int32 ret1 = SDM_SUCCESS;
	uint32 ioctlParam[5] = {0, 0, 0, 0, 0};

	.....
	
    ret1 = SdmmcReinit(ChipSel);
	if (ret1 == SDM_SUCCESS) { /* 卡能识别 */
#ifdef EMMC_NOT_USED_BOOT_PART
		ioctlParam[0] = ChipSel;
		
        .....
            
		/* id blk data */
		ret1 = SDM_Read(ChipSel, SD_CARD_BOOT_PART_OFFSET, 4, gIdDataBuf);//这里就是加载eMMC中id block数据的地方
#ifdef RK_SDCARD_BOOT_EN
		if (ChipSel == 0) {
			if (gIdDataBuf[0] == 0xFCDC8C3B) {
				gSdCardInfoTbl[ChipSel].FwPartOffset = SD_CARD_FW_PART_OFFSET;
				if (0 == gIdDataBuf[128 + 104 / 4]) { /* sd卡升级 */
					gsdboot_mode = SDMMC_SDCARD_UPDATE;
					PRINT_E("SDCard Update.\n");
				} else if (1 == gIdDataBuf[128 + 104 / 4]) { /* sd 卡运行 */
					gsdboot_mode = SDMMC_SDCARD_BOOT;
					PRINT_E("SDCard Boot.\n");
				}
			} else {
	.....
	return ERROR;
}

好的,从上面可以看出,gIdDataBuf里是存在eMMC上某个地方的数据,通过SDM_Read去读取加载的。

其实到这里,已经非常明确了,但是秉着一探到底的原则,我们继续往前~

看看SdmmcInit是哪里调用的?

经过grep跟踪大法一顿操作,加上分析,发现SdmmcInit是以方法结构体的方式存在于u-boot/board/rockchip/common/storage/storage.c文件中,具体如下:

#ifdef RK_SDMMC_BOOT_EN
static MEM_FUN_T emmcFunOp =
{
	2,
	BOOT_FROM_EMMC,
	0,
	SdmmcInit,
	SdmmcReadID,
	SdmmcBootReadPBA,
	SdmmcBootWritePBA,
	SdmmcBootReadLBA,
	SdmmcBootWriteLBA,
	SdmmcBootErase,
	SdmmcReadFlashInfo,
	SdmmcCheckIdBlock,
	NULL,
	NULL,
	NULL,
	SdmmcGetCapacity,
	SdmmcSysDataLoad,
	SdmmcSysDataStore,
	SdmmcBootEraseData,
};
#endif

然后又被包含在了一个结构体指针数组里:

static MEM_FUN_T *memFunTab[] = 
{
#ifdef RK_UMS_BOOT_EN
	&UMSFunOp,
#endif

#ifdef RK_SDCARD_BOOT_EN
	&sd0FunOp,
#endif

#if defined(RK_SDMMC_BOOT_EN) || defined(RK_SDHCI_BOOT_EN)
	&emmcFunOp,
#endif

#ifdef RK_FLASH_BOOT_EN
	&NandFunOp,
#endif

#ifdef CONFIG_RK_NVME_BOOT_EN
	&nvmeFunOp,
#endif
};

最后被StorageInit调用:

#define MAX_MEM_DEV	(sizeof(memFunTab)/sizeof(MEM_FUN_T *))


int32 StorageInit(void)
{
	uint32 memdev;

	memset((uint8*)&g_FlashInfo, 0, sizeof(g_FlashInfo));
	for(memdev=0; memdev<MAX_MEM_DEV; memdev++)
	{
		gpMemFun = memFunTab[memdev];
		if(memFunTab[memdev]->Init(memFunTab[memdev]->id) == 0)
		{
			memFunTab[memdev]->Valid = 1;
			StorageReadFlashInfo((uint8*)&g_FlashInfo);
			vendor_storage_init();
			return 0;
		}
	}

	/* if all media init error, usding null function */
	gpMemFun = &nullFunOp;

	return -1;
}

然后被在RK的板级逻辑u-boot/board/rockchip/rk32xx/rk32xx.c中的board_storage_init调用

int board_storage_init(void)
{
	int ret = 0;

	if (StorageInit() == 0) {
		printf("storage init OK!\n");
		ret = 0;
	} else {
		printf("storage init fail!\n");
		ret = -1;
	}

	return ret;
}

board_storage_init又在u-boot/arch/arm/lib/board.cuboot启动阶段被调用:

/************************************************************************
 *
 * This is the next part if the initialization sequence: we are now
 * running from RAM and have a "normal" C environment, i. e. global
 * data can be written, BSS has been cleared, the stack size in not
 * that critical any more, etc.
 *
 ************************************************************************
 */

void board_init_r(gd_t *id, ulong dest_addr)
{
	ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
	ulong flash_size;
#endif

	.....

#ifdef CONFIG_ROCKCHIP
	board_storage_init();//这里调用的
#endif

	.....
	
#ifdef CONFIG_BOARD_LATE_INIT
	board_late_init();
#endif
	.....
	/* main_loop() can return to retry autoboot, if so just run it again. */
	for (;;) {
		main_loop();
	}

	/* NOTREACHED - no way out of command loop except booting */
}

然后来到uboot最靠前的汇编s文件u-boot/arch/arm/lib/crt0.S里,调用了board_init_r这个C函数:

/* Set up final (full) environment */

	bl	c_runtime_cpu_setup	/* we still call old routine here */

	ldr	r0, =__bss_start	/* this is auto-relocated! */
	ldr	r1, =__bss_end		/* this is auto-relocated! */

	mov	r2, #0x00000000		/* prepare zero to clear BSS */

clbss_l:cmp	r0, r1			/* while not at end of BSS */
	strlo	r2, [r0]		/* clear 32-bit BSS word */
	addlo	r0, r0, #4		/* move to next */
	blo	clbss_l

	bl coloured_LED_init
	bl red_led_on

	/* call board_init_r(gd_t *id, ulong dest_addr) */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
	ldr	pc, =board_init_r	/* this is auto-relocated! */

	/* we should not return here. */

#endif

ENDPROC(_main)

uboot在启动时,从eMMC某块区域读取了一定字节大小的数据,根据芯片厂商定义的偏移地址取出一组sn号,然后再用这串sn号以“androidboot.serialno=”前缀设进cmdline参数里,在启动kernel时传入,然后kernel将收到的cmdline数据写入到/proc/cmdline里,接着启动系统的第一个程序init程序,init程序从/proc/cmdline读出对应的“androidboot.serialno“数据以“ro.boot.serialno”名字设置属性,然后drmserviceinit程序设置的"ro.boot.serialno"属性来设置“ro.serialno,最后系统通过getprop ro.serialno来获取,APP通过Build.getSerial()Build.SERIAL来获取。

至此,大功告成

感谢阅读~

希望能帮到你~

see you~

码字不易,转载请注明原作者 ~ (from:https://erdong.work

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK