分析linux-5.8.5的启动流程以及裁剪移植

获取最新的linux源码:https://kernel.org/
tar.xz后缀的解压指令:tar xJf 文件名  :注意J为大写

获取各个版本的linux kernel源码:https://mirrors.edge.kernel.org/pub/linux/kernel/
linux-kernel源码的基础知识:打开顶层Makefile可以看到
VERSION = 5
PATCHLEVEL = 8
SUBLEVEL = 5
EXTRAVERSION =
    前面两个组成的数字是主版本号,偶数表示稳定版本,奇数表示下一个稳定版本的前身,例如5.8就代表稳定版本了,SUBLEVEL代表次版本号,不分奇偶;
EXTRAVERSION表示扩展号,它可以是纯数组,也可以是-rc6这样的表示,表示正在测试版本;

这篇文章主要是阅读内核源码的时候记录一下的一些重要的点,就是一篇学习的笔记;
   
一.移植一个linux内核到一个新的开发板上,主要有以下主导思路(沿着设备树的各个节点一路配置内核):
   平台基础
   1.初始化map_io(设置页表)
   2.初始化cpu
   3.1初始化chosen节点,修改内核启动命令bootargs
   3.初始化memory
   4.初始化clk
   5.初始化interrupt控制器
   6.初始化gpio或者pinctrl
   7.初始化timer_pwm
   8.初始化watchdog
   9.初始化rtc
   10.初始化串口控制台
   
   设备基础
   1.网卡(用于调试或者通信)
   2.nand(硬盘用处存贮东西或者文件系统镜像)
   3.内核支持挂接yaffs2格式的文件系统
   4.设备树的移植

基础知识:

1.每个目录下面都有一个Makefile,Kconfig.  
  make menuconfig会逐层调用每个目录下面的Kconfig用于生成内核配置界面.最终会导致生成.config文件,make uImage时会根据.config逐层调用Makefile生成内核镜像。
  make menuconfig-->Kconfig-->可视化界面-->生成.config-->Makefile-->uImage镜像 
 
/* menuconfig的一些配置 */ 
        loadable module support:要打开三个:
              允许kernel加载模块:enable loadable module support
              允许卸载已加载的模块:Module unloading
              内核自动加载所需模块:automatic kernel module loading
              
        block layer:一般使用默认设置,允许超过2TB的块设备或者文件加到内核就要进去设置

        System Type:选择cpu,开发板类型等与开发板相关的项

        kernel Features: 是否支持内核抢占以及是否支持动态时钟设置

        Floating point emulation: 选择NWFPE math emulation(仿真浮点运算)

        Userspace binary formats:可执行文件格式,一般选择ELF,a.out

        Networking: 选中Networking support(支持网络),Packet socket(网络socket功能),TCP/ip networking(支持tcp/ip协议),
                    一般选择了第一个支持网络之后,后面使用默认就行了
                    
        File systems:选择内核用支持的文件系统,比如jffs2,yaffs2,EXT2 3等。


makefile:
         obj-y后面可以跟文件或者目录:obj-y += led.o 或者 obj-y += ./led
         要编译进去成二进制的文件只有一个的话:obj-y += led.o,如果有多个要写成两行
         obj-y += led.o
         led-objs := led1.o led2.o
         
         在分析内核的入口函数起决定性作用的两个Makefile:顶层Makefile arch/$(ARCH)/Makefile

移植内核一般是在一款相似的SOC上修改移植,主要是使用它的默认配置文件,要不然得自己make menuconfig一项项地选择太多了,不现实。
默认配置文件位于linux-5.8.5/arch/arm/configs  -> mini2440_defconfig   在make mini2440_defconfig之前可以进去修改一些东西。
在内核里面有很多s3c2440的资料,但是默认配置文件只有一个跟2440相关的mini2440_defconfig,说明其他的2440都只能通过这个mini2440的基础上修改得到;


