LK是什么

LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程代码 ,little kernel 是小内核小操作系统。

lk
lk doc

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流程

lk main流程
1.thread_init_early()

1
2
// get us into some sort of thread context
thread_init_early();

初始化进程(lk 中的简单进程)相关结构体,初始化lk的线程系统。

2.arch_early_init()

1
2
3
// early arch stuff
lk_primary_cpu_init_level(LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_ARCH_EARLY - 1);
arch_early_init();

架构相关早期初始化,arch_early_init 和所使用的 CPU架构有很强的耦合性,这个函数的代码也主要是针对 CPU 的一些特性做设置。做一些如 关闭cache,使能 mmu ,开启一些协处理器特性的 arm 相关工作。

3.platform_early_init()

1
2
3
// do any super early platform initialization
lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH_EARLY, LK_INIT_LEVEL_PLATFORM_EARLY - 1);
platform_early_init();

相关平台的早期初始化,如获取板级信息,初始化时钟、中断、定时器等,为 lk的启动和运行提供硬件环境。

4.target_early_init()

1
2
3
// do any super early target initialization
lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM_EARLY, LK_INIT_LEVEL_TARGET_EARLY - 1);
target_early_init();

初始化板子相关,其中只初始化了串口,开启uart调试接口。

5.heap_init()

1
2
3
4
// bring up the kernel heap
lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET_EARLY, LK_INIT_LEVEL_HEAP - 1);
dprintf(SPEW, "initializing heap\n");
heap_init();

lk系统相关的堆栈初始化,用于malloc等函数的内存分配

6.call_constructors()

1
2
3
// deal with any static constructors
dprintf(SPEW, "calling constructors\n");
call_constructors();

构造函数相关初始化,这个函数是 lk 和 c++ 联合编译使用的特性,主要是为了调用 c++ 代码的构造函数,单纯的 lk 中这个函数并没有作用。

7.kernel_init()

1
2
3
// initialize the kernel
lk_primary_cpu_init_level(LK_INIT_LEVEL_HEAP, LK_INIT_LEVEL_KERNEL - 1);
kernel_init();

内核初始化。

8.创建并唤醒bootstrap2线程

1
2
3
4
5
6
7
8
9
10
11
lk_primary_cpu_init_level(LK_INIT_LEVEL_KERNEL, LK_INIT_LEVEL_THREADING - 1);

// create a thread to complete system initialization
dprintf(SPEW, "creating bootstrap completion thread\n");
thread_t *t = thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_set_pinned_cpu(t, 0);
thread_detach(t);
thread_resume(t);

// become the idle thread and enable interrupts to start the scheduler
thread_become_idle();

用于进一步完成bootloader工作,最终将本线程切换到idle状态。

以上与 boot 启动初始化相关函数是 arch_early_init、 platform_early_init 、bootstrap2

lk_init相关定义:

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
enum lk_init_level {
LK_INIT_LEVEL_EARLIEST = 1,

LK_INIT_LEVEL_ARCH_EARLY = 0x10000,
LK_INIT_LEVEL_PLATFORM_EARLY = 0x20000,
LK_INIT_LEVEL_TARGET_EARLY = 0x30000,
LK_INIT_LEVEL_HEAP = 0x40000,
LK_INIT_LEVEL_VM = 0x50000,
LK_INIT_LEVEL_KERNEL = 0x60000,
LK_INIT_LEVEL_THREADING = 0x70000,
LK_INIT_LEVEL_ARCH = 0x80000,
LK_INIT_LEVEL_PLATFORM = 0x90000,
LK_INIT_LEVEL_TARGET = 0xa0000,
LK_INIT_LEVEL_APPS = 0xb0000,

LK_INIT_LEVEL_LAST = UINT_MAX,
};

enum lk_init_flags {
LK_INIT_FLAG_PRIMARY_CPU = 0x1,
LK_INIT_FLAG_SECONDARY_CPUS = 0x2,
LK_INIT_FLAG_ALL_CPUS = LK_INIT_FLAG_PRIMARY_CPU | LK_INIT_FLAG_SECONDARY_CPUS,
LK_INIT_FLAG_CPU_SUSPEND = 0x4,
LK_INIT_FLAG_CPU_RESUME = 0x8,
};

thread_init_early()

lk系统的thread初始化

1
2
for (i=0; i < NUM_PRIORITIES; i++)     //NUM_PRIORITIES=32
list_initialize(&run_queue[i]); //初始化32个运行队列

thread_init_early 所完成的主要工作就是为 lk 完成早期的线程初始化工作,包括运行队列 的初始化,线程链表的初始化,第一个线程的创建等工作。在这个过程中一共有以下重要的数据结构:

  • 运行队列 run_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];
    run_queue 是作为多线程的调度中心存在,数组不同的下标对应不同的运行优先级,具体的应用会在后面涉及到。
  • 线程链表 thread_list
    1
    static struct list_node thread_list;
    全局的线程链表,保存了所有创建的线程信息。
  • bootstrap 线程
    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);
    每个线程的上下文信息都通过 thread_t 结构体保存,而 bootstrap 是作为 lk的第一个线程存在, 拥有 最高优先级(HIGHEST_PROORITY) 。
    1
    2
    /* since we're implicitly uniprocessor, store a pointer to the current thread here */
    thread_t *_current_thread;
    current_thread 是一个全局变量,保存了当前运行的线程的信息。
    这些数据结构都进行了初始化后,lk 就具有了创建和管理线程的结构及能力。

arch_early_init()

不同的芯片架构初始化内容不同,lk支持microblaze、or1k、mips、arm、arm-m、arm64,以arm64为例。函数定义在lk/arch/arm64/arch.c中。

1
2
3
4
5
void arch_early_init(void)
{
arm64_cpu_early_init();
platform_init_mmu_mappings();
}
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
2
3
4
5
6
7
8
9
10
11
void platform_early_init(void)
{
/* initialize the interrupt controller */
platform_init_interrupts();

/* initialize the timer block */
platform_init_timer();

/* initialize the display */
platform_init_display();
}

完成中断初始化、定时器初始化和显示初始化。

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
2
lk_primary_cpu_init_level(LK_INIT_LEVEL_THREADING, LK_INIT_LEVEL_ARCH - 1);
arch_init();

1.initialize the arch

1
2
3
dprintf(SPEW, "initializing platform\n");
lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH, LK_INIT_LEVEL_PLATFORM - 1);
platform_init();

2.initialize the rest of the platform

1
2
3
dprintf(SPEW, "initializing target\n");
lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM, LK_INIT_LEVEL_TARGET - 1);
target_init();

3.initialize the target

1
2
3
4
dprintf(SPEW, "calling apps_init()\n");
lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET, LK_INIT_LEVEL_APPS - 1);
apps_init();
lk_primary_cpu_init_level(LK_INIT_LEVEL_APPS, LK_INIT_LEVEL_LAST);

4.calling apps_init