s3c2440_MMU(3)

        之前跑裸机时搞到MMU那一块的时候只进行了原理上的学习,没有进行s3c2440的MMU实例操作,今天分析linux内核启动时创建临时页表那部分有些地方还是卡住了不理解,就比如说为什么要创建临时页表这个问题,虽说代码实现过程可以分析明白,就是不明白为什么要建立1:1的映射关系?这里好是纠结。决定在裸机MMU那部分再进行实例分析。

代码要实现的效果:

将第二部分代码,即led控制那部分程序运行在虚拟地址空间,并且通过虚拟地址来控制4颗led的循环显示。

代码具体完成的任务:

1.建立物理地址空间第一个1M映射:(VA)0x00000000-0x00100000---->(PA)0x00000000-0x00100000【实际用到的物理地址0x00000000-0x00000800】

2.建立寄存器GPBCON和GPBDAT的地址映射:(VA)0xA0000000-0xA0100000---->(PA)0x56000000-0x56100000【实际中用到的实际物理地址0x56000010、0x56000014】

3.建立第二部分代码地址空间leds.o的映射:(VA)0xB0000000-0xB0100000---->(PA)0x30000000-0x30100000【实际用到的物理地址0x30004000-0x30004800(2048-4096拷贝过去的内容)】

注:每次映射都是要完整1M的地址映射。

链接脚本文件mmu.lds:

SECTIONS { 
  firtst    0x00000000 : { head.o init.o }
  second    0xB0004000 : AT(2048) { leds.o }
} 

这个链接脚本将三个目标文件分成两块来构建elf格式的可执行文件。意思是:将head.o和init.o目标文件依次连在一起放到物理运行空间的0地址处,作为可执行文件的第一部分“first”,并且这cpu运行这部分代码时是从地址0处开始取指令执行;接着将leds.o目标文件强行放到物理运行空间的2048地址处,从这里开始存放leds.o文件,即leds.o文件在elf可执行文件中的偏移是2048,cpu取指令时要让他“自以为”看到这段代码存放到0xB0004000处。忽悠cpu这种事情是linux内核中常有的事情,不过这是从我们敲代码的人的角度来看待这一种事实的。要是cpu会跟人一样说话,说不定还会不屑的说:“我的世界你不懂,说句实在话,我才懒得管去哪里取‘材料’呢,我有的是钱,我雇了个MMU,让她专门负责把‘材料’的存放位置告诉那些运输部门,我想要什么‘材料’只管看看我自己的大脑空间里边找找看,找到了跟MMU说就行了。”

启动引导文件head.S:

.text
.global _start
_start:
    ldr sp, =4096                       @ 设置栈指针,以下都是C函数,调用前需要设好栈
    bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启
    bl  memsetup                        @ 设置存储控制器以使用SDRAM
    bl  copy_2th_to_sdram               @ 将第二部分代码复制到SDRAM
    bl  create_page_table               @ 设置页表
    bl  mmu_init                        @ 启动MMU
    ldr sp, =0xB4000000                 @ 重设栈指针,指向SDRAM顶端(使用虚拟地址)
    ldr pc, =0xB0004000                 @ 跳到SDRAM中继续执行第二部分代码
    @ ldr pc, =main                     @ 完全可以使用这一句来代替上面的一句话
halt_loop:
    b   halt_loop
调用mmu_init函数之后,页表也创建好了mmu也启动完成,但是下一句。看下一个代码。

init.c文件:主要是关于看门狗、SDRAM和MMU的函数

#define WTCON           (*(volatile unsigned long *)0x53000000)
/* 存储控制器的寄存器起始地址 */
#define MEM_CTL_BASE    0x48000000

void disable_watch_dog(void)
{
    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可
}

void memsetup(void)
{
    /* SDRAM 13个寄存器的值 */
    unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON
                                            0x00000700,     //BANKCON0
                                            0x00000700,     //BANKCON1
                                            0x00000700,     //BANKCON2
                                            0x00000700,     //BANKCON3  
                                            0x00000700,     //BANKCON4
                                            0x00000700,     //BANKCON5
                                            0x00018005,     //BANKCON6
                                            0x00018005,     //BANKCON7
                                            0x008C07A3,     //REFRESH
                                            0x000000B1,     //BANKSIZE
                                            0x00000030,     //MRSRB6
                                            0x00000030,     //MRSRB7
                                    };
    int     i = 0;
    volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
    for(; i < 13; i++)
        p[i] = mem_cfg_val[i];
}

