uboot到kernel启动过程中内存布局变化、初始化
内核编译(make)之后会生成两个文件,一个是Image,一个是zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件。uImage是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没什么区别。uImage的64字节的头结构如下
*/
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address used ************* */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
uboot从flash中读出uimage的头部,解析出镜像大小(ih_size),镜像ddr存放地址(ih_load-0x40),内核起始物理地址(ih_ep),uboot同时setup_memory_tags设置bootatag,这里设置的ddr起始地址(0x22500000)和大小(0x03900000)。
起始地址由内核编译生成的zimage头读出(makefile.uboot),大小uboot中指定。
我这边内存分布如下
uboot设置pc地址到0x22508000后进入Linux kernel流程,head.S执行内核解压过程,解压地址还是0x22508000,地址重叠由解压的代码来处理。
内核解压后转到start_kernel
start_kernel执行过程中,setup_arch中解析ATAG,对于内存调用parse_tag_mem32,将内存解析出来填入meminfo结构体中。
arm_memblock_init
初始化memblock,将meminfo中记录的内存信息加入到memblock分配器中,同时对_stext--_end这段代码段和数据段内存进行reserve,避免后面进入到伙伴系统中,同时还需要保留的还有页表空间。
head.S中建立了最开始的段页表,每个pgd映射1M空间
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
...
.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE
其中
#define CONFIG_PAGE_OFFSET 0xC0000000
TEXT_OFFSET:0x00008000 (arch/arm/Makefile)
PG_DIR_SIZE 0x4000 (16kb 4096*4byte)
paging_init进行页表映射,包括lowmem,devicemem、iomem、异常向量表等等,内核lowmem优先进行段映射,非1M对其的再进行页映射。
后续memmap_init对系统全局页表管理进行初始化(alloc_node_mem_map),关联每个page和物理页地址。
mem_map以page管理所有物理页,后面给伙伴系统用
随后mm_init中对伙伴系统、kmalloc、vmalloc等
free_all_bootmem_core中对系统中bootmem阶段未申请的内存释放到伙伴系统中。
最后应该类似下面这样: