0%

u-boot启动流程分析

RISCV start.S uboot 初始化总体框架解析

u-boot 运行在S-mode下

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
+ _start:
\-- mv tp, a0 "tp register save hartid"
|-- csrw   MODE_PREFIX(tvec), t0 "设置中断stvec 入口为 trap_entry"
|-- li t0, SIE_SSIE
|-- csrs   MODE_PREFIX(ie), t0 "设置sie 协处理器, 打开中断, 用来处理ipi 中断"
|-+ call_board_init_f "Set sp in internal/ex RAM to call board_init_f"
\ -- li t1, CONFIG_SYS_INIT_SP_ADDR "设置初始栈基址"
| -- li t0, -16
| -- and    sp, t1, t0 "将堆栈16 bits对齐"
|-+ call_board_init_f_0 "从堆栈开始的地方,为u-boot的global data(struct global_data)分配空间"
\ -- mv a0, sp
| -+ jal board_init_f_alloc_reserve
\ - top -= CONFIG_VAL(SYS_MALLOC_F_LEN) "如定义了CONFIG_SYS_MALLOC_F_LEN,需预留出early malloc所需的空间"
| -- slli t0, tp, CONFIG_STACK_SIZE_SHIFT "tp代表的是hartid, 每个hart 分配STACK_SIZE 空间"
| -- sub sp, a0, t0 "sp = a0 - t0, 地址在初始栈基址上向上增长, a0 初始栈基址 top - malloc空间"
| -- bnez   tp, secondary_hart_loop "其他hart 跳转到 secondary_hart_loop, hart 0 继续"
| -+ jal board_init_f_init_reserve "初始化uboot的global data, 置0"
\ -- "如定义了SYS_MALLOC_F_LEN,则会初始化gd->malloc_base"
| -- jal icache_enable
   | -- jal dcache_enable
   | -+ jalr -> board_init_f "调用board_init_f接口,执行前置的初始化操作" common/board_f.c
    \ -+ initcall_run_list(init_sequence_f) "执行init_sequence_f" 里的一系列初始化函数
    \ -+ init_sequence_f
    \ -- initf_malloc
    | -- arch_cpu_init
    | -- arch_cpu_init_dm
    | -- timer_init
    | -- env_init
    | -- console_init_f
    | -- print_cpuinfo
    | -- dram_init
    | -- ..
    | -- reloc_fdt
    | -- reloc_bootstage
    | -- setup_reloc
    | -+ jump_to_copy
    \ -+ relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr) "开始进行主核relocate"
    \ -- mv s2, a0          /* save addr_sp */
    | -- mv s3, a1          /* save addr of gd */
    | -- mv s4, a2          /* save addr of destination */
    | -+ stack_setup
    \ -- slli   t0, tp, CONFIG_STACK_SIZE_SHIFT
    | -- sub    sp, s2, t0 "gd->start_addr_sp - offset(of hartid)"
    设置新的sp, 这个sp 是reloc 后的sp "gd->start_addr_sp = gd->relocaddr;"
    | -- clear_bss clbss_l "将__bss_start到__bss_end" 之间reloc 到 ram上的bss 区段清0
    | -- copy_loop "reloc 将_start到_bss_start之间的代码段拷贝到 relocaddr处"
    | -+ fix_rela_dyn "Update dynamic relocations after board_init_f"
    | -+ relocate_secondary_harts
    \ -- la  t0, secondary_hart_relocate
    | -- add a0, t0, t6 "到reloc 地址上执行secondary_hart_relocate"
    a0 为 secondary_hart_relocate
    | -- mv a1, s2 "s2 为 addr_sp"
    | -- mv a2, s3 "s3 为 gd 指针"
    | -+ jal    smp_call_function
    \ -+ send_ipi_many(&ipi); "发送ipi中断", 前面传的三个参数保存在 gd->arch.ipi 中
    \ -+ riscv_send_ipi(reg) "reg为从uboot fdt中查到的所有的hart"
    \ -- SBI_CALL_1(SBI_SEND_IPI, hart_mask);
    "调用opensbi的函数, 给指定的hart发送ipi中断"
    -----------------------------ipi中断, 跳入其他核----------------------------------------------
    | -+ secondary_hart_loop "其他hart处在secondary_hart_loop 处调了 wfi 等待"
    \ -- csrr   t0, MODE_PREFIX(ip) "读sip"
    | -- andi   t0, t0, SIE_SSIE
    | -- mv a0, tp "hartid 为第一个参数"
    | -+ jal handle_ipi "如果来了software 中断, 即会跳到 handle_ipi 处理, 否则继续 secondary_hart_loop"
    \-+ handle_ipi(hartid) "对应的hart 收到ipi 中断"
    \ -- riscv_clear_ipi(hart); "会导致 mip.SSIP = sip.SSIP 清0"
    | -- invalidate_icache_all();
    | -- smp_function = gd->arch.ipi[hart].addr;
    "此处为传递的第一个参数, secondary_hart_relocate"
    | -+ smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
    \ -+ secondary_hart_relocate
    \ -- slli   t0, tp, CONFIG_STACK_SIZE_SHIFT
    | -- sub    sp, a1, t0 "a1 为 new_sp, new_sp - offset(of hartid)"
    | -- mv gp, a2 "更新gd指针到gp寄存器"
    | -- secondary_hart_loop wfi
    "其他核响应完ipi后, 只更新了sp和gp, 然后继续挂起等待"
    ---------------------------------回主核-------------------------------------------------
    | -+ call_board_init_r
    \ -- jal invalidate_icache_all
    | -- jal flush_dcache_all
    | -- mv  a0, s3          /* gd_t */
    | -- mv  a1, s4          /* dest_addr */
    | -+ jalr -> board_init_r "跳转到reloc board_init_r执行"
    \ -+ initcall_run_list(init_sequence_r)
    \ -+ init_sequence_r
    \ -- initr_reloc_global_data
    | -- initr_barrier
    | -- bootstage_relocate
    | -- board_init
    | -- arch_early_init_r
    | -- initr_secondary_cpu
    | -- interrupt_init
    | -- board_late_init "board 相关修改"
    | -+ run_main_loop "loop 等待接收输入的命令"

