A53系统移植、内核、文件系统

A53系统移植、内核、文件系统的学习步骤

操作系统—》Linux操作系统

bootloaer-----> 引导内核,让内核运行起来
1) 完成最基本硬件初始化,调试串口,DRAM,看门狗
2) 将烧写在emmc芯片上的uImage (内核镜像文件) 搬运到DRAM里;
(dram:为动态随机存储存储器,存储时间短,关机丢失数据,需每隔一段时间刷新,常为系统内存)
3) CPU就从DRAM取uImage的指令开始运行,内核就运行起来了。




kernel ----》操作系统的核心,实现了操作系统的核心功能。
1) 进程调度,进程管理
2)内存管理功能
3)文件系统
4)网络子系统 .....


rootfs----->根文件系统,保存各种数据文件和程序文件。

Linux操作系统支持各种文件系统

文件系统的概念 

1) 保存各种文件和目录的地方,也就是文件和目录的集合。

2) 实现读写存放在磁盘上各种文件格式的代码
}

//系统移植代码位置   /home/fsa53
{
烧写uboot到fs6818开发板(网络)
1. FS6818# setenv serverip "ip地址" 
2. FS6818# saveenv
3. FS6818# tftp 48000000 ubootpak.bin
4. FS6818# update_mmc 2 2ndboot 0x48000000 0x200 0x555f0     
通过uboot引导操作系统
{
//使用网络文件系统,通过网络启动内核和挂载网络文件系统

//终端软件使用secureCRT
设置bootcmd和bootargs

setenv bootcmd "tftp 48000000 uImage;bootm 48000000"
saveenv
挂载nfs文件系统

setenv bootargs "root=/dev/nfs nfsroot=192.168.1.110:/nfsroot/rootfs rw console=ttySAC0,115200 init=/linuxrc ip=192.168.1.198 loglevel=8"

bootargs=root=/dev/nfs nfsroot=192.168.1.167:/nfsroot/rootfs rw console=ttySAC0,115200 init=/linuxrc ip=192.168.1.157 loglevel=7
}

}

一、什么是系统移植,为什么需要系统移植?

{
1. 嵌入式系统的构成
{
引导加载程序(BootLoader)
包括固化在固件(firmware)中的 boot 代码(可选),和 BootLoader 两大部分。
是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,
从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。
Linux 内核(Kernel)
特定于嵌入式平台的定制内核。
操作系统内核,是指大多数操作系统的核心部分。通常由CPU资源管理、内存管理、文件系统、
设备驱动和网络单元组成。
文件系统(Rootfs)
包括了系统命令和应用程序。
根文件系统,是存放运行、维护系统所必须的各种工具软件、库文件、脚本、
配置文件和其他特殊文件的地方,也可以安装各种软件包。
没有根文件系统,Linux内核只是一个内核,不能和用户交互,也就没有实际的应用价值。		
}

2. 为什么需要系统移植,什么是系统移植?
{
嵌入式的硬件不具备通用性(没有标准化),软件也不能做到标准化
不同硬件嵌入式平台所运行系统是有差异的,没有一个可以直接使用的通用的操作系统
需要我们对系统进行修改(移植)之后才能在目标平台上运行
我们对系统的修改就称之为系统移植
嵌入式系统主流的操作系统是Linux,所以我们系统移植主要研究的就是如何移植Linux

嵌入式系统的本质就是根据实际项目的需要定制的计算机系统。 不同的项目,所采用的CPU,外设资源,对性能的
要求都不一样,通常需要根据实际项目硬件的需要对计算机的软件系统包括(bootloader,kernel 和根文件系统)
进行裁剪,修改和定制,这就是系统移植。

}

3. 系统移植的任务
{
移植Bootloader,通常是移植UBOOT
移植Kernel,裁剪和定制Linux内核
制作根文件系统,定制根文件系统
}


}

二、搭建嵌入式Linux开发,系统移植环境

{
1. NFS(网络文件系统)
{
NFS服务器:NFS服务器可以把服务器上的一个目录导出,从而客户端主机可以通过网络将NFS服务器上
导出的目录挂载到客户端主机的一个目录下,从而实现文件共享。

}

2. 开发环境搭建步骤
{
1) 安装虚拟机vmware12.01, 安装ubuntu16.04

2) 安装tfTP server,安装 NFS 网络文件系统

3) 安装交叉编译器  toolchain-4.5.1-farsight.tar.bz2
#tar xvf toolchain-4.5.1-farsight.tar.bz2  -C /opt
arm-none-linux-gnueabi-gcc -v

4) 设置arm-linux-gcc 交叉编译器路径环境变量

在 /root/.bashrc  脚本文件中添加如下设置 PATH环境变量的命令
export PATH=$PATH:/opt/toolchain-4.5.1-farsight/bin

5) 我们的交叉编译器是32位的,ubuntu是64位的,所以还需要安装如下支持包
#apt-get install lib32z1 lib32ncurses5

6) 安装USB转串口驱动,超级终端,串口调试助手

7) 安装curses开发库
curses是一个在Linux/Unix下广泛应用的图形函数库,在字符终端界面下绘制图形界面。
在源码配置时需要。
$sudo apt-get install libncurses5-dev
}   

3. 烧写uboot到启动tf卡
{
使用 s5p6818-tfmmc.sh 脚本烧写uboot到tf卡
#sudo ./s5p6818-tfmmc.sh /dev/tfb ubootpak.bin

/dev/tfb是读卡器的设备文件
ubootpak.bin是要烧写的Bootloader镜像文件


将拨码开关拨至从tf启动,这样开发板就可以从tf卡启动了。

OM1   OM2   OM3
OFF   OFF   OFF    tf卡启动
ON    OFF   ON     eMMC启动

}

