Linux启动流程_LK流程_aboot_init(不包含recovery boot)(2.1)

    深入,并且广泛
    				-沉默犀牛

此篇博客原博客来自freebuf,原作者SetRet。原文链接:https://www.freebuf.com/news/135084.html

写在前面的话

写这篇文章之前,我只好假定你所知道的跟我一样浅薄(针对本文这一方面),所以如果你看到一些实在是太小儿科的内容,请你多加担待,这确实就是我目前的水平,谢谢。

这里就开始啦!

上一篇博客分析了bootstrap2都做了些什么,内容并不多,我们大概总结一下:
先后进行了:初始化了 SPMI(system power management interface) 系统电源管理结构的控制器,检测音量上下按键的状态并记录,emmc,读分区表,检测pwr_key按键时间、震动,初始化加密引擎,然后就遍历了.apps段,运行在这个字段中的init函数,接下来就进入到了aboot_init函数了。(很多平台在这个阶段只有aboot这个app)

大致描述aboot_init

aboot_init中的代码比较多,分为四个部分来介绍:
1.init部分
2.检测启动方式
3.fastboot部分模式启动
4.非fastboot模式启动

init部分

1.获取分页大小,并保存到全局变量 page_size 和 page_mask 中

/* Setup page size information for nv storage */
	if (target_is_emmc_boot())
	{
		page_size = mmc_page_size();
		page_mask = page_size - 1;
		mmc_blocksize = mmc_get_device_blocksize();
		mmc_blocksize_mask = mmc_blocksize - 1;
	}
	else
	{
		page_size = flash_page_size();
		page_mask = page_size - 1;
	}
	ASSERT((MEMBASE + MEMSIZE) > MEMBASE);

2.从 emmc 中的 aboot 分区或 deviceinfo 分区获取 device

read_device_info(&device);

这里的 device 是一个全局变量,其结构如下:

typedef struct device_info device_info;

#define DEVICE_MAGIC "ANDROID-BOOT!"
#define DEVICE_MAGIC_SIZE 13
#define MAX_PANEL_ID_LEN 64
#define MAX_VERSION_LEN  64

struct device_info
{
  unsigned char magic[DEVICE_MAGIC_SIZE];
  bool is_unlocked;
  bool is_tampered;
  bool is_verified;
  bool charger_screen_enabled;
  char display_panel[MAX_PANEL_ID_LEN];
  char bootloader_version[MAX_VERSION_LEN];
  char radio_version[MAX_VERSION_LEN];
};

static device_info device = {DEVICE_MAGIC, 0, 0, 0, 0, {0}, {0},{0}};

其中保存的信息在后期经常会用到,比如 device_info.is_unlocked 就是 bootloader 是否解锁的标志位。


3.从 emmc 中的 config 分区或 frq 分区获取 is_allow_unlock 标志位

read_allow_oem_unlock(&device);

一般都为允许,使用加密手段来限制解锁


4.初始化开始屏幕信息和全局的屏幕信息缓存 display_panel_buf,大小为 128

#if DISPLAY_SPLASH_SCREEN
#if NO_ALARM_DISPLAY
	if (!check_alarm_boot()) {
#endif
		dprintf(SPEW, "Display Init: Start\n");
#if DISPLAY_HDMI_PRIMARY
	if (!strlen(device.display_panel))
		strlcpy(device.display_panel, DISPLAY_PANEL_HDMI,
			sizeof(device.display_panel));
#endif
#if ENABLE_WBC
		/* Wait if the display shutdown is in progress */
		while(pm_app_display_shutdown_in_prgs());
		if (!pm_appsbl_display_init_done())
			target_display_init(device.display_panel);
		else
			display_image_on_screen();
#else
		target_display_init(device.display_panel);
#endif
		dprintf(SPEW, "Display Init: Done\n");
#if NO_ALARM_DISPLAY
	}
#endif
#endif

5.获取序列号并保存在全局变量 sn_buf 中

	target_serialno((unsigned char *) sn_buf);
	dprintf(SPEW,"serial number: %s\n",sn_buf);

序列号就存储在 target_sdc_init 中初始化的 dev 变量中,还记得target_sdc_init这个函数吗?
在bootstrap2 -> target_init -> target_sdc_init //初始化emmc


2.检测启动方式

这部分的代码只是通过检测关机的方式,按键的状态来设定确定进入哪种模式的启动方式,但是不同的机型,对应的按键组合并不相同

