Linux启动流程_LK流程_bootstrap2(1)

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

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

写在前面的话

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

这里就开始啦!

上一篇我们已经分析过Kmain函数了,现在来回顾一遍发现Kmain显示初始化了一些硬件资源,包括线程初始化,CPU的一些设置,平台初始化(board、clk、qgic、scm),和串口调试,开启这些资源就是为LK和kernel的运行准备环境。(这一点从完成了这些初始化之后才打印 welcome lk 也能看得出来)接下来就是一些软件的初始化,包括为bootstate赋值(bootloader的启动时间),堆初始化,dpc初始化,定时器初始化。做完这些准备工作之后,就启动了bootstrap2线程,我们这篇文章就看一下这个线程做了什么。

大致描述bootstrap2

本文介绍一下两个函数,其他两个函数在很多平台是空函数
	target_init();					//	初始化spmi、emmc,检测pwr key按键和震动等
	apps_init();					//	调用所有apps字段的init函数

target_init

void target_init(void)
{
  uint32_t base_addr;
  uint8_t slot;

  dprintf(INFO, "target_init()\n");

  /*初始化 SPMI(system power management interface) 系统电源管理结构的控制器*/
  spmi_init(PMIC_ARB_CHANNEL_NUM, PMIC_ARB_OWNER_ID);

  /*获取并设置 音量上键 和 音量下键 的状态,而所有按键的状态都保存在全局数组 key_bitmap 中*/
  target_keystatus();

  target_sdc_init();			//初始化 emmc
  if (partition_read_table())	//读取分区表
    {
      dprintf(CRITICAL, "Error reading the partition table info\n");
      ASSERT(0);
    }

#if LONG_PRESS_POWER_ON
  shutdown_detect();			//检测开机时间不足够长则关机
#endif

#if PON_VIB_SUPPORT
  vib_timed_turn_on(VIBRATE_TIME);		//开启手机震动 4/1 秒,提示用户手机开启
#endif

  if (target_use_signed_kernel())
    target_crypto_init_params();		//初始化加密解密引擎,用于解密内核
}

现在针对这个函数做一些说明:

1.emmc

emmc 是目前手机领域流行的存储设备,相当于 pc 端的 ssd 硬盘,这里涉及到一个比较重要的全局数据 static struct mmc_device *dev:

/*
 * sdhci host structure, holding information about host
 * controller parameters
 */
struct sdhci_host {
  uint32_t base;           /* Base address for the host */
  uint32_t cur_clk_rate;   /* Running clock rate */
  uint32_t timing;         /* current timing for the host */
  bool tuning_in_progress; /* Tuning is being executed */
  uint8_t major;           /* host controller minor ver */
  uint16_t minor;          /* host controller major ver */
  bool use_cdclp533;       /* Use cdclp533 calibration circuit */
  event_t* sdhc_event;     /* Event for power control irqs */
  struct host_caps caps;   /* Host capabilities */
  struct sdhci_msm_data *msm_host; /* MSM specific host info */
};

/* mmc card register */
struct mmc_card {
  uint32_t rca;            /* Relative addres of the card*/
  uint32_t ocr;            /* Operating range of the card*/
  uint32_t block_size;     /* Block size for the card */
  uint32_t wp_grp_size;    /* WP group size for the card */
  uint64_t capacity;       /* card capacity */
  uint32_t type;           /* Type of the card */
  uint32_t status;         /* Card status */
  uint8_t *ext_csd;        /* Ext CSD for the card info */
  uint32_t raw_csd[4];     /* Raw CSD for the card */
  uint32_t raw_scr[2];     /* SCR for SD card */
  uint32_t rpmb_size;      /* Size of rpmb partition */
  uint32_t rel_wr_count;   /* Reliable write count */
  struct mmc_cid cid;      /* CID structure */
  struct mmc_csd csd;      /* CSD structure */
  struct mmc_sd_scr scr;   /* SCR structure */
  struct mmc_sd_ssr ssr;   /* SSR Register */
};