4. 交叉编译工具链使用
{
arm-linux-gcc   编译器
arm-linux-ld    链接器
arm-linux-as    汇编器

arm-linux-objcopy    文件类型转换工具ELF --> binary
例:arm-none-linux-gnueabi-objcopy --gap-fill=0xff  -O binary  u-boot  u-boot.bin

arm-linux-objdump    反汇编工具
例:arm-none-linux-gnueabi-objdump -d nm.o 

arm-linux-strip  删除目标文件中符号链接和调试信息
例:arm-none-linux-gnueabi-strip 	hello

}

}

三、Bootloader的基本概念和系统启动过程

{
1.什么是bootloader
{
简单地说,BootLoader就是在操作系统运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备,
从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。
}

2. bootloader的功能
{
核心功能:
  Bootloader最主要的核心功能就是启动操作系统。Bootloader 引导操作系统主要分以下3个步骤:
初始化最底层的硬件,例如串口,中断等;
搬运内核映像文件到内存中;
将CPU控制权移交给内核执行;
CPU控制权移交给内核后,操作系统开始启动运行,Bootloader的任务就完成了。

其他功能:
Bootloader 除了具备核心的操作系统加载功能外,为了使用方便,我们在Bootloader 中
通常还实现了一些其他的附加功能,通过这些附加功能,可以非常方便我们开发,安装,
调试嵌入式系统软件。Bootloader通常会实现如下功能:

烧写内核和文件系统功能
硬件测试功能
内核启动参数设置功能
设置系统启动界面和公司LOGO
……


}

3. S5P6818启动过程
{
---》芯片上电;

---》当上电后运行的第一个代码就是iROM(20K)里的代码
iROM运行起来后,搬运2ndboot(BL1)和部分u-boot到内部SRAM中; (最大搬运约 56K字节大小)

---》运行内部SRAM中的2nboot和部分u-boot代码
2nboot和部分u-boot代码完成DDR3,时钟,串口和其他基本硬件的初始化,并复制u-boot.bin到DDR3中;

---》在DRAM中运行u-boot.bin。

}

4. Uboot简介
{
U-Boot,全称Universal Bootloader,即通用的Bootloader引导程序,是bootloader的一种 。

U-BOOT的特点
开放源码;
支持多种嵌入式操作系统内核,如Linux、VxWorks ; 
支持多个处理器系列,如PowerPC、ARM、x86、MIPS;
较高的可靠性和稳定性;
高度灵活的功能设置;
丰富的设备驱动源码,如串口、以太网、tfRAM、LCD等;
较为丰富的开发调试文档与强大的网络技术支持;

}

5. uboot常用命令的使用
{
help 帮助命令
帮助命令,查看支持的所有U-Boot命令,并简单描述命令的作用

printenv 或 pri 打印环境变量,常用的环境变量如下:
{		
bootdelay:U-Boot进入自启动模式的倒计时时间
ipaddr:目标机IP地址
serverip:tfTP服务器主机IP地址
bootcmd:U-Boot进入自启动模式后执行的命令
bootargs:传递给Linux内核的启动参数,例如: 
bootargs=root=/dev/nfs nfsroot=192.168.0.10:/source/rootfs rw noinitrd console=ttySAC0,115200 
init=/linuxrc ip=192.168.0.100
{
root:用来指定rootfs的位置,例如
root=/dev/mmcblk0p2 
root=/dev/nfs nfsroot=192.168.0.100:/source/rootfs rw
在指定root=/dev/nfs之后,还需要指定nfsroot=serverip:nfs_dir
即指明文件系统存在那个主机的那个目录下面。

rootfstype:这个选项需要跟root一起配合使用,指明文件系统的类型。
console:Linux内核使用的控制台,用于内核打印信息输出。
initrd, noinitrd:当没有使用ramdisk启动系统的时候,需要使用noinitrd这个参数,
但是如果使用了的话,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,
size表示initrd的大小。
init:指定内核启动挂载好根文件系统后执行的第一个用户空间的初始化程序。
ip:指定系统启动之后目标机的IP地址

}
}

setenv  添加、修改、删除环境变量
{
添加、修改、删除环境变量
setenv name value
添加环境变量:setenv myenv somevalue
修改环境变量:setenv myenv othervalue
删除环境变量:setenv myenv
}

saveenv  保存环境变量到Flash中
{
保存环境变量到固态存储器中,重启后也有效
}

md 内存操作命令
{
md:显示内存区的内容。
mm:修改内存,地址自动递增。
nm:修改内存,地址不递增。
mw:填充内存。
mtest:测试内存。
cp:在不同的内存区间复制内容。
cmp:比较两块内存区间的内容。

//例如
FS6818 # md b 48000000 10
48000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

}

mmc 操作命令
{
mmc list
列出可用的mmc设备

mmcinfo
打印当前mmc设备的信息

mmc part 
显示当前EMMC的所有分区信息

mmc read:读取mmc的相应块到内存中
mmc read addr blk# cnt

addr:读取到内存的位置
blk:读取block位置,这个位置是mmc的0地址的偏移量,是16进制,block单位是512字节。
cnt:读取block个数,要读取到内存的数据大小,是16进制。
如:mmc read 0x48000000 0x800 0x3000
分析:表示从mmc上2048*512字节开始处,读取12288*512字节到内存0x48000000处。

mmc write:将内存中的数据写入到mmc的相应块
mmc write addr blk# cnt

mmc erase blk# cnt
blk: 擦除mmc中block位置,这个位置是mmc的0地址的偏移量,是16进制,block单位是512自己
Cnt:擦除的mmc中block个数,是16进制,block单位是512字节。					
}

boot  bootm  运行内核
{
boot:  运行bootcmd环境变量中的命令

bootm: 运行指定内存中的代码
}

run
{
运行环境变量中的命令
FS6818 # setenv cmd bootm 48000000
FS6818 # run cmd 
}
}


}

四、Uboot移植与源码分析

{
1. u-boot编译,与烧写
{
编译u-boot 
#make distclean    //删除所有的配置和目标文件,之后需要从头配置
#make clean   //删除目标文件

#make fs6818_config  //配置uboot
#make

编译成功后生成 ubootpak.bin 二进制烧写文件

烧写uboot到fs6818开发板
1.  FS6818# setenv serverip "ip地址" 
2.  FS6818# saveenv
3. FS6818# tftp 48000000 ubootpak.bin
4. FS6818# update_mmc 2 2ndboot 0x48000000 0x200 0x555f0        

2:表示第二个mmc设备.
2ndboot:分区名称
0x48000000:源数据地址
0x200:写入分区的偏移位置
0x555f0:要写入的字节数( >=uboot的大小)

}

2. ubootpak.bin组成
{
//u-boot.bin的生成。
u-boot.bin: u-boot FORCE
$(call if_changed,objcopy)
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
$(BOARD_SIZE_CHECK)
./tools/mk6818 ubootpak.bin nsih.txt 2ndboot u-boot.bin

nsih.txt  主要是对芯片一些简单寄存器的配制,包括启动方式,PLL,DDR等,一般不需要改动。
2ndboot   由厂家提供的启动代码


}

3. uboot的源码结构
{
//uboot目录结构 

board:  开发板相关代码,对应不同的开发板
arch :  CPU体系架构相关代码,对应不同的CPU体系架构
common: 与CPU体系结构无关的通用代码
drivers: 外围芯片驱动代码
include: 头文件和开发板配置文件,开发板的配置文件
都放在 include/configs 目录下;
fs:  文件系统相关代码
net: 各种网络协议相关代码

//uboot关键源码分析
start.s 
arch/arm/cpu/slsiap/s5p6818/start.s, 是uboot运行的第一个文件。该文件主要初始化了时钟,
内存,完成了代码重定位。

u-boot.lds
uboot总目录下, 链接器用的链接脚本文件,用来指定映像文件中各段的加载地址和运行地址

include/configs/fs6818.h     
目标开发板FS6818配置文件,与目标板相关的配置文件都存放在该头文件中,
我们进行uboot移植的时候,主要也是针对目标的具体硬件配置,修改该头文件。

board.c
board/s5p6818/fs6818/board.c  
目标板硬件配置相关源码文件,我们对目标板的修改,定制主要修改该源码文件。

bootm.c
arch/arm/lib/bootm.c   引导内核的主要文件,该文件中 do_bootm_linux函数,
是跳转到内核之前运行的最后一个函数,对我们研究内核引导过程及参数传递有很大的意义。

common/cmd_bootm.c 
启动内核主要文件,bootm命令所对应的执行函数do_bootm 就在该文件中,
该函数是我们研究内核启动过程一个很好的入口。
} 
4. uboot源码的启动流程
{
//uboot源码启动
//没有看到tfRAM初始化代码? 在nsih.txt文件中保存了tfRAM的初始化信息

BL1(在CPU内部RAM中运行):
通过查看u-boot.lds链接文件,可以知道uboot的入口在
arch/arm/cpu/slsiap/s5p6818/start.S

--》reset     (执行reset函数)
则禁止中断,进入SVC模式
关看门狗disable watchdog

--》cpu_init_cp15 (关闭TLB、icache、MMU、cache等)
cpu_init_crit (板级初始化,获得cpu的id,加入SMP(多处理器))

--》relocate_to_text (判断代码当前uboot是在内部RAM中运行还是tfRAM中运行?代码重定位)

--》copy_loop_text (如果当前uboot在内部RAM中运行,则从emmc搬运uboot到tfRAM中)

--》从tfRAM重新运行uboot

BL2(在tfRAM中运行):

// BSS段通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域,在程序运行前通常会清0
clear_bss:
--》 mmu_turn_on  //打开MMU

--》 清除bss段

--》设置栈指针寄存器SP

--》board_init_f (common/board_f.c)
{
//gd_t(uboot全局结构体)    bd_t(uboot开发板结构体)
依次执行init_sequence_f函数指针数组(指向一些硬件初始化函数)中保存的函数
如果出错就进入死循环
}

--》gdt_reset  //进一步初始化gd_t(uboot全局结构体)  

--》board_init_r (common/board_r.c)
//依次执行init_sequence_r函数指针数组(指向一些硬件初始化函数)中保存的函数

--》run_main_loop,进入main_loop循环
// 此时uboot进入了主循环,开进入delay倒计时,如果倒计时被打断进入命令行
// 没被打断就去引导内核
}   

5. uboot移植

{
//uboot移植基本思路
分析U-Boot已经支持的开发板,选择出硬件配置最接近的开发板,验证一下这个参考开发板的U-Boot,
修改相应的交叉编译器,至少能够配置编译通过

修改开发板相关配置文件以适应自己开发板的硬件配置;

根据自己开发板的实际硬件配置修改其他相关源码文件

一般修改比较多的配置文件为uboot的目标板配置文件 include/configs/fs6818xxx.h 


//修改交叉编译器为 arm-none-linux-gnueabi-
//修改uboot顶层目录下的Makefile文件
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif

后添加:
ARCH = arm
ifeq (arm,$(ARCH))
CROSS_COMPILE ?= arm-none-linux-gnueabi-
endif
}  
}
五、通过uboot引导操作系统
{
//使用网络文件系统,通过网络启动内核和挂载网络文件系统

//终端软件使用secureCRT
设置bootcmd和bootargs

setenv bootcmd "tftp 48000000 uImage;bootm 48000000"
saveenv
挂载nfs文件系统

setenv bootargs "root=/dev/nfs nfsroot=192.168.1.110:/nfsroot/rootfs rw console=ttySAC0,115200 init=/linuxrc ip=192.168.1.198 loglevel=8"

bootargs=root=/dev/nfs nfsroot=192.168.1.167:/nfsroot/rootfs rw console=ttySAC0,115200 init=/linuxrc ip=192.168.1.157 loglevel=7
}

六、uboot命令系统

{
uboot的命令的代码保存在common文件加下,命令结构U_BOOT_CMD

定义在include/command.h文件

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)  \
U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

name:命令名字************
_maxargs:最大参数个数
_rep:重复执行次数
_cmd:命令对应的处理函数**********
_usage:用法信息
_help:帮助信息

往UBOOT添加一个命令的方法 
1) 编写命令对应的函数
2) 用U_BOOT_CMD 宏定义 定义命令
// 参考cmd_demo.c

//某个命令文件是否被编译取决于对应宏是否被定义
//这些宏定义在配置头文件中(inlcude/configs/fs6818xxx.h)

}

七、将uboot kernel rootfs 固化到emmc上

{
1.将emmc进行分区
{
fdisk 设备序号 分区个数 第二个分区的起始地址:第二个分区的地址 ......

#fdisk 2 2 0x100000:0xa00000 0xb00000:0x40000000

分为2个区:
boot分区:    起始地址  0         大小  0x100000  (1M)
uImage分区: 起始地址:0x100000 大小 0xa00000 (10M)
rootfs分区:  起始地址:0xb00000 大小 0x40000000 (1G)

//写文件系统命令  0xb00
000 / 512 = 0x5800  (将16进制地址 转为 block地址)
FS6818# mmc write 48000000 0x5800 0x12000

文件系统大小:  0x12000 * 512 /1024/1024 = 36M
}

2.将uboot uImage rootfs写入到emcc上
{
//mmc write/read 数据源地址 读写起始扇区数,读写的整体大小(扇区)
FS6818# tftp 48000000 ubootpak.bin
FS6818# update_mmc 2 2ndboot 0x48000000 0x200 0x555f0 

FS6818# tftp 48000000 uImage
FS6818# mmc write 48000000  0x800 0x3000


FS6818# tftp 48000000 rootfs.ext4
FS6818# mmc write 48000000 0x5800 0x12000 

}

3.配置环境变量从emmc加载内核和文件系统
{
//设置 bootcmd 环境变量 
//用超级终端可能 setenv bootcmd "mmc read 48000000 0x800 0x3000\;bootm 48000000"
setenv bootcmd "mmc read 48000000 0x800 0x3000;bootm 48000000"

//设置bootargs环境变量
setenv bootargs "root=/dev/mmcblk0p2 rootfstype=ext4 console=ttySAC0,115200 loglevel=8"
}
}

八、Linux内核及其特点

Linux内核裁剪与移植

{
内核是什么?
操作系统“内核”指的是提供硬件抽象层,磁盘和文件系统控制,多任务管理,硬件设备驱动等功能的一个系统软件,
一个内核不是一个完整的操作系统

通常情况下我们说的Linux操作系统包括Linux内核,工具集,各种库,桌面管理器,应用程序等一起的发布包(发行版)

一些常见的Linux发行版

Debian
Ubuntu
RedHat
centos
Fedora

Linux内核特点 
支持的硬件平台广泛,支持各种CPU及体系架构
支持32bit/64bit平台
可裁剪,可扩展,完全开源免费(GNU)
高稳定性和可靠性
很强网络功能	
对多任务,多用户有很好的支持,多个用户同时登陆系统同时工作
模块化设计,方便修改,更新和调试
遵循GPL开源许可协议
编程方便,资源丰富

Linux内核的主要功能模块

系统调用接口
进程管理
虚拟文件系统和文件子系统
存储管理模块
网络子系统 
设备驱动模块

Linux内核版本信息获取和源码下载
www.kernel.org Linux内核官方网站,获取Linux内核最新的版本信息

}

九、Linux内核源码组织

{
arch: 体系架构相关的代码,不同架构的cpu有相应的子目录。例如:arch/arm
arch/arm/mach-xxx:具体的CPU相关开发板的代码		     
include: 内核头文件保存路径
include/asm是体系结构相关的头文件。
include/linux是Linux内核基本的头文件
kernel: Linux内核的核心代码,包含进程调度子系统,以及和进程调度相关的模块	
mm:  内存管理代码	
fs: 文件系统代码			
net: 网络支持代码,主要是网络协议
samples: 一些内核编程范例
lib: 各种库子程序,例如:zlib, crc32
ipc: 进程间通信的代码
init: Linux系统启动初始化相关的代码,例如 main.c		
drivers: 各种设备驱动程序,例如:drivers/char    drivers/block		
crypto: 加密、压缩、CRC校验算法
sound:声卡驱动
tools:内核工具
}

十、内核的配置、裁剪与编译

