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 ret = amd_init(); 12 #endif 13 if (ret) { 14 mymtd = NULL; 15 return ret; 16 } 17 return 0; 18 } 显而易见,该函数应取第二项,这项在autoconf.h中定义了。 [main(int argc, char *argv[]) > mtd_dev_init() > mtd_init() > smc_init()] 1 static int 2 smc_init(void) 3 { /*struct mtd_info *mymtd,数据类型在include/mtd/mtd.h*/ /*strcut nand_chip在include/mtd/nand.h中定义*/ 4 struct nand_chip *this; 5 u_int16_t nfconf; /* Allocate memory for MTD device structure and private data */ 6 mymtd = mmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)); 7 if (!mymtd) { 8 printk("Unable to allocate S3C2410 NAND MTD device structure.\n"); 9 return -ENOMEM; 10 } /* Get pointer to private data */ 11 this = (struct nand_chip *)(&mymtd[1]); /* Initialize structures */ 12 memset((char *)mymtd, 0, sizeof(struct mtd_info)); 13 memset((char *)this, 0, sizeof(struct nand_chip)); /* Link the private data with the MTD structure */ 14 mymtd->priv = this; /* set NAND Flash controller */ 15 nfconf = NFCONF; /* NAND Flash controller enable */ 16 nfconf |= NFCONF_FCTRL_EN; /* Set flash memory timing */ 17 nfconf &= ~NFCONF_TWRPH1; /* 0x0 */ 18 nfconf |= NFCONF_TWRPH0_3; /* 0x3 */ 19 nfconf &= ~NFCONF_TACLS; /* 0x0 */ 20 NFCONF = nfconf; /* Set address of NAND IO lines */ 21 this->hwcontrol = smc_hwcontrol; 22 this->write_cmd = write_cmd; 23 this->write_addr = write_addr; 24 this->read_data = read_data; 25 this->write_data = write_data; 26 this->wait_for_ready = wait_for_ready; /* Chip Enable -> RESET -> Wait for Ready -> Chip Disable */ 27 this->hwcontrol(NAND_CTL_SETNCE); 28 this->write_cmd(NAND_CMD_RESET); 29 this->wait_for_ready(); 30 this->hwcontrol(NAND_CTL_CLRNCE); 31 smc_insert(this); 32 return 0; 33 } 6 -14行构造了一个mtd_info结构和nand_flash结构,前者对应MTD设备,后者对应nand flash芯片(如果您用的是其他类型的存储器件,比如nor flash,这里的nand_flash结构应该换为其他类型的数据结构)。MTD设备是具体存储器件的抽象,那么在这些代码中这种关系如何体现呢——第 14行的代码把两者连结在一起了。事实上,mtd_info结构中各成员的实现(比如read、write函数),正是由priv变量所指向的 nand_flash的各类操作函数(比如read_addr、read_data等)来实现的。 15-20行是初始化S3C2410上的 NAND FLASH控制器。前面分配的nand_flash结构还是空的,现在当然就是填满它的各类成员了,这正是21-26行做的事情。27-30行对这块 nand flash作了一下复位操作。最后,也是最复杂的部分,根据刚才填充的nand_flash结构,构造mtd_info结构,这由31行的 smc_insert函数调用smc_scan完成。