Mini2440 Linux 内存布局

发布时间: 2024-06-21
来源: 电子工程世界

在学习linux内存寻址的过程中,注意到在x86架构上,分段与分页机制共存。而在RSIC体系结构下一般只支持分页。《深入理解linux内核》是在x86架构上介绍的linux物理内存布局。在x86架构上,linux被安装在ram从物理地址的0x00100000也就是第二个1M的地方。内核态的线性地址:0xc0000000~0xffffffff,在内核态可以寻址0x00000000~0xbfffffff的地址,用户态的线性地址范围为:0x00000000~0xbfffffff,用户态的程序不能访问内核态的线性地址。这几个是线性地址只是CPU寻址的时候用,最终都是要映射到实际的物理地址。在内核镜像包括代码段,数据段。在数据段的后面保存了全局页表描述了线性地址怎样转化成物理地址的。在内核态的线性地址空间里,内核要映射全部的物理RAM,前8M的RAM有两个映射分别对应于线性地址0x00000000~0x0x007fffff与0xc0000000~0xc07fffff,这个是为了在内核初始化的时候,MMU开启前后的操作方便,这是临时映射。最终的内核态映射是线性地址与物理地址线性映射,就是每个线性地址都是物理地址加上一个偏移量,在x86上这个偏移量就是0xc0000000。以上就是x86架构上linux的物理内存布局。而mini2440的物理内存布局会有很大的不同,以64M的SDRAM来说,RAM的物理地址是从0x30000000开始的,结束与0x34000000。要了解linux在mini2440上的内存布局首先要看System.map文件,这个链接器生成的文件。描述了linux镜像在内存中的布局,地址全部是线性地址。

  1. c0004000 A swapper_pg_dir  

  2. c0008000 T __init_begin  

  3. c0008000 T _sinittext  

  4. c0008000 T _stext  

  5. c0008000 T stext  

  6. c0008034 t __enable_mmu  

  7. ......  

  8. ......  

  9. c04b08d8 B proc_net_rpc  

  10. c04b08dc b sunrpc_table_header  

  11. c04b08e0 B rpc_debug  

  12. c04b08e4 B nfs_debug  

  13. c04b08e8 B nfsd_debug  

  14. c04b08ec B nlm_debug  

  15. c04b08f0 b nullstats.25712  

  16. c04b0910 B __bss_stop  

  17. c04b0910 B _end  


