MTK lk源码解析2( lk 阶段main.c中的kmain.c函数解析)

来源:互联网 发布:淘宝购买流量怎么退款 编辑:程序博客网 时间:2024/06/06 03:23

http://blog.csdn.net/xichangbao/article/details/51484564

  1. kmain()。

/* 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
}


  1. thread_init_early()。
void thread_init_early(void)
{
    int i;

    /* initialize the run queues */
    for (i=0; i < NUM_PRIORITIES; i++)
        list_initialize(&run_queue[i]); // 初始化32个运行队列

    /* initialize the thread list */ 
    list_initialize(&thread_list); // 初始化运行队列

    /* create a thread to cover the current running state */
    thread_t *t = &bootstrap_thread; // bootstrap_thread是一个全局静态thread_t结构体
    init_thread_struct(t, "bootstrap"); // 对bootstrap_thread进行清零,设置thread魔数,设置thread name为bootstrap

    /* half construct this thread, since we're already running */
    t->priority = HIGHEST_PRIORITY; // 设置bootstrap优先级为最高优先级
    t->state = THREAD_RUNNING; // 设置bootstrap状态为正在运行
    t->saved_critical_section_count = 1; // 没看懂
    list_add_head(&thread_list, &t->thread_list_node); // 将bootstrap加入thread链表中
    current_thread = t; // 设置当前thread为bootstrap
}


  1. arch_early_init()
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;
}


  1. platform_early_init()。
void platform_early_init(void)
{
    board_init(); // 从共享内存中读取由xbl提供的板级信息
    platform_clock_init(); // 初始化时钟
    qgic_init(); // 初始化ARM GIC中断控制器
    qtimer_init(); // 初始化timer,只是获取了计时器的频率
    scm_init(); // 初始化Secure Channel Manager,用于和tz进行通信
}

  1. target_early_init()。
void target_early_init(void)
{
#if WITH_DEBUG_UART
    uart_dm_init(8, 0, BLSP2_UART1_BASE); // 初始化串口,lk的log可以通过串口打印到终端
#endif
}

  1. call_constructors()。
static void call_constructors(void) // 目前好像没有实际用到
{
    void **ctor;

    ctor = &__ctor_list;
    while(ctor != &__ctor_end) {
        void (*func)(void);

        func = (void (*)())*ctor;

        func();
        ctor++;
    }
}


  1. heap_init()。
void heap_init(void)
{
    LTRACE_ENTRY;

    // set the heap range
    theheap.base = (void *)HEAP_START; // lk bss段的结束地址
    theheap.len = HEAP_LEN; // lk的内存空间结束地址减去lk bss段的结束地址,由于lk的MEMSIZE只有4MB,自身还要使用一部分,因此剩余的内存很少了

    LTRACEF("base %p size %zd bytes\n", theheap.base, theheap.len);

    // initialize the free list
    list_initialize(&theheap.free_list); // 初始化空闲的heap链表

    // create an initial free chunk
    heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len)); // 现将所有heap可用内存创建一个大的chunk,然后将它插入空闲的heap链表中

    // dump heap info
//    heap_dump();

//    dprintf(INFO, "running heap tests\n");
//    heap_test();
}

  1. thread_init()。
void thread_init(void)
{
#if PLATFORM_HAS_DYNAMIC_TIMER // 宏未定义,不关注
    timer_initialize(&preempt_timer);
#endif
}

  1. dpc_init()。
void dpc_init(void)
{
    event_init(&dpc_event, false, 0); // 初始化一个dpc_event

    thread_resume(thread_create("dpc", &dpc_thread_routine, NULL, DPC_PRIORITY, DEFAULT_STACK_SIZE)); // 创建一个dpc thread,用于在thread退出时,做一些清除动作
}

  1. timer_init()。
void timer_init(void)
{
    list_initialize(&timer_queue);

    /* register for a periodic timer tick */
    platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */ // 设置一个间隔10ms的timer中断,timer_tick为中断处理函数
}

  1. thread_create()和thread_resume()。
详见lk thread分析

  1. exit_critical_section()。
static inline __ALWAYS_INLINE void exit_critical_section(void)
{
    critical_section_count--;
    if (critical_section_count == 0)
        arch_enable_ints(); // 当critical_section_count为0时使能中断
}

  1. thread_become_idle()。
void thread_become_idle(void)
{
    thread_set_name("idle"); // 将bootstrap线程改名为idle线程
    thread_set_priority(IDLE_PRIORITY); // 将优先级降为最低
    idle_thread = current_thread; // idle_thread指向当前线程
    idle_thread_routine();
}

static void idle_thread_routine(void)
{
    for(;;)
        arch_idle();
}

FUNCTION(arch_idle)
#if ARM_CPU_CORTEX_A8
    wfi // Wait for interrupt,等待中断唤醒,进入cpu idle状态
#elif ARM_CPU_ARM1136 || ARM_CPU_ARM926
    mov    r0, #0
    mcr    p15, 0, r0, c7, c0, #4
#elif ARM_CPU_ARM7
    /* nothing to do here */
#else
#error unknown cpu
#endif
    bx    lr


0 0
原创粉丝点击