S3C2440移植Linux4.19.275内核以及过程中遇到的问题

您所在的位置:网站首页 linux移植arm过程 S3C2440移植Linux4.19.275内核以及过程中遇到的问题

S3C2440移植Linux4.19.275内核以及过程中遇到的问题

2023-03-21 11:11| 来源: 网络整理| 查看: 265

目录

1 问题一:内核移植时MTD分区问题

2 问题二:uboot的MTDPARTS_DEFAULT定义的MTD分区,bootargs中的文件系统分区,内核的mtd_partition smdk_default_nand_part定义的分区,三者要对应起来

3 问题三:uboot不再传tag地址了,那内核怎么知道bootargs的

4 问题四:内核是怎么解析uboot传过来的tags地址或者设备树地址的

5 问题五:linux内核官网的疑问

6 下载内核源码

7 安装交叉编译工具链

8 修改顶层Makefile

9 修改时钟频率

10 修改MTD分区

11 关闭软件ECC校验

12 支持yaffs文件系统

13 支持设备树

14 配置内核

14.1 make s3c2410_defconfig

14.2 make menuconfig

15 编译内核和设备树文件

16 下载内核

17 end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b

18 编译4.0.1内核

18.1 error: 'struct file' has no member named 'f_dentry'

18.2 Warning: Neither atags nor dtb found

18.3 end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000004

在移植linux内核之前,我先把韦老师的开发手册过了一遍,发现了好几个不太明白的问题,先把这些问题都弄明白再移植。

1 问题一:内核移植时MTD分区问题

Linux开发手册上,内核移植MTD分区那里,0地址保存的竟然是kernel,我记得之前0地址不都是保存的bootloader吗, 

看到这里不理解,如果0地址保存的是kernel,那不是把bootloader覆盖了吗,搞不懂,一直没弄懂怎么回事,直到我往前翻,看到了uboot移植的介绍那里,我发现了这个

原来韦老师是把bootloader放到了norflash里面,怪不得nandflash的0就直接放kernel,因为我是先移植内核,先看的内核移植部分,所以没看到这里。 不过我移植内核的时候不会按老师这种方式做,我还是nandflash的0地址先放bootloader。

2 问题二:uboot的MTDPARTS_DEFAULT定义的MTD分区,bootargs中的文件系统分区,内核的mtd_partition smdk_default_nand_part定义的分区,三者要对应起来

这个是什么意思呢,就是uboot中有分区定义,如下

#define MTDIDS_DEFAULT "nand0=nandflash0" #define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \ "128k(device_tree)," \ "128k(params)," \ "4m(kernel)," \ "-(root)"

这里uboot定义的是前面256k存放bootloader,然后接下来128k存放的是设备树,然后接下来128k存放的是参数,然后接下来4m保存的是内核,后面的是文件系统。

那么bootargs里面,root赋值就要是 root=/dev/mtdblock4

bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";

然后内核中 就要这么定义,

static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "bootloader", .size = SZ_256K, .offset = 0, }, [1] = { .name = "device_tree", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "params", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [3] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = SZ_4M, }, [4] = { .name = "rootfs", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } }; 3 问题三:uboot不再传tag地址了,那内核怎么知道bootargs的

我看了韦老师的设备树里面用的uboot,他那里的uboot在调用theKernel的时候,第三个参数传进来的是设备树文件的地址,并没有传启动参数的保存地址也就是tag地址,那么内核怎么得到bootargs那些参数呢,这个问题也没想明白,在技术群里问了下,他们说可能是启动参数直接放设备树里面了,于是我去看了下设备树,在设备树中找到了下面一行,

chosen { bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200"; };

原来是把启动参数放到设备树里面了。

但是,看了下新版的uboot源码,其实新版里面是可以传tag,也可以传设备树文件地址。新版源码如下图,

 而韦老师是因为用的老版的uboot,但是又想在老板uboot里面支持设备树,所以韦老师修改如下

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); + /* 100ask for device tree, no initrd image used */ + if (argc == 4) { + of_flat_tree = (char *) simple_strtoul(argv[3], NULL, 16); + + if (be32_to_cpu(*(ulong *)of_flat_tree) == OF_DT_HEADER) { + printf ("\nStarting kernel with device tree at 0x%x...\n\n", of_flat_tree); + + cleanup_before_linux (); + theKernel (0, bd->bi_arch_number, of_flat_tree); + + } else { + printf("Bad magic of device tree at 0x%x!\n\n", of_flat_tree); + } + + } +

如果参数等于4就把设备树地址传给内核,这是因为使用设备树时候,我们输入的bootm命令是4个参数。

bootm // 无设备树,bootm 0x30007FC0 bootm // 有设备树