上述过程中, riscv_send_ipi 发送ipi 中断给其他核, 其他核是在M-mode 的opensbi 下处理的ipi中断, 处理时会调用sbi_ipi_process_smode 函数将 MIP.SSIP 置位, 因为开启了mideleg的 MIP_SSIP 位, 会导致sip.SSIP 也会被置位. 返回给uboot后, 从核在secondary_hart_loop 中loop, 判断sip.SSIP & sie.SSIE, 这个时候都置位了, 从核跳出secondary_hart_loop, 执行后面的handle_ipi 函数

u-boot 设计规范

u-boot是一个bootloader,有些情况下,它可能位于系统的只读存储器(ROM或者flash)中,并从那里开始执行。
因此,这种情况下,在u-boot执行的前期(在将自己copy到可读写的存储器之前),它所在的存储空间,是不可写的,这会有两个问题:

  1. 堆栈无法使用,无法执行函数调用,也即C环境不可用。
  2. 没有data段(或者正确初始化的data段)可用,不同函数或者代码之间,无法通过全局变量的形式共享数据。
    对于问题1,通常的解决方案是:
    u-boot运行起来之后,在那些不需要执行任何初始化动作即可使用的、可读写的存储区域,开辟一段堆栈(stack)空间。
    一般来说,大部分的平台,都有自己的SRAM,可用作堆栈空间。如果实在不行,也有可借用CPU的data cache的方法(不再过多说明)。

对于问题2,解决方案要稍微复杂一些:
首先,对于开发者来说,在u-boot被拷贝到可读写的RAM(这个动作称作relocation)之前,永远不要使用全局变量。
其次,在relocation之前,不同模块之间,确实有通过全局变量的形式传递数据的需求。怎么办?这就是global data需要解决的事情。

为了在relocation前通过全局变量的形式传递数据,u-boot设计了一个巧妙的方法:

  1. 定义一个struct global_data类型的数据结构,里面保存了各色各样需要传递的数据

  2. 堆栈配置好之后,在堆栈开始的位置,为struct global_data预留空间(可参考后面的说明),并将开始地址(就是一个struct global_data指针)保存在一个寄存器中,后续的传递,都是通过保存在寄存器中的指针实现

    board_init_f_alloc_reserve 的返回值(a0)就是global data的指针