{
//内核配置,编译步骤
1.设置交叉编译器
修改内核顶层目录Makefile 约 195行:
ARCH		?= arm
CROSS_COMPILE	?= arm-none-linux-gnueabi-

2. 清除之前的所有配置信息和目标文件
#make distclean

3)对内核进行初始配置
#make fs6818_defconfig

4)使用make menuconfig 对内核进行配置裁剪
#make menuconfig

5) 编译内核
#make zImage    生成一个压缩的内核镜像
#make uImage    生成专用于uboot引导的镜像(需要uboot提供工具)
将uboot源代码目录下tools/mkimage 拷贝到/usr/bin
如何制作根文件系统镜像?
{
mkimage 命令可以制作根文件系统镜像

bootm命令是:
用来引导经过u-boot的工具mkimage打包后的kernel image的,
什么叫做经过u-boot的工具mkimage打包后的kernel image,
这个就要看mkimage的代码,看看它做了些什么,

mkimage的用法:
uboot源代码的tools/目录下有mkimage工具,
这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。

mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,
记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,
哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么
}


//内核编译过程

xxx.c ---- >xxx.o  //将所有的源码文件编译成相应的目标文件 .o

同一个目录下的.o ----> built-in.o

子目录下built-in.o  ---->vmlinux.o

vmlinux.o 链接 ------> vmlinux(ELF)

vmlinux(ELF) ---objcopy---->Image(非压缩的内核镜像) (50 ~60M)

Image -----压缩----->zImage(压缩的内核镜像) (3 ~5M)

zImage -----mkimage---->uImage

******	内核的镜像在arch/arm/boot目录下 *****

编译内核的时候,什么? 将哪些文件编译进内核 ?

答:Makefile 来决定 将哪些文件编译进内核.

裁剪内核的 第一个方法: 直接修改内核的Makefile

为了让我们更好的配置和裁剪内核,前辈就做了一个内核配置工具---->简化内配置

通过内核配置工具,我们配置内核就简单,向去吃饭点菜一样简单。

}

十一、Linux内核配置原理分析

{

1. 内配配置命令 make menuconfig 
{   
//make menuconfig 作用
读取arch/$(ARCH)/Kconfig文件生成配置主界面
每个子目录中的Kconfig生成对应子目录源文件的配置界面

顶层目录的Makefile  
%config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config  //--(1) 创建响应的目录
$(Q)$(MAKE) $(build)=scripts/kconfig $@    //--(2)   

/*
$@  Makefile 预定义变量,表示目标的完整名称 --》menuconfig

make  scripts/kconfig  menuconfig

*/

//$@ 目标完整名称  scripts/kconfig 总目录下的子目录
(2)命令的意思是 执行 scripts/kconfig 下Makefile 的 menuconfig 规则

我们查看 	scripts/kconfig/Makefile 文件
menuconfig: $(obj)/mconf   //mconf就是一个内核配置管理工具(可执行文件)
$< $(Kconfig)     //$< 为第1个依赖文件,即$(obj)/mconf   Kconfig的值为arch/arm/Kconfig


$(obj)/mconf   arch/arm/Kconfig

//这条命令的意思就是读取内核配置文件 kconfig文件,并显示可配置的信息,给我们进行内核配置。

几乎每个目录都有kconfig文件, arch/arm/Kconfig文件就属于总的kconfig 文件


}

2. Kconfig文件语法分析
{
1) config 定义一个 .config 配置条目
{
config EXT2_FS_XATTR  //定义一个变量,系统会在变量名前加前缀CONFIG_
变量名:CONFIG_EXT2_FS_XATTR

a.变量类型,后面的字符串就是该条目在配置界面的文字

bool:布尔型变量(y/n)
tristate:三态型变量(y/m/n)
string:字符串
int:整型
hex:十六进制

b.depends on:依赖关系

如果被依赖的配置没有被选中,此选项将不会出现

c.help:帮助信息

例如:
config FS_ADC
bool "adc driver for farsight FS6818 all platform"  //字符串为配置界面显示的文字
default y    //默认选中该配置项
help
different sensor select different channel


}

2) menu 用于定义一个配置菜单,包含很多配置条目
{
例如:

menu "lzembed device driver support"

comment "Guohui Cao Kconfig Example"

config LZ_HELLO1
bool "hello1 module for lzembed platform"
default y
help
kconfig file test1

config LZ_HELLO2
bool "hello2 module for lzembed platform depend hello1"
depends on LZ_HELLO1
default y
help
kconfig file test2
endmenu
}

3) choice 将多个类似的配置选项组合在一起,供用户单选或多选
{  //choice 用法
choice
prompt "hello module select"
default LZ_HELLO1

config LZ_HELLO1
bool "hello1 module for lzembed platform"
help
kconfig file test1

config LZ_HELLO2
bool "hello2 module for lzembed platform depend hello1"
help
kconfig file test2

endchoice
}

4) select //该配置项被选中时,该配置项后面的select 配置项会自动被选中
{
menu  "lzembed device driver support"

config LZ_HELLO1
bool "hello1 module for lzembed platform"
default y
select LZ_HELLO2   //选中 LZ_HELLO1 配置项时, LZ_HELLO2 LZ_HELLO3 会自动选中
select LZ_HELLO3
help
kconfig file test1

config LZ_HELLO2
bool "hello2 module for lzembed platform depend hello1"
depends on LZ_HELLO1
default n
help
kconfig file test2

config LZ_HELLO3
bool "hello3 module for lzembed platform depend hello1"
depends on LZ_HELLO1
default n
help
kconfig file test2

config LZ_HELLO4
bool "hello4 module for lzembed platform depend hello1"
depends on LZ_HELLO1
default n
help
kconfig file test2

endmenu

}