另外还一个问题就是在使用设备树的时候,如果也还是传输过来了bootargs,那么bootargs的优先级更高,

从设备树(dtb格式数据)中解析出bootargs_dts bootargs_正在起飞的蜗牛的博客-CSDN博客

(1)内核启动参数bootargs保存在设备树的chosen节点的bootargs属性; (2)bootargs数据可以是在dts源文件中定义,也可以是uboot启动内核时传递给内核; (3)优先级:uboot传递的bootargs参数优先级高于dts中定义的bootargs; (4)如果是uboot传递的bootargs,在内核解压缩阶段就会调用atags_to_fdt()函数将tag中的bootargs参数转换成dtb的格式,写进dtb数据中;

这时候内核用的应该是zImage-dtb格式:vmlinuz/vmlinux、Image、zImage与uImage的区别_vmlinux和uimage_正在起飞的蜗牛的博客-CSDN博客

4 问题四:内核是怎么解析uboot传过来的tags地址或者设备树地址的

之前老的uboot里面,调用theKernel函数的时候,第三参数是tag地址也就是bootargs的那些地址,但是,新版的uboot里面,调用theKernel函数启动内核的时候,第三个参数可能是tags地址,也可能是设备树地址,那么内核肯定是两种方式都支持,那么是怎么支持的,这个我去看了下这一块的内核源码,先不看head.S了,直接去看start_kernel函数,

asmlinkage __visible void __init start_kernel(void) { char *command_line; char *after_dashes; set_task_stack_end_magic(&init_task); smp_setup_processor_id(); debug_objects_early_init(); cgroup_init_early(); local_irq_disable(); early_boot_irqs_disabled = true; /* * Interrupts are still disabled. Do necessary setups, then * enable them. */ boot_cpu_init(); page_address_init(); pr_notice("%s", linux_banner); setup_arch(&command_line); .... ....//其他代码

然后看这里面的setup_arch函数,在arch/arm/kernel/setup.c里面,

void __init setup_arch(char **cmdline_p) { const struct machine_desc *mdesc; setup_processor(); /*这个__atags_pointer就是uboot穿进来的第三个参数,也就是tag地址或者设备树地址,然后这 个setup_machine_fdt里面是对设备树是否有效,*/ mdesc = setup_machine_fdt(__atags_pointer); if (!mdesc) mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);//上面判断设备树无效,那说明穿进来的就是tag地址了。那么解析tag。 if (!mdesc) { early_print("\nError: invalid dtb and unrecognized/unsupported machine ID\n"); early_print(" r1=0x%08x, r2=0x%08x\n", __machine_arch_type, __atags_pointer); if (__atags_pointer) early_print(" r2[]=%*ph\n", 16, phys_to_virt(__atags_pointer)); dump_machine_table(); } machine_desc = mdesc;

这里面首先是setup_machine_fdt函数,在这个setup_machine_fdt函数里面调用early_init_dt_verify函数判断是否是有效的设备树,然后再解析设备树。

/** * setup_machine_fdt - Machine setup when an dtb was passed to the kernel * @dt_phys: physical address of dt blob * * If a dtb was passed to the kernel in r2, then use it to choose the * correct machine_desc and to setup the system. */ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) { const struct machine_desc *mdesc, *mdesc_best = NULL; #if defined(CONFIG_ARCH_MULTIPLATFORM) || defined(CONFIG_ARM_SINGLE_ARMV7M) DT_MACHINE_START(GENERIC_DT, "Generic DT based system") .l2c_aux_val = 0x0, .l2c_aux_mask = ~0x0, MACHINE_END mdesc_best = &__mach_desc_GENERIC_DT; #endif if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))//判断是否有效的dtb return NULL; mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach); if (!mdesc) { const char *prop; int size; unsigned long dt_root; early_print("\nError: unrecognized/unsupported " "device tree compatible list:\n[ "); dt_root = of_get_flat_dt_root(); prop = of_get_flat_dt_prop(dt_root, "compatible", &size); while (size > 0) { early_print("'%s' ", prop); size -= strlen(prop) + 1; prop += strlen(prop) + 1; } early_print("]\n\n"); dump_machine_table(); /* does not return */ } /* We really don't want to do this, but sometimes firmware provides buggy data */ if (mdesc->dt_fixup) mdesc->dt_fixup(); early_init_dt_scan_nodes();

 如果early_init_dt_verify函数判断不是有效的设备树,那么就调用setup_machine_tags函数,把第三个参数当成tag地址来解析,总结一下就是

setup_arch setup_machine_fdt if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))//判断是否有效的dtb early_init_dt_scan_nodes(); mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);前面的没成立,那么说明传进来的不是设备树文件地址,而是启动参数的tag地址,那么这里是直接解析tag参数了, 5 问题五:linux内核官网的疑问