前置的板级初始化操作

global data准备好之后,u-boot会执行前置的板级初始化动作, u-boot很有可能还在只读的存储器中
这个阶段写的代码需要follow u-boot的设计规范, 不能使用全局变量, 数据传递要使用gd 指针
进行各式各样的初始化动作。后面将会简单介绍一些和RISCV平台有关的、和平台的移植工作有关的、比较重要的API

arch_cpu_init

cpu级别的初始化操作,可以在需要的时候由CPU有关的code实现。

board_early_init_f

如果定义CONFIG_BOARD_EARLY_INIT_F,则调用board_early_init_f接口,执行板级的early初始化。平台的开发者可以根据需要,实现board_early_init_f接口,以完成特定的功能。

timer_init

初始化系统的timer。
该接口应该由平台或者板级的代码实现,初始化成功后,u-boot会通过其它的API获取当前的timestamp,后面用到的时候再详细介绍。

misc_init_f

如果使能了CONFIG_MISC_INIT_F,则调用misc_init_f执行misc driver有关的初始化。

dram_init

调用dram_init接口,初始化系统的DDR。dram_init应该由平台相关的代码实现。
如果DDR在SPL中已经初始化过了,则不需要重新初始化,只需要把DDR信息保存在global data中即可

DRAM空间的分配

DRAM初始化完成后,就可以着手规划u-boot需要使用的部分,如下图:
image-20240416113254452

  1. 考虑到后续的kernel是在RAM的低端位置解压缩并执行的,为了避免麻烦,u-boot将使用DRAM的顶端地址,即gd->ram_top所代表的位置。其中gd->ram_top是由setup_dest_addr函数配置的。
  2. u-boot所使用的DRAM,主要分为三类:
    • 各种特殊功能所需的空间,如log buffer、MMU page table、LCD fb buffer、trace buffer、等等;
    • u-boot的代码段、数据段、BSS段所占用的空间(就是u-boot relocate之后的执行空间),由gd->relocaddr标示;
    • 堆栈空间,从gd->start_addr_sp处递减。
  3. 特殊功能以及u-boot所需空间,是由reserve_xxx系列函数保留的,具体可参考source code,这里不再详细分析。

setup_reloc

计算relocation有关的信息,主要是 gd->reloc_off,计算公式如下:
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
其中CONFIG_SYS_TEXT_BASE是u-boot relocation之前在(只读)memory的位置(也是编译时指定的位置),gd->relocaddr是relocation之后的位置,因此gd->reloc_off代表u-boot relocation操作之后的偏移量,后面relocation时会用到。同时,该函数顺便把global data拷贝到了上图所示的“new global data”处

relocation的时间点,可以是“系统可读写memory始化完成之后“的任何时间点。根据u-boot当前的代码逻辑,是在board_init_f执行完成之后,因为board_init_f中完成了很多relocation有关的准备动作

后置的板级初始化操作

