📄 嵌入式系统 boot loader 技术内幕 -- 詹荣开.rtf
字号:
7? #endif
/*lib/memory.c,将起始地址为USER_RAM_BASE,长度为USER_RAM_SIZE的内存清0*/
8?? clear_mem((unsigned long)USER_RAM_BASE, (unsigned long) USER_RAM_SIZE);
9? }
先写到这儿吧。
(未完待续)
S3C2410 bootloader ----VIVI阅读笔记2(续笔记1)
2、Step 2:board_init()????
board_init调用2个函数用于初始化定时器和设置各GPIO引脚功能,代码在arch/s3c2410/smdk.c中:
[main(int argc, char *argv[]) > board_init()]
1? int board_init(void)
2? {
3????? init_time();? /*arch/s3c2410/proc.c*/
4????? set_gpios();? /*arch/s3c2410/smdk.c */
5????? return 0;
6? }
init_time() 这个函数对寄存器进行了简单的操作:
void init_time(void)
{
??????? TCFG0 = (TCFG0_DZONE(0) | TCFG0_PRE1(15) | TCFG0_PRE0(0));
??????? /*s3c2410 data sheet P298*/
??????? /*TCFG0 = 0 | 0xf00 | 0 */
}
寄存器TCFG0由三部分组成,prescaler0,prescaler1,deadzone和reserve四部分,前三部分分别对应 TCFG0_PRE0、TCFG0_PRE1、TCFG0_DZONE,TCFG0_PRE0(0)实际值为0x00,TCFG0_PRE1(15)实际值为0x0f00,而TCFG0_DZONE(0)实际值为 0x000000。实际中,vivi并未使用定时器,这个函数就可以忽略。set_gpios()用于选择GPA至GPH端口各引脚的功能及是否使用各引脚的内部上拉电阻,并设置外部中断源寄存器EXTINT0-2(vivi中未使用外部中断)。
1??????? void set_gpios(void)
2??????? {
3??????????????? GPACON? = vGPACON;
4??????????????? GPBCON? = vGPBCON;
5??????????????? GPBUP?? = vGPBUP;
6??????????????? GPCCON? = vGPCCON;
7??????????????? GPCUP?? = vGPCUP;
8??????????????? GPDCON? = vGPDCON;
9??????????????? GPDUP?? = vGPDUP;
10??????????????? GPECON? = vGPECON;
11??????????????? GPEUP?? = vGPEUP;
12??????????????? GPFCON? = vGPFCON;
13??????????????? GPFUP?? = vGPFUP;
14??????????????? GPGCON? = vGPGCON;
15??????????????? GPGUP?? = vGPGUP;
16??????????????? GPHCON? = vGPHCON;
17??????????????? GPHUP?? = vGPHUP;
18??????????????? EXTINT0 = vEXTINT0;
19??????????????? EXTINT1 = vEXTINT1;
20??????????????? EXTINT2 = vEXTINT2;
21??????? }
??????? 以第三行为例,vGPACON的值为0x007fffff,查找s3c2410用户手册可知,该参数将GPACON的23位全部置1。各位功能需察看s3c2410用户手册
3、Step 3:建立页表和启动MMU
????????? mem_map_init();
mmu_init();
mem_map_init函数用于建立页表,vivi使用段式页表,只需要一级页表。它调用3个函数,代码在arch/s3c2410/mmu.c中:
[main(int argc, char *argv[]) > mem_map_init(void)]
1??????? void mem_map_init(void)
2??????? {
3??????? #ifdef CONFIG_S3C2410_NAND_BOOT????????
/*CONFIG_S3C2410_NAND_BOOT = y ,在文件include/autoconf.h中定义*/
4??????????????? mem_map_nand_boot();??????
/* 最终调用mem_mepping_linear, 建立页表 */
5??????? #else
6??????????????? mem_map_nor();
7??????? #endif
8??????????????? cache_clean_invalidate();/* 清空cache,使无效cache */?
9??????????????? tlb_invalidate();??????? /* 使无效快表TLB */
10??????? }
第9、10行的两个函数可以不用管它,他们做的事情在下面的mmu_init函数里又重复了一遍。对于本开发板,在.config中定义了 CONFIG_S3C2410_NAND_BOOT。mem_map_nand_boot()函数调用mem_mapping_linear()函数来最终完成建立页表的工作。页表存放在SDRAM物理地址0x33dfc000开始处,共16K:一个页表项4字节,共有4096个页表项;每个页表项对应 1M地址空间,共4G。mem_map_init先将4G虚拟地址映射到相同的物理地址上,NCNB(不使用cache,不使用write buffer)——这样,对寄存器的操作跟未启动MMU时是一样的;再将SDRAM对应的64M空间的页表项修改为使用cache。 mem_mapping_linear函数的代码在arch/s3c2410/mmu.c中:
[main(int argc, char *argv[]) > mem_map_init(void) > mem_map_nand_boot( ) > mem_mapping_linear(void)]
1? static inline void mem_mapping_linear(void)
2? {?
3????? unsigned long pageoffset, sectionNumber;
4??????????? putstr_hex("MMU table base address = 0x", (unsigned long)
mmu_tlb_base);
5?????? /* 4G 虚拟地址映射到相同的物理地址. not cacacheable, not bufferable */
6?????? /* mmu_tlb_base = 0x33dfc000*/
7?????? for (sectionNumber = 0; sectionNumber < 4096; sectionNumber++) {
8?????????????? pageoffset = (sectionNumber << 20);
9???????????????? *(mmu_tlb_base + (pageoffset >> 20)) = pageoffset |
??????????? MMU_SECDESC;
10????? }
?????????????
11????? /* make dram cacheable */
12????? /* SDRAM物理地址0x3000000-0x33ffffff,
13????????? DRAM_BASE=0x30000000,DRAM_SIZE=64M
14????? */
15????? for (pageoffset = DRAM_BASE; pageoffset < (DRAM_BASE+DRAM_SIZE); \
16????????? pageoffset += SZ_1M) {
17????????? //DPRINTK(3, "Make DRAM section cacheable: 0x%08lx\n",???????????? pageoffset);
18????????? *(mmu_tlb_base + (pageoffset >> 20)) = \
pageoffset | MMU_SECDESC | MMU_CACHEABLE;
19????? }
20 }
mmu_init()函数用于启动MMU,它直接调用arm920_setup()函数。arm920_setup()的代码在arch/s3c2410/mmu.c中:
[main(int argc, char *argv[]) > mmu_init( ) > arm920_setup( )]
1? static inline void arm920_setup(void)
2? {
3????????????????? unsigned long ttb = MMU_TABLE_BASE;
/* MMU_TABLE_BASE = 0x33dfc000 */
4? __asm__(
5?????? /* Invalidate caches */
6?????? "mov??? r0, #0\n"
7?????? "mcr??? p15, 0, r0, c7, c7, 0\n"??? /* invalidate I,D caches on v4 */
8?????? "mcr??? p15, 0, r0, c7, c10, 4\n"?? /* drain write buffer on v4 */
9?????? "mcr??? p15, 0, r0, c8, c7, 0\n"??? /* invalidate I,D TLBs on v4 */
?????????? 10????? /* Load page table pointer */
11????? "mov??? r4, %0\n"
12????? "mcr??? p15, 0, r4, c2, c0, 0\n"??? /* load page table pointer */
13????? /* Write domain id (cp15_r3) */
????????? 14????? "mvn??? r0, #0\n"??? /* Domains 0b01 = client, 0b11=Manager*/
????????? 15????? "mcr??? p15, 0, r0, c3, c0, 0\n"
/* load domain access register,write domain 15:0, 用户手册P548(access permissions)*/
16????? /* Set control register v4 */
17????? "mrc??? p15, 0, r0, c1, c0, 0\n"??? /* get control register v4 */
??????????? /*数据手册P545:read control register */
18????? /* Clear out 'unwanted' bits (then put them in if we need them) */
19????? /* ..VI ..RS B... .CAM */?? /*这些位的含义在数据手册P546*/
20????? "bic r0, r0, #0x3000\n"? /* ..11 .... .... .... */
??????????? /*I(bit[12])=0 = Instruction cache disabled*/
21????? /*V[bit[13]](Base location of exception registers)=0 = Low addresses = 0x0000 0000*/
22????? "bic r0, r0, #0x0300\n"????? /* .... ..11 .... .... */
?????????????
23????? /*R(ROM protection bit[9])=0*/
?????????????????? /*S(System protection bit[8])=0*/
?????????????????? /*由于TTB中AP=0b11(line141),所以RS位不使用(P579)*/
24????? "bic r0, r0, #0x0087\n"????? /* 0x0000000010000111 */
??????????????? /*M(bit[0])=0 = MMU disabled*/
??????????????? /*A(bit[1])=0 =Data address alignment fault checking disable*/
??????????????? /*C(bit[2])=0 = Data cache disabled*/
??????????????? /*B(bit[7])=0= Little-endian operation*/
25????? /* Turn on what we want */
26????? /* Fault checking enabled */
27????? "orr r0, r0, #0x0002\n"????? /* .... .... .... ..10 */
??????????? /*A(bit[1])=1 = Data address alignment fault checking enable*/
28????????????????? #ifdef CONFIG_CPU_D_CACHE_ON???? /*is not set*/
29????? "orr??? r0, r0, #0x0004\n"????? /* .... .... .... .100 */
??????????? /*C(bit[2])=1 = Data cache enabled*/
30 #endif?
31 #ifdef CONFIG_CPU_I_CACHE_ON???? /*is not set*/
32????? "orr??? r0, r0, #0x1000\n"? /* ...1 .... .... .... */
??????????? /*I(bit[12])=1 = Instruction cache enabled*/
33 #endif?
??????????????
34????????? /* MMU enabled */
35??????????????? "orr??? r0, r0, #0x0001\n"????? /* .... .... .... ...1 */
??????????? /*M(bit[0])=1 = MMU enabled*/
36??????????????? "mcr??? p15, 0, r0, c1, c0, 0\n"??? /* write control register */
??????????? /*数据手册P545*/
37??????????????? : /* no outputs */
38??????????????? : "r" (ttb) );
39 }
[未完]
S3C2410 bootloader ----VIVI阅读笔记3
4、Step 4:heap_init()????
第4步调用了heap_init(void)函数,并返回值。该值是函数heap_init()调用的mmalloc_init()函数的返回值。其实,这步就是申请一块内存区域。
[lib/heap.c->heap_init(void)]
1??????? int heap_init(void)
2??????? {
3??????????????? return mmalloc_init((unsigned char *)(HEAP_BASE), HEAP_SIZE);??????
4??????? }
内存动态分配函数mmalloc就是从heap(堆)中划出一块空闲内存。相应的mfree函数则将动态分配的某块内存释放回heap中。
heap_init函数在SDRAM中指定了一块1M大小的内存作为heap(起始地址HEAP_BASE = 0x33e00000),并在heap的开头定义了一个数据结构blockhead。事实上,heap就是使用一系列的blockhead数据结构来描述和操作的。每个blockhead数据结构对应着一块heap内存,假设一个blockhead数据结构的存放位置为A,则它对应的可分配内存地址为“A + sizeof(blockhead)”到“A + sizeof(blockhead) + size - 1”。blockhead数据结构在lib/heap.c中定义:
1? typedef struct blockhead_t {
2?????? int32 signature;???? //固定为BLOCKHEAD_SIGNATURE
3?????? bool allocated;????? //此区域是否已经分配出去:0-N,1-Y
4?????? unsigned long size;? //此区域大小
5?????? struct blockhead_t *next;?? //链表指针
6?????? struct blockhead_t *prev;?? //链表指针
7? } blockhead;
现在来看看heap是如何运作的(如果您不关心heap实现的细节,这段可以跳过)。vivi对heap的操作比较简单,vivi中有一个全局变量 static blockhead *gHeapBase,它是heap的链表头指针,通过它可以遍历所有blockhead数据结构。假设需要动态申请一块sizeA大小的内存,则 mmalloc函数从gHeapBase开始搜索blockhead数据结构,如果发现某个blockhead满足:
(1) allocated = 0? //表示未分配
(2) size > sizeA,则找到了合适的blockhead,
满足上述条件后,进行如下操作:
a.allocated设为1
b.如果size – sizeA > sizeof(blockhead),则将剩下的内存组织成一个新的blockhead,放入链表中
c.返回分配的内存的首地址释放内存的操作更简单,直接将要释放的内存对应的blockhead数据结构的allocated设为0即可。
heap_init函数直接调用mmalloc_init函数进行初始化,此函数代码在lib/heap.c中,比较简单,初始化gHeapBase即可:
[main(int argc, char *argv[]) > heap_init(void) > mmalloc_init(unsigned char *heap, unsigned long size)]
1???????? static inline int mmalloc_init(unsigned char *heap, unsigned long size)
2???????? {
3????? if (gHeapBase != NULL) return -1;
??? 4??? DPRINTK("malloc_init(): initialize heap area at 0x%08lx, size = 0x%08lx\n", heap, size);
5?????? gHeapBase = (blockhead *)(heap);
6?????? gHeapBase->allocated=FALSE;
7?????? gHeapBase->signature=BLOCKHEAD_SIGNATURE;
8?????? gHeapBase->next=NULL;
9?????? gHeapBase->prev=NULL;
10????? gHeapBase->size = size - sizeof(blockhead);
11????? return 0;
12???????? }
static blockhead *gHeapBase = NULL; 这个就是上面称赞的全局变量了,定义在lib/heap.c中。上面就是个链表操作,数据结构,看来搞这个也得好好学数据结构啊,不然内存搞的溢出、浪费可就哭都来不及了。
5、Step 5:mtd_dev_init()??
所谓MTD(Memory Technology Device)相关的技术。在linux系统中,我们通常会用到不同的存储设备,特别是FLASH设备。为了在使用新的存储设备时,我们能更简便地提供它的驱动程序,在上层应用和硬件驱动的中间,抽象出MTD设备层。驱动层不必关心存储的数据格式如何,比如是FAT32、ETX2还是FFS2或其它。它仅仅提供一些简单的接口,比如读写、擦除及查询。如何组织数据,则是上层应用的事情。MTD层将驱动层提供的函数封装起来,向上层提供统一的接口。这样,上层即可专注于文件系统的实现,而不必关心存储设备的具体操作。这段乱七八糟的话也许比较让人晕,也可以这样理解在设备驱动(此处指存储设备)和上层应用之间还存在着一层,共三层,这个中间层就是MTD技术的产物。通常可以将它视为驱动的一部分,叫做上层驱动,而那些实现设备的读、写操作的驱动称为下层驱动,上层驱动将下层驱动封装,并且留给其上层应用一些更加容易简单的接口。
在我们即将看到的代码中,使用mtd_info数据结构表示一个MTD设备,使用nand_chip数据结构表示一个nand flash芯片。在mtd_info结构中,对nand_flash结构作了封装,向上层提供统一的接口。比如,它根据nand_flash提供的 read_data(读一个字节)、read_addr(发送要读的扇区的地址)等函数,构造了一个通用的读函数read,将此函数的指针作为自己的一个成员。而上层要读写flash时,执行mtd_info中的read、write函数即可。
mtd_dev_init()用来扫描所使用的NAND Flash的型号,构造MTD设备,即构造一个mtd_info的数据结构。对于S3C2410来说,它直接调用mtd_init(),mtd_init 又调用smc_init(),此函数在drivers/mtd/maps/s3c2410_flash.c中:
[main(int argc,char *argv[])>mtd_dev_init()>mtd_init()]
1??????? int mtd_init(void)
2? {
3? int ret;
4? #ifdef CONFIG_MTD_CFI??????????????? /*is not set*/
5? ret = cfi_init();
6??????? #endif
7??????? #ifdef CONFIG_MTD_SMC9??????????????? /* =y */
8??????????????? ret = smc_init();
9??????? #endif
10??????? #ifdef CONFIG_S3C2410_AMD_BOOT??????? /*is not set*/
11???????????&nb
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -