Android 开发之 ---- bootloader (LK)

LK是什么

LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程代码  ,little kernel 是小内核小操作系统。

LK 代码 在 bootable/bootloadler/lk 目录下

LK 代码结构

+app            // 应用相关

+arch           // arm 体系

+dev            // 设备相关

+include      // 头文件

+kernel        // lk系统相关

+platform    // 相关驱动

+projiect     // makefile文件

+scripts      // Jtag 脚本

+target        // 具体板子相关

LK 流程分析

在 bootable/bootloadler/lk/arch/arm/ssystem-onesegment.ld 连接文件中 ENTRY(_start)指定 LK 从_start 函数开始,_start
在 lk/arch/crt0.S中 。crt0.S 主要做一些基本的 CPU 的初始化再通过 bl  kmain ;跳转到 C 代码中。

kmain 在 lk/kernel/main.c 中

kmain()

kmain 主要做两件事:1、本身 lk 这个系统模块的初始化;2、boot 的启动初始化动作。

kmain 源码分析:

   void kmain()

          {

           1.初始化进程(lk
中的简单进程)相关结构体。

             thread_init_early();

           2.做一些如 关闭 cache,使能 mmu 的 arm 相关工作。

            arch_early_init();

           3.相关平台的早期初始化

            platform_early_init();

           4.现在就一个函数跳转,初始化UART(板子相关)

            target_early_init();

           5.构造函数相关初始化

            call_constructors();

           6.lk系统相关的堆栈初始化

            heap_init();

           7.简短的初始化定时器对象

            thread_init();

.lk系统控制器初始化(相关事件初始化)

            dpc_init();

           9.初始化lk中的定时器

            timer_init();

           
10.新建线程入口函数 bootstrap2 用于boot 工作(重点)

           thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));

 }

以上与 boot 启动初始化相关函数是 arch_early_init、  platform_early_init 、bootstrap2,这些是启动的重点,我们下面慢慢来看。

arch_early_init()

         体系架构相关的初始化我们一般用的 ARM 体系

         1.关闭cache

         arch_disable_cache(UCACHE);

         2.设置向量基地址(中断相关)

         set_vector_base(MEMBASE);

         3.初始化MMU

         arm_mmu_init();

         4.初始化MMU映射__平台相关

         platform_init_mmu_mappings();

         5.开启cache         

         arch_enable_cache(UCACHE)

         6.使能 cp10 和 cp11

         __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));

 

        7.设置使能 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));

        8.使能循环计数寄存器

        __asm__ volatile("mrc    p15, 0, %0, c9, c12, 0" : "=r" (en));

        en &= ~(1<<3); /*循环计算每个周期*/

        en |= 1; 

        __asm__ volatile("mcr    p15, 0, %0, c9, c12, 0" :: "r" (en));

       9.使能循环计数器

       en = (1<<31);

       __asm__ volatile("mcr    p15, 0, %0, c9, c12, 1" :: "r" (en));

platform_early_init()

平台相关初始化不同平台不同的初始化下面是msm7x30

1.初始化中断

platform_init_interrupts();

2.初始化定时器

platform_init_timer();

bootstrap2

bootstrap2 在kmain的末尾以线程方式开启。主要分三步:platform_init、target_init、apps_init。

1.platform_init

platform_init 中主要是函数 acpu_clock_init。

在 acpu_clock_init 对
arm11 进行系统时钟设置,超频

2.target_init

针对硬件平台进行设置。主要对 arm9 和 arm11 的分区表进行整合,初始化flash和读取FLASH信息

3.apps_init

apps_init 是关键,对 LK 中所谓 app 初始化并运行起来,而 aboot_init 就将在这里开始被运行,android linux 内核的加载工作就在 aboot_init 中完成的 。

aboot_init

1.设置NAND/ EMMC读取信息页面大小

        if (target_is_emmc_boot())

        {

                  page_size = 2048;

                  page_mask = page_size - 1;

        }

       else

       {

                 page_size = flash_page_size();

                 page_mask = page_size - 1;

        }

2.读取按键信息,判断是正常开机,还是进入 fastboot ,还是进入recovery 模式

。。。。。。。。。

通过一系列的 if (keys_get_state() == XXX) 判断

。。。。。。。。。

3.从 nand 中加载 内核

boot_linux_from_flash();

 

      partition_dump();

      sz = target_get_max_flash_size();

      fastboot_init(target_get_scratch_address(), sz);

      udc_start(); // 开始 USB 协议



boot_linux_from_flash

  主要是内核的加载过程,我们的 boot.img 包含:kernel 头、kernel、ramdisk、second stage(可以没有)。

           1.读取boot 头部

           flash_read(p, offset, raw_header, 2048) 

           offset += 2048;

           
2.读取 内核    

           memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)

           n = (hdr->kernel_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1));

           flash_read(p, offset, (void*) hdr->kernel_addr, n)

           offset += n;

           