5) source 用于读取另一个Kconfig文件,如:
source "lzembed/Kconifg"

6)comment  用于定义一些帮助信息,出现在界面的第一行
{
menu "lzembed device driver support"

comment "Guohui Cao Kconfig Example"  //定义帮助信息,出现在配置界面第1行

config LZ_HELLO1
bool "hello1 module for lzembed platform"
default y
help
kconfig file test1

endmenu
}

}

3. .config  内核配置文件
{
用于保存make menuconfig配置结果,Makefile通过读取.config来决定某个源文件是否编译进内核
make menuconfig是通过Kconfig生成配置界面,配置完成后配置结果保存在.config文件中,
makefile编译时读取.config文件中变量的值来决定某个文件是否编译进内核

在配置界面这可以直接搜变量名来找到变量对应的条目,
也可以在配置选项的帮助信息中找到变化所在的Kconfig和使用它的Makefile

}

4. Linux 内核Makefile文件分析
{
Makefile用于控制如何进行编译

a.顶层Makefile(复杂)

确定编译器

ARCH = arm

CROSS_COMPILE = arm-linux-

编译哪些目录的文件

508 # Objects we will link into vmlinux / subdirs we need to visit
509 init-y          := init/
510 drivers-y       := drivers/ sound/ firmware/
511 net-y           := net/
512 libs-y          := lib/
513 core-y          := usr/

711 core-y          += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

编译的分块处理和链接文件

757 vmlinux-init := $(head-y) $(init-y)
758 vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
759 vmlinux-all  := $(vmlinux-init) $(vmlinux-main)
760 vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds

b.子目录中的Makefile(简单)

用于控制本目录下的源文件是否编译

obj-$(xxx) += yyy.o

obj-y  -------> 编译进内核

obj-m --------> 编译成模块

obj-n --------> 不编译

某个源文件是否被编译,如何编译取决于对应变量CONFIG_xxx的取值

变量CONFIG_xxx的值保存在.config中

}

}

十二、Linux内核启动流程分析

{
1 内核启动流程
{
内核自解压
进行内核各部分的初始化
挂载根文件系统
启动init进程  --> 祖宗进程
}

2 内核启动过程源码分析
{
a. 自解压: arch/arm/boot/compressed/head.S
{
//putstr("Uncompressing Linux...");
//putstr(" done, booting the kernel.\n");
bl	decompress_kernel

bl	cache_clean_flush
bl	cache_off
}

b. 内核入口: arch/arm/kernel/head.S
{
//获取CPUID 和 内核的CPUID进行比较,查看是否匹配
bl	__lookup_processor_type
bl  __create_page_tables  //创建页表

ldr r13, =__mmap_switched      //使能mmu之后调用

b __enable_mmu     //使能mmu

}

c. 执行 arch/arm/kernel/head-common.S
{
__mmap_switched:

b start_kernel    //进入内核的第一个C语言函数,相当于main函数
}

d. 执行 init/main.c
{

void __init start_kernel(void)

--》setup_arch(&command_line);//处理传递过来的参数 arch/arm/kernel/setup.c
mdesc = setup_machine_fdt(__atags_pointer);//搜索内核中与之匹配的机器

console_init();//初始化console

rest_init();

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //创建内核线程

kernel_init:    

--》prepare_namespace();
mount_root();//挂载指定的根文件系统
init_post();

--》run_init_process(ramdisk_execute_command);//启动用户空间的init进程,祖宗进程
}
}

3 内核移植通常需要修改的文件
{   
arch/arm/mach-xxx(s5p6818)
arch/arm/plat-xxx(s5p6818)
}

}

十三、内核和驱动调试方法

