[X]关闭

pmon启动流程概述

文档创建者:LINUX课程
浏览次数:123
最后更新:2024-11-22
本帖最后由 LINUX课程 于 2024-11-25 09:18 编辑

pmon启动流程概述

以龙芯处理器LS2K1000为例进行讲解
一 总体启动流程
  • CPU执行start.S中的代码:ls2k1000 CPU开始执行Targets/LS2K/ls2k/start.S中的代码。这段代码是PMON启动的起点,它包含了一些宏定义、程序入口指示和CPU初始化所需的指令。
  • 跳转到init_loongarch函数:然后,代码会跳转到zloader.ls2k/init_loongarch.c中的init_loongarch函数中执行。在这个函数中,PMON会将BIOS数据(biosdata)解压到特定的内存地址上。
  • 调用realinit_loongarch函数:接下来调用zloader.ls2k/init_loongarch.c中的realinit_loongarch函数,跳转到Targets/LS2K/ls2k/tgt_machdep.c中的init_loongarch函数。在这个阶段,PMON会完成更多的初始化工作,并准备跳转到C语言编写的代码部分。
  • 执行C语言代码:一旦PMON跳转到内存中的代码,它就可以使用栈和C语言编写的函数了。接下来的代码会继续进行硬件初始化、环境变量设置、PCI设备初始化等工作,并最终加载和执行操作系统内核。

为了更好的理解启动流程,以下是对pmon.bin.c生成过程的详细解释,以及它如何与biosdata和gzrom.bin相关联:

pmon.bin.c的生成过程
1.编译PMON源码:
  • 首先,需要编译ls2k的PMON源码。PMON是一种固件,用于处理处理器的启动和电源管理。
  • 在编译过程中,会生成多个目标文件(*.o),这些文件位于Targets/LS2K/compile/ls2k目录下。

2.链接生成elf文件:
  • 使用链接脚本(如Targets/LS2K/conf/ld.script)将这些目标文件链接成一个elf格式的文件,即pmon。
  • 这个elf文件包含了PMON的所有代码和数据。

3.去除符号表并压缩:
  • elf文件通常包含符号表,这些符号表在最终的产品中是不需要的。因此,需要去除符号表,得到pmon.bin文件。
  • 接着,使用gzip命令对pmon.bin文件进行压缩,得到pmon.bin.gz文件。

4.转换为C数组:
  • 为了将pmon.bin.gz文件嵌入到C代码中,使用bin2c工具将其转换为一个C数组。这个数组就是biosdata,它保存在pmon.bin.c文件中。
  • 在这个过程中,pmon.bin.gz文件的内容被读取并转换为一个字符数组的形式,这个数组在C代码中可以被直接访问和使用。
biosdata与gzrom.bin的关联
1.biosdata的作用:

  • biosdata是一个在pmon.bin.c文件中定义的数组,它包含了PMON固件的所有代码和数据(经过压缩)。
  • 在PMON的启动过程中,这个数组会被解压并加载到内存中,以执行PMON的代码。

2.gzrom.bin的生成:
  • gzrom.bin文件是通过将zload.o和start.o等文件链接而成,并去除了符号表得到的。
  • 其中,zload.o包含了initmips.c和pmon.bin.c等文件的编译结果,而pmon.bin.c文件又包含了biosdata数组。
  • 因此,gzrom.bin文件间接地包含了biosdata数组的内容。

3.启动流程中的使用:
  • 在龙芯处理器的启动过程中,gzrom.bin文件会被加载到内存中,并解压执行其中的代码。
  • 这个过程中,会跳转到initmips函数中执行,而initmips函数又会使用到biosdata数组中的数据来初始化PMON固件。

二 start.S 启动概述
image.jpg
  1.            .globl        _start
  2.            .globl        start
  3.            .globl        __main
  4. _start:
  5. start:
  6.            .globl        stack
  7. stack = start + LOCK_CACHE_SIZE         /* Place PMON stack in the end of 2M RAM */

复制代码
……一系列初始化
  1. start_now:

  2.              bnez        s0, 1f

  3.              li.w        a0, 128
  4.              la        ra, init_loongarch  #跳转到init_loongarch函数
  5.              jirl        zero, ra, 0
复制代码

三 在zloader.ls2k/init_loongarch.c中init_loongarch(…) 概述
image.jpg

  1. void realinit_loongarch();
  2. void init_loongarch(unsigned long long msize)
  3. {
  4.         unsigned long i;
  5.         char * biosdata_flash = (unsigned long long)biosdata & 0xfffffULL | 0x1c000000ULL | (0x900000000f010000 & (0xffULL << 56));
  6.         char * biosdata_mem = (0x900000000f010000 - sizeof(biosdata)) & ~0xffULL;

  7.         early_printf("Copy Bios to memory ");        //Copy the biosdata from flash to the memory
  8.         for (i = 0; i < sizeof(biosdata); i += sizeof(unsigned long)) {
  9.                 *(volatile unsigned long*)(biosdata_mem + i) = *(volatile unsigned long*)(biosdata_flash + i);
  10.         }
  11.         early_printf("OK, Uncompressing Bios");
  12.         while(1) {
  13.                 if(run_unzip(biosdata_mem, 0x900000000f010000) >= 0)
  14.                         break;
  15.         }
  16.         memset((void *)0x900000000f27a228, 0, 0x900000000f2ec678 - 0x900000000f27a228);        //clear bss
  17.         memset((void *)0x900000000f010000 - 0x1000, 0, 0x1000);        //0x900000000f010000-0x1000 for frame(registers),memset for pretty
  18.         early_printf("OK, Booting Bios\r\n");
  19.         realinit_loongarch(msize);
  20. }


  21. void realinit_loongarch(unsigned long long msize)
  22. {
  23.         __asm__ ("li.d  $r3,0x900000000f010000-0x4000;\n" \
  24.                 "        li.d $r12,0x900000000f0af8f0;\n" \      //该地址就是Targets/LS2K/ls2k/tgt_machdep.c中的init_loongarch(...)函数地址
  25.                 "        move $r4,%0;\n" \

  26.                 "        jirl $r0,$r12, 0;\n" \
  27.                 :
  28.                   : "r" (msize)
  29.                 : "$r3", "$r12");
  30. }