make menuconfig需要配置的选项:
            Loadable module support:打开可加载模块支持:Enable loadable module support
                                    允许卸载已加载模块:Module unloading
                                    允许内核自动加载需要的模块:Automatic kernel module loading
            System Type: 选择CPU架构,SOC类型,board相关的配置
            floadting point emulation: 一般选择NWFPE math emulation
            Userspace binary formats:可执行文件格式,一般选择ELF,a.out
            Networking: 选择Networking support,然后默认就行了
            File systems: 在里面选择要支持的文件系统,yaffs2,jffs2等
            System Type: 选择arm的s3c2440系统类型,选择配置串口0为调试串口,支持DMA,s3c2440里面选择支持SMDK2440开发板(8种开发板中选择一种),选择支持ARM920T的处理器
            Device Drivers: MTK磁盘分区选上,Network device support选上dm90000(Device Drivers->Ethernet driver support->DM9000 support),Character device要选上(方便练习和串口调试)
            IIC选上
            File systems > Miscellaneous filesystems->yaffs2 file system support

            打开串口调试:Kernel hacking > arm Debugging > 选上下面两项
                                                                [*] Kernel low-level debugging functions (read help!)
                                                                          Kernel low-level debugging port (Use Samsung S3C UART 0 for low-level debug)  --->
                                                                [*] Early printk


编译内核:
       修改顶层Makefile,指定cpu架构已经编译工具
            ARCH = arm
            CROSS_COMPILE = arm-linux-

       在相似的SOC的默认配置文件上进行修改:make smdk2440_defconfig
       对内核进行配置:make menuconfig
            选择uart0作为调试串口:Kernel hacking->arm Debugging->[*] Kernel low-level debugging functions (read help!)->(X) Use Samsung S3C UART 0 for low-level debug

make mini2440_defconfig

make menuconfig进行上诉的配置

make uImage可以顺利编译出uImage,说命编译环境没有问题;

分析内核的启动流程

linux内核的入口:
            
            顶层Makefile里面有一句话:include arch/$(SRCARCH)/Makefile 
            /*最早编译进内核的文件arch/arm/Makefile,这个Makefile决定使用哪个单板                                     *arch/arm/mach-s3c24xx,即是编译哪个mach-xxx(soc相关的配置)
             *这个Makefile也决定编译哪个平台文件,比如arch/arm/plat-samsung(cpu相关的配置)
             */

在arch/arm/Makefile可以看到 head-y        := arch/arm/kernel/head$(MMUEXT).o说明第一个被编译进内核的文件是arch/arm/kernel/head.S

