buildroot 学习
buildroot 可以让简化固件生成。
buildroot 可以让简化固件生成。
使用 韦东山 的 imx6ul 的开发板,按照手册上面的 nfs rootfs 的说明进行操作,结果开发板加载的时候,打印 VFS: Unable to mount root fs via NFS, trying floppy.然后直接卡住不动了。
经过仔细分析,原来是因为我 虚拟机使用的是 桥接网络,所以端口不需要特别的设定。
setenv netargs 'setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp,port=2049,mountport=9999'
上面这个端口设定是不需要的,改成下面这种就可以,或者直接取消这条命令。
setenv netargs 'setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp'
打补丁,patch -p1 < ../linux-2.6.22.6.patch 。 -p1 这个命令参数的意思是忽略补丁文件中目录的第一个 / 之前的内容。后面参数的意思是把指定目录中的 patch 文件打到当前目录。
配置。可以使用 find -name "*.defconfig" 查找一下内核当前支持的一些 board 的配置。找到 s3c2410_defconfig 比较相近,就执行 make s3c2410_defconfig 进行配置,这时候 s3c2410_defconfig 里面的配置就写入到 .config 文件中了。然后使用 make menuconfig 在 s3c2410_defconfig 的基础上进行修改。一般内核先配置 make s3c2410_defconfig,然后编译 make menuconfig。有时候厂家会提供 config_厂家,这时候只需 cp config_厂家 .config,然后 make menuconfig 就可以了。
编译内核直接用 make,但是给 uboot 用的内核需要用 make uImage 来编译,这个和正常的内核相比,多了一个头部内容。适合给 uboot 引导使用。
uboot 中烧录内核使用 k 命令,这个命令具体可以在 cmd_menu.c 中找到 k 的具体内容, usbslave 1 0x30000000, nand erase kernel, nand write.jffs2 0x30000000 kernel
如果删除了 root 分区,那么 内核启动到一定程度的时候,就会卡在那边。
make uImage 时候,首先根据 .config 生成 autoconfig.h 给源代码使用,生成 auto.conf 给 顶层 makefile 包含,给子目录的 makefile 使用。
linux-版本号 / Doc / kbuild 这里面的 makefile.txt 里面有内核 makefile 的详细讲解。 如果要编译为一个模块,类似的写法是:
obj_m += ab.o
ab_objs := a.o b.o
这样 a.c, b.c 两个文件就会被先编译为 a.o, b.o,然后被链接为 ab.ko 这个模块。
tq2440 里面需要编译的是 zImage,然后使用配套的 uboot,可以直接下载 zImage 内核并启动。 tq2440 make zImage 的时候,会提示错误 Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at kernel/timeconst.pl line 373. /opt/ARM/mini6410/linux/linux-2.6.38/kernel/Makefile:140: recipe for target 'kernel/timeconst.h' failed make[1]: [kernel/timeconst.h] Error 255 Makefile:916: recipe for target 'kernel' failed make: [kernel] Error 2
报错信息提示我们文件kernelkernel/timeconst.pl的第373不能使用'defined(@array)',将kernel/timeconst.pl中第373行的defined()去掉只留下@val就可以了.
考虑去掉defined(),改为:
372 @val = @{
关于 zImage 和 uImage 的差别,可以参考 https://www.cnblogs.com/linhaostudy/p/6735697.html
compress 目录下面的 head.S 是为了压缩内核的。如果内核过大,可以压缩内核,然后在内核前面加上自解压代码,这样组合起来变成一个小一点的内核。运行的时候,先自解压,之后再执行内核。
3: .long .
.long arch_info_begin
.long arch_info_end
adr r3, 3b @ 让 r3 等于 标号3所在地方的物理地址。 ldmia r3, {r4, r5, r6} @ 让 r4 等于 r3 的虚拟地址,也就是 标号3 的虚拟地址, 让 r5 等于 arch_info_begin, r6 等于 arch_info_end. sub r3, r3, r4 @ r3 等于虚拟地址和物理地址之间的偏移 add r5, r5, r3 @ r5 是对应的 物理地址 add r6, r6, r3 @ r6 是对应的 物理地址。
11. head.S 中内核启动时,首先判断是否支持 cpu,然后判断是否支持 单板(这个通过 u-boot 中 执行内核的 kernel 函数调用时传入的 machine_id),然后建立页表, 使能 mmu,然后跳转 start_kernel 来处理 u-boot 传入的启动参数。
start_kernel setup_arch //解析 u-boot 传入的启动参数 setup_command_line //解析 u-boot 传入的启动参数 parse_early_param do_early_param 从 setup_start 到 setup_end,调用 early 函数。 unknown_bootoption obsolute_checksetup 从 setup_start 到 setup_end,调用非 early 函数。 rest_init kernel_init prepare_namespace mount_root 这样就能挂载根文件系统了。 init_post 这个函数里面 打开 console,然后执行 init 等应用程序
挂载根文件系统按照 u-boot 中的 boot_args 这个参数来做。 uboot 中的 boot_args 里面的 root=/dev/mtdblock3 是和 linux 中的 arch/arm/plat-s3c24xx 中的 common-smdk.c 定义的 smdk_default_nand_part[] 这个分区结构体数组对应的。
使用 patch 命令打补丁, -p1 标识忽略第一级目录。 在串口上使用 q 命令,推出菜单界面,然后 print 可以打印出环境变量。想要返回菜单,只要输入 menu 即可。输入 ? 打印出可选命令。输入多条命令,可以使用分号隔开。输入 help 是各个命令的 usage 短帮助信息,输入 help print 这样指定命令,就会出现 help 长帮助信息。使用 set 命令修改环境变量,使用 save 保存环境变量,使用 reset 复位。boot 命令可以启动内核。 make 最后打印出来的信息是可以看到 ld 的具体命令,里面包含了链接地址,这个就是 sdram 中的位置。 start.s 汇编文件里面包含了启动汇编。 增加 uboot 命令,只需要在源文件中增加 类似于 do_bootm 这样的函数 和 U_BOOT_CMD 这样的宏,然后修改相应文件夹下的 makefile,增加对应的 obj 即可。然后 make,实在不行就 make disclean, make xxx_config, make。 linux 分区: boot, env, kernel, root ,具体分区在源码里面写死。MTDPARTS_DEFAULT 这个地方定义的,包含在单板头文件里面。使用命令 mtd 可以看到当前分区情况。do_bootm 是用来启动内核的, jffs2 这种格式不要求对齐,其他格式可能需要各种对齐。
flash 上存在的内核是 uImage,这个是由头部和真正的内核两部分组成。in_load 加载地址, in_ep 入口地址。加载地址是 u-boot 需要把真正的内核移动到那个地址。普通 bootm 的时候,可能把 uImage 放到 0x30007FC0,然后前面64字节是头部,真正的内核在 0x30008000 这个位置,如果 in_load 也是这个地址,那么 u-boot 就可以省略移动内核这个步骤,否则还需要把内核移动到 in_load 的地址。真正的启动命令 do_bootm_linux, 里面的 theKernel 就等于 in_ep 入口地址,然后从这个地址开始执行内核。
u-boot 需要把一些参数给 内核,这些参数放在一些约定好的地址,然后从这些地址按照指定的格式读取,可能是 0x30000100 地址。通过 setup_start_tag(bd) 这样的方式设定,最后一个是 setup_end_tag(bd)。bd->bi_arch_number 机器地址。
ram 初始化: 在 start.S 中, bl cpu_init_crit 这句,在 tq2440 中是直接调用,在韦东山里面是通过和 TEXT_BASE 进行比较,如果从 RAM 中运行就不进行 初始化。
uboot 第一阶段初始化主要在 start.S 里面,主要是芯片本身初始化。第二阶段初始化是从 lib_arm/board.c 里面的 start_armboot 开始的,里面有一个 for 循环,通过函数指针调用函数,存储这些指针的结构体 gd 在 SDRAM 中的 128 字节的 CPU_GBL_DATA_SIZE 里面,相应的函数实现在 board/EmbedSky/EmbedSky.c 里面。
在 start_armboot 函数中,韦东山代码增加了一个判断,从调试器加载时,相应的处理。if (PreLoadedONRAM)
command.c command.h 中有 u_boot_cmd 这个段相关的变量,最后通过 链接文件,链接到一起。然后函数执行的时候,遍历段中所有的结构体的命令,相等的就执行相应的函数。
增加 uboot 命令,可以仿照 dobootm 命令来制作自己的命令,需要提供 函数 do 和 通过宏定义来实现的结构体变量 U_BOOT_CMD。还有别忘了相应的头文件。
uboot 的 清除编译过程文件是 make distclean,配置命令 make EmbedSky_config,编译是 make。
uboot 读出内核,通过 nand read.jffs2 0x30007FC0 kernel 来从 nand 中的 kernel 分区里面读出内核。nand 中的 kernel 分区是在 include/configs/EmbedSky.h 中定义的,通过宏定义 MTDPARTS_DEFAULT 来定义具体的各个分区。也可以通过 uboot 的命令行模式中输入 mtd 命令来查看当前 uboot 的 分区信息。 分区的名字只是个标识不重要,主要的是分区的开始偏移地址和分区大小。
命令 nand read.jffs2 不需要页对齐,可以长度是任意的,其他的 read 命令需要对齐。
uboot 的内核镜像是 uImage,由头部和真正的内核组成,头部中的 ih_load 是加载地址,ih_ep 是入口地址,uboot 通过读取头部数据,把内核放到加载地址,然后跳转到入口地址执行。
编译时链接地址 0x30007FC0,这个地址加上 64 字节的 uImage 头部内容,结果就是 0x30008000,这个就是真正的内核加载地址。这样做的好处是,因为地址能够匹配上,那么 cmd_bootm.c 中 ntohl(hdr->ih_load) == addr 成立,就不会移动内核,否则通过 memmove 函数在调用把内核移动到加载地址处,这样能够加快启动速度。
do_bootm_linux 来启动内核。
uboot 通过设置启动参数的方法告诉内核一些必要的参数,然后跳转到入口地址,真正的开始启动内核。
ih_ep 中包含函数指针,把这个函数指针交给 theKernel,然后调用 theKernel 来启动内核。
uboot 在 0x30001000 按照一定的格式存放内核参数,然后由内核来读取这些参数。 在 cmd_bootm.c 中使用 setup_start_tag(bd),setup_memory_tags(bd), setup_end_tag(bd) 这种格式来存放 tag 参数。
uboot 中 armlinux.c 里面 memory_tags 里面关于 sdram 开始地址和大小,是在 board 文件中的 dram_init()中 通过 gd->bd->bi_dram[0].start, .size 来赋值的。
commandline_tas() 中的 参数 commandline 来源于 getenv 中的 “bootargs",这个可以在 uboot 运行起来会直接 print 查看,也可以查看源码。root= 是根文件系统位置,init= 这个是第一个应用程序,console= 这个是信息打印的窗口。
theKernel(0, bd->bi_arch_number, bd->bi_boot_parms), 第三个参数是从 0x30001000 开始的 start_tag 开始的一连串的 parms。bi_arch_number 这个是机器ID,在 board 文件里面通过 gd->bd->bi_arch_number 来定义,arch_number 不同的电路板是不一样的数值。uboot 把 arch_number 交给内核,内核检查是否支持这个 arch_number 的PCBA。如果支持,系统控制权就交给内核,后面就没有 uboot 的事情了。
You configure the suspend and resume support by adding parameters to the kernel parameter line.
Read syntax diagramSkip visual syntax diagram suspend and resume kernel parameter syntax
-resume=
--+---------------------+--+-----------+->< '- no_console_suspend-' '- noresume-'
where:
specifies the standard device node of the swap partition with the data that is required for resuming the Linux instance.
prevents Linux consoles from being suspended early in the suspend process. Without this parameter, you cannot see the kernel messages that are issued by the suspend process.
boots the kernel without resuming a previously suspended Linux instance. Add this parameter to circumvent the resume process, for example, if the data written by the previous suspend process is damaged.
To use a partition /dev/disk/by-path/ccw-0.0.2f50-part1 as the swap partition and prevent Linux consoles from being suspended early in the suspend process specify:
resume=/dev/disk/by-path/ccw-0.0.2f50-part1 no_console_suspend
The example uses a "by-path" definition of the disk to ensure that the correct device is used, for example, after a reboot.
本次网卡移植参考国嵌实验
在include/configs/tq2440.h中
58行左右 / modified by ptz , for tq2440, dm9000 /
//#define NETWORK_DRIVER_CS8900 1
112行左右 / add by ptz, for tq2440, PING CMD/
119行左右 / modified by ptz , for tq2440, dm9000 /
在打开 driver/net/dm9000x.c 吧MII接口的注释掉 388行左右 / delete by ptz, for tq2440, for dm9000 /
i = 0; while (!(phy_read(1) & 0x20)) { / autonegation complete bit / udelay(1000); i++; if (i == 10000) { printf("could not establish link\n"); return 0; } } / see what we've got / lnk = phy_read(17) >> 12; printf("operating at "); switch (lnk) { case 1: printf("10M half duplex "); break; case 2: printf("10M full duplex "); break; case 4: printf("100M half duplex "); break; case 8: printf("100M full duplex "); break; default: printf("unknown: %d ", lnk); break; } printf("mode\n");
这样就修改好了
打开 u-boot 主上目录下的 Makefile , 找到 smdk2410_config, 在其下 , 加入
smdk2410_config : unconfig
@
根据需要配置CROSS_COMPILE
在 board 子目录下建立自己的开发板 ptz/tq2440 目录,然后,将 smdk2410 目录下的文件拷入此目录中,然后,将 tq2440 目录下的 smdk2410.c 改 为tq2440.c ,同时还得修改 board/ptz/tq2440/Makefile 文件。 COBJS :=tq2440.o flash.o
在 include/configs/ 下建立 tq2440.h 配置头文件,把smdk2410 的相应头文件复制一份在相同目录下,并改名为 tq2440.h
编译测试一下是否能够通过 [...]# make distclean [...]# make tq2440_config Configuring for tq2440 board... [...]# make CROSS_COMPILE=arm-linux-
如果没有错误出现,就可以进行下一步的修改了。