void copy_2th_to_sdram(void)
{
    unsigned int *pdwSrc  = (unsigned int *)2048;
    unsigned int *pdwDest = (unsigned int *)0x30004000;
    
    while (pdwSrc < (unsigned int *)4096)
    {
        *pdwDest = *pdwSrc;
        pdwDest++;
        pdwSrc++;
    }
}
//下边是创建页表,有三部分的映射
void create_page_table(void)
{

#define MMU_FULL_ACCESS     (3 << 10)   /* 访问权限 */
#define MMU_DOMAIN          (0 << 5)    /* 属于哪个域 */
#define MMU_SPECIAL         (1 << 4)    /* 必须是1 */
#define MMU_CACHEABLE       (1 << 3)    /* cacheable */
#define MMU_BUFFERABLE      (1 << 2)    /* bufferable */
#define MMU_SECTION         (2)         /* 表示这是段描述符 */
#define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
                             MMU_SECTION)
#define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
                             MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
#define MMU_SECTION_SIZE    0x00100000

    unsigned long virtuladdr, physicaladdr;
    unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;
    
    /*
     * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,
     * 为了在开启MMU后仍能运行第一部分的程序,之所以还要运行第一部分的程序是因为在跳转到第二部分main函数之时是非1:1的虚实地址映射,
     * 若此时进行跳转c函数main中,pc指针只识得映射后的0xBxxxxxxx之类的地址,那sp指针该怎么办,他还在指着0x0-0x40000的某一处,这些地址cpu理都不理啊
     * 为了避免这样的事情发生,重新设置cpu认识的sp,而这个设置过程就是要可执行的,因此将0~1M的虚拟地址映射到同样的物理地址,这里的物理地址实际上没有1M,而只有Steppingstone的4K大小
     * 看透了之后发现,这一页的映射就为了执行ldr sp, =0xB4000000和ldr pc, =0xB0004000两条语句,就两条,就·······
     * 文章开头提出来的问题到这里得到了解决!
     */
    virtuladdr = 0;
    physicaladdr = 0;
    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
                                            MMU_SECDESC_WB;
    /*
     * 0x56000000是GPIO寄存器的起始物理地址,
     * GPBCON和GPBDAT这两个寄存器的物理地址0x56000010、0x56000014,
     * 为了在第二部分程序中能以地址0xA0000010、0xA0000014来操作GPBCON、GPBDAT,
     * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
     */
    virtuladdr = 0xA0000000;
    physicaladdr = 0x56000000;
    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
                                            MMU_SECDESC;

    /*
     * SDRAM的物理地址范围是0x30000000~0x33FFFFFF,
     * 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
     * 总共64M,涉及64个段描述符
     */
    virtuladdr = 0xB0000000;
    physicaladdr = 0x30000000;
    while (virtuladdr < 0xB4000000)
    {
        *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
                                                MMU_SECDESC_WB;
        virtuladdr += 0x100000;
        physicaladdr += 0x100000;
    }
}

/*
 * 将页表基地址告诉MMU,并启动MMU
 */
void mmu_init(void)
{
    unsigned long ttb = 0x30000000;

// ARM休系架构与编程
// 嵌入汇编:LINUX内核完全注释
__asm__(
    "mov    r0, #0\n"
    "mcr    p15, 0, r0, c7, c7, 0\n"    /* 使无效ICaches和DCaches */
    
    "mcr    p15, 0, r0, c7, c10, 4\n"   /* drain write buffer on v4 */
    "mcr    p15, 0, r0, c8, c7, 0\n"    /* 使无效指令、数据TLB */
    
    "mov    r4, %0\n"                   /* r4 = 页表基址 */
    "mcr    p15, 0, r4, c2, c0, 0\n"    /* 设置页表基址寄存器 */
    
    "mvn    r0, #0\n"                   
    "mcr    p15, 0, r0, c3, c0, 0\n"    /* 域访问控制寄存器设为0xFFFFFFFF,
                                         * 不进行权限检查 
                                         */    
    /* 
     * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,
     * 然后再写入
     */
    "mrc    p15, 0, r0, c1, c0, 0\n"    /* 读出控制寄存器的值 */
    
    /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM
     * R : 表示换出Cache中的条目时使用的算法,
     *     0 = Random replacement;1 = Round robin replacement
     * V : 表示异常向量表所在的位置,
     *     0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
     * I : 0 = 关闭ICaches;1 = 开启ICaches
     * R、S : 用来与页表中的描述符一起确定内存的访问权限
     * B : 0 = CPU为小字节序;1 = CPU为大字节序
     * C : 0 = 关闭DCaches;1 = 开启DCaches
     * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查
     * M : 0 = 关闭MMU;1 = 开启MMU
     */
    
    /*  
     * 先清除不需要的位,往下若需要则重新设置它们    
     */
                                        /* .RVI ..RS B... .CAM */ 
    "bic    r0, r0, #0x3000\n"          /* ..11 .... .... .... 清除V、I位 */
    "bic    r0, r0, #0x0300\n"          /* .... ..11 .... .... 清除R、S位 */
    "bic    r0, r0, #0x0087\n"          /* .... .... 1... .111 清除B/C/A/M */

    /*
     * 设置需要的位
     */
    "orr    r0, r0, #0x0002\n"          /* .... .... .... ..1. 开启对齐检查 */
    "orr    r0, r0, #0x0004\n"          /* .... .... .... .1.. 开启DCaches */
    "orr    r0, r0, #0x1000\n"          /* ...1 .... .... .... 开启ICaches */
    "orr    r0, r0, #0x0001\n"          /* .... .... .... ...1 使能MMU */
    
    "mcr    p15, 0, r0, c1, c0, 0\n"    /* 将修改的值写入控制寄存器 */
    : /* 无输出 */
    : "r" (ttb) ); //变量ttb通过通用寄存器R0-R15中的某一个传递,这里是c向汇编输入的第一个变量,汇编里边用“%0”表示
}