arch/arm/kernel/head.S->ENTRY(stext)//整个内核的入口
    __lookup_processor_type  //比较cpuID,将读硬件cpu的协处理器cp15得到的真实cpuID与内核所                                            支持的所有cpuID(在arch/arm/mm/proc-*.S)进行比对;
                                         *

  
    __create_page_tables(设置页表)//这个函数使用与定义都在arch/arm/kernel/head.S
    ldr    r13, =__mmap_switched        @ address to jump to after,使能mmu之后要执行的指令,其实是在__mmap_switched中跳到start_kernel,所以这个函数很重要    
    __enable_mmu(使能mmu)         //这个函数使用与定义都在arch/arm/kernel/head.S
        __turn_mmu_on             //这个函数定义在arch\arm\kernel\head.S
            mov    r3, r13;ret    r3    //这里实现跳转到__mmap_switched
                __mmap_switched   //定义在arch\arm\kernel\head-common.S
                    bl    memset(复制段,清bss)      //@ clear .bss
                    b    start_kernel              //@ 进入linux启动的第二阶段,不会再返回,调用start_kernel函数(进入第二个启动阶段)
                            start_kernel          //定义在init/main.c
                                //1.修改map_io,初始化dtb中的cpu,memory
                                setup_arch        //定义在arch\arm\kernel\setup.c
                                    setup_machine_fdt(__atags_pointer);//先通过设备树找到soc结构体(找到的过程用这个函数arch_get_next_mach找到每个编译到段__arch_info_begin的machine_desc的dt_compat属性),并且根据设备数的信息进行了一些先期的处理
                                            /*核心第一步
                                             *找到machine_description,拿每一个编译进内核的machine_description(在arch\arm\mach-s3c24xx\mach-smdk2440.c中定义)的dt_compat
                                             *与设备树中的根节点的compatible = "friendlyarm,mini6410", "samsung,s3c6410"进行比较,跟dtb中的compatible属性对比成功的mach-smdk2440.c中机器信息结构体mdesc将会被返回
                                             */
                                            of_flat_dt_match_machine 
                                                data = get_next_compat(&compat)
                                                of_flat_dt_match(dt_root, compat)
                                                    of_fdt_is_compatible(initial_boot_params, node, *compat)
                                                        of_compat_cmp(cp, compat, strlen(compat)) == 0/*将设备树的根节点的compatible字符串与编译进内核.arch.info.init段的机器信息结构体machine_desc的dt_compat成员进行比较,如果字符串相同就用这个machine_desc作为返回;
                                                                                                       *用设备树启动内核时就不用对比machine ID了,以前对比machine ID的原因是将uboot传来的machine ID与.arch.info.init段的每一个machine_desc结构体的成员nr进行对比,如果对比成功就会返回这个machine_desc*/
                                            
                                            //核心第二步
                                            early_init_dt_scan_nodes();
                                                //3.1初始化chosen节点,修改内核启动命令bootargs,不用添加驱动文件,写上bootargs命令行就行了;
                                                early_init_dt_scan_chosen //设备树dts文件里面的chosen节点的bootargs属性的值copy到boot_command_line
                                                
                                                //3.初始化memory,如果是移植内核到自己公司的芯片,那么只需在dts文件添加memory节点(格式可以仿照其他soc,都是通用的),节点说明内存的offest和size就行了,不用添加驱动文件;
                                                early_init_dt_scan_memory //将dtb里面的memory信息(内存的起始地址以及使用范围记录下来给内核使用,)
                                                                                        
                                            /*核心第三步 Change machine number to match the mdesc we're using */
                                            __machine_arch_type = mdesc->nr;//将mach-smdk2440.c->machine_desc->机器ID保存在变量__machine_arch_type里面
                                        
                                        //2.初始化cpu,这个在设备树中一定要配置正确,否则后带来致命性的错误;(仿照其他soc添加就行了,格式都是通用的)
                                        arm_dt_init_cpu_maps//有多少个cpu就要在设备树中描述多少个,可能要添加对应的cpu配置驱动文件,比如设置时钟频率,关cache,关中断那些;
                                        
                                        paging_init(mdesc);//(根据mem_info重新设置页表,针对mmu,要关注其中的io_map函数)
                                            devicemaps_init(mdesc);
                                                mdesc->map_io()
                                                    smdk2440_map_io//(arch\arm\mach-s3c24xx\mach-smdk2440.c)映射io页表,开发板相关的,移植内核时这个要重点修改;
                                        
                                        mdesc->init_early(); /*如果arch/arm/mach-smdk2440/mach-smdk2440.c的struct machine_desc结构体实现了成员函数init_early,就会在setup_arch的最后调用这个接口,
                                                              *说明soc的一些早期的初始化可以在这里执行;
                                                              */
                                        
                                //4.修改clk,如果是移植内核到自己公司的芯片,先在设备树添加clocks节点,再在drivers\clk\下添加驱动文件clk-mysoc.c,在驱动文件里实现CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init)就行了;
                                time_init()//arch\arm\kernel\time.c
                                    if (machine_desc->init_time)
                                    {
                                        machine_desc->init_time();
                                    }
                                    else
                                        of_clk_init(NULL);            //将 struct of_device_id 变量放到 __clk_of_table
                                            matches = &__clk_of_table;//__clk_of_table(实质是一个of_device_id结构体)<-of_device_id结构体 <- 宏_OF_DECLARE(拼装成struct of_device_id放在段_of_table) <- 宏OF_DECLARE_1 <- CLK_OF_DECLARE <- 宏drivers\clk\samsung\clk-s3c2410.c定义了CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init);
                                            parent->clk_init_cb = match->data;//核心
                                            list_add_tail(&parent->node, &clk_provider_list);//核心
                                            list_for_each_entry_safe(clk_provider, next,&clk_provider_list, node)
                                                clk_provider->clk_init_cb(clk_provider->np);//在这里调用到s3c2410_clk_init时钟初始化函数drivers\clk\samsung\clk-s3c2410.c
                                
                                //5.初始化interrupt控制器,如果是想移植内核到自己公司的芯片,先在设备树添加interrupt-controler节点,再在drivers\irqchip\添加驱动文件irq-mysoc.c,在驱动文件里实现宏IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of)来调用s3c2410_init_intc_of函数就行了
                                start_kernel->
                                    init_IRQ->
                                        machine_desc->
                                            init_irq()->
                                                irqchip_init->
                                                    of_irq_init(__irqchip_of_table)->根据dtb的interrupt-controller节点调用中断控制器驱动drivers\irqchip\irq-s3c24xx.c的入口函数s3c2410_init_intc_of
                                
                                //6.初始化gpio或者pinctrl,为设备驱动,在下面第11步调用;
                                //7.初始化timer_pwm,为设备驱动,在下面第11步调用;
                                //8.初始化watchdog,为设备驱动,在下面第11步调用;
                                //9.初始化rtc,为设备驱动,在下面第11步调用;
                                
                                //10.初始化串口控制台
                                parse_early_param();/*对启动参数进行先期的处理,但没有实质处理命令行,控制台也没有初始化
                                                     *strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
                                                     *处理命令行当中的earlycon,如果命令行里出现了earlycon字符串,就会在这里对进行对应的处理
                                                     */

                                /*这里处理启动命令中的"console=ttySAC0,115200",处理的方法是调用kernel/printk/printk.c下的函数__setup("console=", console_setup)来对命令行来处理,将命令行的console信息保存在一个控制台信息结构体里面*/                     
                                after_dashes = parse_args("Booting kernel",static_command_line,__start___param,__stop___param - __start___param,-1, -1, NULL, &unknown_bootoption);
                                
                                console_init();//利用上面组装得到的控制台信息结构体的名字ttySAC0选择/driver/tty/serial/samsung_tty.c->console_initcall(s3c24xx_serial_console_init)的一个串口作为控制台,所以命令行console=xx,这个name要与driver/tty/samsung_tty.c里面所注册的控制台的name要一致;

                                
                                //11.初始化用设备框架编写的驱动如watch_dog等;
                                //11.将dtb中的设备驱动转化为platform_device后注册调用对应的probe函数和调用machine_desc->machine_init
                                arch_call_rest_init
                                    rest_init();
                                        kernel_thread(kernel_init, NULL, CLONE_FS);//这里的kernel_init函数会将上面由设备树转成的device_node一一转化成platform_device并注册进设备总线匹配之后会调用probe
                                            kernel_init
                                                kernel_init_freeable();
                                                    do_basic_setup();
                                                        do_initcalls();//核心,调用machine_desc->machine_init函数和将device_node转化成platform_device并且注册进platform_device_list
                                                            /*在这里根据level的大小依次调用各个段注册的函数,level相当于优先级,有level小的先执行;各个level分配的情况为:
                                                             *
                                                             * #define core_initcall(fn)        __define_initcall(fn, 1)
                                                             * #define core_initcall_sync(fn)        __define_initcall(fn, 1s)
                                                             * #define postcore_initcall(fn)        __define_initcall(fn, 2)
                                                             * #define postcore_initcall_sync(fn)    __define_initcall(fn, 2s)
                                                             * #define arch_initcall(fn)        __define_initcall(fn, 3)  //mach-smdk2440.c中的machine_desc的init_machine成员函数就是用这个宏注册进去被调用的->arch_initcall(customize_machine);arch\arm\kernel\setup.c
                                                             * #define arch_initcall_sync(fn)        __define_initcall(fn, 3s)//将设备驱动中的device_node转化为platform并注册到platform_device链表的过程就是通过这个函数调用的->arch_initcall_sync(of_platform_default_populate_init);drivers\of\platform.c
                                                             * #define subsys_initcall(fn)        __define_initcall(fn, 4)
                                                             * #define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
                                                             * #define fs_initcall(fn)            __define_initcall(fn, 5)
                                                             * #define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
                                                             * #define rootfs_initcall(fn)        __define_initcall(fn, rootfs)
                                                             * #define device_initcall(fn)        __define_initcall(fn, 6)  //模块启动中的module_init中的函数就是从这个宏开始调用的;
                                                             * #define device_initcall_sync(fn)    __define_initcall(fn, 6s)
                                                             * #define late_initcall(fn)        __define_initcall(fn, 7)
                                                             * #define late_initcall_sync(fn)        __define_initcall(fn, 7s)
                                                             */
                                                            for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
                                                            {
                                                                arch_initcall(customize_machine);调用machine_desc->machine_init这个函数提不提供都可以(arch\arm\kernel\setup.c)
                                                                arch_initcall_sync(of_platform_default_populate_init)->of_platform_default_populate_init//device_node转化为platform并注册调用probe(drivers\of\platform.c)
                                                            }

            