复制代码
2.函数 realinit_loongarch(unsigned long long msize)
  • 参数: msize - 内存大小,传递给BIOS初始化函数。
  • 功能: 使用内联汇编跳转到BIOS的初始化函数。

四 在Targets/LS2K/ls2k/tgt_machdep.c中 init_loongarch(…)概述
image.jpg

  1. void init_loongarch(unsigned long long tgt_memsz)
  2. {
  3.         unsigned int hi;
  4.         unsigned short i;

  5.         unlock_scache(LOCK_CACHE_BASE, LOCK_CACHE_SIZE);
  6.         //core1 run wait_for_smp_call function in ram
  7.         asm volatile("st.d %1,%0,0x20;"::"r"(0x800000001fe01100),"r"(&slave_main));

  8.         mem_win_cfg(tgt_memsz);

  9. #ifdef CONFIG_UART0_SPLIT
  10.         readq(LS2K1000_GENERAL_CFG1) |= 0xe;
  11. #endif
  12.         /*enable float */
  13.         tgt_fpuenable();

  14.         get_memorysize(tgt_memsz);

  15.         mul_pin_def_cfg();

  16.         /*
  17.          *  Probe clock frequencys so delays will work properly.
  18.          */

  19.     ls2k_i2c_init(0, LS2K1000_I2C0_REG_BASE);
  20.     ls2k_i2c_init(0, LS2K1000_I2C1_REG_BASE);
  21.         tgt_cpufreq();
  22.         SBD_DISPLAY("DONE", 0);

  23.         /*
  24.          *  Init PMON and debug
  25.          */
  26.         cpuinfotab[0] = &DBGREG;

  27.         dbginit(NULL);

  28.         bcopy(LoongArchException, (char *)GEN_EXC_VEC, LoongArchExceptionEnd - LoongArchException);
  29.         cpu_set_ebase();
  30.         cpu_set_tlb_ebase();
  31.         tgt_printf("set ebase done\n");

  32.         /*
  33.          *  Set up exception vectors.
  34.          */
  35.         SBD_DISPLAY("BEV1", 0);
  36.         printf("BEV in SR set to zero.\n");

  37. #if NNAND
  38. #ifdef CONFIG_LS2K_NAND
  39.         readq(LS2K1000_GENERAL_CFG0) |= (1 << 9);
  40.         ls2k_nand_init();
  41. #else
  42.         /*nand pin as gpio*/
  43.         readq(LS2K1000_GENERAL_CFG0) &= ~(1 << 9);
  44. #endif
  45. #if NSPINAND_MT29F || NSPINAND_LLD
  46.         ls2k_spi_nand_probe();
  47. #endif
  48. #if NM25P80
  49.         ls2k_m25p_probe();
  50. #endif
  51. #else
  52.         /*nand pin as gpio*/
  53.         readq(LS2K1000_GENERAL_CFG0) &= ~(1 << 9);
  54. #endif

  55. #ifdef DTB
  56.         verify_dtb();
  57. #endif
  58.         clear_pcie_inter_irq();

  59.         /*
  60.          * Launch!
  61.          */
  62.         main();
  63. }
复制代码
以下是对该函数主要操作的解释:
  • 解锁缓存:通过调用unlock_scache函数,解锁指定基地址和大小的缓存区域。这是为了确保后续的内存访问不会受到缓存的限制。
  • 核心间通信:使用内联汇编指令st.d,将slave_main函数的地址写入到一个特定的内存位置(0x800000001fe01100),这可能是为了通知另一个核心(如核心1)执行wait_for_smp_call函数。
  • 内存窗口配置:调用mem_win_cfg函数,根据tgt_memsz参数配置内存窗口。
  • UART配置:如果定义了CONFIG_UART0_SPLIT,则修改UART0的相关配置。
  • 启用浮点运算:通过调用tgt_fpuenable函数,启用浮点运算单元。
  • 获取内存大小:调用get_memorysize函数,根据tgt_memsz参数进一步处理或确认内存大小。
  • 多引脚默认配置:通过mul_pin_def_cfg函数,设置多个引脚的默认配置。
  • 时钟频率探测:初始化I2C接口,并调用tgt_cpufreq函数探测CPU时钟频率,确保延时操作能正确工作。
  • 显示初始化状态:通过SBD_DISPLAY宏显示初始化过程中的一些状态信息。
  • 调试和PMON初始化:设置调试寄存器信息,初始化调试功能,复制异常向量表到指定位置,设置异常基地址(ebase)。
  • 异常向量设置:通过设置SR寄存器中的BEV位为0,使异常向量位于低地址空间。
  • NAND和SPI NAND初始化:根据编译时的配置,初始化NAND闪存或SPI NAND闪存。如果未配置使用NAND,则将相关引脚配置为GPIO。
  • 设备树验证:如果定义了DTB,则验证设备树。
  • 清除PCIe中断:通过clear_pcie_inter_irq函数清除PCIe相关的中断。
  • 启动主程序:最后,调用main函数启动主程序。


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则