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.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章