现在是通过设备树给内核传参:内核对参数的处理parse_args主要是对设备树里面的如下参数进行处理,不同的SOC的console的名字不一样,比如smdk2440叫ttySAC0,vc0768叫ttyS0,那怎么确定其名字呢,通过了解内核控制台的注册console_init来发现其名字;
    chosen {
        bootargs = "console=ttySAC0 root=/dev/mtdblock4";
    };
    如果用设备树启动内核,内核的start_kernel里面对bootargs的处理就会只处理设备树里面的bootargs,而不会处理u-boot的环境保存的bootargs;但是为了保险起见,一般设备树和u-boot的环境变量都会设置为同样的指令;

核心:移植Linux内核要重点关注的是cpu信息结构体struct proc_info_list(arch\arm\mm\proc-arm920.S)和机器信息结构体也叫单板信息结构体struct machine_des(arch\arm\mach-s3c24xx\mach-smdk2440.c),这两个结构体的内容要重点关注,其中第一个结构体要关注cpuID,cpuID_mask;第二个结构体要关注全部,重点关注io_map;
      移植一个未经裁剪的内核到一个新的开发板,要在start_kernel函数中或者之前找到这些入口,并且修改下面这些:
      内核的启动流程就是根据设备树的硬件资源去配置开发板,所以移植内核时修改源码主要是沿着设备树里的设备找到各个设备的配置接口修改就行了;
      
      主要参考这篇文章:http://www.wowotech.net/linux_kenrel/dt-code-analysis.html 同样内容但是排版更好的文章https://www.sohu.com/a/253975328_654301
        (初始化map_io)//在arch\arm\mach-s3c24xx\mach-smdk2440->smdk2440_map_io里实现,但不理解为什么这么做;      
        (初始化cpu节点)//在start_kernel->setup_arch->arm_dt_init_cpu_maps(),这个可能由bootloader设置好了,不需要设置了
        (初始化memory节点)//在start_kernel->setup_arch->setup_machine_fdt->early_init_dt_scan->early_init_dt_scan_memory
        (初始化clk)//start_kernel->time_init();//核心设置clk 参考:https://blog.csdn.net/li5830/article/details/106942104/
        (初始化interrupt)//start_kernel->init_IRQ->machine_desc->init_irq()->irqchip_init->of_irq_init(__irqchip_of_table)->根据dtb的interrupt-controller节点调用中断控制器驱动drivers\irqchip\irq-s3c24xx.c,中断控制器的驱动只要用宏IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of)来调用s3c2410_init_intc_of函数就行了
        (初始化gpio或者pinctrl)/*设备树用platform_driver框架写的,kernel 初始化时()参考https://blog.csdn.net/wh_19910525/article/details/16370863
                                *start_kernel->arch_call_rest_init->rest_init->kernel_init->kernel_init_freeable->do_basic_setup->do_initcalls  @init/main.c开一条内核线程将setup_arch中转化设备树得到的device_node一一转化成
                                *platform_device结构体并注册进platform_device_list再调用probe函数,在这里就可以初始化一些用platform_device编写的设备驱动比如pinctrl,gpio
                                *
                                *rest_init->kernel_thread(kernel_init, NULL, CLONE_FS);//这里的kernel_init函数会将上面由设备树转成的device_node
                                *一一转化成platform_device并注册进设备总线匹配之后会调用probe
                                */
        (初始化timer_pwm)//用平台设备platform驱动框架写的
        (初始化watchdog) //用平台设备platform驱动框架写的
        (初始化rtc)      //用平台设备platform驱动框架写的
        (初始化串口控制台)/*用平台设备platform驱动框架写的,但是这个串口要作为控制台,要再调用控制台接口console_init,这个控制台接口会根据我们的bootargs中选定的串口控制台绑定platform_device中的串口驱动作为控制台通信;
            console_init   *新内核的一种新型的串口作为调试控制台的方法:在bootargs命令行里面写上earlycon,在串口驱动里面实现OF_EARLYCON_DECLARE(s3c2440,"samsung,s3c2440-uart",s3c2440_early_console_setup);就可以实现串口的早期打印了,但这好像并不是控制台,只是一个早期输出类似于早期printk,只有内核的输出信息,没有用户的输入命令;
                           */

                                                            
                                                            
设备树dts文件里面的chosen节点的bootargs属性的值copy到boot_command_line
命令行bootargs的每个参数的处理函数参考:__setup的参考资料:https://blog.csdn.net/silvervi/article/details/7042185


进入start_kernel之后如果没看到串口打印信息一般原因有两个:
    1:bootloader给kernel传递的参数bootargs不对(console名字不对或者波特率不对);
    2:start_kernel中针对开发板的配置不对(setup_arch这个函数不对);
    
start_kernel核心的函数:
    1. setup_arch:开发板相关配置的函数
    2. console_init:初始化串口控制台
    3. 处理tag(mem tag,comline tag)
            comline tag
                处理命令行中的console=ttySAC0
                    console_init//初始化注册控制台串口;


__lookup_processor_type:比较CPUid,读cpu寄存器得到一个硬件id放在r9,而在__arm920_proc_info(arch\arm\mm\proc-arm920.S)中有两个数值:r4,r5,
                         如果r4==r5&r9,则对比通过。

__machine_arch_type:MACHINE_START(S3C2440, "SMDK2440") 在arch\arm\mach-s3c24xx\mach-smdk2440.c

内核启动第一个阶段:对比cpu_id构建procesor_info_list结构体,对比machine_id构建machine_desc结构体
内核启动第二个阶段:解析booterloarer传进来的启动参数(用设备树或者tag的形式传给内核),执行与开发板相关的函数,初始化串口输出(serial)


linux第一阶段的分析:
            entry(stext)
                __lookup_processor_type  (判断是否支持此cpu,arch\arm\kernel\head.S)
                __mmap_switched 
                    start_kernel  (进入第二个启动阶段)
                          setup_arch(&command_line)
                                mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);//在这里进行machine ID的对比以及TAG参数的解析处理
                                     确定bootloader给kernel传输的tag的基地址,解析处理每个tag(mem,commandline)
                                     arch\arm\kernel\atags_parse.c
                                     __tagtable(ATAG_CMDLINE, parse_tag_cmdline);//会解析这个TAG,将命令行参数复制到default_command_line
                                     __tagtable(ATAG_MEM, parse_tag_mem32);//会解析这个函数,将当前DDR的地址和size信息添加到memblock.memory里面记录下来
                              parse_early_param     //解析启动参数命令行("console=ttySAC0,115200...)

移植内核主要是修改与开发板相关的函数
     第一阶段:__lookup_processor_type ,__machine_arch_type
     第二阶段:start_kernel(init/main.c)
                    setup_arch(arch\arm\kernel\setup.c)
                          paging_init(mdesc)
                            devicemaps_init(mdesc);
                                mdesc->map_io();
                                    smdk2440_map_io;(其中这个函数是最重要的,修改开发板的时钟晶震)

               start_kernel(init/main.c)
                    console_init();(arch\arm\kernel\setup.c) 
                        ret = call();
                            console_initcall(s3c24xx_serial_console_init);drivers\tty\serial\samsung_tty.c
                                register_console(&s3c24xx_serial_console);drivers\tty\serial\samsung_tty.c
                                    s3c24xx_serial_console
                                    {
                                        static struct console s3c24xx_serial_console = {
                                        .name        = S3C24XX_SERIAL_NAME,//查看u-boot传进来的关于串口的名字是否正确
                                        .device        = uart_console_device,
                                        .flags        = CON_PRINTBUFFER,
                                        .index        = -1,//表示所有都匹配
                                        .write        = s3c24xx_serial_console_write,
                                        .setup        = s3c24xx_serial_console_setup,
                                        .data        = &s3c24xx_uart_drv,
                                        };
                                    }