可以看出,内核镜像起始地址为0xc0004000,终止地址为0xc04b0910,但是起始地址处到0xc0008000之间32K的地址似乎没有内容,内核镜像大小大约4M。那么这个内核镜像在物理内存是如何布局的呢。在/arch/arm/kernel/head.S中有描述:

  1. #define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET)   

  2. //这个是内核线性地址的开始,PAGE_OFFSET = 0xc0000000 而TEXT_OFFSET = 0x00008000,所以KERNEL_RAM_VADDR = 0xc0008000   

  3. #define KERNEL_RAM_PADDR    (PHYS_OFFSET + TEXT_OFFSET)  

  4. //这个是内核物理地址的开始处,PHYS_OFFSET = 0x30000000 而TEXT_OFFSET = 0x00008000,所以KERNEL_RAM_PADDR = 0x30008000,所以bootloader将内核装载到这个地址处,装载到其他地址是不行的  

  5.   

  6. #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000  

  7. #error KERNEL_RAM_VADDR must start at 0xXXXX8000  

  8. #endif  

  9. //检查定义的是否合法,内核开始物理地址必须是0xXXXX8000  

  10.   

  11.     .globl  swapper_pg_dir  

  12.     .equ    swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000  

  13. //swapper_pg_dir这个变量是内核全局页表的起始地址 可以看出这里是0xc0004000,与内核链接符号表相同  

  14.     .macro  pgtbl, rd  

  15.     ldr rd, =(KERNEL_RAM_PADDR - 0x4000)  

  16.     .endm  

  17. //声明一个宏,作用就是将0x30004000赋值给rd  

  18. #ifdef CONFIG_XIP_KERNEL ///没定义  

  19. #define KERNEL_START    XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)  

  20. #define KERNEL_END  _edata_loc  

  21. #else  

  22. #define KERNEL_START    KERNEL_RAM_VADDR  

  23. #define KERNEL_END  _end  //_end是内核链接符号表中的变量,代表内核结束线性地址  

  24. #endif  

  25. ENTRY(stext)  

  26.     setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode  

  27.                         @ and irqs disabled  

  28.     mrc p15, 0, r9, c0, c0      @ get processor id  

  29.     bl  __lookup_processor_type     @ r5=procinfo r9=cpuid  

  30.     movs    r10, r5             @ invalid processor (r5=0)?  

  31.     beq __error_p           @ yes, error 'p'  

  32.     bl  __lookup_machine_type       @ r5=machinfo  

  33.     movs    r8, r5              @ invalid machine (r5=0)?  

  34.     beq __error_a           @ yes, error 'a'  

  35.     bl  __vet_atags  

  36.     bl  __create_page_tables  

  37.   

  38.     /*  

  39.      * The following calls CPU specific code in a position independent  

  40.      * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of  

  41.      * xxx_proc_info structure selected by __lookup_machine_type  

  42.      * above.  On return, the CPU will be ready for the MMU to be  

  43.      * turned on, and r0 will hold the CPU control register value.  

  44.      */  

  45.     ldr r13, __switch_data      @ address to jump to after  

  46.                         @ mmu has been enabled  

  47.     adr lr, BSYM(__enable_mmu)      @ return (PIC) address  

  48.  ARM(   add pc, r10, #PROCINFO_INITFUNC )  

  49.  THUMB( add r12, r10, #PROCINFO_INITFUNC    )  

  50.  THUMB( mov pc, r12             )  

  51. ENDPROC(stext)  


由内核链接符号表可以看出,这段代码是内核最早运行的代码,其地址在0xc0008000。这段代码是bootloader将内核解压后执行的代码,执行的环境是:没有开启MMU,r0 = 0, r1 = machine nr, r2 = atags pointer atags pointer是标记列表的指针,这个是UBOOT或者其他bootloader传递给内核的参数。前面的汇编代码主要是检查机器吗,与提取内核参数。最后调用 __create_page_tables来创建内核临时页表。

  1. __create_page_tables:  

  2.     pgtbl   r4              @ page table address  

  3. //r4中保存了内核临时页表的地址0x30004000   

  4.     /* 

  5.      * Clear the 16K level 1 swapper page table 

  6.      */  

  7.     mov r0, r4  

  8.     mov r3, #0  

  9.     add r6, r0, #0x4000  

  10. 1:  str r3, [r0], #4  

  11.     str r3, [r0], #4  

  12.     str r3, [r0], #4  

  13.     str r3, [r0], #4  

  14.     teq r0, r6  

  15.     bne 1b  

  16. //将从0x30004000~0x30008000的内存清零   

  17.     ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags  

  18.   

  19.     /* 

  20.      * Create identity mapping for first MB of kernel to 

  21.      * cater for the MMU enable.  This identity mapping 

  22.      * will be removed by paging_init().  We use our current program 

  23.      * counter to determine corresponding section base address. 

  24.      */  

  25.     mov r6, pc  

  26.     mov r6, r6, lsr #20         @ start of kernel section  

  27.     orr r3, r7, r6, lsl #20     @ flags + kernel base  

  28.     str r3, [r4, r6, lsl #2]        @ identity mapping  

  29. //一级页表使用段,每个段描述符都能映射1M的物理地址,这里只是映射前1M的物理地址   

  30. //内核物理地址从0xc0008000开始,所以一级页表表述符要存放在页表首地址的偏移0x0000c000这个位置上   

  31. //这里就是将一级页表表述符存放到此处,可以看出段基地址为0x30000000   

  32.     /* 

  33.      * Now setup the pagetables for our kernel direct 

  34.      * mapped region. 

  35.      */  

  36.     add r0, r4,  #(KERNEL_START & 0xff000000) >> 18  

  37.     str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!  

  38. //这段代码将虚拟地址0x30008000开始的1M内存也映射到了0x30008000处了   

  39.     ldr r6, =(KERNEL_END - 1)  

文章来源于: 电子工程世界 原文链接

本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。