{
1.  点灯大法

2. 通过printk 输出调试信息, console_init()函数之后才可以使用
{
//内核打印级别, 数字越小,打印级别越高
#define KERN_EMERG        "<0>" /* system is unusable */
#define KERN_ALERT        "<1>" /* action must be taken immediately */
#define KERN_CRIT         "<2>" /* critical conditions */
#define KERN_ERR          "<3>" /* error conditions */
#define KERN_WARNING      "<4>" /* warning conditions */
#define KERN_NOTICE       "<5>" /* normal but significant condition */
#define KERN_INFO         "<6>" /* informational */
#define KERN_DEBUG        "<7>" /* debug-level messages */


//打印级别的设置  Linux的日子级别在内核 kernel/printk.c 文件中被初始化

//控制台日志级别:优先级高于该值的消息将被打印至控制台
DEFAULT_CONSOLE_LOGLEVEL

//默认消息打印级别, 指定日志级别的printk() 采用的默认级别是DEFAULT_MESSAGE_LOGLEVEL
DEFAULT_MESSAGE_LOGLEVEL

//最低的控制台日志级别,控制台日志级别可被设置的最小值(最高优先级)
MINIMUM_CONSOLE_LOGLEVEL     /* minimum_console_loglevel */

//默认的控制台日志级别:控制台日志级别的缺省值
DEFAULT_CONSOLE_LOGLEVEL      /* default_console_loglevel */


通过命令查看Linux内核各种日志级别:  #cat /proc/sys/kernel/printk
8                   7                   1                        7
控制台日志级别   默认消息打印级别    最低的控制台日志级别     默认的控制台日志级别


也通过uboot参数loglevel 传递控制台日志级别给内核
例如: 传递控制台打印日志级别为 8 ,所有级别高于该级别的消息都被打印到控制台
bootargs=root=/dev/mmcblk0p2 rootfstype=ext4 console=ttySAC0,115200 loglevel=8

}

3. 查看内核奔溃的OOPS信息
{
产生oops信息的原因:

内存非法访问
非法指令
访问NULL指针
使用不正确的指针的值

oops的内容:

CPU中寄存器的值
页表的位置
其他信息

例如: 空指针导致内核崩溃的  oops信息

Unable to handle kernel NULL pointer dereference at virtual address 00000000
[    2.143000] pgd = c0004000
[    2.146000] [00000000] *pgd=00000000
[    2.150000] Internal error: Oops: 805 [#1] PREEMPT SMP ARM
[    2.155000] Modules linked in:
[    2.158000] CPU: 6    Not tainted  (3.4.39-farsight #2)
[    2.163000] PC is at led_init+0x1c/0xc0
[    2.167000] LR is at do_one_initcall+0x34/0x17c
[    2.172000] pc : [<c030e018>]    lr : [<c0008710>]    psr: 60000113
[    2.172000] sp : e3043f80  ip : 60000113  fp : 00000000
[    2.183000] r10: c09c1b54  r9 : 000001ae  r8 : c030dffc
[    2.188000] r7 : 00000000  r6 : e3042000  r5 : 00000000  r4 : c0a54b00
[    2.195000] r3 : 0000005a  r2 : c08b60ec  r1 : 00000001  r0 : 1f400000
[    2.201000] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
[    2.209000] Control: 10c5383d  Table: 4000406a  DAC: 00000015

}

}

十四、嵌入式Linux根文件系统基本概念

{
1. 什么是根文件系统?
根文件系统(root  filesystem)是存放运行、维护系统所必须的各种工具软件、库文件、
脚本、配置文件和其他特殊文件的地方,也可以安装各种软件包。

因该文件系统挂接在 "/" 根目录下,因此称为根文件系统。


2. 根文件系统基本组成
{
/bin    保存linux基本命令对应的可执行程序
/dev 	  块、字符设备节点文件
/etc  	主要配置文件和初始化执行文件
/home   用户目录
/lib  	基本的库文件,例如C库,和内核模块(modules)
/mnt  	挂载点,临时挂载文件系统用
/opt  	额外安装的附加软件包
/proc 	虚拟文件系统,用于内核和进程通讯
/sbin   基本的系统管理程序
/usr  	更多的用户程序,包括X server
/sys  	虚拟文件系统 sysfs 挂载点
/var  	可变信息储存,如log等
/tmp 		临时文件目录
}   

挂载tf卡到 mnt目录下
mount -t vfat /dev/mmcblk1p1 /mnt

}

十五、构建嵌入式Linux根文件系统

{
1. 构建根文件系统步骤
{
采用Busybox创建基本命令
创建基本的目录 /lib /etc /var /tmp /dev /sys /proc等
添加glibc基本动态库
创建基本的设备节点
添加启动配置和脚本程序 /etc/inittab   /etc/fstab /etc/init.d/rcS
测试根文件系统
制作根文件系统镜像
}

2. 移植busybox
{
Busybox是一个UNIX系统工具集,它将很多普通的UNIX工具集成到一个很小的可执行文件中,
为普通用户提供大多数常用的命令;
BusyBox维护的主要指令包括   
cat, chmod, chown, cp, chroot, copi, date, dd, df, dmesg, 
dos2unix, du, echo, env, expr, find, grep, gunzip, gzip, halt,
id, ifconfig, init, insmod
我们要用的大部分常用命令,busybox 都提供。

a. 解压busybox 源码包  解压bz2压缩文件,用tar jxvf
#tar jxvf busybox-1.22.1.tar.bz2

b. 修改顶层目录Makefile 约 164行处,设置交叉编译器 ,如下所示
CROSS_COMPILE ?= arm-none-linux-gnueabi-

c.  配置,编译安装busybox
#make menuconfig 配置busybox选项
#make 
#make install 

make install安装完成后,在busybox/_install目录下 就保存了 busybox生成的
基本应用工具集合。包括: bin  sbin  usr 目录 和 linuxrc 链接文件。	 	
}

3. 创建根文件系统
{
a. 创建根文件系统目录 rootfs
b. 复制busybox 安装目录 _install中的文件和文件夹到 rootfs中;
cp -r /busybox/busybox-1.22.1/_install/* ./         */

c. 创建dev目录,并在dev目录下创建 console ,null 设备文件

#mknod  console  c   5    1
#mknod  null     c   1    3

d. 创建lib目录,复制交叉编译器目录下的lib库到该目录
cp -r /opt/toolchain-4.5.1-farsight/arm-none-linux-gnueabi/libc/lib/* ./lib/         */


e. 创建根文件系统的其他目录和文件
{
创建 etc 目录
---》在etc目录下新建 inittab 文件,
inittab文档中每一行的语法格式为: <id>:<runlevels>:<action>:<process>
{
id:控制台,省略
runlevels:运行级别,省略
action:表示控制这个子程序的运行方式,一共8种
{
sysinit:表示系统启动后最先执行,只执行一次,init进程会等待他执行结束后
再执行其他动作
wait:执行完sysinit后执行,只执行一次
once:执行完wait后执行,只执行一次
respawn:执行完once后执行,init进程监控子程序,当子程序退出时重新启动
askfirst:执行完respawn后执行,行为和respawn类似,不同的是会多打印一个提示信息
shutdown:关机时执行
restart:重启时执行
ctrlaltdel:按下ctrl+alt+del时执行
}

process:要执行的程序和参数,可以是程序,也可以是脚本,
如果是交互的程序,需要在前面加-
}		

inittab文件内容
{ //inittab 文件内容如下
::sysinit:/etc/init.d/rcS 
::askfirst:-/bin/sh
::restart:/sbin/init 
::ctrlaltdel:/sbin/reboot
}

---》在etc目录下新建 init.d目录,在init.d 目录下新建启动脚本文件rcS
{ //启动脚本文件内容
#!/bin/sh 
/bin/mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug 
/sbin/mdev -s
}

---》在etc目录下新建fstab 文件
{
mount -a命令会挂载虚拟文件系统,需要配置文件/etc/fstab

#device mount-point 	type		 options 	dump 	fsck order
proc   	/proc 			proc  	defaults 	 	0  			0
tmpfs  	/tmp  			tmpfs 	defaults  	0  			0
sysfs  	/sys   			sysfs 	defaults 	 	0  			0
tmpfs  	/dev   			tmpfs 	defaults 	 	0  			0
}  	       
}


}

}

十六、创建ext4 根文件系统

{

1.	在ubuntu用户目录下新建目录rootfs_tmp文件,用于临时挂载文件系统:
#mkdir -p rootfs_tmp

2. 制作一个32M(32x1024=32768)的ext4空白文件:
#dd if=/dev/zero of=rootfs.ext4 bs=1024 count=32768

3. 将新建的rootfs.ext4文件格式化为ext4格式:
#mkfs.ext4 rootfs.ext4

4. 将rootfs.ext4文件挂载到前面我们新建的临时目录rootfs_tmp,注意这里我们要使用mount –o loop的属性,
表示我们要把rootfs.ext4当作硬盘分区挂载到rootfs_tmp	
#mount -o loop rootfs.ext4 ./rootfs_tmp

5. 复制根文件系统的内容到 rootfs_tmp文件夹中

6. 卸载挂载的rootfs.ext4文件,即完成了文件系统的制作
#umount ./rootfs_tmp

这样就完成ext4格式的rootfs文件系统的制作。

}

Makefile简单编写
{
obj-y = init.o  ip.o 
obj-y += hello.o

uImage: $obj-y
gcc -o uImage $obj-y

(hello1.c hello2.c hello3.c hello4.c)
hello --> hello1.o hello2.o hello3.o hello4.o

//------------------------------------------------
obj = hello1.o hello2.o hello3.o hello4.o

hello: $obj
gcc -o hello $obj
}
ARM ---烧写uboot
{
上位机:Window + linux虚拟机
下位机:fs6818开发板
1)制作启动 tf卡 
要求 分区格式必须是 FAT32 
制作完成 用windows 打开 tf卡 里面什么都没有 
2)将开发板的 uart0 与 上位机的uart0 连接起来(串口线) 
3)将开发板的 网络接口RJ45 与 上位机的一个网口相连 
4)在上位机上 windows系统中 按照 secureCRT 软件 
(解压 并将可执行程序发送一个快捷方式到桌面 .reg) 
5)打开secureCRT 软件新建一个 串口连接 
协议 serial 
波特率 115200 
流控 全部取消 
端口 com1 
6)启动开发板 在倒计时 时候 按 一个空格键 
可以进入到 x6818 的uboot 中 
x6818# 
7)将上位机中与开发板相连的网口的ip地址设置为 
192.168.1.168 
对应的网络连接改名 叫ARM 
8)在下位机中输入 ping 192.168.1.168 
观察网络是否联通 
正常        live 
不正常    not live 
9)设置uboot 中 服务器的ip地址 以及开发板的ip地址 
setenv    severip 192.168.1.8 
setenv    ipaddr    192.168.1.6 
save 
10) 进入虚拟机 
①创建有线网络连接 命名TPAD  
ip地址为192.168.1.8 
子网掩码255.255.255.0 
网关    192.168.1.1 
②将虚拟机的网络模式选择为 桥接模式 
虚拟机->设置->网络设置->桥接 并勾选已连接选项 
③设置 虚拟网络设置 
编辑->虚拟网络编辑器->桥接到->不能选自动 一定要选一块网卡 
11)测试 从下位机到 服务器(虚拟机)的网络是否ok 
ping 192.168.1.8 
正常        live 
不正常    not live 
如果还不通 请将虚拟网络编辑器中 选中 另一块网口 

12)在虚拟linux系统中创建目录 
sudo mkdir /tftpboot 
sudo chown tarena /tftpboot 
sudo chgrp tarena /tftpboot 

13)在windows 和linux 系统之间 建立共享目录 
虚拟机->设置->选项->共享文件夹-> 
选中总是启用 
将文件夹框中的所有内容 一律删除 
选择添加  
将windows 桌面 添加为共享目录 
并命名为 desk 
14)将ubootpak.bin 拷贝到windows桌面 
在linux中 执行命令 将ubootpak.bin 拷贝到tftpboot目录中 
cp /mnt/hgfs/desk/ubootpak.bin  /tftpboot 
15) 将ubootpak.bin 下载到下位机的内存中 
tftp 48000000 ubootpak.bin 
如果下载不成功 重新启动 tftpd-hpa服务 
sudo /etc/init.d/tftpd-hpa restart 
或者  
sudo sevice tftpd-hpa restart 
16) 将ubootpak.bin烧录到 emmc(硬盘)中 
update_mmc 2 2ndboot 48000000 0x200 0x78000 
拔掉tf卡 
重启开发板 
出现 
tarena#  
说明烧写成功 

}
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

相信你可以

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值