第二阶段主体思路:
    start_kernel:
        setup_arch():启动参数的处理(将tag的信息copy到commandline里,然后逐个解析)
            开发板相关的函数
        console_init():初始化调试串口

移植内核:最好找cpu,board两者都相同的板子来移植,就内核来说,至少要找一个cpu与自己的板子的cpu相同的板子来移植(只修改board相关的代码就行了)

修改MTD分区:arch\arm\mach-s3c24xx\common-smdk.c
             修改smdk_default_nand_part

使kernel支持挂接yaffs2文件系统
            yaffs2文件的源码下载:https://yaffs.net/get-yaffs   ,里面有个git clone ssh://www.aleph1.co.uk/home/aleph1/git/yaffs2   (如果服务器上已经安装好git服务器了,就直接使用此命令可以下载yaffs2文件)


当使用eabi的交叉编译工具(arm-linux-gnueabi-gcc)编译内核时,在配置内核时选上:
     kernel_feather
        [*]Use the ARM EABI to compile the kernel
            [*]Allow old ABI binaries to run with this kernel (EXPERIMENTAL)

编译内核:
      在顶层Makefile 架构以及编译工具
      make mini2440_defconfig
      make menuconfig
      make uImage

烧写kernel
nfs 0x30000000 10.150.50.162:/home/share/zjq_162/uImage
nand erase 0xa0000 0x3A0748(或者nand erase.part kernel,如果有分区kernel的话)
nand write.jffs2 0x30000000 0xa0000 0x3A0748(用nand write.jffs2而不用nand write是因为前者不用字节对齐,而后者需要512页对齐)


烧写设备树:
nfs 0x31000000 10.150.50.162:/home/share/zjq_162/s3c2440-smdk2440.dtb
nand erase 0x5a0000 0x180C(或者nand erase.part dtb,如果有分区dtb的话)
nand write.jffs2 0x31000000 0x5a0000 0x180C

烧写yaffs2文件系统:
nfs 0x30000000 10.150.50.162:/home/share/zjq_162/first_rootfs.yaffs2
nand erase 0x5c0000 0x16E6240
nand write.yaffs2 0x30000000 0x5c0000 0x16E6240


/


 

猜你喜欢

转载自blog.csdn.net/qq_43418840/article/details/118703268