这一段代码已经在代码中进行了比较详细的注释,就不在独立出来分析。

led控制主程序leds.c:

#define GPBCON      (*(volatile unsigned long *)0xA0000010)     // 物理地址0x56000010
#define GPBDAT      (*(volatile unsigned long *)0xA0000014)     // 物理地址0x56000014

/*
 * LED1,LED2,LED4对应GPB5、GPB6、GPB7、GPB8
 */
#define	GPB5_out	(1<<(5*2))
#define	GPB6_out	(1<<(6*2))
#define	GPB7_out	(1<<(7*2))
#define	GPB8_out	(1<<(8*2))

/*
 * wait函数加上“static inline”是有原因的,
 * 这样可以使得编译leds.c时,wait嵌入main中,编译结果中只有main一个函数。
 * 于是在连接时,main函数的地址就是由连接文件指定的运行时装载地址。
 * 而连接文件mmu.lds中,指定了leds.o的运行时装载地址为0xB4004000,
 * 这样,head.S中的“ldr pc, =0xB4004000”就是跳去执行main函数。
 */
static inline void wait(volatile unsigned long dly)
{
    for(; dly > 0; dly--);
}

int main(void)
{
	unsigned long i = 0;
	
	// LED1,LED2,LED3,LED4对应的4根引脚设为输出
	GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out;

	while(1){
		wait(30000);
		GPBDAT = (~(i<<5));	 	// 根据i的值,点亮LED1,2,3,4
		if(++i == 16)
			i = 0;
	}
	return 0;
}

讲述完毕!微笑

另附反汇编文件mmu.dis

mmu_elf:     file format elf32-littlearm

Disassembly of section firtst:

写程序的时候我们说的地址是没有虚拟地址和物理地址之分的,是单纯的地址,是cpu看到的

(运行地址)
  链接地址  机器码