3.读取 ramdisk

           n = (hdr->ramdisk_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1));

           flash_read(p, offset, (void*) hdr->ramdisk_addr, n)

           offset += n;

            4.启动内核

boot_linux();//在boot_linux
中entry(0,machtype,tags);从kernel加载在内核中的地址开始运行了。

到这里LK的启动过程就结束了。

Android Kernel - Boot Loader

Android Boot loader 的 code 在 bootable/bootloader/lk 底下, LK 是 Little Kernel 的缩写, 是 andriod bootloader 的核心精神.

入口函数在 kernel/main.c 中的 kmain(), 以下就来读读这一段 code.

  1. void kmain(void)
  2. {
  3. // get us into some sort of thread context
  4. thread_init_early();
  5. // early arch stuff
  6. arch_early_init();
  7. // do any super early platform initialization
  8. platform_early_init();
  9. // do any super early target initialization
  10. target_early_init();
  11. dprintf(INFO, "welcome to lk/n/n");
  12. // deal with any static constructors
  13. dprintf(SPEW, "calling constructors/n");
  14. call_constructors();
  15. // bring up the kernel heap
  16. dprintf(SPEW, "initializing heap/n");
  17. heap_init();
  18. // initialize the threading system
  19. dprintf(SPEW, "initializing threads/n");
  20. thread_init();
  21. // initialize the dpc system
  22. dprintf(SPEW, "initializing dpc/n");
  23. dpc_init();
  24. // initialize kernel timers
  25. dprintf(SPEW, "initializing timers/n");
  26. timer_init();
  27. #if (!ENABLE_NANDWRITE)
  28. // create a thread to complete system initialization
  29. dprintf(SPEW, "creating bootstrap completion thread/n");
  30. thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
  31. // enable interrupts
  32. exit_critical_section();
  33. // become the idle thread
  34. thread_become_idle();
  35. #else
  36. bootstrap_nandwrite();
  37. #endif
  38. }

In include/debug.h: 我们可以看到 dprintf 的第一个参数是代表 debug level.

[c-sharp:nogutter] view plaincopyprint?
  1. /* debug levels */
  2. #define CRITICAL 0
  3. #define ALWAYS 0
  4. #define INFO 1
  5. #define SPEW 2

In include/debug.h:

[c-sharp:nogutter] view plaincopyprint?
  1. #define dprintf(level, x...) do { if ((level) <= DEBUGLEVEL) { _dprintf(x); } } while (0)

所以 dprintf 会依 DEBUGLEVEL 来判断是否输出信息.

来看第一个 call 的函数: thread_init_early, define in thread.c

[c-sharp:nogutter] view plaincopyprint?
  1. void thread_init_early(void)
  2. {
  3. int i;
  4. /* initialize the run queues */
  5. for (i=0; i < NUM_PRIORITIES; i++)
  6. list_initialize(&run_queue[i]);
  7. /* initialize the thread list */
  8. list_initialize(&thread_list);
  9. /* create a thread to cover the current running state */
  10. thread_t *t = &bootstrap_thread;
  11. init_thread_struct(t, "bootstrap");
  12. /* half construct this thread, since we're already running */
  13. t->priority = HIGHEST_PRIORITY;
  14. t->state = THREAD_RUNNING;
  15. t->saved_critical_section_count = 1;
  16. list_add_head(&thread_list, &t->thread_list_node);
  17. current_thread = t;
  18. }

#define NUM_PRIORITIES 32 in include/kernel/thread.h

list_initialize() defined in include/list.h: initialized a list

[c-sharp:nogutter] view plaincopyprint?
  1. static inline void list_initialize(struct list_node *list)
  2. {
  3. list->prev = list->next = list;
  4. }

run_queue 是 static struct list_node run_queue[NUM_PRIORITIES]

thread_list 是 static struct list_node thread_list

再来要 call  的函数是: arch_early_init() defined in arch/arm/arch.c

[c-sharp:nogutter] view plaincopyprint?
  1. void arch_early_init(void)
  2. {
  3. /* turn off the cache */
  4. arch_disable_cache(UCACHE);
  5. /* set the vector base to our exception vectors so we dont need to double map at 0 */
  6. #if ARM_CPU_CORTEX_A8
  7. set_vector_base(MEMBASE);
  8. #endif
  9. #if ARM_WITH_MMU
  10. arm_mmu_init();
  11. platform_init_mmu_mappings();
  12. #endif
  13. /* turn the cache back on */
  14. arch_enable_cache(UCACHE);
  15. #if ARM_WITH_NEON
  16. /* enable cp10 and cp11 */
  17. uint32_t val;
  18. __asm__ volatile("mrc   p15, 0, %0, c1, c0, 2" : "=r" (val));
  19. val |= (3<<22)|(3<<20);
  20. __asm__ volatile("mcr   p15, 0, %0, c1, c0, 2" :: "r" (val));
  21. /* set enable bit in fpexc */
  22. val = (1<<30);
  23. __asm__ volatile("mcr  p10, 7, %0, c8, c0, 0" :: "r" (val));
  24. #endif
  25. }

