《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
牢记 u-boot 的本质作用
1. 将内核从Flash读出内核到SDRAM
2. 启动内核
其它都是附加功能,分析第二阶段代码之前我们要明确目标,由于代码量比较庞大,不要花费很多精力去分析不是太重要的代码。
第二阶段代码从 start_armboot 开始
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); // gd 是一个结构体指针,指向内存 CFG_GBL_DATA_SIZE 128 字节(韦老师书本 P260) 全局变量,保存各种参数,比如 内存大小、内存起始地址
flash_init (); // Flash 初始化
nand_init(); /* go init the NAND */
....... /* 其它一系列的初始化 */
for (;;) {//进入死循环
main_loop ();
}
分析 main_loop()
1. s = getenv ("bootdelay"); // 读取环境变量,bootdelay u-boot 启动延时时间
2. 倒计时 倒计时没有按下空格
a. s = getenv ("bootcmd"); 读出环境变量 bootcmd = nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0
b. run_command (s, 0);//启动内核s = getenv ("bootcmd")
3. 如果倒计时按下空格
a. len = readline (CFG_PROMPT);//读取串口的命令
b. run_command (lastcommand, flag);
所以u-boot 的核心是 run_command() 各种命令的集合
关于怎么自定义命令,请看我的另一个帖子,这里不重要,可跳过。
------------------------------------------------------------------
自定义u-boot命令 及u-boot 链接脚本 .u_boot_cmd 段的理解
https://whycan.cn/t_1295.html
------------------------------------------------------------------
我们继续分析代码
怎么将内核拷贝到SDRAM
s = getenv ("bootcmd")//获取环境变量 bootcmd
run_command (s, 0) //执行环境变量对应的命令 nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0
相当于执行了nand read.jffs2 0x30007FC0 kernel 和 bootm 0x30007FC0 两条命令
分析第一条命令前先分析一下什么是环境变量?
在u-boot 命令行下 输入 print 打印出来的就是一些常用的环境变量
pc 机每个硬盘上都有一个分区表 C 盘 D 盘 ...
嵌入式Linux 没有分区表,需要我们自己约定每个分区,一般NAND Flash 上的划分为:
bootloader||环境变量(参数)||kernel||文件系统(root)
由于没有分区表,所以这些分区在源码里写死的。在配置文件 100ask24x0.h 中
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \ // 从0开始的256K是bootloader
"128k(params)," \ // 128K 是 params
"2m(kernel)," \ // 2M 是内核
"-(root)" // 剩下的实际 root 文件系统
可以输入 u-boot 命令 mtd 查看
#: name size offset mask_flags
0: boot_loader 0x00040000 0x00000000 0
1: params 0x00020000 0x00040000 0
2: kernel 0x00200000 0x00060000 0
3: root 0x0fda0000 0x00260000 0
active partition: nand0,0 - (bootloader) 0x00040000 @ 0x00000000
defaults:
mtdids : nand0=nandflash0
mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root
在命令 nand read.jffs2 0x30007FC0 kernel 中 kernel也是一个环境变量 0x00200000 0x00060000
所以
nand read.jffs2 0x30007FC0 kernel
等价于
nand read.jffs2 0x30007FC0 0x00200000 0x00060000
意思是:从NAND Flash 的 0x00060000 位置读取 0x00200000(2M) 的数据到 SDRAM 的 0x30007FC0
名字kernel 不重要 重要的是他对应的地址
nand read.jffs2 0x30007FC0 kernel
nand read.jffs2 0x30007FC0 0x00200000 0x00060000
执行,是一样的效果。
由命令 nand read.jffs2 0x30007FC0 kernel 可以知道 是 nand 命令实现的烧写,所以我们分析 nand 命令对应的函数 do_nand
do_nand 函数里面的 !strcmp(s, ".jffs2") || !strcmp(s, ".e") 进而调用 ret = nand_read_opts(nand, &opts); 拷贝 内核到 SDRAM
jffs2 是一种文件格式,这里跟文件格式没有关系,只是用这个命令,对页对齐 块对齐没有限制
怎么调用内核?
bootm 0x30007FC0
分析 do_bootm
先来说明一下内核的格式
uimage: 头 + 真正的内核
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address 数据加载地址,如果当前地址不等于加载地址,则把内核搬运到加载地址 */
uint32_t ih_ep; /* Entry Point Address 入口地址,搬运完毕内核后,跳到入口地址启动内核 */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
bootm 启动内核分析内核当前地址是否等于加载地址,如果不等于加载地址,则把内核移动到加载地址,然后跳到内核入口去启动内核。
我们所用内核的加载地址是0x30008000
0x30008000 - 0x30007FC0 = 64 字节
64 字节刚好等于 内核头接结构 image_header_t 的长度,所以内核不用从新搬运,从而提高启动速度。
if(ntohl(hdr->ih_load) == data) ,位置相同,不搬运
继续讲怎么启动内核
1. uboot 通过特定数据格式(tag)告诉内核一些参数
setup_serial_tag (¶ms);//起始标签
setup_memory_tags (bd);//内存标签 告诉内核可用内存个数和大小
setup_commandline_tag (bd, commandline);//命令标签
setup_end_tag (bd);//结束标签
2. 跳转到内核入口地址
void (*theKernel)(int zero, int arch, uint params);//定义函数指针
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);//获取内核入口
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//传递参数 启动内核
一些分析过程和对应的文件
----------------------------------------------------------
lib_arm.c
调用 do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
查看 Armlinux.c 下的 do_bootm_linux 函数
设置启动参数:
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd);
#endif
启动内核
void (*theKernel)(int zero, int arch, uint params);//定义函数指针
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);//获取内核入口
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//传递参数 启动内核
uboot 启动完内核就退出了,怎么传递参数呢?
1. 在某个地址(开发版:0x30000100)按照双方约定的某种格式tag保存数据。
setup_start_tag (bd); //起始标签
setup_memory_tags (bd); // 内存的块数起始地址和大小
setup_commandline_tag (bd, commandline); //传递命令行
setup_end_tag (bd); //结束标签
setup_start_tag lib_arm.c
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params; // 100ask24x0.c (board\100ask24x0): gd->bd->bi_boot_params = 0x30000100;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params); // #define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))
}
tag结构
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
struct tag_core {
u32 flags; /* bit 0 = read-only */
u32 pagesize;
u32 rootdev;
};
struct tag_header {
u32 size;
u32 tag;
};
联合体与结构体的区别:
联合体内的成员共用同一个内存,占用内存空间为最大成员所占用的空间。同一时间联合体内部只能存放其中一个成员。
结构体每个成员都占用一定的内存空间。同一时间结构体内部可以存放多个成员。
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
unsigned char bi_enetaddr[6]; /* Ethernet adress */
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t;
关于好多地方用到的gd 全局变量 搜索 gd_t *gd
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! */
unsigned long bus_clk;
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t;
setup_start_tag 在内存中的排列
U32 u.core.rootdev = 0 0x30000110
U32 u.core.pagesize = 0 0x3000010C
U32 u.core.flags=0 0x30000108
U32 hdr.size = tag_size (tag_core) = 5 0x30000104 #define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) // 右移2 等于 /4
U32 hdr.tag = ATAG_CORE=(0x54410001) 0x30000100
setup_memory_tags (bd);
获取内存块数、起始地址、大小
setup_commandline_tag (bd, commandline)
char *commandline = getenv ("bootargs");// 来自于环境变量 bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
----------------------------------------------------------
最近编辑记录 xinxiaoci (2018-06-12 19:34:43)
离线
uboot,源码我开了好久都卡不懂了,看韦东山了直接把我看懵逼了,
离线