/*
	 * Check power off reason if user force reset,
	 * if yes phone will do normal boot.
	 */
	if (is_user_force_reset())
		goto normal_boot;

	/* Check if we should do something other than booting up */
	if (keys_get_state(KEY_VOLUMEUP) && keys_get_state(KEY_VOLUMEDOWN))
	{
		dprintf(ALWAYS,"dload mode key sequence detected\n");
		reboot_device(EMERGENCY_DLOAD);
		dprintf(CRITICAL,"Failed to reboot into dload mode\n");

		boot_into_fastboot = true;
	}
	if (!boot_into_fastboot)
	{
		if (keys_get_state(KEY_HOME) || keys_get_state(KEY_VOLUMEUP))
			boot_into_recovery = 1;
		if (!boot_into_recovery &&
			(keys_get_state(KEY_BACK) || keys_get_state(KEY_VOLUMEDOWN)))
			boot_into_fastboot = true;
	}
	#if NO_KEYPAD_DRIVER
	if (fastboot_trigger())
		boot_into_fastboot = true;
	#endif

#if USE_PON_REBOOT_REG
	reboot_mode = check_hard_reboot_mode();
#else
	reboot_mode = check_reboot_mode();
#endif
	if (reboot_mode == RECOVERY_MODE)
	{
		boot_into_recovery = 1;
	}
	else if(reboot_mode == FASTBOOT_MODE)
	{
		boot_into_fastboot = true;
	}
	else if(reboot_mode == ALARM_BOOT)
	{
		boot_reason_alarm = true;
	}
#if VERIFIED_BOOT
	else if (VB_M <= target_get_vb_version())
	{
		if (reboot_mode == DM_VERITY_ENFORCING)
		{
			device.verity_mode = 1;
			write_device_info(&device);
		}
#if ENABLE_VB_ATTEST
		else if (reboot_mode == DM_VERITY_EIO)
#else
		else if (reboot_mode == DM_VERITY_LOGGING)
#endif
		{
			device.verity_mode = 0;
			write_device_info(&device);
		}
		else if (reboot_mode == DM_VERITY_KEYSCLEAR)
		{
			if(send_delete_keys_to_tz())
				ASSERT(0);
		}
	}
#endif

常用的就只有 普通模式/recovery 模式/fastboot 模式 3 种,也是需要重点分析的 3 种
非 fastboot 模式启动就是 recovery 模式 或者 普通模式启动,这两者所使用的是同一套加载流程,所以可以归类为同一类。


3.fastboot部分模式启动

fastboot 模式启动fastboot 模式是 android 定义的一套通信协议,可以指定参数写入 emmc 分区的方法,通俗的说就是刷机的接口

fastboot:
	
	aboot_fastboot_register_commands();			//fastboot指令注册
	partition_dump();
	
	/*设置 usb 监听的线程,并且启动了解析指令的线程*/
	fastboot_init(target_get_scratch_address(), target_get_max_flash_size());		

fastboot 本身就是 lk 的一部分,负责对外的一个接口而已,并不是一个单独的系统,是依赖于 lk 存在的。fastboot 可以分为以下几个方面:

  • fastboot 指令注册
  • fastboot 监听启动
  • fastboot 指令解析

按照这个分类来一步步分析整个 fastboot 的框架

fastboot 指令注册