现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要处理器中的MMU(Memory Management Unit,

内存管理单元)提供支持。

CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Address,以下简称VA),而MMU将这个地

址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将VA映射成PA

MMU将VA映射到PA是以页(Page)为单位的,32位处理器的页尺寸通常是4KB。例如,MMU可以通过一个映射项将VA的一页

0xb7001000~0xb7001fff映射到PA的一页0x2000~0x2fff,如果CPU执行单元要访问虚拟地址0xb7001008,则实际访问到的物理地

址是0x2008。物理内存中的页称为物理页面或者页帧(Page Frame)。虚拟内存的哪个页面映射到物理内存的哪个页帧是通过页

表(Page Table)来描述的,页表保存在物理内存中,MMU会查找页表来确定一个VA应该映射到什么PA。

操作系统和MMU是这样配合的:

1. 操作系统在初始化或分配、释放内存时会执行一些指令在物理内存中填写页表,然后用指令设置MMU,告诉MMU页表在物理内存中

的什么位置。

2. 设置好之后,CPU每次执行访问内存的指令都会自动引发MMU做查表和地址转换操作,地址转换操作由硬件自动完成,不需要用指令

控制MMU去做。

MMU除了做地址转换之外,还提供内存保护机制。各种体系结构都有用户模式(User Mode)和特权模式(Privileged Mode)之分,

操作系统可以在页表中设置每个内存页面的访问权限,有些页面不允许访问,有些页面只有在CPU处于特权模式时才允许访问,有些页面

在用户模式和特权模式都可以访问,访问权限又分为可读、可写和可执行三种。这样设定好之后,当CPU要访问一个VA时,MMU会检查

CPU当前处于用户模式还是特权模式,访问内存的目的是读数据、写数据还是取指令,如果和操作系统设定的页面权限相符,就允许访

问,把它转换成PA,否则不允许访问,产生一个异常(Exception)

常见的 segmentation fault 产生的原因:

用户程序要访问一段 VA, 经 MMU 检查后无权访问, MMU 会产生异常, CPU 从用户模式切换到特权模式, 跳转到内核代码中执行异常服务程序.

内核就会把这个异常解释为 segmentation fault, 将引发异常的程序终止.

简单的讲一下 NEON: NEON technology can accelerate multimedia and signal processing algorithms such as video encode/decode,

2D/3D graphics, gaming, audio and speech processing, image processing, telephony, and sound synthesis.

platform_early_init() defined in platform/<your-platform>/platform.c

[c-sharp:nogutter] view plaincopyprint?
  1. void platform_early_init(void)
  2. {
  3. uart_init();
  4. platform_init_interrupts();
  5. platform_init_timer();
  6. }

uart_init.c defined in platform/<your-platform>/uart.c 所有用到的变数,也都定义在 uart.c

[c-sharp:nogutter] view plaincopyprint?
  1. void uart_init(void)
  2. {
  3. uwr(0x0A, UART_CR);  /* disable TX and RX */
  4. uwr(0x30, UART_CR);  /* reset error status */
  5. uwr(0x10, UART_CR);  /* reset receiver */
  6. uwr(0x20, UART_CR);  /* reset transmitter */
  7. #if PLATFORM_QSD8K
  8. /* TCXO */
  9. uwr(0x06, UART_MREG);
  10. uwr(0xF1, UART_NREG);
  11. uwr(0x0F, UART_DREG);
  12. uwr(0x1A, UART_MNDREG);
  13. #else
  14. /* TCXO/4 */
  15. uwr(0xC0, UART_MREG);
  16. uwr(0xAF, UART_NREG);
  17. uwr(0x80, UART_DREG);
  18. uwr(0x19, UART_MNDREG);
  19. #endif
  20. uwr(0x10, UART_CR);  /* reset RX */
  21. uwr(0x20, UART_CR);  /* reset TX */
  22. uwr(0x30, UART_CR);  /* reset error status */
  23. uwr(0x40, UART_CR);  /* reset RX break */
  24. uwr(0x70, UART_CR);  /* rest? */
  25. uwr(0xD0, UART_CR);  /* reset */
  26. uwr(0x7BF, UART_IPR); /* stale timeout = 630 * bitrate */
  27. uwr(0, UART_IMR);
  28. uwr(115, UART_RFWR); /* RX watermark = 58 * 2 - 1 */
  29. uwr(10, UART_TFWR);  /* TX watermark */
  30. uwr(0, UART_RFWR);
  31. uwr(UART_CSR_115200, UART_CSR);
  32. uwr(0, UART_IRDA);
  33. uwr(0x1E, UART_HCR);
  34. //  uwr(0x7F4, UART_MR1); /* RFS/ CTS/ 500chr RFR */
  35. uwr(16, UART_MR1);
  36. uwr(0x34, UART_MR2); /* 8N1 */
  37. uwr(0x05, UART_CR); /* enable TX & RX */
  38. uart_ready = 1;
  39. }

