此文件为新唐 nuc980单板、arm926ejs架构,U_Boot-2016.11版本uboot工作流程分析说明。
(一). 程序业务流程
arch/arm/cpu/u-boot.lds /**(1). u-boot 链接文件,指定00存放向量表 *(.vectors) */ |
|
|
V
arch/arm/lib/vectors.S /**(2). 00开始存放的向量表 */
|
|
V
arch/arm/cpu/arm926ejs/start.S /**(3). start.S文件的 reset向量入口处 */ |
|
|
V
arch/arm/lib/crt0.S /**(4). _main 程序入口点文件,最后执行 ldr pc, =board_init_r */ |
|
|
V
common/board_r.c /**(5). board_init_r 程序入口点文件,执行板级初始化函数 */ |
|
|
V
common/main.c /** (6). 初始化函数数组run_main_loop调用main_loop函数入口文件;
* 此函数获取bootarg参数延时等待后,执行 bootm 命令引导
* 内核启动。至此 uboot 引导内核任务就完成。
*/
(1). arm架构的u-boot链接文件: arch/arm/cpu/u-boot.lds ,简化的文件内容
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { #ifndef CONFIG_CMDLINE /DISCARD/ : { *(.u_boot_list_2_cmd_*) } #endif
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.__image_copy_start) /** (!). uboot image 开始地址,源码中搬迁uboot时使用的开始地址 */
*(.vectors) /** (2). 向量表,00开始存放 */
CPUDIR/start.o (.text*) /** (3). reset向量指向的 start.S 文件 */
*(.text*) }
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data*) }
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : { KEEP(*(SORT(.u_boot_list*))); }
. = ALIGN(4);
.__efi_runtime_start : {
*(.__efi_runtime_start) }
.efi_runtime : {
*(efi_runtime_text)
*(efi_runtime_data) }
.__efi_runtime_stop : {
*(.__efi_runtime_stop) }
.efi_runtime_rel_start :
{
*(.__efi_runtime_rel_start) }
.efi_runtime_rel : {
*(.relefi_runtime_text)
*(.relefi_runtime_data) }
.efi_runtime_rel_stop :
{
*(.__efi_runtime_rel_stop) }
. = ALIGN(4);
.image_copy_end :
{
*(.__image_copy_end) }
/*
* 代码重定向数据段
*
*/ .rel_dyn_start :
{
*(.__rel_dyn_start) }
.rel.dyn : {
*(.rel*) }
.rel_dyn_end :
{
*(.__rel_dyn_end) }
.end :
{
*(.__end) }
_image_binary_end = .;
/* MMU 数据段
* Deprecated: this MMU section is used by pxa at present but
* should not be used by new boards/CPUs.
*/ . = ALIGN(4096);
.mmutable : {
*(.mmutable) }
/* 编译器保留的数据段
* Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
* __bss_base and __bss_limit are for linker only (overlay ordering)
*/
.bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start));
__bss_base = .; }
.bss __bss_base (OVERLAY) : {
*(.bss*) . = ALIGN(4);
__bss_limit = .; }
.bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end)); }
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) } }
(2). arch/arm/lib/vectors.S /** (2). 向量表,00开始存放 */
#include <config.h>
.globl _start /** _start 入口声明,与lds文件中对应 */ .section ".vectors", "ax"
_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif
b reset /** 地址:0000 是复位向量地址,指向 reset 入口处 */ ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
#ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK
#include <asm/arch/boot0.h>
ARM_SOC_BOOT0_HOOK
#endif
/*
*************************************************************************
*
* Indirect vectors table
*
* Symbols referenced here must be defined somewhere else
*
*************************************************************************
*/
.globl _undefined_instruction
.globl _software_interrupt
.globl _prefetch_abort
.globl _data_abort
.globl _not_used
.globl _irq
.globl _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
/*** SPL interrupt handling: just hang */
#ifdef CONFIG_SPL_BUILD
.align 5
undefined_instruction:
software_interrupt:
prefetch_abort:
data_abort:
not_used:
irq:
fiq:
1: bl 1b /* hang and never return */
#else /* !CONFIG_SPL_BUILD */ /*** 通用 uboot 模式的终端向量 */ /* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN: .word 0x0badc0de
#ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START: .word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START: .word 0x0badc0de
#endif /* CONFIG_USE_IRQ */
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
/* ... */
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
@ carve out a frame on current user stack
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12 ldr r2, IRQ_STACK_START_IN
@ get values for "aborted" pc and cpsr (into parm regs)
ldmia r2, {r2 - r3}
add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp @ save current stack into r0 (param register)
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
@ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good. add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, IRQ_STACK_START_IN @ setup our mode stack
str lr, [r13] @ save caller lr in position 0 of saved stack
mrs lr, spsr @ get the spsr
str lr, [r13, #4] @ save spsr in position 1 of saved stack
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13 @ switch modes, make sure moves will execute
mov lr, pc @ capture return pc
movs pc, lr @ jump to next instruction & switch modes.
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/*
* exception handlers
*/
.align 5 undefined_instruction: get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5 software_interrupt: get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5 prefetch_abort: get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5 data_abort: get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5 not_used: get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5 irq: get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5 fiq: get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */ irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5 irq: get_bad_stack
bad_save_user_regs
bl do_irq
.align 5 fiq: get_bad_stack
bad_save_user_regs
bl do_fiq
#endif /* CONFIG_USE_IRQ */
#endif /* CONFIG_SPL_BUILD */
(3). reset向量指向的 start.S 文件
#include <asm-offsets.h>
#include <config.h>
#include <common.h>
/*************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram
* setup stack
********************************************************/
.globl reset /** vectors.S文件中 b reset跳转过来的,reset入口点*/
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
bl _main /** 跳转到 arch/arm/lib/crt0.S 文件中_main入口处 */
(4). _main 程序入口点文件 //arch/arm/lib/crt0.S 文件
#include <config.h>
#include <asm-offsets.h>
#include <linux/linkage.h>
#ifdef CONFIG_CPU_V7M
#include <asm/armv7m.h>
#endif
/*
* entry point of crt0 sequence
*/
ENTRY(_main) /** 程序入口点 ENTRY( _main ) */
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) #endif
#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
mov r3, sp
bic r3, r3, #7
mov sp, r3
#else
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#endif
mov r0, sp
bl board_init_f_alloc_reserve
mov sp, r0
/* set up gd here, outside any C code */
mov r9, r0
bl board_init_f_init_reserve
mov r0, #0
bl board_init_f
#if ! defined(CONFIG_SPL_BUILD)
/*
* Set up intermediate environment (new sp and gd) and call
* relocate_code(addr_moni). Trick here is that we'll return
* 'here' but relocated.
*/
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
mov r3, sp
bic r3, r3, #7
mov sp, r3
#else
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#endif
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */
adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
#if defined(CONFIG_CPU_V7M)
orr lr, #1 /* As required by Thumb-only */
#endif
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code here: /*
* now relocate vectors
*/
bl relocate_vectors /* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD
/* Use a DRAM stack for the rest of SPL, if requested */
bl spl_relocate_stack_gd
cmp r0, #0
movne sp, r0
movne r9, r0
# endif
ldr r0, =__bss_start /* this is auto-relocated! */
#ifdef CONFIG_USE_ARCH_MEMSET
ldr r3, =__bss_end /* this is auto-relocated! */
mov r1, #0x00000000 /* prepare zero to clear BSS */
subs r2, r3, r0 /* r2 = memset len */
bl memset
#else
ldr r1, =__bss_end /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l:cmp r0, r1 /* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
itt lo
#endif
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l #endif
#if ! defined(CONFIG_SPL_BUILD)
bl coloured_LED_init
bl red_led_on
#endif
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
#if defined(CONFIG_SYS_THUMB_BUILD)
ldr lr, =board_init_r /* this is auto-relocated! */
bx lr
#else
ldr pc, =board_init_r /* this is auto-relocated!
* 执行跳转到 board_init_r 函数
*/
#endif /* we should not return here. */
#endif
ENDPROC(_main) /** ( _main ) 程序段结束 ENDPROC */
(5). board_init_r 程序入口点文件,执行板级初始化函数
void board_init_r(gd_t *new_gd, ulong dest_addr) { #ifdef CONFIG_NEEDS_MANUAL_RELOC int i; #endif
#ifdef CONFIG_AVR32
mmu_init_r(dest_addr);
#endif
#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
gd = new_gd;
#endif
#ifdef CONFIG_NEEDS_MANUAL_RELOC
for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
init_sequence_r[i] += gd->reloc_off; /** 执行初始化函数指针数组 */ #endif
if (initcall_run_list(init_sequence_r))
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
/**** 函数指针数组 初始化 */
/*
* Over time we hope to remove these functions with code fragments and
* stub funtcions, and instead call the relevant function directly.
*
* We also hope to remove most of the driver-related init and do it if/when
* the driver is later used.
*
* TODO: perhaps reset the watchdog in the initcall function after each call?
*/ init_fnc_t init_sequence_r[] = { /** 初始化函数指针数组 */ initr_trace,
initr_reloc,
/* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
initr_caches,
/* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
* A temporary mapping of IFC high region is since removed,
* so environmental variables in NOR flash is not availble
* until board_init() is called below to remap IFC to high
* region.
*/
#endif
initr_reloc_global_data,
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
initr_unlock_ram_in_cache,
#endif
initr_barrier,
initr_malloc,
initr_console_record,
#ifdef CONFIG_SYS_NONCACHED_MEMORY
initr_noncached,
#endif
bootstage_relocate,
#ifdef CONFIG_DM
initr_dm,
#endif
initr_bootstage,
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
board_init, /* Setup chipselects */
#endif
/*
* TODO: printing of the clock inforamtion of the board is now
* implemented as part of bdinfo command. Currently only support for
* davinci SOC's is added. Remove this check once all the board
* implement this.
*/
#ifdef CONFIG_CLOCKS
set_cpu_clk_info, /* Setup clock information */
#endif
#ifdef CONFIG_EFI_LOADER
efi_memory_init,
#endif
stdio_init_tables,
initr_serial,
initr_announce,
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC initr_manual_reloc_cmdtable, #endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) initr_trap, #endif
#ifdef CONFIG_ADDR_MAP initr_addr_map, #endif
#if defined(CONFIG_BOARD_EARLY_INIT_R) board_early_init_r, #endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_LOGBUFFER initr_logbuffer, #endif
#ifdef CONFIG_POST initr_post_backlog, #endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_SYS_DELAYED_ICACHE initr_icache_enable, #endif
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do early PCI configuration _before_ the flash gets initialised,
* because PCU ressources are crucial for flash access on some boards.
*/
initr_pci,
#endif
#ifdef CONFIG_WINBOND_83C553 initr_w83c553f, #endif
#ifdef CONFIG_ARCH_EARLY_INIT_R arch_early_init_r, #endif
power_init_board,
#ifndef CONFIG_SYS_NO_FLASH initr_flash, #endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) || \ defined(CONFIG_SPARC)
/* initialize higher level parts of CPU like time base and timers */
cpu_init_r,
#endif
#ifdef CONFIG_PPC initr_spi, #endif
#ifdef CONFIG_CMD_NAND initr_nand, #endif
#ifdef CONFIG_CMD_ONENAND initr_onenand, #endif
#ifdef CONFIG_GENERIC_MMC initr_mmc, #endif
#ifdef CONFIG_HAS_DATAFLASH initr_dataflash, #endif
initr_env,
#ifdef CONFIG_SYS_BOOTPARAMS_LEN initr_malloc_bootparams, #endif
INIT_FUNC_WATCHDOG_RESET
initr_secondary_cpu,
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET) mac_read_from_eeprom, #endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do pci configuration
*/
initr_pci,
#endif
stdio_add_devices, initr_jumptable,
#ifdef CONFIG_API initr_api, #endif
console_init_r, /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE show_board_info, #endif
#ifdef CONFIG_ARCH_MISC_INIT arch_misc_init, /* miscellaneous arch-dependent init */ #endif
#ifdef CONFIG_MISC_INIT_R misc_init_r, /* miscellaneous platform-dependent init */ #endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB initr_kgdb, #endif
interrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_AVR32) initr_enable_interrupts, #endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || defined(CONFIG_M68K)
timer_init, /* initialize timer */
#endif
#if defined(CONFIG_STATUS_LED) initr_status_led, #endif /* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET initr_ethaddr, #endif
#ifdef CONFIG_BOARD_LATE_INIT board_late_init, #endif
#if defined(CONFIG_CMD_AMBAPP) ambapp_init_reloc,
#if defined(CONFIG_SYS_AMBAPP_PRINT_ON_STARTUP) initr_ambapp_print,
#endif
#endif
#ifdef CONFIG_SCSI INIT_FUNC_WATCHDOG_RESET
initr_scsi,
#endif
#ifdef CONFIG_CMD_DOC INIT_FUNC_WATCHDOG_RESET
initr_doc,
#endif
#ifdef CONFIG_BITBANGMII initr_bbmii, #endif
#ifdef CONFIG_CMD_NET INIT_FUNC_WATCHDOG_RESET
initr_net,
#endif
#ifdef CONFIG_POST initr_post, #endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE) initr_pcmcia, #endif
#if defined(CONFIG_CMD_IDE) initr_ide, #endif
#ifdef CONFIG_LAST_STAGE_INIT INIT_FUNC_WATCHDOG_RESET
/*
* Some parts can be only initialized if all others (like
* Interrupts) are up and running (i.e. the PC-style ISA
* keyboard).
*/
last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG INIT_FUNC_WATCHDOG_RESET
initr_bedbug,
#endif
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) initr_mem, #endif
#ifdef CONFIG_PS2KBD initr_kbd, #endif
#if defined(CONFIG_SPARC)
prom_init,
#endif
/**
* statge 2: entry dot, uboot 第二阶段的入口函数 run_main_loop 函数
*/
run_main_loop,
};
static int run_main_loop(void) {
#ifdef CONFIG_SANDBOX
sandbox_main_loop_init();
#endif /* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop(); /**调用函数main_loop()此函数在common/main.c文件中*/
return 0;
}
(6). 初始化函数数组run_main_loop调用main_loop函数入口文件
/* We come here after U-Boot is initialised and ready to process commands */ void main_loop(void) { const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
#ifdef CONFIG_VERSION_VARIABLE
setenv("ver", version_string); /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */
cli_init();
run_preboot_environment_command();
#if defined(CONFIG_UPDATE_TFTP)
update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */
s = bootdelay_process();
/**
* s 内容:烧录到spi flash中环境变量内容,参考下面6.2的env.txt文件
* setspi=sf probe 0 30000000
* loadkernel=sf read 0x7fc0 0x200000 0x800000
*
* s 命令内容如下:
* (1) =run sf probe 0 30000000;
* 设置 spi 频率3000000Hz;
* (2) run sf read 0x7fc0 0x200000 0x800000;
* sf read flash 0x200000 to DDR 0x7fc0,length 0x800000;
* (3) bootm 0x7fc0
* 引导uimage内核从 0x7fc0 地址启动,
* 64byte(0x40) + 0x7fc0 = 0x8000 内核地址。
*/
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s); /**此函数执行bootm 0x7fc0 启动引导kernel程序 */
cli_loop();
panic("No CLI available"); }
(6.2) 烧录到spi flash中环境变量内容,参考 env.txt 文件
baudrate=115200
bootdelay=1
stderr=serial
stdin=serial
stdout=serial
setspi=sf probe 0 30000000
loadkernel=sf read 0x7fc0 0x200000 0x800000
bootcmd=run setspi;run loadkernel;bootm 0x7fc0