/* mmc device config data */
struct mmc_config_data {
  uint8_t slot;          /* Sdcc slot used */
  uint32_t pwr_irq;       /* Power Irq from card to host */
  uint32_t sdhc_base;    /* Base address for the sdhc */
  uint32_t pwrctl_base;  /* Base address for power control registers */
  uint16_t bus_width;    /* Bus width used */
  uint32_t max_clk_rate; /* Max clock rate supported */
  uint8_t hs200_support; /* SDHC HS200 mode supported or not */
  uint8_t hs400_support; /* SDHC HS400 mode supported or not */
  uint8_t use_io_switch; /* IO pad switch flag for shared sdc controller */
};

/* mmc device structure */
struct mmc_device {
  struct sdhci_host host;          /* Handle to host controller */
  struct mmc_card card;            /* Handle to mmc card */
  struct mmc_config_data config;   /* Handle for the mmc config data */
};

static struct mmc_device *dev;

2.读取分区表

读取分区表,不论是 MBR 还是 GPT 分区格式,读取后都存放在以下结构中:

struct partition_entry {
  unsigned char type_guid[PARTITION_TYPE_GUID_SIZE];
  unsigned dtype;
  unsigned char unique_partition_guid[UNIQUE_PARTITION_GUID_SIZE];
  unsigned long long first_lba;
  unsigned long long last_lba;
  unsigned long long size;
  unsigned long long attribute_flag;
  unsigned char name[MAX_GPT_NAME_SIZE];
  uint8_t lun;
};

struct partition_entry *partition_entries;

3.检测pwr key

在开启了长按开机键开机的设置后才会生效, 很多平台默认开启了此选项,到这里检测开机时间不足够长则关机,按照这里的代码理解,关机后,每次按下开机键,系统其实已经启动到 shutdown_detect 这个位置了,不过由于按键时间不长,所以没有屏幕没有点亮,系统没有完全启动。


apps_init

extern const struct app_descriptor __apps_start;
extern const struct app_descriptor __apps_end;

struct app_descriptor {
  const char *name;
  app_init  init;
  app_entry entry;
  unsigned int flags;
};

static int app_thread_entry(void *arg)
{
	const struct app_descriptor *app = (const struct app_descriptor *)arg;

	app->entry(app, NULL);

	return 0;
}

static void start_app(const struct app_descriptor *app)
{
	thread_t *thr;
	printf("starting app %s\n", app->name);

	thr = thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
	if(!thr)
	{
		return;
	}
	thread_resume(thr);
}

void apps_init(void)
{
	const struct app_descriptor *app;

	/* call all the init routines */
	for (app = &__apps_start; app != &__apps_end; app++) {
		if (app->init)
			app->init(app);
	}

	/* start any that want to start on boot */
	for (app = &__apps_start; app != &__apps_end; app++) {
		if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {
			start_app(app);
		}
	}
}

整个遍历 app 并启动的过程并不复杂,有趣的是 __apps_start 和 __apps_end 的定义,这两个变量符号在所有源文件中并不存在,而是在 arch/arm/*.ld 链接脚本中存在,这样类似的结构在前面 heap_init 中已经遇到过,区别在于 __apps_start 和 __apps_end 是自定义的两个符号。代表了自定义段 .apps 的开始位置和结束位置。也就是说所有的 app 都通过在特殊的段 .apps 中注册实现了一套插件系统,是一个十分精巧的设计。后续的任何新的 app 只需要使用以下两个宏声明即可注册到 .apps 段中:

#define __SECTION(x) __attribute((section(x)))
#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };

下面是 aboot app 声明的 app 的实际例子:

APP_START(aboot)
.init = aboot_init,
APP_END

宏展开后的实际效果如下:

struct app_descriptor _app_aboot ____attribute((section(".apps"))) = {.name = "aboot", .init = aboot_init};

这样通过遍历 .apps 段就可以获取 aboot 的描述信息,调用 aboot 的 init 函数了


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值