移值linux3.4.2内核之框架及初步修改

前言

先类比下Windows PC的启动流程,一上电后BIOS会去引导扇区读取系统引导程序引导windows内核的启动,内核启动过程中会去识别C盘,D盘,装载驱动程序,启动应用,对于嵌入式LINUX来说,BIOS称为Bootloader,它主要完成的工作有如下3步

1.装载内核到内存中
2.设置TAG参数
3.启动内核,将参数传递给内核,r0=0,r1=机器ID,r2=TAG参数的地址

内核启动中主要完成的工作有如下3步

1.根据r1判断能该内核能否支持该机器,若支持的话调用相应的单板初始化函数
2.装载驱动程序(比如我们从nandflash上读取根文件系统那么必须有nandflash的驱动程序,网络挂载也是同理)
3.解析bootloader传进来的TAG参数(比如分区信息,内存信息.....以便知道从哪个地址去挂载根文件系统等等有用的信息)
4.挂载根文件系统
5.启动应用程序

解压编译内核
1.解压内核
在这里插入图片描述
2.修改内核根目录下的Makefile,修改为arm架构和交叉编译器
移值linux3.4.2内核之框架及初步修改_第1张图片
3.查看arm架构下的配置文件
移值linux3.4.2内核之框架及初步修改_第2张图片
4.配置单板,生成.config文件,然后make uImage
在这里插入图片描述

uboot机器ID传递

我们前面提到,要根据uboot传递给内核的机器ID来调用内核里面对应板子的初始化函数,那么这个机器ID是多少呢?我们进去Bootloader的源代码文件看看
进入cmd_bootm.c,找到对应的bootm命令对应的do_bootm()

int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
boot_os_fn *boot_fn;             //boot_fn是个数组函数
 ... ..

boot_fn(0, argc, argv, &images); //调用数组函数
 ... ...
}

如下所示,boot_os_fn是一个函数类型
移值linux3.4.2内核之框架及初步修改_第3张图片
由于定义了宏CONFIG_BOOTM_LINUX,最终会跳转到do_bootm ->do_bootm_linux()
代码如下所示:

int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
         /* No need for those on ARM */
         if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
                   return -1;
         if (flag & BOOTM_STATE_OS_PREP) {
                   boot_prep_linux(images);
                   return 0;
         }
         if (flag & BOOTM_STATE_OS_GO) {
                   boot_jump_linux(images);
                   return 0;
         }

 
         boot_prep_linux(images);      //该函数会将各个tag参数保存在指定位置,比如:内存tag、bootargs环境变量tag、串口tag等
         boot_jump_linux(images);      //该函数会跳转到内核起始地址
         return 0;
}

最终跳转到do_bootm ->do_bootm_linux-> boot_jump_linux()

static void boot_jump_linux(bootm_headers_t *images)
{
         unsigned long machid = gd->bd->bi_arch_number;     //获取机器ID
         char *s;
         void (*kernel_entry)(int zero, int arch, uint params);
         unsigned long r2;
         kernel_entry = (void (*)(int, int, uint))images->ep;  //设置kernel_entry()的地址为0x30000000
         s = getenv("machid");                     //判断环境变量machid是否设置,若设置则使用环境变量里的值   
         if (s) {       
                   strict_strtoul(s, 16, &machid);      //重新获取机器ID
                   printf("Using machid 0x%lx from environment\n", machid);  //使用环境变量的machid
         }
     ... ...
        r2 = gd->bd->bi_boot_params;     //获取tag参数地址, gd->bd->bi_boot_params在setup_start_tag()函数里被设置 
        kernel_entry(0, machid, r2);     //跳转到0x30000000,r0=0,r1=机器ID,r2=tag参数地址
}

上面的machid默认值为MACH_TYPE_SMDK2410(也就是193),我们也可以在环境变量里设置machid变量,最终,便跳到内核执行代码

网络下载启动内核

移值linux3.4.2内核之框架及初步修改_第4张图片
由上图可知出现了乱码的情况,那肯定是我们的单板初始化串口的函数不对,所以我们必须找到机器ID相应的单板初始化函数,所以任意设置一个ID,这样再次启动内核时,内核识别不出来,就会打印出所有设备对应的机器ID.下面开始测试机器ID是否正确,进入uboot,输入

set machid 33333
tftp 32000000 uImage
bootm 32000000

如下图所示,由于内核不支持这个机器ID,所以打印出内核能支持的ID表:
移值linux3.4.2内核之框架及初步修改_第5张图片
再次烧写启动,发现7cf(mini2440)这个ID,有串口输出正常.下面看下16a(smdk2440)为什么串口乱码,进入mach-smdk2440.c( 位arch/arm/mach-s3c24xx)找到问题出在smdk2440_map_io():

static void __init smdk2440_map_io(void)
{
         s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
         s3c24xx_init_clocks(16934400);             //初始化时钟clock,需要改为12000000
         s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
}

重新编译内核,启动

make  s3c2410_defconfig             //将mach-s3c2440.c配置进内核
make  uImage
cp uImage /work/nfs_root/ uImage_new

uboot设置nfs下载内核启动

set machid 16a
nfs 32000000 172.16.245.101:/work/nfs_root/uImage_new
bootm 32000000

启动成功
移值linux3.4.2内核之框架及初步修改_第6张图片

你可能感兴趣的:(移值linux3.4.2内核之框架及初步修改)