00000000 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000
   4:	eb00000e 	bl	44 <disable_watch_dog>
   8:	eb000011 	bl	54 <memsetup>
   c:	eb00001c 	bl	84 <copy_2th_to_sdram>
  10:	eb000024 	bl	a8 <create_page_table>
  14:	eb000039 	bl	100 <mmu_init> 		; 在这个函数里边就包含了页表的创建和之后的MMU启动
  18:	e3a0d32d 	mov	sp, #-1275068416 	; 0xb4000000  运行到这一条指令mmu已经启动,但是另一个映射区的c运行环境还没有设置好,此时还不能跳转到非1:1的映射区
  1c:	e59ff000 	ldr	pc, [pc, #0]		; 24 <halt_loop+0x4>

00000020 <halt_loop>:
  20:	eafffffe 	b	20 <halt_loop>
  24:	b0004000 	.word	0xb0004000
  28:	00001941 	.word	0x00001941
  2c:	61656100 	.word	0x61656100
  30:	01006962 	.word	0x01006962
  34:	0000000f 	.word	0x0000000f
  38:	00543405 	.word	0x00543405
  3c:	01080206 	.word	0x01080206
  40:	00000109 	.word	0x00000109

00000044 <disable_watch_dog>:
  44:	e3a02000 	mov	r2, #0	; 0x0
  48:	e3a03453 	mov	r3, #1392508928	; 0x53000000
  4c:	e5832000 	str	r2, [r3]
  50:	e12fff1e 	bx	lr

00000054 <memsetup>:
  54:	e3a00312 	mov	r0, #1207959552	; 0x48000000
  58:	e59fc020 	ldr	ip, [pc, #32]	; 80 <memsetup+0x2c>
  5c:	e2800034 	add	r0, r0, #52	; 0x34
  60:	e3a01312 	mov	r1, #1207959552	; 0x48000000
  64:	e08c3001 	add	r3, ip, r1
  68:	e283332e 	add	r3, r3, #-1207959552	; 0xb8000000
  6c:	e5932000 	ldr	r2, [r3]
  70:	e4812004 	str	r2, [r1], #4
  74:	e1510000 	cmp	r1, r0
  78:	1afffff9 	bne	64 <memsetup+0x10>
  7c:	e12fff1e 	bx	lr
  80:	0000014c 	.word	0x0000014c

00000084 <copy_2th_to_sdram>:
  84:	e3a01000 	mov	r1, #0	; 0x0
  88:	e2813203 	add	r3, r1, #805306368	; 0x30000000
  8c:	e5912800 	ldr	r2, [r1, #2048]
  90:	e2811004 	add	r1, r1, #4	; 0x4
  94:	e2833901 	add	r3, r3, #16384	; 0x4000
  98:	e3510b02 	cmp	r1, #2048	; 0x800
  9c:	e5832000 	str	r2, [r3]
  a0:	1afffff8 	bne	88 <copy_2th_to_sdram+0x4>
  a4:	e12fff1e 	bx	lr

000000a8 <create_page_table>:
  a8:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  ac:	e2833ec1 	add	r3, r3, #3088	; 0xc10
  b0:	e3a02ec1 	mov	r2, #3088	; 0xc10
  b4:	e3a01203 	mov	r1, #805306368	; 0x30000000
  b8:	e2811a02 	add	r1, r1, #8192	; 0x2000
  bc:	e282200e 	add	r2, r2, #14	; 0xe
  c0:	e2833002 	add	r3, r3, #2	; 0x2
  c4:	e3a00203 	mov	r0, #805306368	; 0x30000000
  c8:	e5802000 	str	r2, [r0]
  cc:	e5813800 	str	r3, [r1, #2048]
  d0:	e3a0120b 	mov	r1, #-1342177280	; 0xb0000000
  d4:	e2813102 	add	r3, r1, #-2147483648	; 0x80000000
  d8:	e1a03a23 	lsr	r3, r3, #20
  dc:	e1a03a03 	lsl	r3, r3, #20
  e0:	e1a02a21 	lsr	r2, r1, #20
  e4:	e3833ec1 	orr	r3, r3, #3088	; 0xc10
  e8:	e2811601 	add	r1, r1, #1048576	; 0x100000
  ec:	e383300e 	orr	r3, r3, #14	; 0xe
  f0:	e351032d 	cmp	r1, #-1275068416	; 0xb4000000
  f4:	e7803102 	str	r3, [r0, r2, lsl #2]
  f8:	1afffff5 	bne	d4 <create_page_table+0x2c>
  fc:	e12fff1e 	bx	lr

00000100 <mmu_init>:
 100:	e3a03203 	mov	r3, #805306368	; 0x30000000
 104:	e3a00000 	mov	r0, #0	; 0x0
 108:	ee070f17 	mcr	15, 0, r0, cr7, cr7, {0}
 10c:	ee070f9a 	mcr	15, 0, r0, cr7, cr10, {4}
 110:	ee080f17 	mcr	15, 0, r0, cr8, cr7, {0}
 114:	e1a04003 	mov	r4, r3
 118:	ee024f10 	mcr	15, 0, r4, cr2, cr0, {0}
 11c:	e3e00000 	mvn	r0, #0	; 0x0
 120:	ee030f10 	mcr	15, 0, r0, cr3, cr0, {0}
 124:	ee110f10 	mrc	15, 0, r0, cr1, cr0, {0}
 128:	e3c00a03 	bic	r0, r0, #12288	; 0x3000
 12c:	e3c00c03 	bic	r0, r0, #768	; 0x300
 130:	e3c00087 	bic	r0, r0, #135	; 0x87
 134:	e3800002 	orr	r0, r0, #2	; 0x2
 138:	e3800004 	orr	r0, r0, #4	; 0x4
 13c:	e3800a01 	orr	r0, r0, #4096	; 0x1000
 140:	e3800001 	orr	r0, r0, #1	; 0x1
 144:	ee010f10 	mcr	15, 0, r0, cr1, cr0, {0}
 148:	e12fff1e 	bx	lr

0000014c <mem_cfg_val.1252>:
 14c:	22011110 	.word	0x22011110
 150:	00000700 	.word	0x00000700
 154:	00000700 	.word	0x00000700
 158:	00000700 	.word	0x00000700
 15c:	00000700 	.word	0x00000700
 160:	00000700 	.word	0x00000700
 164:	00000700 	.word	0x00000700
 168:	00018005 	.word	0x00018005
 16c:	00018005 	.word	0x00018005
 170:	008c07a3 	.word	0x008c07a3
 174:	000000b1 	.word	0x000000b1
 178:	00000030 	.word	0x00000030
 17c:	00000030 	.word	0x00000030
 180:	43434700 	.word	0x43434700
 184:	5328203a 	.word	0x5328203a
 188:	6372756f 	.word	0x6372756f
 18c:	20797265 	.word	0x20797265
 190:	202b2b47 	.word	0x202b2b47
 194:	6574694c 	.word	0x6574694c
 198:	30303220 	.word	0x30303220
 19c:	2d317139 	.word	0x2d317139
 1a0:	29333032 	.word	0x29333032
 1a4:	332e3420 	.word	0x332e3420
 1a8:	4100332e 	.word	0x4100332e
 1ac:	00000029 	.word	0x00000029
 1b0:	62616561 	.word	0x62616561
 1b4:	1f010069 	.word	0x1f010069
 1b8:	05000000 	.word	0x05000000
 1bc:	06005434 	.word	0x06005434
 1c0:	09010802 	.word	0x09010802
 1c4:	14041201 	.word	0x14041201
 1c8:	17011501 	.word	0x17011501
 1cc:	19011803 	.word	0x19011803
 1d0:	1e021a01 	.word	0x1e021a01
 1d4:	Address 0x000001d4 is out of bounds.


Disassembly of section second: 
;第二段代码就是从这里开始的,发现木有,程序的运行地址是0xb开头的
b0004000 <main>:
b0004000:	e3a0220a 	mov	r2, #-1610612736	; 0xa0000000
b0004004:	e3a03b55 	mov	r3, #87040	; 0x15400
b0004008:	e5823010 	str	r3, [r2, #16]
b000400c:	e3a01000 	mov	r1, #0	; 0x0
b0004010:	e3a03c75 	mov	r3, #29952	; 0x7500
b0004014:	e2833030 	add	r3, r3, #48	; 0x30
b0004018:	e2533001 	subs	r3, r3, #1	; 0x1
b000401c:	1afffffd 	bne	b0004018 <main+0x18>
b0004020:	e1e03281 	mvn	r3, r1, lsl #5
b0004024:	e351000f 	cmp	r1, #15	; 0xf
b0004028:	e5823014 	str	r3, [r2, #20]
b000402c:	12811001 	addne	r1, r1, #1	; 0x1
b0004030:	03a01000 	moveq	r1, #0	; 0x0
b0004034:	eafffff5 	b	b0004010 <main+0x10>
b0004038:	43434700 	movtmi	r4, #14080	; 0x3700
b000403c:	5328203a 	.word	0x5328203a
b0004040:	6372756f 	.word	0x6372756f
b0004044:	20797265 	.word	0x20797265
b0004048:	202b2b47 	.word	0x202b2b47
b000404c:	6574694c 	.word	0x6574694c
b0004050:	30303220 	.word	0x30303220
b0004054:	2d317139 	.word	0x2d317139
b0004058:	29333032 	.word	0x29333032
b000405c:	332e3420 	.word	0x332e3420
b0004060:	4100332e 	.word	0x4100332e
b0004064:	00000029 	.word	0x00000029
b0004068:	62616561 	.word	0x62616561
b000406c:	1f010069 	.word	0x1f010069
b0004070:	05000000 	.word	0x05000000
b0004074:	06005434 	.word	0x06005434
b0004078:	09010802 	.word	0x09010802
b000407c:	14041201 	.word	0x14041201
b0004080:	17011501 	.word	0x17011501
b0004084:	19011803 	.word	0x19011803
b0004088:	1e021a01 	.word	0x1e021a01
b000408c:	Address 0xb000408c is out of bounds.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值