1、概述

之前我们通过直接使用GPIO引脚来控制LED,也就是说中间没有MMU的参与,接下来我们通过使用虚拟地址来点亮led的功能,学习MMU的使用。

 

2、以段的方式(其他两种是粗页 细页)进行映射

解析:先通过虚拟地址的高12位取出来作为一个表(translation table)的偏移也叫一级页表的偏移,找到页表中的某一个表项,表项的高12位就指向段的物理基地址,拿出来该12位值(段基地址),和虚拟地址中的offset拼起来,合并成一个32位的地址,就组成了对应的物理地址。
在整个过程中MMU的功能:
①首先会去查一级页表,MMU是如何找到这种一级页表的呢?mmu会从cp15-c2寄存器中去找该表。也就是程序员在建立一级页表的时候需要把TTB写入到cp15-c2寄存器中。

 

 

 

3、写代码通过虚拟地址访问led
示意图:

 

 

 

 

 

 4、代码

main.c

#define
GPBCON (volatile unsigned long*)0xA0000010 #define GPBDAT (volatile unsigned long*)0xA0000014 /* * 用于段描述符的一些宏定义 */ #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 << 0) /* 段描述符 */ #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) //1.建立页表 void create_page_table(void) { unsigned long *ttb = (unsigned long *)0x30000000; unsigned long vaddr, paddr; 映射LED地址 vaddr = 0xA0000000; paddr = 0x56000000; LED的物理基地址 //*(ttb + (vaddr >> 20))指向页表中的项 *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC; 内存映射地址 vaddr = 0x30000000; paddr = 0x30000000; while (vaddr < 0x34000000) { 由于映射是以0x100000也就是1MB为单位的 *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB; vaddr += 0x100000; paddr += 0x100000; } } void mmu_init() { __asm__( /*设置TTB*/ "ldr r0, =0x30000000\n" "mcr p15, 0, r0, c2, c0, 0\n" /*不进行权限检查*/ "mvn r0, #0\n" "mcr p15, 0, r0, c3, c0, 0\n" /*使能MMU*/ "mrc p15, 0, r0, c1, c0, 0\n" "orr r0, r0, #0x0001\n" "mcr p15, 0, r0, c1, c0, 0\n" : : ); } int gboot_main() { //1.建立页表 2.写入TTB 3.使能MMU create_page_table(); mmu_init(); *(GPBCON) = 0x15400; 此时访问的是虚拟地址GPBCON 0xa000 0010,系统会自动取出来前12位0xa000的值,又由于c2寄存器中TTB的值是0x30000000, 所以对应页表项中0xa000项。又由于该页表项在初始化的时候, *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC,已经写死了一个固定值了。 于是最后访问的就是该写死的值(0x30000000 & 0xFFF00000) | MMU_SECDESC + offset,offset等于0x10 *(GPBDAT) = 0x6BF; return 0; }

Makefile

all: start.o main.o arm
-linux-ld -Tgboot.lds -o gboot.elf $^ arm-linux-objcopy -O binary gboot.elf gboot.bin %.o : %.S arm-linux-gcc -g -c $^ %.o : %.c arm-linux-gcc -g -c $^ .PHONY: clean clean: rm *.o *.elf *.bin

 

start.s

.text
.global _start
_start:
    b reset
    ldr pc, _undifined_instruction
    ldr pc, _software_interrupt
    ldr pc, _prefetch_abort
    ldr pc, _data_abort
    ldr pc, _not_used
    ldr pc, _irq
    ldr pc, _fiq
    

_undifined_instruction: .word undifined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word reset

undifined_instruction:
    nop

software_interrupt:
    nop
    
prefetch_abort:
    nop
    
data_abort:
    nop

not_used:
    nop

irq:
    nop

fiq:    
    nop

reset:
    bl set_svc
    bl disable_watchdog
    bl disable_interrupt
    bl disable_mmu
    bl init_clock
    bl init_sdram
    bl copy_to_ram
    bl init_stack
    bl clean_bss
    ldr pc, =gboot_main
@    bl light_led

set_svc:
    mrs r0, cpsr
    bic r0, r0,#0x1f
    orr r0, r0,#0xd3
    msr cpsr, r0
    mov pc, lr

#define pWTCON 0x53000000
disable_watchdog:
    ldr r0, =pWTCON
    mov r1, #0x0
    str r1, [r0]
    mov pc, lr

disable_interrupt:
    mvn r1, #0x0
    ldr r0, =0x4a000008
    str r1, [r0]
    mov pc, lr
    
disable_mmu:
    mcr p15,0,r0,c7,c7,0
    mrc p15,0,r0,c1,c0,0
    bic r0, r0, #0x00000007
    mcr p15,0,r0,c1,c0,0
    mov pc, lr

#define CLKDIVN 0x4c000014
#define MPLLCON 0x4c000008
#define MPLL_405MHZ ((127<<12)|(2<<4)|(1<<0))

init_clock:
    ldr r0, =CLKDIVN
    mov r1, #0x5
    str r1, [r0]
    
    mcr p15,0,r0,c1,c0,0
    orr r0,r0,#0xc0000000
    mcr p15,0,r0,c1,c0,0
    
    ldr r0, =MPLLCON
    ldr r1, =MPLL_405MHZ
    str r1, [r0]
    mov pc, lr
    
#define mem_contrl 0x48000000
init_sdram:
    ldr r0, =mem_contrl 
    add r3, r0, #4*13
    adrl r1, mem_data

0:
    ldr r2, [r1], #4
    str r2, [r0], #4
    cmp r0, r3
    bne 0b
    mov pc, lr
    
copy_to_ram:
    ldr r0, =0x0
    ldr r1, =0x30008000
    add r3, r0, #1024*4

copy_loop:
    ldr r2, [r0], #4
    str r2, [r1], #4
    cmp r0, r3
    bne copy_loop
    mov pc, lr
    
init_stack:
    ldr sp, =0x34000000
    mov pc ,lr

clean_bss:
    ldr r0, =bss_start
    ldr r1, =bss_end
    cmp r0, r1
    moveq pc, lr

clean_loop:
    mov r2, #0
    str r2, [r0], #4
    cmp r0, r1
    bne clean_loop
    mov pc, lr

mem_data:
    .long 0x22000000 
    .long 0x00000700
    .long 0x00000700
    .long 0x00000700
    .long 0x00000700
    .long 0x00000700
    .long 0x00000700
    .long 0x00018001
    .long 0x00018001
    .long 0x008c04f5
    .long 0x000000b1
    .long 0x00000030
    .long 0x00000030

#define GPBCON 0x56000010
#define GPBDAT 0x56000014
light_led:
    ldr r0, =GPBCON
    ldr r1,=0x15400
    str r1, [r0]
    
    ldr r0, =GPBDAT
    ldr r1,=0x6BF
    str r1, [r0]
    mov pc, lr

 

gboot.lds

OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS {
    . = 0x30008000;
    
    . = ALIGN(4);
    .text :
    {
    start.o (.text)
    *(.text)
    }

    . = ALIGN(4);
    .data : 
    {
    *(.data)
    }
    
    . = ALIGN(4);
    bss_start = .;
    .bss : 
    {
    *(.bss) 
    }
    bss_end = .;
}