platform_init_interrupts: defined in platform/msm8x60/interrupts.c

[c-sharp:nogutter] view plaincopyprint?
  1. void platform_init_interrupts(void)
  2. {
  3. platform_gic_dist_init();
  4. platform_gic_cpu_init();
  5. }

GIC 指的是 Generic Interrupt Controller. The gic-cpu and gic-dist are two subcomponents of GIC.

Devices are wired to the git-dist which is in charge of distributing interrupts to the gic-cpu (per cpu IRQ IF).

platform_init_timer(): defined in platform/<your-platform>/timer.c

[c-sharp:nogutter] view plaincopyprint?
  1. void platform_init_timer(void)
  2. {
  3. writel(0, DGT_ENABLE);
  4. }

DGT: Digital Game Timer: presents the countdowns of two players, it is also called chess timer or chess clock.

target_early_init(): defined in target/init.c

[c-sharp:nogutter] view plaincopyprint?
  1. /*
  2. * default implementations of these routines, if the target code
  3. * chooses not to implement.
  4. */
  5. __WEAK void target_early_init(void)
  6. {
  7. }
  8. __WEAK void target_init(void)
  9. {
  10. }

call_constructors() is defined in kernel/main.c:

[c-sharp:nogutter] view plaincopyprint?
  1. static void call_constructors(void)
  2. {
  3. void **ctor;
  4. ctor = &__ctor_list;
  5. while(ctor != &__ctor_end) {
  6. void (*func)(void);
  7. func = (void (*)())*ctor;
  8. func();
  9. ctor++;
  10. }
  11. }

heap_init is defined in lib/heap/heap.c:

[c-sharp:nogutter] view plaincopyprint?
  1. void heap_init(void)
  2. {
  3. LTRACE_ENTRY;
  4. // set the heap range
  5. theheap.base = (void *)HEAP_START;
  6. theheap.len = HEAP_LEN;
  7. LTRACEF("base %p size %zd bytes/n", theheap.base, theheap.len);
  8. // initialize the free list
  9. list_initialize(&theheap.free_list);
  10. // create an initial free chunk
  11. heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
  12. // dump heap info
  13. //  heap_dump();
  14. //  dprintf(INFO, "running heap tests/n");
  15. //  heap_test();
  16. }

thread_init is defined in kernel/thread.c but nothing coded, 先记下.

[c-sharp:nogutter] view plaincopyprint?
  1. void thread_init(void)
  2. {
  3. }

dpc_init() is defined in kernel/dpc.c:

[c-sharp:nogutter] view plaincopyprint?
  1. void dpc_init(void)
  2. {
  3. event_init(&dpc_event, false, 0);
  4. thread_resume(thread_create("dpc", &dpc_thread_routine, NULL, DPC_PRIORITY, DEFAULT_STACK_SIZE));
  5. }

dpc 为 Delayed Procedure Call 延迟过程调用的缩写.

timer_init() is defined in kernel/timer.c:

[c-sharp:nogutter] view plaincopyprint?
  1. void timer_init(void)
  2. {
  3. list_initialize(&timer_queue);
  4. /* register for a periodic timer tick */
  5. platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */
  6. }

执行 thread_resume 或是 bootstrap_nandwrite

[c-sharp:nogutter] view plaincopyprint?
  1. #if (!ENABLE_NANDWRITE)
  2. // create a thread to complete system initialization
  3. dprintf(SPEW, "creating bootstrap completion thread/n");
  4. thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
  5. // enable interrupts
  6. exit_critical_section();
  7. // become the idle thread
  8. thread_become_idle();
  9. #else
  10. bootstrap_nandwrite();
  11. #endif

In kermel/main.c:

[c-sharp:nogutter] view plaincopyprint?
  1. static int bootstrap2(void *arg)
  2. {
  3. dprintf(SPEW, "top of bootstrap2()/n");
  4. arch_init();
  5. // initialize the rest of the platform
  6. dprintf(SPEW, "initializing platform/n");
  7. platform_init();
  8. // initialize the target
  9. dprintf(SPEW, "initializing target/n");
  10. target_init();
  11. dprintf(SPEW, "calling apps_init()/n");
  12. apps_init();
  13. return 0;
  14. }
  15. #if (ENABLE_NANDWRITE)
  16. void bootstrap_nandwrite(void)
  17. {
  18. dprintf(SPEW, "top of bootstrap2()/n");
  19. arch_init();
  20. // initialize the rest of the platform
  21. dprintf(SPEW, "initializing platform/n");
  22. platform_init();
  23. // initialize the target
  24. dprintf(SPEW, "initializing target/n");
  25. target_init();
  26. dprintf(SPEW, "calling nandwrite_init()/n");
  27. nandwrite_init();
  28. return 0;
  29. }
  30. #endif