relocate完成之后,真正的C运行环境才算建立了起来,接下来会执行“后置的板级初始化操作”,即board_init_r函数。board_init_r和board_init_f的设计思路基本一样,也有一个很长的初始化序列—-init_sequence_r,该序列中包含如下的初始化函数

  1. initr_trace,初始化并使能u-boot的tracing system,涉及的配置项有CONFIG_TRACE。
  2. initr_reloc,设置relocation完成的标志。
  3. initr_caches,使能dcache、icache等。
  4. initr_malloc,malloc有关的初始化。
  5. initr_dm,relocate之后,重新初始化DM,涉及的配置项有CONFIG_DM。
  6. board_init,具体的板级初始化,需要由board代码根据需要实现。
  7. set_cpu_clk_info,Initialize clock framework,涉及的配置项有CONFIG_CLOCKS。
  8. initr_serial,重新初始化串口(不太明白什么意思)。
  9. initr_announce,宣布已经在RAM中执行,会打印relocate后的地址。
  10. board_early_init_r,由板级代码实现,涉及的配置项有CONFIG_BOARD_EARLY_INIT_R。
  11. arch_early_init_r,由arch代码实现,涉及的配置项有CONFIG_ARCH_EARLY_INIT_R。
  12. power_init_board,板级的power init代码,由板级代码实现,例如hold住power。
  13. initr_flash、initr_nand、initr_onenand、initr_mmc、initr_dataflash,各种flash设备的初始化。
  14. initr_env,环境变量有关的初始化。
  15. initr_secondary_cpu,初始化其它的CPU core。
  16. stdio_add_devices,各种输入输出设备的初始化,如LCD driver等。
  17. interrupt_init,中断有关的初始化。
  18. initr_enable_interrupts,使能系统的中断
  19. initr_status_led,状态指示LED的初始化,涉及的配置项有CONFIG_STATUS_LED、STATUS_LED_BOOT。
  20. initr_ethaddr,Ethernet的初始化,涉及的配置项有CONFIG_CMD_NET。
  21. board_late_init,由板级代码实现,涉及的配置项有CONFIG_BOARD_LATE_INIT。
  22. 等等…
  23. run_main_loop/main_loop,执行到main_loop,开始命令行操作。

厂商板级配置

board/<厂商>/
ex: board/thead/ice-c910/ice.c
通常只需要覆盖board_init, 配置时钟, 配置硬件资源
image-20240416113300077

arch 相关修改

覆盖cleanup_before_linux, 该函数会在跳转kernel 前执行

main 函数分析

一切就绪后进入交互状态,cli_init->cli_loop循环读取cmdline中的启动参数,将其存入console_buffer字符数组。若autoboot_command启动内核,则不会执行到cli_loop,若按键,则进入cli_loop函数,循环等待执行命令。

1
2
3
4
5
6
7
8
+ main_loop
\ -+ s = bootdelay_process();
\ -- s = env_get("bootdelay"); "获取等待时间, 用户可以选择menu中的启动项"
| -- bootdelay = menu_show(bootdelay); "配置了 CONFIG_AUTOBOOT_MENU_SHOW"
| -- s = env_get("bootcmd");
| -+ autoboot_command(s);
\ -- run_command_list(s, -1, 0); "执行bootcmd 指令" "如果能跳到kernel, 后面的指令就不会再执行了"
| -- cli_loop(); "autoboot_command没有跳转到kernel, 则会执行这里的, 等待用户输入指令"

uboot中有一个小巧的命令解释器hush shell,run_command_list只是对 hush shell 中的函数 parse_string_outer 进行了一层封装。parse_string_outer函数调用了bush_shell的命令解释器, parse_stream_outer函数来解释bootcmd的命令,而环境变量bootcmd的启动命令用来设置linux必要的启动环境,board_run_command执行命令。然后加载和启动内核

bootcmd 可以理解为shell 命令行, 通常bootcmd必须包含几条命令

  • 从flash的什么地方加载kernel到内存的什么位置, 与文件系统有关
  • bootm 指令, 从内存的什么位置启动kernel

bootm

bootm这个命令用于启动一个操作系统镜像。它会从镜像文件的头部取得一些信息,这些信息包括:文件的基于的cpu架构、其操作系统类型、镜像类型、压缩方式、镜像文件在内存中的加载地址、镜像文件运行的入口地址、镜像文件名等。 
紧接着bootm将镜像加载到指定的地址,如果需要的话,还会解压镜像并传递必要有参数给内核,最后跳到入口地址进入内核。

1
2
3
4
U_BOOT_CMD(
bootm, CONFIG_SYS_MAXARGS, 1, do_bootm,
"boot application image from memory", bootm_help_text
);

需要打开的宏
CONFIG_BOOTM_LINUX=y
CONFIG_CMD_BOOTM=y

uImage有两种格式

  • Legacy-uImage,我们需要另外加载ramdisk和fdt到RAM上面
  • FIT-uImage  kernel镜像、ramdisk镜像和fdt都已经打包到FIT-uImage的镜像中了。

