Download presentation
Presentation is loading. Please wait.
1
College of Computer Science & Technology
第五章 BootLoader开发 College of Computer Science & Technology
2
第五章 BootLoader开发 BootLoader的作用 BootLoader的启动流程 BootLoader之vivi
鲁东大学 LUDONG UNIVERSITY BootLoader的作用 BootLoader的启动流程 BootLoader之vivi BootLoader之Uboot (附)
3
包含依赖于CPU体系结构的硬件初始化代码 汇编语言编写 第二阶段 初始化操作系统内核运行环境 C语言完成
BootLoader结构 鲁东大学 LUDONG UNIVERSITY BootLoader一般分为两个阶段 第一阶段 包含依赖于CPU体系结构的硬件初始化代码 汇编语言编写 第二阶段 初始化操作系统内核运行环境 C语言完成 分段:提高BootLoader的可移植性和可读性
4
第一阶段 基本硬件设备初始化 BootLoader启动流程 为第二阶段准备RAM空间 复制BootLoader第二阶段代码到RAM 堆栈设置
鲁东大学 LUDONG UNIVERSITY 第一阶段 基本硬件设备初始化 为第二阶段准备RAM空间 复制BootLoader第二阶段代码到RAM 堆栈设置 跳转至第二阶段的C程序入口
5
BootLoader vivi第二阶段 vivi第二阶段 vivi第一阶段 head.s —C语言编写的一个main()函数 共包括8个步骤
鲁东大学 LUDONG UNIVERSITY vivi第二阶段 —C语言编写的一个main()函数 vivi第一阶段 head.s … bl main 共包括8个步骤 vivi第二阶段 main.c 完成后,将根据输入,进入操作系统或者vivi-shell step1 … step8 boot_or_vivi boot embeddOS vivi-shell
6
vivi第二阶段代码(1) Next int main(int argc, char *argv[]) { int ret;
鲁东大学 LUDONG UNIVERSITY int main(int argc, char *argv[]) { int ret; /*Step 1*/ putstr("\r\n"); putstr(vivi_banner); reset_handler(); /*Step 2*/ ret = board_init(); if (ret) { putstr("Failed a board_init() procedure\r\n"); error(); } /*Step 3*/ mem_map_init(); mmu_init(); putstr("Succeed memory mapping.\r\n"); Step1 打印vivi版本 判断是否为硬件复位 Step2 开发板初始化 Reset_handler()是无效代码。 Step3 内存映射初始化 mmu初始化 Next
7
vivi第二阶段代码(2) /*Step 4*/ ret = heap_init(); Step4 堆初始化 if (ret) {
鲁东大学 LUDONG UNIVERSITY /*Step 4*/ ret = heap_init(); if (ret) { putstr("Failed initailizing heap region\r\n"); error(); } /*Step 5*/ ret = mtd_dev_init(); /*Step 6*/ init_priv_data(); /*Step 7*/ misc(); init_builtin_vivi_cmds(); / /*Step 8*/ boot_or_vivi(); return 0; Step4 堆初始化 Step5 mtd设备初始化 Step6 私有数据初始化 Step7 vivi命令初始化 Step8 启动os 或者 vivi-shell Back
8
vivi第二阶段分析 步骤1 Step1 ①putstr(vivi_banner) 由串口打印一个字符串vivi_banner
鲁东大学 LUDONG UNIVERSITY Step1 ①putstr(vivi_banner) 由串口打印一个字符串vivi_banner 该字符串为vivi版本信息 定义位置-version.c Vivi_banner 可以修改
9
hard_reset_handler() soft_reset_handler() clear_mem(base,length)
vivi第二阶段 步骤1 鲁东大学 LUDONG UNIVERSITY ②reset_handler() main() reset_handler() 判断如果是硬件复位,则将用户使用的DRAM(内存)清零。 判断复位原因 clear_mem参数 base-user_ram_base length-user_ram_size hard_reset_handler() 条件编译 CONFIG_RESET_HANDLING 是无效的,所以实际上使用的reset_handler()是个空函数。 soft_reset_handler() clear_mem(base,length) Back
10
vivi第二阶段 步骤2 step2 board_init() main() board_init() 调用init_time()
鲁东大学 LUDONG UNIVERSITY step2 board_init() main() board_init() 调用init_time() 初始化定时器 vivi中未用 init_time() 设置IO作用 设置口上拉 设置EXINT 调用set_gpios() 初始化通用IO口 set_gpios() 这一部分与开发板电路有关,移植过程中需要根据实际电路进行修改 return 0 Back
11
mem_mapping_linear() cache_clean_invalidate
vivi第二阶段 步骤3 鲁东大学 LUDONG UNIVERSITY ①mem_map_init();内存映射初始化-建立页表 ②mmu_init();存储器管理单元MMU初始化 main() mem_map_init() mem_map_nand_boot() mem_map_nor() mem_mapping_linear() 源码 mmu.c cache_clean_invalidate tlb_invalidate
12
mem_mapping_linear()-初始化MMU主页表
vivi第二阶段 步骤3 鲁东大学 LUDONG UNIVERSITY mem_mapping_linear()-初始化MMU主页表 基址12bit Description 20bit 4095 0<<20 1<<20 1 4095<<20 … 1MB/页 4GB 4096页 for (sectionNumber = 0; sectionNumber < 4096; sectionNumber++) { pageoffset = (sectionNumber << 20); *(mmu_tlb_base + (pageoffset >> 20)) = pageoffset | MMU_SECDESC; }
13
MMU只使用一级页表地址转换 虚地址 访问页表 基址 1 [1:0] 00-error 01-二级粗表 (4,64K) 10-一级页表
鲁东大学 LUDONG UNIVERSITY 虚地址 12bit 20bit偏移量 访问页表 只使用一级页表 4095 基址 1 … [1:0] 00-error 01-二级粗表 (4,64K) 10-一级页表 (1024K) 11-二级细表 (1,4,64K) 物理地址 基址 20bit偏移量 ARM920T内集成MMU支持2级页表,根据页表项的最低两位进行虚地址-物理地址的转换
14
mmu_init();存储器管理单元MMU初始化-MMU配置
vivi第二阶段 步骤3 鲁东大学 LUDONG UNIVERSITY mmu_init();存储器管理单元MMU初始化-MMU配置 arm920_setup()采用内嵌汇编__asm 操作CP15相关寄存器对MMU进行控制 main() mmu_init() arm920_setup() 禁用I_cache和 D_cache "mcr p15, 0, r0, c7, c7, 0" 耗写并禁用WB "mcr p15, 0, r0, c7, c10, 4 " r0=0 禁用数据/指令TLB "mcr p15, 0, r0, c8, c7, 0 " r4=0x33dfc000 将页表地址写入CP15:C2 "mcr p15, 0, r4, c2, c0, 0" …
15
vivi第二阶段 步骤3 mcr p15, 0, r0, c3, c0, 0 位 符号 使能功能 1 2 3 7 8 9 12 13 M A
鲁东大学 LUDONG UNIVERSITY mcr p15, 0, r0, c3, c0, 0 位 符号 使能功能 1 2 3 7 8 9 12 13 M A C W B S R I V MMU 对齐检测 数据Cache 写缓冲器 大端模式 系统保护位 ROM保护位 指令Cache 高地址向量表 mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x3000 bic r0, r0, #0x0300 bic r0, r0, #0x0087 orr r0, r0, #0x0002 orr r0, r0, #0x0004 orr r0, r0, #0x1000 orr r0, r0, #0x0001 mcr p15, 0, r0, c1, c0, 0
16
MMU页表项说明 主页表项 基地址 SBZ AP 域 1 C B 共16种域 页表项权限控制 每个域控制位权限控制位2位,共32位
鲁东大学 LUDONG UNIVERSITY 主页表项 基地址 SBZ AP 域 1 C B 31 20 19 12 11-10 9 8-5 4 3 2 共16种域 页表项权限控制 每个域控制位权限控制位2位,共32位 CP15的C3寄存器 域-主控权限 页表项权限控制-次控制 域值 访问 说明 11 10 01 00 管理者 保留 客户 不可访问 访问不受控制 不可预料 访问受页表项权限控制 产生域错误 Back
17
vivi第二阶段 步骤4 step4 heap_init() heap—堆,用于动态分配的内存空间 链表形式组织的内存空间 main()
鲁东大学 LUDONG UNIVERSITY step4 heap_init() heap—堆,用于动态分配的内存空间 链表形式组织的内存空间 函数mmalloc从堆中分配空间 相应地,mfree释放空间到堆中 main() heap_init() mmalloc_init() heap_init()调用函数mmalloc_init()对堆内存块链表头指针进行初始化。 根据返回值判断初始化是否成功。
18
vivi堆结构 堆数据结构blockhead 内存块 标识该块是否已分配,0-No,1-Yes
鲁东大学 LUDONG UNIVERSITY 堆数据结构blockhead 标识该块是否已分配,0-No,1-Yes typedef struct blockhead_t { Int32 signature; Bool allocated; unsigned long size; struct blockhead_t *next; struct blockhead_t *prev; } blockhead; 标识该内存块大小 内存块 signature allocated 指向下一块内存块 size of(Blockhead) blockhead size *next 指向上一块内存块 *prev block size
19
vivi堆组织 … static blockhead *gHeapBase ;全局变量,heap链表头指针
鲁东大学 LUDONG UNIVERSITY static blockhead *gHeapBase ;全局变量,heap链表头指针 gHeapBase NULL NULL … mmalloc_init():初始化gHeapBase指向的链表第一项 mmalloc_init((unsign char*)(HEAP_BASE),HEAP_SIZE) 调用后,整个堆初始化为一个大小为HEAP_SIZE的数据块 源码 lib/heap.c
20
vivi内存动态分配 I. mmalloc()-void *mmalloc(unsign long size) 1 N
鲁东大学 LUDONG UNIVERSITY 1 I. mmalloc()-void *mmalloc(unsign long size) N block->size>size? blockptr=gHeapbase Y newblock=blockptr+sizeof(blockhead)+size blockptr==null? Y N newblock->size=blockptr->size -sizeof(blockhead)-size Y blockptr->allocated==yes? newblock->allocated=false newblock->next=blockptr->next newblock->prev=blockptr N return null Y block->size>=size? N blockptr->next=nextblock block->size=size 1 blockptr=block->next return (blockptr+sizeof(blockhead))
21
vivi内存动态释放 vivi内存动态释放
鲁东大学 LUDONG UNIVERSITY II.mfree()-void free(void *block) Y block==null? 回朔,加上头结构 N blckptr=block-sizeof(blockhead) 后一个块是空块? 放回堆中 Y blockptr->allocated=false 与后一块合并,修改size,prev和next 前一个块是空块? Return Y 与前一块合并,修改size,prev和next Back
22
vivi第二阶段 步骤5 step5 mtd_dev_init() mtd-memory technology device 存储技术设备
鲁东大学 LUDONG UNIVERSITY step5 mtd_dev_init() mtd-memory technology device 存储技术设备 Linux操作系统用于访问存储设备的系统,主要是为了更加简单的支持Flash这类存储设备。 Flash硬件驱动 MTD原始设备 MTD块设备 MTD字符设备 块设备节点 字符设备节点 文件系统 根文件系统 封装mtd为块设备 封装mtd为字符设备 大量Flash数据/操作函数,匹配ID 通用时序,读取芯片ID信息
23
mtd_dev_init() vivi中调用mtd_dev_init()函数初始化Flash为MTD设备。 函数调用结构: main()
鲁东大学 LUDONG UNIVERSITY vivi中调用mtd_dev_init()函数初始化Flash为MTD设备。 函数调用结构: main() Step5 mtd_dev_init() mtd_dev_init()函数调用mtd_init() mtd_init()根据autoconfig.h的定义,调用smc_init()函数,初始化sumsungNANDFLASH存储器为MTD设备,构造nand_chip数据结构 mtd_init() smc_init() smc_init()函数构造mfd_info数据结构的过程中,调用smc_scan扫描已知NANDFLASH型号 smc_insert() smc_scan()
24
通过mtd->priv=nand_chip
smc_init() 鲁东大学 LUDONG UNIVERSITY smc_init()初始化了两个数据结构 I.nand_chip II.mtd_info 源文件 s3c2410_flash.c Nand.h MTD.h 将nand_chip结构的各函数 (read,write)指针与smc 定义函数关联 内存开辟空间结构化为 nand_chip和mtd_info 通过函数控制NANDFLASH 控制器进行芯片复位 初始化结构体为全零 通过mtd->priv=nand_chip 进行关联 调用smc_insert()进行 MTD设备初始化 初始化NANDFLASH控制器 (设值NFCONF)
25
smc_scan() smc_insert()为构造mtd设备数据结构mtd_info 主要操作是调用了smc_scan()查找芯片信息
鲁东大学 LUDONG UNIVERSITY smc_insert()为构造mtd设备数据结构mtd_info 主要操作是调用了smc_scan()查找芯片信息 源码 smc_core.c 读取nandflash ID信息 根据ID匹配数组项 进行MTD参数设置 根据ID查找数组 nand_dev_ids[] 完善nand_chip数据项 nand_smc_info[] 根据ID匹配数组项 进行MTD参数设置 关联mtd_info的各函数 到NAND操作函数 Back
26
smc_scan()中用到的两个数组 I.nand_dev_ids[ ]
鲁东大学 LUDONG UNIVERSITY I.nand_dev_ids[ ] 该数组中定义了常用的NANDFLASH芯片相关信息,为结构体nand_flash_dev数组 Nand.h Nand_ids.h EX:nand_dev_ids[ ]={ {"Samsung K9D1G08V0M", NAND_MFR_SAMSUNG, 0x79, 27, 0, 3, 0x4000}, // 128Mb {NULL,}} II.nand_smc_info[ ] 该数组为结构体nand_smc_dev数组,共有8项,分别定义了不同容量,从1M~128M的NAND Flash芯片的操作参数 Nand_ids.h
27
vivi第二阶段 步骤6 step6 init_priv_data() 拷贝参数到内存(SDRAM)定义位置
鲁东大学 LUDONG UNIVERSITY step6 init_priv_data() 拷贝参数到内存(SDRAM)定义位置 init_priv_data() 分别调用两个函数拷贝: I.vivi内置缺省参数 II.用户自定义参数 get_default_priv_data() load_saved_priv_data() get_default_param_tlb() 拷贝的参数分为三类:a.vivi使用参数 b.linux启动命令 c.mtd分区信息 get_default_linux_cmd() get_default_mtd_partition()
28
vivi参数拷贝-默认 get_default_param_tlb() VIVI 1MB get_default_linux_cmd()
鲁东大学 LUDONG UNIVERSITY get_default_param_tlb() 源码 rw.c VIVI 1MB get_default_linux_cmd() get_default_mtd_partition() HEAP 1MB MMU_TABLE 16KB 三个拷贝函数相似,由VIVI内定义数组(默认参数)拷贝到SDRAM固定位置 16KB Linux_CMD VIVI_PARAM MTD_PART VIVI_PRIV_RAM_BASE 16KB STACK 32KB 参数个数 DRAM BASE 8字符开头
29
vivi参数拷贝-用户定义 buffer SDRAM 0x30000000 SDRAM FLASH 0x1FC0000
鲁东大学 LUDONG UNIVERSITY VIVI_PRIV_RAM VIVI_PRIV_ROM buffer SDRAM 0x SDRAM FLASH 0x1FC0000 测试每类头8个字符是否正确 I.首先调用get_default_priv_data 都拷贝到VIVI_PRIV_RAM_BASE II.然后调用load_saved_priv_data 所以,用户定义参数优先级高于默认参数,如果用户定义,则覆盖默认参数,使用用户定义参数。 Back
30
vivi第二阶段 步骤7 Step7 init_builtin_cmd() 初始化vivi shell支持的用户命令 …
鲁东大学 LUDONG UNIVERSITY Step7 init_builtin_cmd() 初始化vivi shell支持的用户命令 VIVI内使用命令链表数据结构包含vivi shell支持的命令 命令链表中每条命令采用cmd结构体 Init_builtin_cmd()通过调用add_cmd()向链表中加入命令 Init_builtin_cmd() 源码 command.c add_cmd(&load_cmd) add_cmd()多次调用,根据需要将所有命令加入命令链表 … add_cmd(&help_cmd)
31
"help [{cmds}] \t\t\t-- Help about help?"
vivi用户命令 数据结构 鲁东大学 LUDONG UNIVERSITY typedef struct user_command { const char *name; void (*cmdfunc)(int argc, const char **); struct user_command *next_cmd; const char *helpstr; } user_command_t; head_cmd tail_cmd name *cmdfunc next_cmd helpstr name *cmdfunc next_cmd helpstr help command_help null helpstr 命令功能函数 命令功能函数 command_help() … "help [{cmds}] \t\t\t-- Help about help?"
32
tail_cmd->next_cmd=cmd head_cmd=tail_cmd=cmd
vivi添加用户命令 鲁东大学 LUDONG UNIVERSITY void add_cmd(user_command_t *cmd) Y head_cmd==null? N tail_cmd->next_cmd=cmd tail_cmd=cmd head_cmd=tail_cmd=cmd Back
33
vivi第二阶段 步骤8 step8 boot_or_vivi() vivi启动的最后步骤 等待按键 step2-初始化开发板
鲁东大学 LUDONG UNIVERSITY step8 boot_or_vivi() vivi启动的最后步骤 等待按键 step2-初始化开发板 step3-初始化mmu step4-初始化堆 step5-初始化mtd设备 step6-拷贝参数 为进入嵌入式操作系统做好了准备 其他 回车/无输入 vivi_shell() run_autoboot() 源码 main.c 同时,stage2的step7-初始化用户命令,即为vivi_shell用户交互准备完成。
34
exec_string()调用parseargs() exec_string()调用exec_cmd()
vivi_shell() 鲁东大学 LUDONG UNIVERSITY 当用户按下除回车键外其他按键时,系统调用vivi_shell(),等待通过串口接收的用户命令,对于命令进行相应的处理。 vivi_shell()函数调用了serial_term,通过串口进行交互 serial_term() 显示提示符 vivi> 调用getcmd() 如果有用户输入 调用exec_string() parseargs()将用户输入的string分离为用户命令及其参数argc,argv[] exec_cmd()根据用户命令,查找step7中初始化的命令链表,调用相应的cmdfunc执行 exec_string()调用parseargs() exec_string()调用exec_cmd()
35
run_autoboot() 当按键输入回车或超时(boot_delay)无输入时,vivi调用run_autoboot()运行。
鲁东大学 LUDONG UNIVERSITY 当按键输入回车或超时(boot_delay)无输入时,vivi调用run_autoboot()运行。 run_autoboot() exec_string(“boot”) run_autoboot()实际执行了无参数的boot用户命令。 parseargs () 用户由vivi_shell输入的boot命令行: 无参数 boot 一参数 boot 设备类型 两参数 boot 设备类型 分区 三参数 boot 设备类型 起始地址 长度 execcmd() command_boot()
36
command_boot() command_boot() 获得kernel所在的设备类型,并根据参数确定kernel的起始位置及长度
鲁东大学 LUDONG UNIVERSITY command_boot() 获得kernel所在的设备类型,并根据参数确定kernel的起始位置及长度 get_mtd_partition(”kernel”) 将kernel从mtd设备拷贝 到SDRAM指定位置,如果未指定 则拷贝到DRAM开始的空闲区域 boot_kernel(from,size,media_type) call_linux(0,mach_type,to) mov pc,r2; //r2=to=boot_mem_base + LINUX_KERNEL_OFFSET
37
附:CP15-C1 鲁东大学 LUDONG UNIVERSITY
38
附:MMU-AP控制 鲁东大学 LUDONG UNIVERSITY
39
附:nand _flash_ids 鲁东大学 LUDONG UNIVERSITY
Similar presentations