在CIA VAULT 7 , NSA Shadow Broker 泄漏事件之后,信息安全也逐渐受到越来越多的关注,作为信息安全行业从业人员,除了参与这一场集体躁动之外,还需回归技术本身,对泄漏的资料和工具进行分析研究,以做到知己知彼。
Sec·Ret 团队在对所有泄漏资料进行分析后,形成了一系列技术研究文章并会陆续进行发表,希望在增强自身技术储备的同时,也能够和同行多多交流,共同成长。
在Bootloader系列文章中,我们对主流bootloader的前身高通 little kernel 进行了深入的源码分析,并对CIA 泄漏的cadmium.pdf文档中的三星bootloader漏洞进行了分析和复现,在此之外,对主流手机厂商如华为,三星,小米系列手机产品的bootloader进行了标准流程化安全审计,这些工作最后都会以文章的形式呈现在bootloader研究系列中。由于自身技术有限,若文章中有表述不准确的地方,忘同行指正。
什么是 bootloader
bootloader 就是在 操作系统内核
启动之间运行的一段程序,它的作用是初始化一些硬件设备,将内核加载从 ROM 加载 RAM 中,并做好映射关系。bootloader 的目标就是为 操作系统
提供合适的运行环境。通俗的说 bootloader 就是将 操作系统
拉起来的程序。
什么是 lk(little kernel)
littlekernel 是一个为嵌入式设备开发的微内核,google 正在研发的 Fuchsia 操作系统的微内核 magenta 也是基于 lk 开发。而我们重点研究的 lk 是高通基于 {aboot} pass partition table through atags · littlekernel/lk@42168f2 commit 后 fork 修改而来。
高通修改后的版本和原始是 lk 已经有了很多的差别,高通主要是将 lk 作为一个 android bootloader 来适配自家的各种不同的芯片,并提供 fastboot 接口来处理 fastboot 命令。
此文章中以后的 lk 均指代高通的 lk,如果需要对 github 的 lk 做描述,则用 origin lk 表示。
lk 下载及编译
-
从 kernel/lk 处 clone 源码到本地。
12<span class="hljs-attribute">git</span> clone git://codeaurora.org/kernel/lk.git -
切换到主分支。
12git checkout -b mylk remotes<span class="hljs-meta-keyword">/origin/</span>master -
配置编译环境。1
123export PATH=$PATH<span class="hljs-symbol">:/path/to/arm-linux-androideabi-/bin</span>export TOOLCHAIN_PREFIX=arm-linux-androideabi- -
开始编译。编译的时候需要选择对应的目标平台编译,对应目标平台可以在
target
目录下找到。2
1234567891011121314151617181920212223242526272829303132333435➔ ll -F target | <span class="hljs-keyword">grep</span> <span class="hljs-string">'/$'</span>drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> apq8084/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">4</span> takboo staff <span class="hljs-number">136</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> armemu/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">4</span> takboo staff <span class="hljs-number">136</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">03</span> beagle/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">6</span> takboo staff <span class="hljs-number">204</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> fsm901<span class="hljs-number">0</span>/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">6</span> takboo staff <span class="hljs-number">204</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> fsm990<span class="hljs-number">0</span>/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">7</span> takboo staff <span class="hljs-number">238</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> mdm9607/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">7</span> takboo staff <span class="hljs-number">238</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> mdm9615/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">8</span> takboo staff <span class="hljs-number">272</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> mdm9625/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> mdm9635/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> mdm964<span class="hljs-number">0</span>/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">8</span> takboo staff <span class="hljs-number">272</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm7627_surf/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm7627a/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">8</span> takboo staff <span class="hljs-number">272</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm7630_surf/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8226/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm861<span class="hljs-number">0</span>/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">8</span> takboo staff <span class="hljs-number">272</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8660_surf/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">10</span> takboo staff <span class="hljs-number">340</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8909/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8916/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">11</span> takboo staff <span class="hljs-number">374</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8952/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm896<span class="hljs-number">0</span>/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">9</span> takboo staff <span class="hljs-number">306</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8974/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">11</span> takboo staff <span class="hljs-number">374</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8994/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">11</span> takboo staff <span class="hljs-number">374</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msm8996/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">6</span> takboo staff <span class="hljs-number">204</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> msmtitanium/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">4</span> takboo staff <span class="hljs-number">136</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">03</span> osk5912/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">3</span> takboo staff <span class="hljs-number">102</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> pc-x86/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">3</span> takboo staff <span class="hljs-number">102</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">03</span> qemu-arm/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">8</span> takboo staff <span class="hljs-number">272</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> qsd8250_ffa/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">8</span> takboo staff <span class="hljs-number">272</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> qsd8250_surf/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">8</span> takboo staff <span class="hljs-number">272</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> qsd8650a_st1x/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">6</span> takboo staff <span class="hljs-number">204</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">03</span> sam7ex256/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">7</span> takboo staff <span class="hljs-number">238</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> surf-msm7k/drwxr-xr-<span class="hljs-keyword">x</span> <span class="hljs-number">6</span> takboo staff <span class="hljs-number">204</span>B May <span class="hljs-number">4</span> <span class="hljs-number">15</span>:<span class="hljs-number">05</span> surf-qsd8k/
当前我们编译msm8916
平台的 lk。
12<span class="hljs-attribute">make</span> msm8916 EMMC_BOOT=<span class="hljs-number">1</span>
编译时可能遇到make[1]: *** No rule to make target build-msm8916/lib/openssl/crypto/bn/asm/armv4-mont.o, needed by build-msm8916/lk. Stop.
的问题。解决的方案是将lib/openssl/crypto/bn/asm
中的armv4-mont.s
文件重命名为armv4-mont.S
即可。
lk 启动流程
- 进行各种早期的初始化工作,包括 cpu, emmc, ddr, clocks, thread etc。
- 判断进入 recovery 或 fastboot 的条件是否被触发。
- 从 emmc 中获取 boot.img 并加载到指定内存区域 (scratch region)。
- 从 scratch region 加载 kernel 到
KERNEL_ADDRESS
。 - 从 scratch region 加载 ramdisk 到
RAMDISK_ADDRESS
。 - 加载设备树到
TAGS_ADDRESS
。 - 关闭 cache, interrupts, 跳转到 kernel。
lk 代码流程
我们目前的研究是基于 msm8916
的代码流程来进行,在 msm8916
中不会使用到的代码暂时不分析。lk 是使用 arm 汇编
和 c
语言联合编译而成的,其中偏向硬件和底层的代码使用 arm 汇编
编写,而偏上层提供功能的代码则使用 c
编写。
入口点
lk 代码的入口点是在 arch/arm
目录下的以 .ld
为后缀的 link 脚本文件中指定。一共有以下 4 个 link 脚本文件,指定的入口点均为位于 arch/arm/crt0.S
文件中的 _start
函数:
- system-onesegment.ld
- system-twosegment.ld
- trustzone-system-onesegment.ld
- trustzone-test-system-onesegment.ld
具体使用哪一文件在 platform/msm8916/rules.mk
中指定。
_start
最主要的作用是设置一些 cpu 的特性,然后初始化各种 c 程序运行需要的栈环境,完成后直接跳转到 kmian
函数进入 c 语言环境。
kmain
kmain
函数位于 kernel/main.c
文件中, kmain
所做的工作可以用下面的流程图表示:
thread_init_early
thread_init_early
的实现位于 kernel/thread.c
文件中,其代码结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<span class="hljs-comment">/** * @brief Initialize threading system * * This function is called once, from kmain() */</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">thread_init_early</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>{ <span class="hljs-keyword">int</span> i; <span class="hljs-comment">/* initialize the run queues */</span> <span class="hljs-keyword">for</span> (i=<span class="hljs-number">0</span>; i < NUM_PRIORITIES; i++) list_initialize(&run_queue[i]); <span class="hljs-comment">/* initialize the thread list */</span> list_initialize(&thread_list); <span class="hljs-comment">/* create a thread to cover the current running state */</span> <span class="hljs-keyword">thread_t</span> *t = &bootstrap_thread; init_thread_struct(t, <span class="hljs-string">"bootstrap"</span>); <span class="hljs-comment">/* half construct this thread, since we're already running */</span> t->priority = HIGHEST_PRIORITY; t->state = THREAD_RUNNING; t->saved_critical_section_count = <span class="hljs-number">1</span>; list_add_head(&thread_list, &t->thread_list_node); current_thread = t; } |
thread_init_early
所完成的主要工作就是为 lk 完成早期的线程初始化工作,包括 运行队列
的初始化, 线程链表
的初始化, 第一个线程
3 的创建等工作。在这个过程中一共有以下 4 个重要的数据结构:
-
运行队列
run_queue。
123456789<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> NUM_PRIORITIES 32</span><span class="hljs-keyword">struct</span> list_node {<span class="hljs-keyword">struct</span> list_node *prev;<span class="hljs-keyword">struct</span> list_node *next;};<span class="hljs-keyword">static</span> <span class="hljs-keyword">struct</span> list_node run_queue[NUM_PRIORITIES];
run_queue 是作为多线程的调度中心存在,数组不同的下标对应不同的运行优先级
,具体的应用会在后面涉及到。 -
线程链表
thread_list。
12<span class="hljs-keyword">static</span> <span class="hljs-keyword">struct</span> list_node thread_list;
全局的线程链表,保存了所有创建的线程信息。 -
bootstrap
线程。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> THREAD_MAGIC 'thrd'</span>typedef <span class="hljs-keyword">struct</span> thread {<span class="hljs-keyword">int</span> magic;<span class="hljs-keyword">struct</span> list_node thread_list_node;<span class="hljs-comment">/* active bits */</span><span class="hljs-keyword">struct</span> list_node queue_node;<span class="hljs-keyword">int</span> priority;<span class="hljs-keyword">enum</span> thread_state state;<span class="hljs-keyword">int</span> saved_critical_section_count;<span class="hljs-keyword">int</span> remaining_quantum;<span class="hljs-comment">/* if blocked, a pointer to the wait queue */</span><span class="hljs-keyword">struct</span> wait_queue *blocking_wait_queue;status_t wait_queue_block_ret;<span class="hljs-comment">/* architecture stuff */</span><span class="hljs-keyword">struct</span> arch_thread arch;<span class="hljs-comment">/* stack stuff */</span><span class="hljs-keyword">void</span> *stack;size_t stack_size;<span class="hljs-comment">/* entry point */</span>thread_start_routine entry;<span class="hljs-keyword">void</span> *arg;<span class="hljs-comment">/* return code */</span><span class="hljs-keyword">int</span> retcode;<span class="hljs-comment">/* thread local storage */</span>uint32_t tls[MAX_TLS_ENTRY];<span class="hljs-keyword">char</span> name[<span class="hljs-number">32</span>];} thread_t;<span class="hljs-keyword">static</span> thread_t bootstrap_thread;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init_thread_struct</span>(<span class="hljs-params">thread_t *t, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *name</span>)</span>{memset(t, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(thread_t));t->magic = THREAD_MAGIC;strlcpy(t->name, name, <span class="hljs-keyword">sizeof</span>(t->name));}init_thread_struct(t, <span class="hljs-string">"bootstrap"</span>);<span class="hljs-comment">/* half construct this thread, since we're already running */</span>t->priority = HIGHEST_PRIORITY;t->state = THREAD_RUNNING;t->saved_critical_section_count = <span class="hljs-number">1</span>;list_add_head(&thread_list, &t->thread_list_node);current_thread = t;
每个线程的上下文信息都通过thread_t
结构体保存,而 bootstrap 是作为 lk 的第一个线程存在, 拥有最高优先级(HIGHEST_PROORITY)
。 -
current_thread
。
123<span class="hljs-comment">/* the current thread */</span><span class="hljs-keyword">thread_t</span> *current_thread;
current_thread
是一个全局变量,保存了当前运行的线程的信息。
这些数据结构都进行了初始化后,lk 就具有了创建和管理线程的结构及能力,在 msm8916
中后面环境初始化部分中的 thread_init
只是一个空接口,没有实现。
arch_early_init
arch_early_init
的代码位于 arch/arm/arch.c
文件中, arch_early_init
和所使用的 CPU 架构有很强的耦合性,这个函数的代码也主要是针对 CPU 的一些特性做设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">arch_early_init</span>(<span class="hljs-params"><span class="hljs-keyword">void</span></span>) </span>{ <span class="hljs-comment">/* turn off the cache */</span> arch_disable_cache(UCACHE); <span class="hljs-comment">/* set the vector base to our exception vectors so we dont need to double map at 0 */</span> <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> ARM_CPU_CORTEX_A8</span> set_vector_base(MEMBASE); <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> ARM_WITH_MMU</span> arm_mmu_init(); <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> <span class="hljs-comment">/* turn the cache back on */</span> arch_enable_cache(UCACHE); <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> ARM_WITH_NEON</span> <span class="hljs-comment">/* enable cp10 and cp11 */</span> uint32_t val; __<span class="hljs-function">asm__ <span class="hljs-title">volatile</span>(<span class="hljs-params"><span class="hljs-string">"mrc p15, 0, %0, c1, c0, 2"</span> : <span class="hljs-string">"=r"</span> (val</span>))</span>; val |= (<span class="hljs-number">3</span><<<span class="hljs-number">22</span>)|(<span class="hljs-number">3</span><<<span class="hljs-number">20</span>); __<span class="hljs-function">asm__ <span class="hljs-title">volatile</span>(<span class="hljs-params"><span class="hljs-string">"mcr p15, 0, %0, c1, c0, 2"</span> :: <span class="hljs-string">"r"</span> (val</span>))</span>; isb(); <span class="hljs-comment">/* set enable bit in fpexc */</span> __<span class="hljs-function">asm__ <span class="hljs-title">volatile</span>(<span class="hljs-params"><span class="hljs-string">"mrc p10, 7, %0, c8, c0, 0"</span> : <span class="hljs-string">"=r"</span> (val</span>))</span>; val |= (<span class="hljs-number">1</span><<<span class="hljs-number">30</span>); __<span class="hljs-function">asm__ <span class="hljs-title">volatile</span>(<span class="hljs-params"><span class="hljs-string">"mcr p10, 7, %0, c8, c0, 0"</span> :: <span class="hljs-string">"r"</span> (val</span>))</span>; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> ARM_CPU_CORTEX_A8</span> <span class="hljs-comment">/* enable the cycle count register */</span> uint32_t en; __<span class="hljs-function">asm__ <span class="hljs-title">volatile</span>(<span class="hljs-params"><span class="hljs-string">"mrc p15, 0, %0, c9, c12, 0"</span> : <span class="hljs-string">"=r"</span> (en</span>))</span>; en &= ~(<span class="hljs-number">1</span><<<span class="hljs-number">3</span>); <span class="hljs-comment">/* cycle count every cycle */</span> en |= <span class="hljs-number">1</span>; <span class="hljs-comment">/* enable all performance counters */</span> __<span class="hljs-function">asm__ <span class="hljs-title">volatile</span>(<span class="hljs-params"><span class="hljs-string">"mcr p15, 0, %0, c9, c12, 0"</span> :: <span class="hljs-string">"r"</span> (en</span>))</span>; <span class="hljs-comment">/* enable cycle counter */</span> en = (<span class="hljs-number">1</span><<<span class="hljs-number">31</span>); __<span class="hljs-function">asm__ <span class="hljs-title">volatile</span>(<span class="hljs-params"><span class="hljs-string">"mcr p15, 0, %0, c9, c12, 1"</span> :: <span class="hljs-string">"r"</span> (en</span>))</span>; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> } |
由于这方面的初始化比较偏向硬件,设置方法都是按住 CPU 手册来进行,所以不详细分析,只需要知道开启了以下几个功能即可:
- 设置异常向量基地址为 0x8F600000。4
- 开启 cpu mmu(memory manager unit) 内存管理单元。
- 开启一些协处理器特性,具体的设置项可以查询 DDI0344K_cortex_a8_r3p2_trm.pdf 。
platform_early_init
platform_early_init
函数位于 platform/msm8916/platform.c
文件中,主要的工作是对 msm8916
平台的硬件设备进行早期初始化,其流程如下:
board_init
board_init
函数位于 platform/msm_shared/board.c
文件中,主要工作是获取主板的相关信息并填充到相关结构中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> SMEM_V11_SMEM_MAX_PMIC_DEVICES 4 <span class="hljs-comment">// this is the max that device tree currently supports</span></span> <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> SMEM_MAX_PMIC_DEVICES SMEM_V11_SMEM_MAX_PMIC_DEVICES</span> <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MAX_PMIC_DEVICES SMEM_MAX_PMIC_DEVICES</span> <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> LINUX_MACHTYPE_UNKNOWN 0</span> <span class="hljs-keyword">enum</span> platform { HW_PLATFORM_UNKNOWN = <span class="hljs-number">0</span>, HW_PLATFORM_SURF = <span class="hljs-number">1</span>, HW_PLATFORM_FFA = <span class="hljs-number">2</span>, HW_PLATFORM_FLUID = <span class="hljs-number">3</span>, HW_PLATFORM_SVLTE = <span class="hljs-number">4</span>, HW_PLATFORM_QT = <span class="hljs-number">6</span>, HW_PLATFORM_MTP_MDM = <span class="hljs-number">7</span>, HW_PLATFORM_MTP = <span class="hljs-number">8</span>, HW_PLATFORM_LIQUID = <span class="hljs-number">9</span>, HW_PLATFORM_DRAGON = <span class="hljs-number">10</span>, HW_PLATFORM_QRD = <span class="hljs-number">11</span>, HW_PLATFORM_HRD = <span class="hljs-number">13</span>, HW_PLATFORM_DTV = <span class="hljs-number">14</span>, HW_PLATFORM_RUMI = <span class="hljs-number">15</span>, HW_PLATFORM_VIRTIO = <span class="hljs-number">16</span>, HW_PLATFORM_BTS = <span class="hljs-number">19</span>, HW_PLATFORM_RCM = <span class="hljs-number">21</span>, HW_PLATFORM_DMA = <span class="hljs-number">22</span>, HW_PLATFORM_STP = <span class="hljs-number">23</span>, HW_PLATFORM_SBC = <span class="hljs-number">24</span>, HW_PLATFORM_32BITS = <span class="hljs-number">0x7FFFFFFF</span>, }; <span class="hljs-keyword">enum</span> platform_subtype { HW_PLATFORM_SUBTYPE_UNKNOWN = <span class="hljs-number">0</span>, HW_PLATFORM_SUBTYPE_MDM = <span class="hljs-number">1</span>, HW_PLATFORM_SUBTYPE_8974PRO_PM8084 = <span class="hljs-number">1</span>, HW_PLATFORM_SUBTYPE_CSFB = <span class="hljs-number">1</span>, HW_PLATFORM_SUBTYPE_SVLTE1 = <span class="hljs-number">2</span>, HW_PLATFORM_SUBTYPE_SVLTE2A = <span class="hljs-number">3</span>, HW_PLATFORM_SUBTYPE_SGLTE = <span class="hljs-number">6</span>, HW_PLATFORM_SUBTYPE_DSDA = <span class="hljs-number">7</span>, HW_PLATFORM_SUBTYPE_DSDA2 = <span class="hljs-number">8</span>, HW_PLATFORM_SUBTYPE_SGLTE2 = <span class="hljs-number">9</span>, HW_PLATFORM_SUBTYPE_POLARIS = <span class="hljs-number">64</span>, HW_PLATFORM_SUBTYPE_32BITS = <span class="hljs-number">0x7FFFFFFF</span> }; <span class="hljs-keyword">struct</span> board_pmic_data { <span class="hljs-keyword">uint32_t</span> pmic_type; <span class="hljs-keyword">uint32_t</span> pmic_version; <span class="hljs-keyword">uint32_t</span> pmic_target; }; <span class="hljs-keyword">enum</span> baseband { BASEBAND_MSM = <span class="hljs-number">0</span>, BASEBAND_APQ = <span class="hljs-number">1</span>, BASEBAND_CSFB = <span class="hljs-number">2</span>, BASEBAND_SVLTE1 = <span class="hljs-number">3</span>, BASEBAND_SVLTE2A = <span class="hljs-number">4</span>, BASEBAND_MDM = <span class="hljs-number">5</span>, BASEBAND_SGLTE = <span class="hljs-number">6</span>, BASEBAND_DSDA = <span class="hljs-number">7</span>, BASEBAND_DSDA2 = <span class="hljs-number">8</span>, BASEBAND_SGLTE2 = <span class="hljs-number">9</span>, BASEBAND_MDM2 = <span class="hljs-number">10</span>, BASEBAND_32BITS = <span class="hljs-number">0x7FFFFFFF</span> }; <span class="hljs-keyword">typedef</span> <span class="hljs-keyword">enum</span> { PMIC_IS_PM6610, PMIC_IS_PM6620, PMIC_IS_PM6640, PMIC_IS_PM6650, PMIC_IS_PM7500, PMIC_IS_PANORAMIX, PMIC_IS_PM6652, PMIC_IS_PM6653, PMIC_IS_PM6658, PMIC_IS_EPIC, PMIC_IS_HAN, PMIC_IS_KIP, PMIC_IS_WOOKIE, PMIC_IS_PM8058, PMIC_IS_PM8028, PMIC_IS_PM8901, PMIC_IS_PM8027 , PMIC_IS_ISL_9519, PMIC_IS_PM8921, PMIC_IS_PM8018, PMIC_IS_PM8015, PMIC_IS_PM8014, PMIC_IS_PM8821, PMIC_IS_PM8038, PMIC_IS_PM8922, PMIC_IS_PM8917, PMIC_IS_INVALID = <span class="hljs-number">0x7fffffff</span>, } pm_model_type_afly; <span class="hljs-keyword">struct</span> board_data { <span class="hljs-keyword">uint32_t</span> platform; <span class="hljs-keyword">uint32_t</span> foundry_id; <span class="hljs-keyword">uint32_t</span> chip_serial; <span class="hljs-keyword">uint32_t</span> platform_version; <span class="hljs-keyword">uint32_t</span> platform_hw; <span class="hljs-keyword">uint32_t</span> platform_subtype; <span class="hljs-keyword">uint32_t</span> target; <span class="hljs-keyword">uint32_t</span> baseband; <span class="hljs-keyword">struct</span> board_pmic_data pmic_info[MAX_PMIC_DEVICES]; <span class="hljs-keyword">uint32_t</span> platform_hlos_subtype; <span class="hljs-keyword">uint32_t</span> num_pmics; <span class="hljs-keyword">uint32_t</span> pmic_array_offset; <span class="hljs-keyword">struct</span> board_pmic_data *pmic_info_array; }; <span class="hljs-keyword">static</span> <span class="hljs-keyword">struct</span> board_data board = {UNKNOWN, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, HW_PLATFORM_UNKNOWN, HW_PLATFORM_SUBTYPE_UNKNOWN, LINUX_MACHTYPE_UNKNOWN, BASEBAND_MSM, {{PMIC_IS_INVALID, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>}, {PMIC_IS_INVALID, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>}, {PMIC_IS_INVALID, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>}}, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">NULL</span>, }; |
board_init
的功能就是获取主板的信息并填充到 board
这个全局变量中,获取信息的具体来源涉及到 sbl1
等其他模块,暂时不分析,可以参考 Android 系统典型 bootloader 分析 这篇文章。
platform_clock_init
platform_clock_init
函数位于 platform/msm8916/platform.c
文件中,作用简单明了,就是初始化 msm8916
平台的一系列时钟,其中最重要的数据结构解释 msm_clocks_8916
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> CLK_LOOKUP(con, c) { .con_id = con, .clk = &c }</span> <span class="hljs-keyword">struct</span> clk_ops { <span class="hljs-keyword">int</span> (*enable)(<span class="hljs-keyword">struct</span> clk *clk); <span class="hljs-keyword">void</span> (*disable)(<span class="hljs-keyword">struct</span> clk *clk); <span class="hljs-keyword">void</span> (*auto_off)(<span class="hljs-keyword">struct</span> clk *clk); <span class="hljs-keyword">int</span> (*reset)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">enum</span> clk_reset_action action); <span class="hljs-keyword">int</span> (*set_rate)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">unsigned</span> rate); <span class="hljs-keyword">int</span> (*set_min_rate)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">unsigned</span> rate); <span class="hljs-keyword">int</span> (*set_max_rate)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">unsigned</span> rate); <span class="hljs-keyword">int</span> (*set_flags)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">unsigned</span> flags); <span class="hljs-keyword">unsigned</span> (*get_rate)(<span class="hljs-keyword">struct</span> clk *clk); <span class="hljs-keyword">int</span> (*list_rate)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">unsigned</span> n); <span class="hljs-keyword">int</span> (*is_enabled)(<span class="hljs-keyword">struct</span> clk *clk); <span class="hljs-keyword">long</span> (*round_rate)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">unsigned</span> rate); <span class="hljs-keyword">int</span> (*set_parent)(<span class="hljs-keyword">struct</span> clk *clk, <span class="hljs-keyword">struct</span> clk *parent); <span class="hljs-keyword">struct</span> clk *(*get_parent)(<span class="hljs-keyword">struct</span> clk *clk); <span class="hljs-keyword">bool</span> (*is_local)(<span class="hljs-keyword">struct</span> clk *clk); }; <span class="hljs-keyword">struct</span> clk { <span class="hljs-keyword">uint32_t</span> flags; <span class="hljs-keyword">uint32_t</span> rate; <span class="hljs-keyword">struct</span> clk_ops *ops; <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *dbg_name; <span class="hljs-keyword">unsigned</span> count; }; <span class="hljs-keyword">struct</span> clk_lookup { <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *con_id; <span class="hljs-keyword">struct</span> clk *clk; }; <span class="hljs-keyword">static</span> <span class="hljs-keyword">struct</span> clk_lookup msm_clocks_8916[] = { CLK_LOOKUP(<span class="hljs-string">"sdc1_iface_clk"</span>, gcc_sdcc1_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"sdc1_core_clk"</span>, gcc_sdcc1_apps_clk.c), CLK_LOOKUP(<span class="hljs-string">"sdc2_iface_clk"</span>, gcc_sdcc2_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"sdc2_core_clk"</span>, gcc_sdcc2_apps_clk.c), CLK_LOOKUP(<span class="hljs-string">"uart2_iface_clk"</span>, gcc_blsp1_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"uart2_core_clk"</span>, gcc_blsp1_uart2_apps_clk.c), CLK_LOOKUP(<span class="hljs-string">"usb_iface_clk"</span>, gcc_usb_hs_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"usb_core_clk"</span>, gcc_usb_hs_system_clk.c), CLK_LOOKUP(<span class="hljs-string">"mdp_ahb_clk"</span>, mdp_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"mdss_esc0_clk"</span>, mdss_esc0_clk.c), CLK_LOOKUP(<span class="hljs-string">"mdss_esc1_clk"</span>, mdss_esc1_clk.c), CLK_LOOKUP(<span class="hljs-string">"mdss_axi_clk"</span>, mdss_axi_clk.c), CLK_LOOKUP(<span class="hljs-string">"mdss_vsync_clk"</span>, mdss_vsync_clk.c), CLK_LOOKUP(<span class="hljs-string">"mdss_mdp_clk_src"</span>, mdss_mdp_clk_src.c), CLK_LOOKUP(<span class="hljs-string">"mdss_mdp_clk"</span>, mdss_mdp_clk.c), CLK_LOOKUP(<span class="hljs-string">"ce1_ahb_clk"</span>, gcc_ce1_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"ce1_axi_clk"</span>, gcc_ce1_axi_clk.c), CLK_LOOKUP(<span class="hljs-string">"ce1_core_clk"</span>, gcc_ce1_clk.c), CLK_LOOKUP(<span class="hljs-string">"ce1_src_clk"</span>, ce1_clk_src.c), CLK_LOOKUP(<span class="hljs-string">"blsp1_qup2_ahb_iface_clk"</span>, gcc_blsp1_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"gcc_blsp1_qup2_i2c_apps_clk_src"</span>, gcc_blsp1_qup2_i2c_apps_clk_src.c), CLK_LOOKUP(<span class="hljs-string">"gcc_blsp1_qup2_i2c_apps_clk"</span>, gcc_blsp1_qup2_i2c_apps_clk.c), CLK_LOOKUP(<span class="hljs-string">"blsp1_qup4_ahb_iface_clk"</span>, gcc_blsp1_ahb_clk.c), CLK_LOOKUP(<span class="hljs-string">"gcc_blsp1_qup4_i2c_apps_clk_src"</span>, gcc_blsp1_qup4_i2c_apps_clk_src.c), CLK_LOOKUP(<span class="hljs-string">"gcc_blsp1_qup4_i2c_apps_clk"</span>, gcc_blsp1_qup4_i2c_apps_clk.c), }; |
qgic_init
qgic_init
函数位于 platform/msm_shared/qgic_common.c
文件中,主要的作用就是初始化 QGIC(Qualcomm GenericInterrupt Controller),分为以下两个部分的初始化。5
- qgic 分配器的初始化。
- qgic cpu 控制器的初始化。
由于具体的初始化工作涉及到具体的 CPU 架构,这里暂不分析。
qtimer_init
qtimer_init
函数位于 platform/msm_shared/qtimer.c
文件中,在 msm8916
平台下,这个频率固定为 19200000
, 作用暂时未知,后续补充。
scm_init
scm_init
函数位于 platform/msm_shared/scm.c
文件中, scm
的全称是 Secure Channel Manager
, 负责 Normal World(普通世界) 和 Secure World(安全世界) 之间的通信。Secure World 就是 TrustZone
, scm_init
的作用则是检查 scm
是否能够使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<span class="hljs-keyword">int</span> is_scm_call_available(uint32_t svc_id, uint32_t cmd_id) { <span class="hljs-keyword">int</span> ret; scmcall_arg scm_arg = {<span class="hljs-number">0</span>}; scmcall_ret scm_ret = {<span class="hljs-number">0</span>}; scm_arg.x0 = MAKE_SIP_SCM_CMD(SCM_SVC_INFO, IS_<span class="hljs-built_in">CALL_AVAIL_CMD</span>); scm_arg.x1 = MAKE_SCM_ARGS(<span class="hljs-number">0x1</span>); scm_arg.x2 = MAKE_SIP_SCM_CMD(svc_id, cmd_id); ret = scm_call2(&scm_arg, &scm_ret); <span class="hljs-keyword">if</span> (!ret) <span class="hljs-keyword">return</span> scm_ret.x1; <span class="hljs-keyword">return</span> ret; } <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> scm_arm_support_available(uint32_t svc_id, uint32_t cmd_id) { <span class="hljs-keyword">int</span> ret; ret = is_scm_call_available(SCM_SVC_INFO, IS_<span class="hljs-built_in">CALL_AVAIL_CMD</span>); <span class="hljs-keyword">if</span> (ret > <span class="hljs-number">0</span>) scm_arm_support = <span class="hljs-literal">true</span>; <span class="hljs-keyword">return</span> ret; } <span class="hljs-keyword">void</span> scm_init() { <span class="hljs-keyword">int</span> ret; <span class="hljs-keyword">if</span> (scm_initialized) <span class="hljs-keyword">return</span>; ret = scm_arm_support_available(SCM_SVC_INFO, IS_<span class="hljs-built_in">CALL_AVAIL_CMD</span>); <span class="hljs-keyword">if</span> (ret < <span class="hljs-number">0</span>) dprintf(CRITI<span class="hljs-built_in">CAL</span>, <span class="hljs-string">"Failed to initialize SCM\n"</span>); } |
在上面的实现中 scm_call2
就相当于是 TrustZone
开放给普通世界的 API 接口,在这个接口中有两个重要的数据结构。
-
scmcall_arg
12345678910111213141516171819202122<span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> {<span class="hljs-keyword">uint32_t</span> x0;<span class="hljs-comment">/* command ID details as per ARMv8 spec :0:7 command, 8:15 service id0x02000000: SIP calls30: SMC32 or SMC6431: Standard or fast calls*/</span><span class="hljs-keyword">uint32_t</span> x1; <span class="hljs-comment">/* # of args and attributes for buffers* 0-3: arg #* 4-5: type of arg1* 6-7: type of arg2* :* :* 20-21: type of arg8* 22-23: type of arg9*/</span><span class="hljs-keyword">uint32_t</span> x2; <span class="hljs-comment">/* Param1 */</span><span class="hljs-keyword">uint32_t</span> x3; <span class="hljs-comment">/* Param2 */</span><span class="hljs-keyword">uint32_t</span> x4; <span class="hljs-comment">/* Param3 */</span><span class="hljs-keyword">uint32_t</span> x5[<span class="hljs-number">10</span>]; <span class="hljs-comment">/* Indirect parameter list */</span><span class="hljs-keyword">uint32_t</span> atomic; <span class="hljs-comment">/* To indicate if its standard or fast call */</span>} scmcall_arg;
这个结构想当于一个数据包,负责携带需要传递给TrustZone
的参数信息。 -
scmcall_ret
123456789101112<span class="hljs-comment">/* Return value for the SCM call:* SCM call returns values in register if its less than* 12 bytes, anything greater need to be input buffer + input len* arguments*/</span><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span>{<span class="hljs-keyword">uint32_t</span> x1;<span class="hljs-keyword">uint32_t</span> x2;<span class="hljs-keyword">uint32_t</span> x3;} scmcall_ret;
这个结构则存储着TrustZone
返回的数据信息,但是只有数据小于 12 字节才用这个结构返回,其他的数据应该在参数中放入一个返回用的 buffer 和长度。
整个 platform_early_init
的作用就是初始化 msm8916
平台的相关硬件设备,包括主板,时钟,中断控制器,scm 等,为 lk 的启动和运行提供硬件环境。
target_early_init
target_early_init
函数位于 target/msm8916/init.c
文件中,这个函数的主要作用是为 msm8916
平台开启调试 uart
调试接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<span class="hljs-comment">/* Defining functions that's exposed to outside world and in coformance to * existing uart implemention. These functions are being called to initialize * UART and print debug messages in bootloader. */</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">uart_dm_init</span><span class="hljs-params">(uint8_t id, uint32_t gsbi_base, uint32_t uart_dm_base)</span> </span>{ <span class="hljs-keyword">static</span> <span class="hljs-keyword">uint8_t</span> port = <span class="hljs-number">0</span>; <span class="hljs-keyword">char</span> *data = <span class="hljs-string">"Android Bootloader - UART_DM Initialized!!!\n"</span>; <span class="hljs-comment">/* Configure the uart clock */</span> clock_config_uart_dm(id); dsb(); <span class="hljs-comment">/* Configure GPIO to provide connectivity between UART block product ports and chip pads */</span> gpio_config_uart_dm(id); dsb(); <span class="hljs-comment">/* Configure GSBI for UART_DM protocol. * I2C on 2 ports, UART (without HS flow control) on the other 2. * This is only on chips that have GSBI block */</span> <span class="hljs-keyword">if</span>(gsbi_base) writel(GSBI_PROTOCOL_CODE_I2C_UART << GSBI_CTRL_REG_PROTOCOL_CODE_S, GSBI_CTRL_REG(gsbi_base)); dsb(); <span class="hljs-comment">/* Configure clock selection register for tx and rx rates. * Selecting 115.2k for both RX and TX. */</span> writel(UART_DM_CLK_RX_TX_BIT_RATE, MSM_BOOT_UART_DM_CSR(uart_dm_base)); dsb(); <span class="hljs-comment">/* Intialize UART_DM */</span> msm_boot_uart_dm_init(uart_dm_base); msm_boot_uart_dm_write(uart_dm_base, data, <span class="hljs-number">44</span>); ASSERT(port < ARRAY_SIZE(port_lookup)); port_lookup[port++] = uart_dm_base; <span class="hljs-comment">/* Set UART init flag */</span> uart_init_flag = <span class="hljs-number">1</span>; } <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">target_early_init</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>{ <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> WITH_DEBUG_UART</span> uart_dm_init(<span class="hljs-number">2</span>, <span class="hljs-number">0</span>, BLSP1_UART1_BASE); <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> } |
WITH_DEBUG_UART
宏定义在 project/msm8916.mk
中, 只要定义了 WITH_DEBUG_UART
宏,就可以开启板子的 uart
调试接口。
environment initialize
之所以将 bs_set_timestamp/call_constructors/heap_init/thread_init/dpc_init/timer_init 等分为一类,是因为前面几项初始化工作更偏向硬件的初始化,是后续运行的基础需求,而现在的几项初始化则是高级运行所需要的环境,是一种软件环境,和具体硬件信息并没有很强的关联性,所以分为一类。
bs_set_timestamp
bs_set_timestamp
函数位于 /platform/msm_shared/boot_stats.c
文件中,这个函数的功能非常简单,就是读取当前的 TimeTick
然后保存到 bootstate
中。在这个过程中比较重要的是以下两个内存地址:
-
TimeTick 地址在
msm8916
平台下,TimeTick 的地址由以下宏定义:
12<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MPM2_MPM_SLEEP_TIMETICK_COUNT_VAL 0x004A3000</span>
这个宏的定义在platform/msm8916/include/platform/iomap.h
文件中。 -
bootstate 地址在
msm8916
平台下,bootstate 即bs
的地址由以下宏定义:
1234<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MSM_SHARED_IMEM_BASE 0x08600000</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> BS_INFO_OFFSET (0x6B0)</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> BS_INFO_ADDR (MSM_SHARED_IMEM_BASE + BS_INFO_OFFSET)</span>
这几个宏同样定义在platform/msm8916/include/platform/iomap.h
文件中。bootstate 的地址其实就是一个 uint32 的数据,每个成员存储了对应的时间信息,具体的成员对应的含义有以下定义:
1234567891011121314<span class="hljs-comment">/* The order of the entries in this enum does not correspond to bootup order.* It is mandated by the expected order of the entries in imem when the values* are read in the kernel.*/</span><span class="hljs-keyword">enum</span> bs_entry {BS_BL_START = <span class="hljs-number">0</span>,BS_KERNEL_ENTRY,BS_SPLASH_SCREEN_DISPLAY,BS_KERNEL_LOAD_TIME,BS_KERNEL_LOAD_START,BS_KERNEL_LOAD_DONE,BS_MAX,};
当前设置的就是BS_BL_START
的时间,代表了 bootloader 的启动时间。
call_constructors
这个函数是 lk 和 c++ 联合编译使用的特性,主要是为了调用 c++ 代码的构造函数,单纯的 lk 中这个函数并没有作用。
heap_init
heap_init
函数位于 lib/heap/heap.c
文件中,顾名思义,这个函数的作用就是初始化堆空间,其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">heap_init</span>(<span class="hljs-params"><span class="hljs-keyword">void</span></span>) </span>{ LTRACE_ENTRY; <span class="hljs-comment">// set the heap range</span> theheap.<span class="hljs-keyword">base</span> = (<span class="hljs-keyword">void</span> *)HEAP_START; theheap.len = HEAP_LEN; LTRACEF(<span class="hljs-string">"base %p size %zd bytes\n"</span>, theheap.<span class="hljs-keyword">base</span>, theheap.len); <span class="hljs-comment">// initialize the free list</span> list_initialize(&theheap.free_list); <span class="hljs-comment">// create an initial free chunk</span> heap_insert_free_chunk(heap_create_free_chunk(theheap.<span class="hljs-keyword">base</span>, theheap.len)); <span class="hljs-comment">// dump heap info</span> <span class="hljs-comment">// heap_dump();</span> <span class="hljs-comment">// dprintf(INFO, "running heap tests\n");</span> <span class="hljs-comment">// heap_test();</span> } |
其中涉及到的最重要的全局变量就是 theheap
, 这个变量保存了 lk 所使用的堆空间的信息,其结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="hljs-keyword">struct</span> list_node { <span class="hljs-keyword">struct</span> list_node *prev; <span class="hljs-keyword">struct</span> list_node *next; }; <span class="hljs-keyword">struct</span> heap { <span class="hljs-keyword">void</span> *base; <span class="hljs-keyword">size_t</span> len; <span class="hljs-keyword">struct</span> list_node free_list; }; <span class="hljs-comment">// heap static vars</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">struct</span> heap theheap; |
theheap.base
和 theheap.len
对应的宏定义如下:
1 2 3 4 5 6 7 8 9 |
<span class="hljs-comment">// end of the binary</span> <span class="hljs-keyword">extern</span> <span class="hljs-keyword">int</span> _end; <span class="hljs-comment">// end of memory</span> <span class="hljs-keyword">extern</span> <span class="hljs-keyword">int</span> _end_of_ram; <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> HEAP_START ((unsigned long)&_end)</span> <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> HEAP_LEN ((size_t)&_end_of_ram - (size_t)&_end)</span> |
_end 和 _end_of_ram 这两个符号都是在链接的时候由链接器来确定的,其符号定义在 arch/arm/system-onesegment.ld
文件中。_end 表示程序代码尾地址, _end_of_ram 表示 lk 内存尾地址,也就是说 lk 堆空间就是程序代码尾部到内存尾部所有空间。theheap.free_list
维护着一个堆链表,其中保存着堆中所有空闲的堆块,现在的初始化阶段,只有一块完整的堆空间。
thread_init
thread_init
函数位于 kernel/thread.c
文件中,关于线程的初始化在 thread_init_early
中已经完成, thread_init
只是一个空接口。
dpc_init
dpc
函数位于 kernel/dpc.c
文件中,它的代码如下:
1 2 3 4 5 6 7 |
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">dpc_init</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>{ event_init(&dpc_event, <span class="hljs-literal">false</span>, <span class="hljs-number">0</span>); thread_resume(thread_create(<span class="hljs-string">"dpc"</span>, &dpc_thread_routine, <span class="hljs-literal">NULL</span>, DPC_PRIORITY, DEFAULT_STACK_SIZE)); } |
代码很简单,就是创建并启动一个名为 dpc
的线程, dpc
的全称是 deferred procedure call
, 就是延期程序调用的意思,它的作用是可以在其中注册函数,然后在触发 event
时调用函数,比如在 thread_exit
中就通过 dpc
来清理线程栈环境。虽然 dpc
系统内容不多,但是这里涉及到了一个有意思的操作,线程的创建和启动, thread_init_early
对线程初始化后,这里是第一次对线程进行使用, 值得分析。 thread_create
函数位于 kernel/thread.c
文件中,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<span class="hljs-keyword">thread_t</span> *thread_create(<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *name, thread_start_routine entry, <span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">int</span> priority, <span class="hljs-keyword">size_t</span> stack_size) { <span class="hljs-keyword">thread_t</span> *t; t = <span class="hljs-built_in">malloc</span>(<span class="hljs-keyword">sizeof</span>(<span class="hljs-keyword">thread_t</span>)); <span class="hljs-keyword">if</span> (!t) <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; init_thread_struct(t, name); t->entry = entry; t->arg = arg; t->priority = priority; t->saved_critical_section_count = <span class="hljs-number">1</span>; <span class="hljs-comment">/* we always start inside a critical section */</span> t->state = THREAD_SUSPENDED; t->blocking_wait_queue = <span class="hljs-literal">NULL</span>; t->wait_queue_block_ret = NO_ERROR; <span class="hljs-comment">/* create the stack */</span> t-><span class="hljs-built_in">stack</span> = <span class="hljs-built_in">malloc</span>(stack_size); <span class="hljs-keyword">if</span> (!t-><span class="hljs-built_in">stack</span>) { <span class="hljs-built_in">free</span>(t); <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; } t->stack_size = stack_size; <span class="hljs-comment">/* inheirit thread local storage from the parent */</span> <span class="hljs-keyword">int</span> i; <span class="hljs-keyword">for</span> (i=<span class="hljs-number">0</span>; i < MAX_TLS_ENTRY; i++) t->tls[i] = current_thread->tls[i]; <span class="hljs-comment">/* set up the initial stack frame */</span> arch_thread_initialize(t); <span class="hljs-comment">/* add it to the global thread list */</span> enter_critical_section(); list_add_head(&thread_list, &t->thread_list_node); exit_critical_section(); <span class="hljs-keyword">return</span> t; } |
thread_create
的逻辑比较简单,总体上来说只有 3 个步骤:
- 申请并填充
thread_t
结构体 - 初始化线程栈空间
- 添加线程到
thread_list
头部
这三个步骤除了第 2 步的栈空间结构,其他结构在 thread_init_early
中已经介绍过,不做赘述,主要介绍下 lk 的线程栈初始化。线程栈的初始化由 arch_thread_initialize
完成,这个函数位于 arch/arm/thread.c
文件中,其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<span class="hljs-keyword">struct</span> context_switch_frame { <span class="hljs-keyword">vaddr_t</span> r4; <span class="hljs-keyword">vaddr_t</span> r5; <span class="hljs-keyword">vaddr_t</span> r6; <span class="hljs-keyword">vaddr_t</span> r7; <span class="hljs-keyword">vaddr_t</span> r8; <span class="hljs-keyword">vaddr_t</span> r9; <span class="hljs-keyword">vaddr_t</span> r10; <span class="hljs-keyword">vaddr_t</span> r11; <span class="hljs-keyword">vaddr_t</span> lr; <span class="hljs-keyword">vaddr_t</span> usp; <span class="hljs-keyword">vaddr_t</span> ulr; }; <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">arch_thread_initialize</span><span class="hljs-params">(thread_t *t)</span> </span>{ <span class="hljs-comment">// create a default stack frame on the stack</span> <span class="hljs-keyword">vaddr_t</span> stack_top = (<span class="hljs-keyword">vaddr_t</span>)t-><span class="hljs-built_in">stack</span> + t->stack_size; <span class="hljs-comment">// make sure the top of the stack is 8 byte aligned for EABI compliance</span> stack_top = ROUNDDOWN(stack_top, <span class="hljs-number">8</span>); <span class="hljs-keyword">struct</span> context_switch_frame *frame = (<span class="hljs-keyword">struct</span> context_switch_frame *)(stack_top); frame--; <span class="hljs-comment">// fill it in</span> <span class="hljs-built_in">memset</span>(frame, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(*frame)); frame->lr = (<span class="hljs-keyword">vaddr_t</span>)&initial_thread_func; <span class="hljs-comment">// set the stack pointer</span> t->arch.sp = (<span class="hljs-keyword">vaddr_t</span>)frame; } |
初始化线程栈空间其实就是在栈顶使用一块空间用于后续保存寄存器信息以方便线程切换。需要保存的寄存器信息定义在 context_switch_frame
结构体中,其中 lr
用于保存线程函数入口。 当这块内存空间设置好以后,线程栈的初始化工作基本就完成了,剩下的就是通过 thread_resume
来启动线程。
thread_resume
函数位于 kernel/thread.c
文件中,其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
status_t thread_resume(thread_t *t) { <span class="hljs-comment">#if THREAD_CHECKS</span> ASSERT(t->magic == THREAD_MAGIC); ASSERT(t->state != THREAD_DEATH); <span class="hljs-comment">#endif</span> <span class="hljs-keyword">if</span> (t->state == THREAD_READY || t->state == THREAD_RUNNING) <span class="hljs-keyword">return</span> ERR_NOT_SUSPENDED; enter_critical_section(); t->state = THREAD_READY; insert_in_run_queue_head(t); thread_yield(); exit_critical_section(); <span class="hljs-keyword">return</span> NO_ERROR; } |
代码逻辑简单明了,只有以下两个步骤:
-
修改 thread 的状态为
THREAD_READY
, 然后添加到run_queue
中。insert_in_run_queue_head
函数位于同一文件中,其代码如下:
12345678910111213<span class="hljs-keyword">static</span> void insert_in_run_queue_head(thread_t *t){<span class="hljs-comment">#if THREAD_CHECKS</span>ASSERT(t->magic == THREAD_MAGIC);ASSERT(t->state == THREAD_READY);ASSERT(!list_in_list(&t->queue_node));ASSERT(in_critical_section());<span class="hljs-comment">#endif</span>list_add_head(&run_queue[t->priority], &t->queue_node);run_queue_bitmap |= (<span class="hljs-number">1</span><<t->priority);}
run_queue
在thread_init_early
中已经有过了解,就是一个大小为 32 的全局链表数组,这个数组每一项对应一个线程优先级,下标越大的数组项优先级越大。比较有趣的是使用了一个 uint32 类型的全局变量run_queue_bitmap
来作为优先级的索引。这个变量的每一位
对应数组的每一项
, 通过检查对应位的值是 0 或 1 就可以知道优先级的使用情况,两者的关系可以用下图表示。 -
调用 thread_yield 来获取 cpu 执行。
thread_yield
函数位于同一文件中,主要是修改current_thread
的状态的THREAD_READY
并插入对应优先级项链表的尾部,然后调用thread_resched
函数来切换线程。thread_resched
函数位于同一文件中,其代码删除了一些无关代码后大体如下:12345678910111213141516171819202122232425262728293031323334353637383940<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">thread_resched</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>{<span class="hljs-keyword">thread_t</span> *oldthread;<span class="hljs-keyword">thread_t</span> *newthread;oldthread = current_thread;<span class="hljs-comment">// at the moment, can't deal with more than 32 priority levels</span>ASSERT(NUM_PRIORITIES <= <span class="hljs-number">32</span>);<span class="hljs-comment">// should at least find the idle thread</span><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> THREAD_CHECKS</span>ASSERT(run_queue_bitmap != <span class="hljs-number">0</span>);<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span><span class="hljs-keyword">int</span> next_queue = HIGHEST_PRIORITY - __builtin_clz(run_queue_bitmap) - (<span class="hljs-number">32</span> - NUM_PRIORITIES);<span class="hljs-comment">//dprintf(SPEW, "bitmap 0x%x, next %d\n", run_queue_bitmap, next_queue);</span>newthread = list_remove_head_type(&run_queue[next_queue], <span class="hljs-keyword">thread_t</span>, queue_node);<span class="hljs-keyword">if</span> (list_is_empty(&run_queue[next_queue]))run_queue_bitmap &= ~(<span class="hljs-number">1</span><<next_queue);newthread->state = THREAD_RUNNING;<span class="hljs-keyword">if</span> (newthread == oldthread)<span class="hljs-keyword">return</span>;<span class="hljs-comment">/* set up quantum for the new thread if it was consumed */</span><span class="hljs-keyword">if</span> (newthread->remaining_quantum <= <span class="hljs-number">0</span>) {newthread->remaining_quantum = <span class="hljs-number">5</span>; <span class="hljs-comment">// XXX make this smarter</span>}<span class="hljs-comment">/* do the switch */</span>oldthread->saved_critical_section_count = critical_section_count;current_thread = newthread;critical_section_count = newthread->saved_critical_section_count;arch_context_switch(oldthread, newthread);}整体流程如下:
- 通过
run_queue_bitmap
获取优先级最高的线程。 -
设置线程状态为
THREAD_RUNNING
, 如果新线程不等于老线程则调用arch_context_switch
切换线程。arch_context_switch
函数位于arch/arm/thread.c
文件中,只是作为转接arm_context_switch
的媒介。arm_context_switch
函数位于arch/arm/asm.S
文件中,是汇编代码,其代码如下:
1234567891011121314151617181920212223242526<span class="hljs-comment">/* arm_context_switch(addr_t *old_sp, addr_t new_sp) */</span>FUNCTION(arm_context_switch)<span class="hljs-comment">/* save all the usual registers + user regs */</span><span class="hljs-comment">/* the spsr is saved and restored in the iframe by exceptions.S */</span>sub r3, sp, <span class="hljs-meta">#(11*4) <span class="hljs-comment">/* can't use sp in user mode stm */</span></span>mov r12, lrstmia r3, { r4-r11, r12, r13, r14 }^<span class="hljs-comment">/* save old sp */</span>str r3, [r0]<span class="hljs-comment">/* clear any exlusive locks that the old thread holds */</span><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> ARM_ISA_ARMV7</span>clrex<span class="hljs-meta">#<span class="hljs-meta-keyword">elif</span> ARM_ISA_ARMV6</span><span class="hljs-comment">/* have to do a fake strex to clear it */</span>ldr r0, =strex_spotstrex r3, r2, [r0]<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span><span class="hljs-comment">/* load new regs */</span>ldmia r1, { r4-r11, r12, r13, r14 }^mov lr, r12 <span class="hljs-comment">/* restore lr */</span>add sp, r1, <span class="hljs-meta">#(11*4) <span class="hljs-comment">/* restore sp */</span></span>bx lr
函数的功能很简单,保存 old_thread 的寄存器环境到内存,从内存加载 new_thread 的寄存器环境,跳转到线程入口。
- 通过
到这里新的线程就会执行起来,但是这里有一个问题,如果存在一个优先级很高的线程,并且是死循环,按照上面的逻辑优先级高的线程一定会先执行,那么这个优先级高的线程就会一直占有 cpu, 这种情况 lk 是如何处理的,还是对代码的理解有问题?
timer_init
timer_init
函数位于 kernel/timer.c
文件中,主要的作用创建 lk 中的定时器链表和定时器处理函数。每个定时器都存储在 struct timer_t
类型的结构体中:
1 2 3 4 5 6 7 8 9 10 11 |
<span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> timer { <span class="hljs-keyword">int</span> magic; <span class="hljs-keyword">struct</span> list_node node; <span class="hljs-keyword">time_t</span> scheduled_time; <span class="hljs-keyword">time_t</span> periodic_time; timer_callback callback; <span class="hljs-keyword">void</span> *arg; } <span class="hljs-keyword">timer_t</span>; |
其中全局链表 timer_queue
的作用就是存储定时器,而 timer_tick
函数的作用则是遍历 timer_queue
来处理其中注册的定时器回调函数。
参考资料
- androidLK 启动过程 – 简书
- 深入 MTK 平台 bootloader 启动之【 lk -> kernel】分析笔记其它综合_INFOCOOL.NET
- Android 启动过程深入解析 – 文章 – 伯乐在线
- Cortex-A8 处理器 – ARM
- Cortex-A9 处理器 – ARM
- ARM Cortex-A – Wikipedia
- DDI0344K_cortex_a8_r3p2_trm.pdf
- Android 系统典型 bootloader 分析 – 电脑系统安全 – 红黑联盟
- MSM8909+Android5.1.1 启动流程(3)—kmain() – 程序园
- SylixOS 中 GIC 通用中断控制器(一)——GIC 简介 – 11168899 – 51CTO 技术博客
- Secure Channel Manager
Footnotes
1 这里的编译环境需要 android NDK 的交叉编译器,可以从 Android Developers 下载 NDK。
2 测试时发现 qemu-arm 无法编译,是因为 project/tools 中的 rules.mk 文件并不存在。
3 这里的第一个线程bootstrap
并不是被创建然后运行的,而是将本身就在运行的信息加入到线程链表中,作用第一线程存在。
4 MEMBASE 的值在target/msm8916/rules.mk
中指定。
5 GIC(通用中断控制器) 是各外设中断和 CPU 之间的桥梁,也是各 CPU 中断之间互相的通道,主要的作用就是检测,管理和分发中断。
转载自: http://www.freebuf.com/news/135084.html