The Linux Kernel Archives

 当我登录linux内核官网后,这里tarball是下载源码,然后后面我发现了两个东西patch和inc.patch,然后我鼠标放到patch提示Download patch to previous mainline,然后鼠标放到inc.patch提示Download incremental patch,搞不懂这两个有什么区别,然后我下载inc.patch发现名字是这样的patch-5.10.169-170.xz,那么这个应该是说5.10.169到5.10.170的补丁,但是patch是啥,在技术交流群里问了下,别人跟我说linux的主版本号,我还是不懂,然后我去查了下linux版本号问题,例如5.10.170.21吧,5.10是主版本好,170是次版本号,然后21是扩展版本号,所以这里的patch:Download patch to previous mainline意思是5.10.170针对5.10增加的补丁文件,好,懂了。

然后还一个就是官网主页只显示了几个内核版本,其他版本在上面的那个Http:Index of /pub/

6 下载内核源码

我去The Linux Kernel Archives 

 这里不下载最新的了,就用4.19.275吧,下载完之后用下面的命令解压。

xz -d linux-4.19.275.tar.xz tar -xavf linux-4.19.275.tar 7 安装交叉编译工具链

这里用gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi.tar.xz,直接解压,然后设置环境变量就可以了,安装完之后我想看一下是否安装成功了,发现如下错误:

arm-linux-gcc --version /usr/local/arm/4.3.2/bin/arm-linux-gcc: line 3: /usr/local/arm/4.3.2/bin/arm-none-linux-gnueabi-gcc: No such file or directory

网上搜了下,这是因为操作系统是64位的,而交叉编译工具链是32位的,所以需要安装下面的包兼容32位。

sudo apt-get install lib32z1 8 修改顶层Makefile

这里修改交叉编译工具链

#ARCH ?= $(SUBARCH) ARCH ?= arm CROSS_COMPILE ?= arm-linux-gnueabi- 9 修改时钟频率

arch/arm/mach-s3c24xx/mach-smdk2440.c中将时钟频率修改为12M,

static void __init smdk2440_init_time(void) { //s3c2440_init_clocks(16934400); s3c2440_init_clocks(12000000); samsung_timer_init(); } 10 修改MTD分区

arch/arm/mach-s3c24xx/common-smdk.c文件中,将代码

static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "Boot Agent", .size = SZ_16K, .offset = 0, }, [1] = { .name = "S3C2410 flash partition 1", .offset = 0, .size = SZ_2M, }, [2] = { .name = "S3C2410 flash partition 2", .offset = SZ_4M, .size = SZ_4M, }, [3] = { .name = "S3C2410 flash partition 3", .offset = SZ_8M, .size = SZ_2M, }, [4] = { .name = "S3C2410 flash partition 4", .offset = SZ_1M * 10, .size = SZ_4M, }, [5] = { .name = "S3C2410 flash partition 5", .offset = SZ_1M * 14, .size = SZ_1M * 10, }, [6] = { .name = "S3C2410 flash partition 6", .offset = SZ_1M * 24, .size = SZ_1M * 24, }, [7] = { .name = "S3C2410 flash partition 7", .offset = SZ_1M * 48, .size = MTDPART_SIZ_FULL, } };

修改为下面的代码

static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "bootloader", .size = SZ_256K, .offset = 0, }, [1] = { .name = "device_tree", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "params", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [3] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = SZ_4M, }, [4] = { .name = "rootfs", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } }; 11 关闭软件ECC校验

修改arch/arm/mach-s3c24xx/common-smdk.c文件:

12 支持yaffs文件系统

Get Yaffs | Yaffs - A Flash File System for embedded use

yaffs官网上让用下面的命令下载

git clone git://www.aleph1.co.uk/yaffs2

然后需要运行文件系统里面的patch-ker.sh脚本文件,先./patch-ker.sh看一下使用说明

./patch-ker.sh usage: ./patch-ker.sh c/l m/s kernelpath if c/l is c, then copy. If l then link if m/s is m, then use multi version code. If s then use single version code

所以这里

13 支持设备树

这里移植内核的时候想把设备树也用上,然后参考了韦东山老师的设备树教程以及彭东林老师的这个博客:https://www.cnblogs.com/pengdonglin137/p/6241895.html

使用下面的设备树文件

// SPDX-License-Identifier: GPL-2.0 /* * SAMSUNG SMDK2440 board device tree source * * Copyright (c) 2018 [email protected] * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb */ #define S3C2410_GPA(_nr) ((0


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3