一、下载、配置、编译、烧写jffs2
1.下载、解压
注意有时解压不了是文件权限的问题!
2.修改Makefile
3.配置
选用一种默认的配置:s3c2410_defconfig,创建配置文件:.config
默认配置在arch/arm/configs/下。。。。
4.make uImage生成内核映像文件
Q1:
sudo apt-get install uboot-mkimage
sudo apt-get install u-boot-tools
OK!
5.在windows下建立SIP
18:10开始
除了arch中有对arm的选择,其余文件看起来都是通用的,全部添加。若遇到问题就在Linux下grep 源码!
6.测试uImage
成功启动内核:
从以上可以看出:
编译的是我carl;CPU是arm920T;Mahine是SMDK2410;S3C2410A的ID;系统初始化时钟(400M,100M,50M);command line就是启动参数bootaegs;内存64M;
以下的MTD分区有问题:那么rootfs肯定不能正常挂载
果然:
7.uboot启动Linux参数之machid
可以从两个地方获取:uboot环境变量(优先) 和 uboot源码:jz2440.c中board_init中设置
uboot命令:set machid xxx
如:set machid 16a // smdk2440 7cf //mini2440
不同的machid会让Linux使用不同的初始化函数,其中涉及单板的相关初始化,比较重要的是波特率,如果启动内核显示乱码则查看波特率的设置。(参数设置为单板使用的晶振大小)在该初始化结构体的smdk_map_io中。bootargs中的115200设置可以避免这个问题。
在Linux源码:arch/arm/mach-s3c24xx/Mach-xxxx.c中:
如:
其中的machid在Linux:include/asm-arm/mach-types.h中可以查看Linux支持的所有machid。
例如:
当uboot环境变量没有设置machid,那么machid一定会采用在uboot代码中的初始化:
果然,此时的machid = id of smdk2410 (当然,此时的整个系统完全能正常启动)
若:
在Linux源码:mach-smdk2440.c中查看:
可见在smdk2440的board初始化函数中使用了16934400这个镜子,改为实际板载的12000000
make uImage后再测试:
解决!
8.uboot传递的bootargs
bootargs = console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=yaffs2
小结:
以下几个步骤可以编译出可以从串口输出的uImage:
a.uboot里设置machid(可以在代码中设置直接给内核,也可以在uboot shell中set machid xxx)
b.根据machid在Linux源码中找到对应的初始化结构体,并在map_io中找到对应的函数,查看波特率设置,并设置爱为单板的晶振(也可以在uboot 的 bootargs中设置为:console=ttySAC0,115200)
c.配置、编译Linux:make s3c2410_defconfig make uImage(前提要修改Makefile 的ARCH和CROSS_COMPLIE)
d.在uboot中设置bootargs = console=ttyASC0,115200 root=/dev/mtdblock3 rootfstype=jffs2 等
*****************************************************************************************************************************************
二、修改Linux分区和制作根文件系统
9.修改Linux分区
在前面已经看出一个大问题,我们设置了参数root=/dev/mtdblock3 即我们想要挂载mtdblock3这个分区的文件系统作为rootfs,但是,现在我们的Linux分区和预先设置的不一样,导致了最后不能挂接,那么我们就来为Linux修改分区:
根据打印分区的信息:
在Linux源码中查到:
很明显应该在comm-smdk.c中:
找到mtd分区的结构体。(arch/arm/mach-s3c24xx/common_smdk.c 注意不同的machid对应的分区结构体不一样)这个结构体每个成员就四个字段:分区name、偏移offset 和 大小size。(注意offset和size使用宏,在结构体定义处)
修改为预先设想的四个分区:
将arch/arm/mach-s3c24xx/common-smdk.c 拖过去编译
编译测试:
内核成功打印出分区信息。
Q:不断输出这两类信息
Q2:CRC错误,读不到内核
这是由于boot command中对内核分区的设置小于内核实际的大小,导致内核没有被完全读出来,同时也会引发连锁反应:在烧写内核时会擦除fs的区域,导致fs不能被读取,同时内核打印出Q1信息。
解决办法:
法一:裁剪内核!(暂时不用这个方法) 法二:不烧写kernel,只是下载到SDRAM,用bootm addr运行它
尝试挂载yaffs2文件系统:
由info:
知,目前系统不支持yaffs2文件系统。(在配置文件.config中可以查看支持哪些)
尝试jffs2:VFS能找到fs挂载,但是init进程出错,也不行!
10.使用busybox制作根文件系统
首先,uboot启动内核,内核到bootargs设置的分区去读取文件系统,并执行文件系统中的一个引导程序,这个引导程序就好像一个配置文件,让内核去执行更多的应用程序,如打开串口,运行某些服务等等。
busybox就是一系列命令的集合,如ls、chmod等。
a.下载busybox源码,解压
注意版本不要太高,否则可能对编译器版本有要求。
b.配置、编译
先vi Makefile设置ARCH 和 CROSS_COMPLILE(这里的交叉编译工具要和编译内核使用的版本一样)
再make menuconfig进入配置界面,再次设置CROSS_COMPLILE,里面有很多的选择,比如对命令的选择。采用默认配置吧,成功配置文件(make menuconfig 依赖于库: libncurses5-dev 要先安装库sudo apt-get install libncurses5-dev)
再make ,有问题就解决。
c.新建一个空的fs_dir,并安装busybox
mkdir rootfs
vi INSTALL 可以看到如何安装:
make CONFIG_PREFIX=。。。。/rotfs/ install
d.安装交叉编译工具的C库到fs_dir
find -name “lib”
安装armv4t的静态库:
注意安装库的时候:加-d选项(把链接文件cp后也作为链接文件,否则会cp链接后的实际文件,导致fs异常庞大)
p是保留源文件属性
e.创建/etc
内核启动fs的第一个进程是init进程,这个init进程可以是/sbin/init 也可以是linuxrc 在bootargs中设置init=linuxrc
创建inittab
https://www.xuebuyuan.com/3209620.html
sysinit指定init首先五执行/etc/init.d/rcS ,这是一个脚本文件
askfirst指定执行shell
ctrlaltdel指定组合键按下时执行reboot的程序
shutdown指定关机时取消所有挂载的fs
restart指定指定init进程
创建/etc/init.d/rcS https://www.cnblogs.com/PengfeiSong/p/6443149.html
指定PATH并export 导出
umask指定创建文件的默认权限(umask是022的时候,默认touch创建一个文件的权限是644)
mount -a指定挂载所有的虚拟文件系统,要依赖于/etc/fstab中固定格式指定的fs
mdev就是在/dev下自动创建设备驱动文件
创建fstab
这些挂载点的文件也是要创建的,不然没法挂载。
f.创建/dev
g.创建其他目录
h.制作jffs2映像文件
用mkfs.jffs2 制作映像文件,先安装mtd-utils
烧写测试:
Q:成功挂载,但init被kill,exitcode = 4 (SIGILL 非法指令)
由于arm-linux-gcc是采用eabi接口的编译方式,那么内核一定要启用 ARM EABI接口,重新make menuconfig ,使能ARM EABI后
make uImage
注意在make menuconfig中使用/查找很方便。
再次测试:成功挂载jffs2文件系统
11.使Linux支持yaffs2文件系统
yaffs2源码并没有合并到Linux中,总结的说就是将yaffs2源码合并到Linux源码,然后在Linux配置中启用yaffs2。
a.下载、解压
b.合并到Linux
vi README-linux
可知:在yaffs的目录下执行patch-ker.sh这个脚本文件,将yaffs源码打补丁到Linux源码中
c.配置
d.编译和debug,再编译,直到编译成功
可能有两类错误:
a.mtd结构体成员提示没有,实际上是有的,被定义为_xxxx,在使用的时候全用成xxxx。(在较新的yaffs2中没这个bug)
b.r_root 的获取函数没定义,参考其他文件系统使用的d_make_root函数,直接替换。(在较新的yaffs2中没这个bug)
e.制作yaffs2映像文件
e.下载测试:
如果出现问题,可能是uboot在烧写的时候对yaffs的支持不够友好,修改uboot源码。
采用替换法,将uboot替换成可以用的,若还是同样的错误uboot肯定问题。
以下是对uboot中yaffs部分的源码分析:
/common/cmd_nand.c do_nand函数中:
#ifdef CONFIG_CMD_NAND_YAFFS //针对write.yaffs 命令 如nand write.yaffs 30000000 offset size
} else if (!strcmp(s, ".yaffs")) {
if (read) {//如果是read就输出错误
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize, //主要的烧写函数 off 和 rwsize对应上面的offset size
(u_char *)addr, WITH_YAFFS_OOB);
#endif
进入nand_write_skip_bad函数:drivers/mtd/nand/nand_until.c
/*
* nand_write_skip_bad:
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
* block instead as long as the image is short enough to fit even after
* skipping the bad blocks.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length
* @param buffer buffer to read from
* @param flags flags modifying the behaviour of the write to NAND
* @return 0 in case of success
*/
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
u_char *p_buffer = buffer;
int need_skip;
#ifdef CONFIG_CMD_NAND_YAFFS
//WITH_YAFFS_OOB是作为参数传进来的
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)//肯定是不行的。。。
return -EINVAL;
int pages; //erasesize writesize 在nand_get_flash_type中设定
//128K / 2K = 64页
pages = nand->erasesize / nand->writesize;
//blocksize 就是一块总的大小 = 128K + (oobsize*64页)
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
printf ("Attempt to write incomplete page"
" in yaffs mode\n");
return -EINVAL;
}
} else
#endif
{
blocksize = nand->erasesize;//不执行
}
/*
* nand_write() handles unaligned, partial page writes.
*
* We allow length to be unaligned, for convenience in
* using the $filesize variable.
*
* However, starting at an unaligned offset makes the
* semantics of bad block skipping ambiguous (really,
* you should only start a block skipping access at a
* partition boundary). So don't try to handle that.
*/
if ((offset & (nand->writesize - 1)) != 0) {//对齐检查
printf ("Attempt to write non page aligned data\n");
*length = 0;
return -EINVAL;
}
/*check_skip_len返回值:
当offset在nand flash中无效时,返回-1(程序员应该不会范这种低级错误。。。)
当offset在nand flash中有效时,若在offset和length之间有坏块则返回1,否则返回0
*/
need_skip = check_skip_len(nand, offset, *length);
if (need_skip < 0) {
printf ("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
/*当need_skip = 0(无坏块) 且 flag中不存在WITH_DROP_FFS
目前flag之没有WITH_DROP_FFS的,所以当没有坏块时就会在这里面执行nand_write,
都不会到下面的while中取写了,所以下面的修改也就没意义了!!!
重点:下面的while是针对有坏块的情况,涉及OOB的操作,所以可以在可以添加一个条件:
!(WITH_YAFFS_OOB)即当没有定义WITH_YAFFS_OOB这个宏时。
*/
//if (!need_skip && !(flags & WITH_DROP_FFS)) {
if (!need_skip && !(flags & WITH_DROP_FFS) && !(WITH_YAFFS_OOB)) {
rval = nand_write (nand, offset, length, buffer);
if (rval == 0)
return 0;
*length = 0;
printf ("NAND write to offset %llx failed %d\n",offset, rval);
return rval;
}
while (left_to_write > 0) {//循环写
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size, truncated_write_size;
WATCHDOG_RESET ();
if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
printf ("Skip bad block 0x%08llx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
if (left_to_write < (blocksize - block_offset))
write_size = left_to_write;
else
write_size = blocksize - block_offset;
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
int page, pages;
size_t pagesize = nand->writesize;
size_t pagesize_oob = pagesize + nand->oobsize;
struct mtd_oob_ops ops;
ops.len = pagesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OOB_RAW;//原来是MTD_OOB_AUTO;
ops.ooboffs = 0;
pages = write_size / pagesize_oob;
for (page = 0; page < pages; page++) {
WATCHDOG_RESET();
ops.datbuf = p_buffer;
ops.oobbuf = ops.datbuf + pagesize;
rval = nand->write_oob(nand, offset, &ops);
//write_oob写第一页时,返回0,成功,但是下面的!会break写
if (rval) //原本为if(!rval)
break;
offset += pagesize;
p_buffer += pagesize_oob;
}
}
else
#endif
{
truncated_write_size = write_size;
#ifdef CONFIG_CMD_NAND_TRIMFFS
if (flags & WITH_DROP_FFS)
truncated_write_size = drop_ffs(nand, p_buffer,
&write_size);
#endif
rval = nand_write(nand, offset, &truncated_write_size,
p_buffer);
offset += write_size;
p_buffer += write_size;
}
if (rval != 0) {
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
*length -= left_to_write;
return rval;
}
left_to_write -= write_size;
}
return 0;
}
经以上改正的三个bug,再次测验就OK!
测验:
12.在默认配置上裁剪Linux
首先要清除make clean make mrproper make distclean的区别:
make clean:清除一些编译生成的文件。
make mrproper:在clean的基础上,清除配置文件(.config),和各种备份文件
make distclean:在mrproper的基础上,清除一些编辑备份文件和补丁文件
在下载源码后,本身就是干净了,就算执行mrproper也费不了时间,无所谓。在第一次配置生成配置文件后,若只修改源文件最好不要mrproper,会拖长编译时间,因为make本身就会检查该文件是否需要重新编译。当需要全部重新从源码配置才需要mrproper。至于make distclean的清除范围更大了,应该会回到源码的初始状态,不如重新tar。
现在重0开始配置、编译、裁剪:
a.修改顶层Makefile之ARCH = arm CROSSXXX = arm-linux-
b.初始配置:make s3c2410_defconfig 会生成.config文件(采用Linux中的一种默认soc配置,可以在arch/arm/configs中找到各种默认配置文件)
c.二次配置:make menuconfig 进入图形化配置界面,可视化的菜单配置(比如CROSS_XXX、ABI、EABI、board support、serial等)
第一大类:General setup是一些通用设置,比如编译器前缀设置、版本设置(选auto即可)、页交换swap支持、IPC、POSIX、BSD、IRQ、SLAB(内存管理机制)等。可以只修改编译器前缀,其他的采用默认配置即可。
第二大类:Enable loadable module support 这是支持可加载模块,必须的,不然后续驱动都从源码添加再编译太麻烦。
第三大类:Enabe the block layer 这是块设备的一些支持,使用硬盘/USB/SCSI设备者必选默认即可。
2TB+ 是支持2TB以上的大块设备支持,在嵌入式系统中一般不会用这么大,不选。
SG v4 为块设备启用第四版SG(SCSI generic)支持.v4相比v3能够支持更复杂的SCSI指令(可变长度的命令描述块,双向数据传输,通用请求/应答协议),而且UDEV也要用它来获取设备的序列号.对于使用systemd的系统来说,必须选"Y".
SG v4 lib 不需要手动开启此选项,如果有其他模块需要使用,会被自动开启。
data integrity support 如果设备支持 T10/SCSI Data Integrity Field 或者 T13/ATA External Path Protection 特性,那么可以开启此选项,否则建议关闭.
I/O调度默认即可。
第四大类:system Type 系统类型:选择CPU架构,开发板类型以及与board相关的配置选项。
第五大类:Bus support PC卡的支持,不用就不选。
第六大类:Kernel feature 设置内核的一些特性,如EABI、ARM ABI(EABI取决于编译器,否则启动内核会发生指令异常 exitcode = 4)以及Linux的内存特性机制,嵌入式中一般用不到那么复杂的内存管理机制。
其中的EXPERIMENTAL是实验阶段,可选可不选。
menory的3G/1G是说:内核空间1G,用户空间3G。对ARM来说是2G/2G。
high menory是物理内存<896M时,没有high memory,因为所有的内存都被kernel直接映射了。
详情见:https://blog.csdn.net/acs713/article/details/8575235
KSM是一种共享内存机制,嵌入式中一般不用。
第七大类:Boot options 内核启动参数的设置,一般默认即可,再说了,uboot不都设置了启动参数了嘛。
可选:use bootlader 的参数。
第八大类:CPU Power mangement CPU电源管理,默认即可。
允许软件控制的空闲进程电源管理,arm linux一般不用选。
第九大类:Floating point emulation 浮点运算管理。
若cpu支持硬浮点,选VFP浮点体系,否则选NWFPE。(这是浮点模拟器,当CPU不支持硬浮点处理时,必选NWFPE浮点模拟器,采用未定义指令异常来处理浮点)
第十大类:Userspace binary formats 内核支持什么样的可执行文件,一般选a.out elf
MISC是杂项设备的意思,不管。
第十一大类:Power management options 电源管理配置
第一个是说系统休眠后仅保持内存供电,系统启动时直接从内存开始运行。
最后一个是高级电源管理,一般都用不到。
第十二大类:Networking support 对arm-linux来说这是必须的吧
Networking options是unix socket 和TCP/IP的一些设置,另外wireless比较重要,其他的看是否需要。
第十三大类:Device Derivers 设备驱动配置,对应各种字符设备、块设备和网卡设备、杂项设备。以及各种总线接口支持,如IIC、SPI、并口、STAT、SISC等。
第十四大类:File systems 选择Linux支持的各种文件系统
其中里面有JFFS2 YAFFS2的选项。
第十五大类:kernel hacking 调试内核时使用,不用管。
第十六大类:Security options 安全选项,默认即可。
第十七大类:Cryptographic API 加密处理的API
第十八大类:Library routines 库:包含CRC32校验函数,zlib压缩函数等。不包含在内核源码中的第三方内核模块可能需要这些库,可以全不选,内核中若有其他部分依赖会自动选上。
第十九大类:Load an Alternate Configuration File 就是在make menuconfig时打开的配置文件
Save an Alternate Configuration File 退出mank menuconfig时将配置保存到某个文件中。
以上展示的二次配置是s3c2410_defconfig的配置,在这个过程中可以动态配置不要的东西。
现在的uImage:
d.在二次配置后若大小还不满足要求,继续裁剪内核。
主要考虑裁剪:支持的多余单板、支持多以的文件系统、支持不必要的输入设备接口、支持不必要的驱动接口
配合.config文件 可以直接make menuconfig了:
(1)先去掉不必要的单板吧,在system type中:
原本支持了很多不必要的单板,修改为以下:
(2)再去掉不必要的文件系统,在File system中:
去掉了ext2/3,Reiserfs,CD-ROMfs,tmpfs,ROMfs。
保留了ext4,JFFS2,YAFFS2,DOS,Networking fs等。
(3)输入设备无非就是鼠标键盘,不要就去了呗,还有他们的接口,PS/2已经不用了!输出设备,主要是触摸屏,不用触摸屏就去了。
在驱动中:
(4)还有一些SISC、IDE/ATA 、STAT等硬盘接口,这些接口一般是通用计算机用的。去掉吧,还是在驱动中:SISC保留吧!
(5)声卡和显卡,以太网驱动等,没有就不要,还在驱动中。
剪裁完成,OK!来make uImage:(若最后大小还是大了就只有修改uboot、内核mtd分区)
尴尬!2.08M,再挤挤吧,哈哈哈
在.config中看看有什么可以去掉:
//PM就是电源管理
在menuconfig中/搜索:
这是其他选择后被动选择的,以下两个也不管了。
在menuconfig中/搜索:
最后:
这是串口驱动,暂时保留吧!
声卡,没用!去掉:
这个和上面相连,不用管了。
加密算法,暂时保留吧!
再编译试试:
运气!!!小于2048!!!
测试:
启动错误:
Q1:
设置 rootfstype = yaffs2
Q2:uncorrectable error : //连续输出
可能是nand 中保存的数据出错,重新erase nand,再write
Q3:
https://blog.csdn.net/Blazar/article/details/79247464 //对应解决
http://blog.chinaunix.net/uid-27159438-id-3280213.html //同类解决
最终结果:uboot2012 + Linux 3.4 + yaffs2 都是自己移植的
uboot2012 + Linux-3.4.2 + rootfs.yaffs2(均为自己移植)
效果:
前面的bug:
没有 + x:
那就chmod 755 rcS加个可行性权限,再重启测试:
使uImage支持虚拟文件系统
如上解决后make uImage,再重启测试:
OK!
13.ECC
14.总结
对jffs2的支持一贯比较好,但是在使用yaffs2的文件系统时,会遇到很多的问题!
第一,uboot中 nand write.yaffs 命令的支持问题:nand_write_skip_bad函数
第二,Linux中,添加yaffs2后,yaffs中的bug:yaffs_getxattr选择问题!
附:以下调试的命令
tftp 30000000 uImage;bootm 30000000
tftp 30000000 uImage;nand erase.part kernel;nand write 30000000 kernel
tftp 30000000 uImage_4.3;nand erase.part kernel;nand write 30000000 kernel
tftp 30000000 uImage_new;nand erase.part kernel;nand write 30000000 kernel
tftp 30000000 fs_mini_mdev_new.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize
tftp 30000000 fs_mini.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize
tftp 30000000 fs_mini_mdev_new.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize
tftp 30000000 fs_mini.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize
tftp 30000000 u-boot_new;nand erase.part u-boot;nand write 30000000 u-boot
tftp 30000000 u-boot.bin;protect off all;erase 0 3FFFF;cp.b 30000000 0 40000
set bootargs console=ttySAC0,115200 root=/dev/mtdblock3