continue to see apps_init(): app/app.c:void apps_init(void)

  1. #include <app.h>
  2. #include <kernel/thread.h>
  3. extern const struct app_descriptor __apps_start;
  4. extern const struct app_descriptor __apps_end;
  5. static void start_app(const struct app_descriptor *app);
  6. /* one time setup */
  7. void apps_init(void)
  8. {
  9. const struct app_descriptor *app;
  10. /* call all the init routines */
  11. for (app = &__apps_start; app != &__apps_end; app++) {
  12. if (app->init)
  13. app->init(app);
  14. }
  15. /* start any that want to start on boot */
  16. for (app = &__apps_start; app != &__apps_end; app++) {
  17. if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {
  18. start_app(app);
  19. }
  20. }
  21. }
  22. static int app_thread_entry(void *arg)
  23. {
  24. const struct app_descriptor *app = (const struct app_descriptor *)arg;
  25. app->entry(app, NULL);
  26. return 0;
  27. }
  28. static void start_app(const struct app_descriptor *app)
  29. {
  30. printf("starting app %s/n", app->name);
  31. thread_resume(thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
  32. }

至于会有那些 app 被放入 boot thread section, 则定义在 include/app.h 中的 APP_START(appname)

[c-sharp:nogutter] view plaincopyprint?
  1. #define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
  2. #define APP_END };

在 app 中只要像 app/aboot/aboot.c 指定就会在 bootloader bootup 时放入 thread section 中被执行.

[c-sharp:nogutter] view plaincopyprint?
  1. APP_START(aboot)
  2. .init = aboot_init,
  3. APP_END

在我的 bootloader 中有 app/aboot/aboot.c, app/tests/tests.c, app/shell/shell.c, 及 app/stringtests/string_tests.c 皆有此声明.

接下来关注: aboot.c 中的 aboot_init()

[c-sharp:nogutter] view plaincopyprint?
  1. void aboot_init(const struct app_descriptor *app)
  2. {
  3. unsigned reboot_mode = 0;
  4. unsigned disp_init = 0;
  5. unsigned usb_init = 0;
  6. //test_ram();
  7. /* Setup page size information for nand/emmc reads */
  8. if (target_is_emmc_boot())
  9. {
  10. page_size = 2048;
  11. page_mask = page_size - 1;
  12. }
  13. else
  14. {
  15. page_size = flash_page_size();
  16. page_mask = page_size - 1;
  17. }
  18. /* Display splash screen if enabled */
  19. #if DISPLAY_SPLASH_SCREEN
  20. display_init();
  21. dprintf(INFO, "Diplay initialized/n");
  22. disp_init = 1;
  23. diplay_image_on_screen();
  24. #endif
  25. /* Check if we should do something other than booting up */
  26. if (keys_get_state(KEY_HOME) != 0)
  27. boot_into_recovery = 1;
  28. if (keys_get_state(KEY_BACK) != 0)
  29. goto fastboot;
  30. if (keys_get_state(KEY_CLEAR) != 0)
  31. goto fastboot;
  32. #if NO_KEYPAD_DRIVER
  33. /* With no keypad implementation, check the status of USB connection. */
  34. /* If USB is connected then go into fastboot mode. */
  35. usb_init = 1;
  36. udc_init(&surf_udc_device);
  37. if (usb_cable_status())
  38. goto fastboot;
  39. #endif
  40. init_vol_key();
  41. if(voldown_press())
  42. goto fastboot;
  43. reboot_mode = check_reboot_mode();
  44. if (reboot_mode == RECOVERY_MODE) {
  45. boot_into_recovery = 1;
  46. else if(reboot_mode == FASTBOOT_MODE) {
  47. goto fastboot;
  48. }
  49. if (target_is_emmc_boot())
  50. {
  51. boot_linux_from_mmc();
  52. }
  53. else
  54. {
  55. recovery_init();
  56. boot_linux_from_flash();
  57. }
  58. dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
  59. "to fastboot mode./n");
  60. fastboot:
  61. if(!usb_init)
  62. udc_init(&surf_udc_device);
  63. fastboot_register("boot", cmd_boot);
  64. if (target_is_emmc_boot())
  65. {
  66. fastboot_register("flash:", cmd_flash_mmc);
  67. fastboot_register("erase:", cmd_erase_mmc);
  68. }
  69. else
  70. {
  71. fastboot_register("flash:", cmd_flash);
  72. fastboot_register("erase:", cmd_erase);
  73. }
  74. fastboot_register("continue", cmd_continue);
  75. fastboot_register("reboot", cmd_reboot);
  76. fastboot_register("reboot-bootloader", cmd_reboot_bootloader);
  77. fastboot_publish("product", TARGET(BOARD));
  78. fastboot_publish("kernel", "lk");
  79. fastboot_init(target_get_scratch_address(), 120 * 1024 * 1024);
  80. udc_start();
  81. target_battery_charging_enable(1, 0);
  82. }

target_is_emmc_boot() is defined in target/init.c: _EMMC_BOOT 是 compiler 时的 flags

[c-sharp:nogutter] view plaincopyprint?
  1. __WEAK int target_is_emmc_boot(void)
  2. {
  3. #if _EMMC_BOOT
  4. return 1;
  5. #else
  6. return 0;
  7. #endif
  8. }

check_reboot_mode is defined in target/<your-platform>/init.c:

[c-sharp:nogutter] view plaincopyprint?
  1. unsigned check_reboot_mode(void)
  2. {
  3. unsigned restart_reason = 0;
  4. void *restart_reason_addr = 0x401FFFFC;
  5. /* Read reboot reason and scrub it */
  6. restart_reason = readl(restart_reason_addr);
  7. writel(0x00, restart_reason_addr);
  8. return restart_reason;
  9. }

reboot mode in bootloader:

[c-sharp:nogutter] view plaincopyprint?
  1. #define RECOVERY_MODE   0x77665502
  2. #define FASTBOOT_MODE   0x77665500

再来就会执行 boot_linux_from_mmc():

[c-sharp:nogutter] view plaincopyprint?
  1. int boot_linux_from_mmc(void)
  2. {
  3. struct boot_img_hdr *hdr = (void*) buf;
  4. struct boot_img_hdr *uhdr;
  5. unsigned offset = 0;
  6. unsigned long long ptn = 0;
  7. unsigned n = 0;
  8. const char *cmdline;
  9. uhdr = (struct boot_img_hdr *)EMMC_BOOT_IMG_HEADER_ADDR;
  10. if (!memcmp(uhdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
  11. dprintf(INFO, "Unified boot method!/n");
  12. hdr = uhdr;
  13. goto unified_boot;
  14. }
  15. if(!boot_into_recovery)
  16. {
  17. ptn = mmc_ptn_offset("boot");
  18. if(ptn == 0) {
  19. dprintf(CRITICAL, "ERROR: No boot partition found/n");
  20. return -1;
  21. }
  22. }
  23. else
  24. {
  25. ptn = mmc_ptn_offset("recovery");
  26. if(ptn == 0) {
  27. dprintf(CRITICAL, "ERROR: No recovery partition found/n");
  28. return -1;
  29. }
  30. }
  31. if (mmc_read(ptn + offset, (unsigned int *)buf, page_size)) {
  32. dprintf(CRITICAL, "ERROR: Cannot read boot image header/n");
  33. return -1;
  34. }
  35. if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
  36. dprintf(CRITICAL, "ERROR: Invaled boot image header/n");
  37. return -1;
  38. }
  39. if (hdr->page_size && (hdr->page_size != page_size)) {
  40. page_size = hdr->page_size;
  41. page_mask = page_size - 1;
  42. }
  43. offset += page_size;
  44. n = ROUND_TO_PAGE(hdr->kernel_size, page_mask);
  45. if (mmc_read(ptn + offset, (void *)hdr->kernel_addr, n)) {
  46. dprintf(CRITICAL, "ERROR: Cannot read kernel image/n");
  47. return -1;
  48. }
  49. offset += n;
  50. n = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask);
  51. if (mmc_read(ptn + offset, (void *)hdr->ramdisk_addr, n)) {
  52. dprintf(CRITICAL, "ERROR: Cannot read ramdisk image/n");
  53. return -1;
  54. }
  55. offset += n;
  56. unified_boot:
  57. dprintf(INFO, "/nkernel  @ %x (%d bytes)/n", hdr->kernel_addr,
  58. hdr->kernel_size);
  59. dprintf(INFO, "ramdisk @ %x (%d bytes)/n", hdr->ramdisk_addr,
  60. hdr->ramdisk_size);
  61. if(hdr->cmdline[0]) {
  62. cmdline = (char*) hdr->cmdline;
  63. else {
  64. cmdline = DEFAULT_CMDLINE;
  65. }
  66. dprintf(INFO, "cmdline = '%s'/n", cmdline);
  67. dprintf(INFO, "/nBooting Linux/n");
  68. boot_linux((void *)hdr->kernel_addr, (void *)TAGS_ADDR,
  69. (const char *)cmdline, board_machtype(),
  70. (void *)hdr->ramdisk_addr, hdr->ramdisk_size);
  71. return 0;
  72. }

mmc_read() is defined in platform/<your-platform>/mmc.c:

[c-sharp:nogutter] view plaincopyprint?
  1. unsigned int mmc_read (unsigned long long data_addr, unsigned intout, unsigned int data_len)
  2. {
  3. int val = 0;
  4. val = mmc_boot_read_from_card( &mmc_host, &mmc_card, data_addr, data_len, out);
  5. return val;
  6. }

boot_linux(): 启动 Linux, 看看函数定义

[c-sharp:nogutter] view plaincopyprint?
  1. void boot_linux(void *kernel, unsigned *tags,
  2. const char *cmdline, unsigned machtype,
  3. void *ramdisk, unsigned ramdisk_size)
  4. {
  5. unsigned *ptr = tags;
  6. unsigned pcount = 0;
  7. void (*entry)(unsigned,unsigned,unsigned*) = kernel;
  8. struct ptable *ptable;
  9. int cmdline_len = 0;
  10. int have_cmdline = 0;
  11. int pause_at_bootup = 0;
  12. /* CORE */
  13. *ptr++ = 2;
  14. *ptr++ = 0x54410001;
  15. if (ramdisk_size) {
  16. *ptr++ = 4;
  17. *ptr++ = 0x54420005;
  18. *ptr++ = (unsigned)ramdisk;
  19. *ptr++ = ramdisk_size;
  20. }
  21. ptr = target_atag_mem(ptr);
  22. if (!target_is_emmc_boot()) {
  23. /* Skip NAND partition ATAGS for eMMC boot */
  24. if ((ptable = flash_get_ptable()) && (ptable->count != 0)) {
  25. int i;
  26. for(i=0; i < ptable->count; i++) {
  27. struct ptentry *ptn;
  28. ptn =  ptable_get(ptable, i);
  29. if (ptn->type == TYPE_APPS_PARTITION)
  30. pcount++;
  31. }
  32. *ptr++ = 2 + (pcount * (sizeof(struct atag_ptbl_entry) /
  33. sizeof(unsigned)));
  34. *ptr++ = 0x4d534d70;
  35. for (i = 0; i < ptable->count; ++i)
  36. ptentry_to_tag(&ptr, ptable_get(ptable, i));
  37. }
  38. }
  39. if (cmdline && cmdline[0]) {
  40. cmdline_len = strlen(cmdline);
  41. have_cmdline = 1;
  42. }
  43. if (target_is_emmc_boot()) {
  44. cmdline_len += strlen(emmc_cmdline);
  45. }
  46. if (target_pause_for_battery_charge()) {
  47. pause_at_bootup = 1;
  48. cmdline_len += strlen(battchg_pause);
  49. }
  50. if (cmdline_len > 0) {
  51. const char *src;
  52. char *dst;
  53. unsigned n;
  54. /* include terminating 0 and round up to a word multiple */
  55. n = (cmdline_len + 4) & (~3);
  56. *ptr++ = (n / 4) + 2;
  57. *ptr++ = 0x54410009;
  58. dst = (char *)ptr;
  59. if (have_cmdline) {
  60. src = cmdline;
  61. while ((*dst++ = *src++));
  62. }
  63. if (target_is_emmc_boot()) {
  64. src = emmc_cmdline;
  65. if (have_cmdline) --dst;
  66. have_cmdline = 1;
  67. while ((*dst++ = *src++));
  68. }
  69. if (pause_at_bootup) {
  70. src = battchg_pause;
  71. if (have_cmdline) --dst;
  72. while ((*dst++ = *src++));
  73. }
  74. ptr += (n / 4);
  75. }
  76. /* END */
  77. *ptr++ = 0;
  78. *ptr++ = 0;
  79. dprintf(INFO, "booting linux @ %p, ramdisk @ %p (%d)/n",
  80. kernel, ramdisk, ramdisk_size);
  81. if (cmdline)
  82. dprintf(INFO, "cmdline: %s/n", cmdline);
  83. enter_critical_section();
  84. platform_uninit_timer();
  85. arch_disable_cache(UCACHE);
  86. arch_disable_mmu();
  87. #if DISPLAY_SPLASH_SCREEN
  88. display_shutdown();
  89. #endif
  90. entry(0, machtype, tags);
  91. }

配合 boot.img 来看会比较好理解.

由此可知 boot_img_hdr 中各成员值为:

TAGS_ADDR 如上 target/<your-platform>/rules.mk 所定义的 : 0x40200100, 所以 boot_linux(), 就是传入TAGS_ADDR,

然后将资料写入 tag, tag 的结构如下所示.

然后进入到 kernel 的入口函数: entry(0, machtype, tags)

Android Bootloader LittleKernel的两篇文章的更多相关文章

  1. Android Bootloader LittleKernel的两篇文章 【转】

    转自:http://blog.csdn.net/loongembedded/article/details/41747523 2014-12-05 14:37 3599人阅读 评论(2) 收藏 举报 ...

  2. 两篇文章带你走入.NET Core 世界:Kestrel+Nginx+Supervisor 部署上云服务器(二)

    背景: 上一篇:两篇文章带你走入.NET Core 世界:CentOS+Kestrel+Ngnix 虚拟机先走一遍(一) 已经交待了背景,这篇就省下背景了,这是第二篇文章了,看完就木有下篇了. 直接进 ...

  3. vnext 技术两篇文章和评论

    研究vnext的两篇 好文章,重点看评论! http://www.cnblogs.com/shanyou/p/4589930.html http://www.cnblogs.com/shanyou/p ...

  4. 两篇文章带你走入.NET Core 世界:CentOS+Kestrel+Ngnix 虚拟机先走一遍(一)

    背景: 上一篇:ASP.Net Core on Linux (CentOS7)共享第三方依赖库部署 已经交待了背景,这篇就省下背景了. 折腾的过程分两步: 第一步是:本机跑虚拟机部署试一下: 第二步是 ...

  5. 有关C#写一个WindowsService的两篇文章

    1.http://blog.csdn.net/yysyangyangyangshan/article/details/10515035 上面的这篇文章一共两段,第二段讲的是使用代码来安装发布这个Win ...

  6. 【Kubernetes】两篇文章 搞懂 K8s 的 fannel 网络原理

    近期公司的flannel网络很不稳定,花时间研究了下并且保证云端自动部署的网络能够正常work. 1.网络拓扑 拓扑如下:(点开看大图)  容器网卡通过docker0桥接到flannel0网卡,而每个 ...

  7. 学习OpenCV——粒子滤波(网上两篇文章总结)

    粒子滤波的理论实在是太美妙了,用一组不同权重的随机状态来逼近复杂的概率密度函数.其再非线性.非高斯系统中具有优良的特性.opencv给出了一个实现,但是没有给出范例,学习过程中发现网络上也找不到.le ...

  8. BigDecimal带精度的运算的两篇文章

    转自:http://guoliangqi.iteye.com/blog/670908 之前提到过在商业运算中要使用BigDecimal来进行相关的钱的运算(java中关于浮点运算需要注意的 ),可是实 ...

  9. solr中facet及facet.pivot理解(整合两篇文章保留参考)

    Facet['fæsɪt]很难翻译,只能靠例子来理解了.Solr作者Yonik Seeley也给出更为直接的名字:导航(Guided Navigation).参数化查询(Paramatic Searc ...

随机推荐

  1. github pages + Hexo + 域名绑定搭建个人博客增强版

    概述 前面我们用github pages + Hexo 搭建了一个简单版的个人博客系统,但是里面的内容单调,很多功能不够完善,所以我们需要对yelle 的主题进行优化和完善.基本搭建请访问:http: ...

  2. app控件唯一相对Xpath自动生成(增强版uiautomatorviewer)

    作者:cryanimal QQ:164166060 工具由来 前面的一篇博文较详细地介绍了uiautomatorviewer:扫描和分析Android应用程序的UI控件的工具. 熟悉控件获取的同学都知 ...

  3. Dynamics CRM2013 流程拷贝

    在CRM中工作流是一个非常不错的功能,在实际业务场景中能满足各种业务需求.在我们设置一个工作流的时候,同一个实体一个逻辑功能可能需要多个工作流来实现,而多个工作流的不同之处可能只是启动时间或者是步骤中 ...

  4. (一二三)基于GCD的dispatch_once实现单例设计

    要实现单例,关键是要保证类的alloc和init只被调用一次,并且被自身强引用防止释放. 近日读唐巧先生的<iOS开发进阶>,受益匪浅,通过GCD实现单例就是收获之一,下面把这个方法与大家 ...

  5. Linux之mailx的使用

    mailx是UNIX系统上用来处理邮件的工具,使用它可以发送,读取邮件.下面看看如何使用它来发送邮件. 发送格式 mailx -s subject user@xxx.com < message_ ...

  6. Android初级教程实现电话录音

    需求:设置来电后自动录音. 首先设置一个按钮,代码很简单这里就不再给出. 建一个类,RecorderServicer extends Service package com.ydl.recorder; ...

  7. Linux上程序调试的基石(1)--ptrace

    引子: 1.在Linux系统中,进程状态除了我们所熟知的TASK_RUNNING,TASK_INTERRUPTIBLE,TASK_STOPPED等,还有一个TASK_TRACED.这表明这个进程处于什 ...

  8. springMVC 使用ajax 出现No serializer found for class异常

    转自  http://mxdba.iteye.com/blog/668155 google了一下,发现坛子里已经有人解答了 http://godfox.iteye.com/blog/646887 不过 ...

  9. ROS_Kinetic_13 ROS数据录制与回放

    ROS_Kinetic_13 ROS数据录制与回放 官网教程:http://wiki.ros.org/cn/ROS/Tutorials/Recording%20and%20playing%20back ...

  10. 我眼中的Linux设备树(三 属性)

    三 属性(property)device_type = "memory"就是一个属性,等号前边是属性,后边是值.节点是一个逻辑上相对独立的实体,属性是用来描述节点特性的,根据需要一 ...