示例: 加载 kernel、ramdisk、fdt
bootm 0x80008000 0x81000000 0x82000000

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
+ do_bootm "示例: argv[1]=0x80008000, arv[2]=0x81000000, argv[3]=0x82000000"
\ -+ do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
BOOTM_STATE_LOADOS | BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
BOOTM_STATE_OS_GO, &images, 1);
\ -- bootm_start(cmdtp, flag, argc, argv); "实现 verify 和 lmb"
| -- bootm_find_os(cmdtp, flag, argc, argv); "查 os 和 ep。填充到 images 成员"
| -- bootm_find_other(cmdtp, flag, argc, argv); "查 rd_start, rd_end,ft_addr 和 initrd_end, 填充images成员"
| -+ bootm_load_os(images, 0) "会对 kernel 镜像进行 load 到对应的位置上,并且如果 kernel 镜像是被 mkimage 压缩过的,那么会先经过解压之后再进行 load, 这里的压缩和 Image 压缩成 zImage 并不是同一个,而是 uboot 在 Image 或者 zImage 的基础上进行的压缩"
\ -- ulong load = os.load; "kernel 最终被加载到的地址"
| -- load_buf = map_sysmem(load, 0);
| -- ulong image_start = os.image_start; "kernel 实际存在的位置"
| -- ulong image_len = os.image_len; "kernel 的长度"
| -- image_buf = map_sysmem(os.image_start, image_len);
| -- image_decomp(os.comp, load, os.image_start, os.type, "comp: 压缩类型 NONE, GZIP, LZMA"
load_buf, image_buf, image_len,
CONFIG_SYS_BOOTM_LEN, &load_end);
"kernel 镜像就被解压到 load 位置上了"
| -- bootm_os_get_boot_func(images->os.os); "获取对应操作系统的启动函数,存放到 boot_fn, 这里配置了 CONFIG_BOOTM_LINUX , 该函数为 do_bootm_linux"
| -+ boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images)
\ -+ do_bootm_linux(BOOTM_STATE_OS_PREP, argc, argv, images)
\ -+ boot_prep_linux(images);
\ -+ image_setup_linux(images) "设置cmdline 和 fdt"
\ -- boot_get_cmdline(lmb, &images->cmdline_start, &images->cmdline_end)
| -- boot_relocate_fdt(lmb, of_flat_tree, &of_size)
| -- image_setup_libfdt(images, *of_flat_tree, of_size, lmb)
| -+ boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,images, boot_fn) "跳转到操作系统中,且不再返回了"
\ -- arch_preboot_os() "平台定制"
| -- board_preboot_os(); "board定制"
| -+ boot_fn(state, argc, argv, images);
\ -+ do_bootm_linux(BOOTM_STATE_OS_GO, argc, argv, images)
\ -+ boot_jump_linux(images, flag);
\ -- kernel = (void (*)(ulong, void *))images->ep;
| -- bootstage_mark(BOOTSTAGE_ID_RUN_OS);
| -+ announce_and_cleanup(fake);
\ -- bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
| -- cleanup_before_linux() "arch平台定制"
| -+ ret = smp_call_function(images->ep, (ulong)images->ft_addr, 0);
"多核情况, 给其他发送ipi中断, 本核继续往下走, 没有hang住, 走下面的kernel 函数"
\ -+ send_ipi_many(&ipi) "遍历fdt /cpus节点, 给其他核发送ipi中断"
\ -- gd->arch.ipi[reg].addr = ipi->addr; "kernel ep _start地址"
| -- gd->arch.ipi[reg].arg0 = ipi->arg0;
| -- gd->arch.ipi[reg].arg1 = ipi->arg1;
| -- riscv_send_ipi(reg); "给其他hart 发送ipi中断"
-----------------------------------------收到ipi中断, 其他核的处理流程--------------------------
\ -- smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr; "kernel 的_start"
| -- smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
"其他核 跳到kernel start entry, a0 寄存器存hartid, a1 存fdt_addr, 最终会等在secondary_park处"
-----------------------------------------回到本核, 跳转到kernel--------------------------------
| -- kernel(gd->arch.boot_hart, images->ft_addr); "单核跳转到kernel, a0 存boot_hartid, ft_addr 放入a1寄存器"

