Little Kernel 01
LK是什么
LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程代码 ,little kernel 是小内核小操作系统。
LK主要功能:
- 初始化硬件模块,如时钟,中断,UART,USB,LCD,PMIC,eMMC/UFS等。
- 更新cmdline。其中重要的是区分启动模式。
- 选择和更新device tree。
- 设置好系统状态,跳转到kernel。 MMU = off, D-cache = off, I-cache = on or off,x0 = physical address to the FDT blob。
- fastboot功能。
- 鉴权。
LK 代码结构:
- app
// 应用相关 - arch
// arm 体系 - dev
// 设备相关 - include
// 头文件 - kernel
// lk系统相关 - platform
// 相关驱动 - projiect
// makefile文件 - scripts
// Jtag 脚本 - target
// 具体板子相关
一些宏定义
宏定义 | 意思 |
---|---|
WITH_KERNEL_VM | 支持虚拟内存 |
WITH_SMP | 支持多核 |
ARM_CPU_CORTEX_A8 | A8架构 |
ARM_WITH_MMU | 支持MMU |
LK流程分析
lk主要任务:
- 1.进行各种早期的初始化工作,包括 cpu, emmc, ddr, clocks, thread etc。
- 2.判断进入 recovery 或 fastboot 的条件是否被触发。
- 3.从 emmc 中获取 boot.img 并加载到指定内存区域 (scratch region)。
- 4.从 scratch region 加载 kernel 到 KERNEL_ADDRESS 。
- 5.从 scratch region 加载 ramdisk 到 RAMDISK_ADDRESS 。
- 6.加载设备树到 TAGS_ADDRESS 。
- 7.关闭 cache, interrupts, 跳转到 kernel。
在 lk/arch/arm64/ssystem-onesegment.ld 连接文件中 ENTRY(_start)指定 LK 从_start 函数开始,_start 在 lk/arch/start.S中 。
start.S 主要做一些基本的 CPU 的初始化。
- 1.enable caches so atomics and spinlocks work
- 2.set up the mmu(主要部分)
- 3.clear bss
- 4.Set up the stack
最后,通过bl lk_main跳转到 C 代码中。
在lk/top/main.c中定义了lk_main函数。
lk_main流程
1.thread_init_early()
1 | // get us into some sort of thread context |
初始化进程(lk 中的简单进程)相关结构体,初始化lk的线程系统。
2.arch_early_init()
1 | // early arch stuff |
架构相关早期初始化,arch_early_init 和所使用的 CPU架构有很强的耦合性,这个函数的代码也主要是针对 CPU 的一些特性做设置。做一些如 关闭cache,使能 mmu ,开启一些协处理器特性的 arm 相关工作。
3.platform_early_init()
1 | // do any super early platform initialization |
相关平台的早期初始化,如获取板级信息,初始化时钟、中断、定时器等,为 lk的启动和运行提供硬件环境。
4.target_early_init()
1 | // do any super early target initialization |
初始化板子相关,其中只初始化了串口,开启uart调试接口。
5.heap_init()
1 | // bring up the kernel heap |
lk系统相关的堆栈初始化,用于malloc等函数的内存分配
6.call_constructors()
1 | // deal with any static constructors |
构造函数相关初始化,这个函数是 lk 和 c++ 联合编译使用的特性,主要是为了调用 c++ 代码的构造函数,单纯的 lk 中这个函数并没有作用。
7.kernel_init()
1 | // initialize the kernel |
内核初始化。
8.创建并唤醒bootstrap2线程
1 | lk_primary_cpu_init_level(LK_INIT_LEVEL_KERNEL, LK_INIT_LEVEL_THREADING - 1); |
用于进一步完成bootloader工作,最终将本线程切换到idle状态。
以上与 boot 启动初始化相关函数是 arch_early_init、 platform_early_init 、bootstrap2
lk_init相关定义:
1 | enum lk_init_level { |
thread_init_early()
lk系统的thread初始化
1 | for (i=0; i < NUM_PRIORITIES; i++) //NUM_PRIORITIES=32 |
thread_init_early 所完成的主要工作就是为 lk 完成早期的线程初始化工作,包括运行队列 的初始化,线程链表的初始化,第一个线程的创建等工作。在这个过程中一共有以下重要的数据结构:
- 运行队列 run_queuerun_queue 是作为多线程的调度中心存在,数组不同的下标对应不同的运行优先级,具体的应用会在后面涉及到。
1
2
3
4
5
6
7
8#define NUM_PRIORITIES 32
struct list_node {
struct list_node *prev;
struct list_node *next;
};
static struct list_node run_queue[NUM_PRIORITIES]; - 线程链表 thread_list全局的线程链表,保存了所有创建的线程信息。
1
static struct list_node thread_list;
- bootstrap 线程每个线程的上下文信息都通过 thread_t 结构体保存,而 bootstrap 是作为 lk的第一个线程存在, 拥有 最高优先级(HIGHEST_PROORITY) 。
1
2
3
4
5
6
7
8
9
10
11
12
13/* create a thread to cover the current running state */
thread_t *t = idle_thread(0);
init_thread_struct(t, "bootstrap");
/* half construct this thread, since we're already running */
t->priority = HIGHEST_PRIORITY;
t->state = THREAD_RUNNING;
t->flags = THREAD_FLAG_DETACHED;
thread_set_curr_cpu(t, 0);
thread_set_pinned_cpu(t, 0);
wait_queue_init(&t->retcode_wait_queue);
list_add_head(&thread_list, &t->thread_list_node);
set_current_thread(t);current_thread 是一个全局变量,保存了当前运行的线程的信息。1
2/* since we're implicitly uniprocessor, store a pointer to the current thread here */
thread_t *_current_thread;
这些数据结构都进行了初始化后,lk 就具有了创建和管理线程的结构及能力。
arch_early_init()
不同的芯片架构初始化内容不同,lk支持microblaze、or1k、mips、arm、arm-m、arm64,以arm64为例。函数定义在lk/arch/arm64/arch.c中。
1 | void arch_early_init(void) |
arm64_cpu_early_init
- 1.设置向量基地址(中断相关)
- 2.switch to EL1
- 3.enable fiqs
platform_init_mmu_mappings
初始化MMU映射,与platform有关,定义在platform文件夹下对应平台的platform.c文件,大部分定义为空函数。
platform_early_init()
与平台相关,一般任务为从共享内存中读取板级信息、初始化时钟、初始化ARM GIC中断控制器、初始化timer。在lk/platform/armemu/platform.c中:
1 | void platform_early_init(void) |
完成中断初始化、定时器初始化和显示初始化。
target_early_init()
在目标平台定义。
heap_init()
堆栈初始化
start the heap off with some spare memory in the page allocator.
malloc等内存分配函数在heap_wrapper.c文件下实现。
call_constructors()
构造函数相关初始化
bootstrap2
bootstrap2 在kmain的末尾以线程方式开启。主要分三步:platform_init、target_init、apps_init。
1 | lk_primary_cpu_init_level(LK_INIT_LEVEL_THREADING, LK_INIT_LEVEL_ARCH - 1); |
1.initialize the arch
1 | dprintf(SPEW, "initializing platform\n"); |
2.initialize the rest of the platform
1 | dprintf(SPEW, "initializing target\n"); |
3.initialize the target
1 | dprintf(SPEW, "calling apps_init()\n"); |
4.calling apps_init
本文标题:Little Kernel 01
文章作者:Mr Bluyee
发布时间:2018-11-03
最后更新:2019-07-15
原始链接:https://www.mrbluyee.com/2018/11/03/Little-Kernel-01/
版权声明:The author owns the copyright, please indicate the source reproduced.