/* called from crt0.S */
void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
void kmain(void)
{
// get us into some sort of thread context
thread_init_early(); // 初始化lk的线程系统
// early arch stuff
arch_early_init(); // 架构相关早期初始化,如使能mmu,cache等
// do any super early platform initialization
platform_early_init(); // 平台相关早期初始化,如获取板级信息,初始化时钟、中断、定时器等
// do any super early target initialization
target_early_init(); // 目前只有一个功能,就是初始化串口
dprintf(INFO, "welcome to lk\n\n");
bs_set_timestamp(BS_BL_START); // 设置bootloader初始的时间戳
// deal with any static constructors
dprintf(SPEW, "calling constructors\n");
call_constructors(); // 构造函数相关初始化
// bring up the kernel heap
dprintf(SPEW, "initializing heap\n");
heap_init(); // 堆初始化,用于malloc等函数的内存分配
__stack_chk_guard_setup(); // 生成了一个随机数保存在全局变量__stack_chk_guard中
// initialize the threading system
dprintf(SPEW, "initializing threads\n");
thread_init(); // 仅简单的初始化了定时器对象
// initialize the dpc system
dprintf(SPEW, "initializing dpc\n");
dpc_init(); // delayed procedure call 延迟过程调用
// initialize kernel timers
dprintf(SPEW, "initializing timers\n");
timer_init(); // 初始化定时器
#if (!ENABLE_NANDWRITE)
// create a thread to complete system initialization
dprintf(SPEW, "creating bootstrap completion thread\n");
thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE)); // 创建并唤醒bootstrap2线程,用于进一步完成bootloader工作
// enable interrupts
exit_critical_section(); // 使能中断,执行后critical_section_count等于0
// become the idle thread
thread_become_idle(); // 将本线程切换到idle状态
#else
bootstrap_nandwrite();
#endif
}
void arch_early_init(void)
{
/* turn off the cache */
arch_disable_cache(UCACHE); // 禁用cache
/* set the vector base to our exception vectors so we dont need to double map at 0 */
#if ARM_CPU_CORTEX_A8
set_vector_base(MEMBASE); // armv8重新设置异常向量表到MEMBASE
#endif
#if ARM_WITH_MMU
arm_mmu_init(); // 初始化mmu,lk中实现了两个arm_mmu_init() 函数,我们使用的是实现了大物理地址扩展 (LPAE)的
#endif
/* turn the cache back on */
arch_enable_cache(UCACHE); // 使能cache
#if ARM_WITH_NEON // NEON 技术是 ARM Cortex™-A 系列处理器的 128 位 SIMD(单指令,多数据)架构扩展,旨在为消费性多媒体应用程序提供灵活、强大的加速功能,从而显著改善用户体验。
/* enable cp10 and cp11 */
uint32_t val;
__asm__ volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (val));
val |= (3<<22)|(3<<20);
__asm__ volatile("mcr p15, 0, %0, c1, c0, 2" :: "r" (val)); // 使能cp10,cp11
isb();
/* set enable bit in fpexc */
__asm__ volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (val));
val |= (1<<30);
__asm__ volatile("mcr p10, 7, %0, c8, c0, 0" :: "r" (val)); // 使能Advanced SIMD and Floating-point (VFP) Extensions
#endif
#if ARM_CPU_CORTEX_A8
/* enable the cycle count register */
uint32_t en;
__asm__ volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (en)); // Performance Monitors Control Register
en &= ~(1<<3); /* cycle count every cycle */
en |= 1; /* enable all performance counters */
__asm__ volatile("mcr p15, 0, %0, c9, c12, 0" :: "r" (en));
/* enable cycle counter */
en = (1<<31);
__asm__ volatile("mcr p15, 0, %0, c9, c12, 1" :: "r" (en)); // Performance Monitors Count Enable Set register
#endif
}
void arm_mmu_init(void)
{
/* set some mmu specific control bits:
* access flag disabled, TEX remap disabled, mmu disabled
*/
arm_write_cr1(arm_read_cr1() & ~((1<<29)|(1<<28)|(1<<0))); // 清除SCTLR, System Control Register(对应armv7的cp15中寄存器1)的AFE,TRE,M位关闭MMU
platform_init_mmu_mappings(); // 初始化平台的内存映射
/* set up the translation table base */
arm_write_ttbr((uint32_t)mmu_l1_pagetable); // 将一级页表地址保存到协处理器cp15的c2的TTBR0, Translation Table Base Register 0
/* set up the Memory Attribute Indirection Registers 0 and 1 */
arm_write_mair0(MAIR0); // 设置Memory Attribute Indirection Registers 0
arm_write_mair1(MAIR1);
// 设置Memory Attribute Indirection Registers 1
/* TTBCR.EAE = 1 & IRGN0 [9:8], ORNG0 bits [11:10]: 01 */
arm_write_ttbcr(0x80000500); // bit[31]为1Large Physical Address Extension说明支持40位内存地址即1TB
/* Enable TRE */
arm_write_cr1(arm_read_cr1() | (1<<28)); // Remap enabled
/* turn on the mmu */
arm_write_cr1(arm_read_cr1() | 0x1); // 使能MMU
}
static mmu_section_t default_mmu_section_table[] = // 此结构体定义的内存映射都是物理内存地址等于虚拟内存地址的
{
/* Physical addr, Virtual addr, Mapping type , Size (in MB), Flags */
{ 0x00000000, 0x00000000, MMU_L2_NS_SECTION_MAPPING, 512, IOMAP_MEMORY}, // 映射0地址开始的512MB内存
{ MEMBASE, MEMBASE, MMU_L2_NS_SECTION_MAPPING, (MEMSIZE / MB), LK_MEMORY}, // 映射加载lk的4MB内存
{ MIPI_FB_ADDR, MIPI_FB_ADDR, MMU_L2_NS_SECTION_MAPPING, 40, LK_MEMORY}, // 映射LCD framebuffer使用的40MB内存
{ SCRATCH_ADDR, SCRATCH_ADDR, MMU_L2_NS_SECTION_MAPPING, SCRATCH_SIZE, SCRATCH_MEMORY}, // 映射lk临时使用的内存
{ MSM_SHARED_BASE, MSM_SHARED_BASE, MMU_L2_NS_SECTION_MAPPING, MSM_SHARED_SIZE, COMMON_MEMORY}, // 映射整个系统使用的共享内存
{ RPMB_SND_RCV_BUF, RPMB_SND_RCV_BUF, MMU_L2_NS_SECTION_MAPPING, RPMB_SND_RCV_BUF_SZ, IOMAP_MEMORY}, // 不清楚是不是eMMC的rpmb
};
void platform_init_mmu_mappings(void)
{
int i;
int table_sz = ARRAY_SIZE(default_mmu_section_table);
mmu_section_t kernel_mmu_section_table;
uint64_t ddr_size = smem_get_ddr_size();
switch(ddr_size)
{
case MEM_4GB:
case MEM_3GB:
ddr_start = 0x80000000;
break;
default:
dprintf(CRITICAL, "Unsupported ddr\n");
ASSERT(0);
};
kernel_mmu_section_table.paddress = ddr_start; // 物理内存地址
kernel_mmu_section_table.vaddress = ddr_start; // 虚拟内存地址
kernel_mmu_section_table.type = MMU_L2_NS_SECTION_MAPPING; // 使用二级页表
kernel_mmu_section_table.size = 88; // 88MB
kernel_mmu_section_table.flags = SCRATCH_MEMORY; // 临时使用的内存
arm_mmu_map_entry(&kernel_mmu_section_table); // 进行内存映射;MMU现在现代计算机系统是非常重要的,提高了内存是有效使用率和不同进程间的内存隔离
/* Map default memory needed for lk , scratch, rpmb & iomap */
for (i = 0 ; i < table_sz; i++)
arm_mmu_map_entry(&default_mmu_section_table[i]); // 对几个lk需要用到的内存区间进行映射
if (scm_device_enter_dload()) // 判断是否进入下载,这段代码出现在这里,感到莫名其妙
{
/* TZ & Hyp memory can be mapped only while entering the download mode */
table_sz = ARRAY_SIZE(dload_mmu_section_table);
for (i = 0 ; i < table_sz; i++)
arm_mmu_map_entry(&dload_mmu_section_table[i]);
}
}
void arm_mmu_map_entry(mmu_section_t *entry)
{
ASSERT(entry); // 断言
if (entry->type == MMU_L1_NS_SECTION_MAPPING)
mmu_map_l1_entry(entry);
else if(entry->type == MMU_L2_NS_SECTION_MAPPING) // 我们使用的是二级页表
mmu_map_l2_entry(entry);
else
dprintf(CRITICAL, "Invalid mapping type in the mmu table: %d\n", entry->type);
}
uint64_t mmu_l1_pagetable[ROUNDUP(L1_PT_SZ, CACHE_LINE)] __attribute__ ((aligned(4096))); /* Max is 8 */ //
L1_PT_SZ = 4, 128个一级页表,4K地址对齐
uint64_t mmu_l2_pagetable[ROUNDUP(L2_PT_SZ*MMU_L2_PT_SIZE, CACHE_LINE)] __attribute__ ((aligned(4096))); /* Macro from target code * 512 */ //
L2_PT_SZ = 3,
MMU_L2_PT_SIZE = 512,
3*512个二级页表,4K地址对齐
static void mmu_map_l2_entry(mmu_section_t *block)
{
uint64_t *l2_pt = NULL;
uint64_t address_start;
uint64_t address_end;
uint64_t p_addr;
/* First initialize the first level descriptor for each 1 GB
* Bits[47:12] provide the physical base address of the level 2 page table
*
* ________________________________________________________________________________
* | | | | | | | | | |
* |63|62-61|60| 59|58---52|51----40|39------------------------12|11----2|1------- 0|
* |NS| AP |XN|PXN|Ignored|UNK|SBZP|Next-level table addr[39:12]|Ignored|Descriptor|
* |__|_____|__|___|_______|________|____________________________|_______|__________|
* NS: Used only in secure state
* AP: Access protection
*/
/* Convert the virtual address[38:30] into an index of the L1 page table */
address_start = (block->vaddress & LPAE_MASK) >> 30; // 使用虚拟内存地址的
[39:30]bit作为一级页表索引,因此每一个一级页表可映射1GB内存空间,英文原始注释有误
/* Check if this 1GB entry has L2 page table mapped already
* if L1 entry hasn't mapped any L2 page table, allocate a L2 page table for it
*/
if((mmu_l1_pagetable[address_start] & PT_TABLE_DESC_BIT) == 0) // 判断对应的1GB内存空间是否已经映射
{
ASSERT(avail_l2_pt); // 断言是否还剩余可用二级页表
/* Get the first l2 empty page table and fill in the L1 PTE with a table descriptor,
* The l2_pt address bits 12:39 are used for L1 PTE entry
*/
l2_pt = empty_l2_pt; // 获取一个可用二级页表
/* Bits 39.12 of the page table address are mapped into the L1 PTE entry */
mmu_l1_pagetable[address_start] = ((uint64_t)(uintptr_t)l2_pt & 0x0FFFFFFF000) | MMU_PT_TABLE_DESCRIPTOR; // 将获取的二级可用页表内存地址的[
39:12
]bit(由于静态变量,其内存在lk的MEMBASE到MEMBASE+MEMSIZE之间,因此其物理内存地址等于虚拟内存地址)赋值给相应一级页表项中
/* Advance pointer to next empty l2 page table */
empty_l2_pt += MMU_L2_PT_SIZE; // 由于使用了一个二级页表,因此要更新剩余页表
avail_l2_pt--; // 可用页表减1
arch_clean_invalidate_cache_range((addr_t) mmu_l1_pagetable, L1_PT_SZ); // 刷新cache,确保一级页表的修改刷新到ddr中
}
else
{
/* Entry has L2 page table mapped already, so just get the existing L2 page table address */
l2_pt = (uint64_t *) (uintptr_t)(mmu_l1_pagetable[address_start] & 0xFFFFFFF000); // 如果已经映射根据一级页表的索引取出二级页表的内存地址
}
/* Get the physical address of 2MB sections, bits 21:39 are used to populate the L2 entry */
p_addr = block->paddress & L2_PT_MASK; // 以2MB为单位进行内存映射,因此只需要保存物理内存地址的[39:21]bit即可,同时也说明在2MB以内的偏移,物理内存地址和虚拟内存地址是相等的
/* Start index into the L2 page table for this section using the virtual address[29:21]*/
address_start = (block->vaddress & L2_INDEX_MASK) >> 21; // 以虚拟内存地址的
[29:21]bit作为二级页表的索引
/* The end index for the given section. size given is in MB convert it to number of 2MB segments */
address_end = address_start + ((block->size) >> 1); // 从1MB转换为2MB为单位
/*
* ___________________________________________________________________________________________________________________
* | | | | | | | | | | | | | | | |
* |63---59|58----55|54|53 |52 |51----40|39--------------21|20----12|11|10|9 8|7 6|5 |4-----------2| 1 0 |
* |Ignored|Reserved|XN|PXN|Cont|UNK|SBZP|Output addr[39:21]|UNK|SBZP|nG|AF|SH[1:0]|AP[2:1]|NS|AttrIndx[2:0]|Descriptor|
* |_______|________|__|___|____|________|__________________|________|__|__|_______|_______|__|_____________|__________|
*/
/* Map all the 2MB segments in the 1GB section */
while (address_start < address_end) // 以2MB为单位进行内存映射
{
l2_pt[address_start] = (p_addr) | MMU_PT_BLOCK_DESCRIPTOR | MMU_AP_FLAG | block->flags; // 将物理内存地址的
[39:21]bit赋值到二级页表项中
address_start++;
/* Increment to the next 2MB segment in current L2 page table*/
p_addr += SIZE_2MB;
arm_invalidate_tlb(); // 使传输后援存储器无效,Translation Lookside Buffer
}
arch_clean_invalidate_cache_range((addr_t) mmu_l2_pagetable, (L2_PT_SZ*MMU_L2_PT_SIZE));
// 刷新cache,确保二级页表的修改刷新到ddr中
}
// 趁热打铁,接下来看看知道虚拟内存地址如何取到物理内存地址
uint64_t virtual_to_physical_mapping(uint32_t vaddr)
{
uint32_t l1_index;
uint64_t *l2_pt = NULL;
uint32_t l2_index;
uint32_t offset = 0;
uint64_t paddr = 0;
/* Find the L1 index from virtual address */
l1_index = (vaddr & LPAE_MASK) >> 30; //
使用虚拟内存地址的
[39:30]bit作为一级页表索引
if ((mmu_l1_pagetable[l1_index] & MMU_PT_TABLE_DESCRIPTOR) == MMU_PT_TABLE_DESCRIPTOR) // 判断是否存在二级页表
{
/* Get the l2 page table address */
l2_pt = (uint64_t *) (uintptr_t) (mmu_l1_pagetable[l1_index] & 0x0FFFFFFF000); // 获取二级页表的内存地址
/* Get the l2 index from virtual address */
l2_index = (vaddr & L2_INDEX_MASK) >> 21; // 使用
虚拟内存地址的
[29:21]bit作为二级页表的索引
/* Calculate the offset from vaddr. */
offset = vaddr & 0x1FFFFF; // 计算虚拟地址的2MB内偏移
[20:0]bit
/* Get the physical address bits from 21 to 39 */
paddr = (l2_pt[l2_index] & L2_PT_MASK) + offset; 从二级页表中取出物理内存地址的[39:21]bit,在加上[20:0]bit的偏移,即可得到完整的物理内存地址
} else if ((mmu_l1_pagetable[l1_index] & MMU_PT_TABLE_DESCRIPTOR) == MMU_PT_BLOCK_DESCRIPTOR) // 只使用了一级页表的情况
{
/* Calculate the offset from bits 0 to 30 */
offset = vaddr & 0x3FFFFFFF;
/* Return the entry from l1 page table */
paddr = (mmu_l1_pagetable[l1_index] & L1_PT_INDEX) + offset;
} else
{
ASSERT(0);
}
return paddr;
}