状态说明

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
BOOTM_STATE_START    开始执行 bootm 的一些准备动作。  
#define BOOTM_STATE_START (0x00000001) 

BOOTM_STATE_FINDOS  查找操作系统镜像
#define BOOTM_STATE_FINDOS (0x00000002) 

BOOTM_STATE_FINDOTHER  查找操作系统镜像外的其他镜像,比如 FDT\ramdisk 等等
#define BOOTM_STATE_FINDOTHER (0x00000004) 

BOOTM_STATE_LOADOS  加载操作系统
#define BOOTM_STATE_LOADOS (0x00000008) 

BOOTM_STATE_RAMDISK  操作 ramdisk
#define BOOTM_STATE_RAMDISK (0x00000010) 

BOOTM_STATE_FDT  操作 FDT
#define BOOTM_STATE_FDT (0x00000020) 

BOOTM_STATE_OS_CMDLINE  操作 commandline
#define BOOTM_STATE_OS_CMDLINE (0x00000040) 

BOOTM_STATE_OS_BD_T 
#define BOOTM_STATE_OS_BD_T (0x00000080)
BOOTM_STATE_OS_PREP  跳转到操作系统的前的准备动作
#define BOOTM_STATE_OS_PREP (0x00000100) 

BOOTM_STATE_OS_FAKE_GO  伪跳转,一般都能直接跳转到 kernel 中去
#define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* ‘Almost’ run the OS */ 

BOOTM_STATE_OS_GO  跳转到 kernel 中去
#define BOOTM_STATE_OS_GO (0x00000400) 

在这些流程中,起传递作用的是 bootm_headers_t images 这个数据结构,有些流程是解析镜像,往这个结构体里写数据。 
而跳转的时候,则需要使用到这个结构体里面的数据。

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
typedef struct bootm_headers {  
    /*
     * Legacy os image header, if it is a multi component image
     * then boot_get_ramdisk() and get_fdt() will attempt to get
     * data from second and third component accordingly.
     */
    image_header_t  *legacy_hdr_os;     /* image header pointer */  // Legacy-uImage 的镜像头
    image_header_t  legacy_hdr_os_copy; /* header copy */ // Legacy-uImage 的镜像头备份
    ulong       legacy_hdr_valid; // Legacy-uImage 的镜像头是否存在的标记

// ... 省略FIT 格式

    image_info_t    os;     /* os image info */ // 操作系统信息的结构体
    ulong       ep;     /* entry point of OS */ // 操作系统的入口地址
    ulong       rd_start, rd_end;/* ramdisk start/end */ // ramdisk 在内存上的起始地址和结束地址
    char        *ft_addr;   /* flat dev tree address */ // fdt 在内存上的地址
    ulong       ft_len;     /* length of flat device tree */ // fdt 在内存上的长度
    ulong       initrd_start; // 
    ulong       initrd_end; // 
    ulong       cmdline_start; // 
    ulong       cmdline_end; // 
    bd_t        *kbd; // 

    int     verify;     /* getenv("verify")[0] != 'n' */ // 是否需要验证
    int     state; // 状态标识,用于标识对应的 bootm 需要做什么操作,具体看下面 2.

#ifdef CONFIG_LMB
    struct lmb  lmb;        /* for memory mgmt */
#endif

 LMB 的概念。LMB 是指 logical memory blocks,主要是用于表示内存的保留区域,主要有 fdt 的区域,ramdisk 的区域等等。 
boot_prep_linux 主要的目的是修正 LMB,并把 LMB 填入到 fdt 中。

Gdb 调试

u-boot 重定位后很难继续用 gdb debug
使用”add-symbol-file” 命令重定位后,使用代码 offset = gd->relocaddr 重定位该符号:

#uboot_reloc_debug

1
2
3
4
5
6
$ symbol-file build/u-boot                            --> only for "gd_t" definition
$ set $offset = ((gd_t *)$gp)->relocaddr --> get relocation offset
$ symbol-file --> clear previous symbol
$ add-symbol-file build/u-boot $offset

$ add-symbol-file build/u-boot 0xfff53000