void aboot_fastboot_register_commands(void)
{
  int i;

  struct fastboot_cmd_desc cmd_list[] = {
                      /* By default the enabled list is empty. */
                      {"", NULL},
                      /* move commands enclosed within the below ifndef to here
                       * if they need to be enabled in user build.
                       */
#ifndef DISABLE_FASTBOOT_CMDS
                      /* Register the following commands only for non-user builds */
                      {"flash:", cmd_flash},
                      {"erase:", cmd_erase},
                      {"boot", cmd_boot},
                      {"continue", cmd_continue},
                      {"reboot", cmd_reboot},
                      {"reboot-bootloader", cmd_reboot_bootloader},
                      {"oem unlock", cmd_oem_unlock},
                      {"oem unlock-go", cmd_oem_unlock_go},
                      {"oem lock", cmd_oem_lock},
                      {"oem verified", cmd_oem_verified},
                      {"oem device-info", cmd_oem_devinfo},
                      {"preflash", cmd_preflash},
                      {"oem enable-charger-screen", cmd_oem_enable_charger_screen},
                      {"oem disable-charger-screen", cmd_oem_disable_charger_screen},
                      {"oem select-display-panel", cmd_oem_select_display_panel},
#if UNITTEST_FW_SUPPORT
                      {"oem run-tests", cmd_oem_runtests},
#endif
#endif
                      };

  int fastboot_cmds_count = sizeof(cmd_list)/sizeof(cmd_list[0]);
  for (i = 1; i < fastboot_cmds_count; i++)
    fastboot_register(cmd_list[i].name,cmd_list[i].cb);
/*********************************************************************************************************/
  /* publish variables and their values */
  fastboot_publish("product",  TARGET(BOARD));
  fastboot_publish("kernel",   "lk");
  fastboot_publish("serialno", sn_buf);

  /*
   * partition info is supported only for emmc partitions
   * Calling this for NAND prints some error messages which
   * is harmless but misleading. Avoid calling this for NAND
   * devices.
   */
  if (target_is_emmc_boot())
    publish_getvar_partition_info(part_info, ARRAY_SIZE(part_info));

  /* Max download size supported */
  snprintf(max_download_size, MAX_RSP_SIZE, "\t0x%x",
      target_get_max_flash_size());
  fastboot_publish("max-download-size", (const char *) max_download_size);
  /* Is the charger screen check enabled */
  snprintf(charger_screen_enabled, MAX_RSP_SIZE, "%d",
      device.charger_screen_enabled);
  fastboot_publish("charger-screen-enabled",
      (const char *) charger_screen_enabled);
  snprintf(panel_display_mode, MAX_RSP_SIZE, "%s",
      device.display_panel);
  fastboot_publish("display-panel",
      (const char *) panel_display_mode);
  fastboot_publish("version-bootloader", (const char *) device.bootloader_version);
  fastboot_publish("version-baseband", (const char *) device.radio_version);
}

这个函数可以分为两部分:
1.fastboot 指令注册,fastboot 的指令使用了一个 struct fastboot_cmd_desc 类型的局部数组来保存,这个结构包含了 fastboot 的 指令字符串 和 处理函数。有了这个数组后,就可以使用 fastboot_register 函数将指令注册到全局链表 cmd_list 中,这样fastboot 所有的指令都可以通过遍历链表而得到。

2.fastboot 数据注册,fastboot 模式还保存了一些主要的设备和厂商信息,这些信息都统一由 fastboot_publish 来注册,注册的信息和指令一样,存储在一个全局链表 varlist 中,这个结构比较简单,只有名称和对应的数据,和指令一样的道理,通过遍历 varlist 就可以找到全部的数据。

通过上面的两个步骤后, fastboot 的所有 指令 和 数据 就注册完成了,接下来的需要启动 fastboot 对 USB 设备的监听,以接送 fastboot 命令


fastboot 监听启动

fastboot_init 的代码可以分为以下 3 个流程:

  1. usb_if(usb controller interface) 初始化

  2. usb_if 绑定

  3. fastboot 线程启动

usb_if(usb controller interface) 初始化

usb_if 是一个全局变量,这个阶段就是为这个结构体赋予一些需要的值,方便后面使用

  1. 初始化了两个全局变量 download_basedownload_size, 这块空间是 fastboot 刷入系统时的缓冲区
  2. 获取序列号并赋值给 surf_udc_device
  3. 初始化 usb_if 中的各控制函数,
usb_if 绑定

usb_if 初始化完成后,需要设置通信的渠道,和响应命令的方法
创建了两个事件, usb_onlinetxn_done:
usb_online 是响应 usb 上线的事件,然后等待处理命令
txn_done 则是在请求 usb 操作时等待返回的信号,由 req_complete 函数发送此信号

fastboot 线程启动

这一部分比较简单,主要的功能就是启动一个线程等待 fastboot 的指令传入,并且启动 udc

  1. 新注册了以下两条指令 getvar: 和 download:, 以及一条数据 version
  2. 创建并启动 fastboot 线程,线程的功能就是等待 usb_online 事件,然后解析 fastboot 指令
  3. 开启 udc

fastboot 指令解析

申请并清零缓冲区,然后使用 usb_read 接口获取 usb 数据。
遍历 cmdlist, 比对 指令 调用对应指令的处理函数


猜你喜欢

转载自blog.csdn.net/qq_35065875/article/details/84838016
2.1