android编译和uboot-kernel流程


0.翻译google docs:
http://source.android.com/source/downloading.html
1.下载源代码
1).安装Repo
Repo是一种工具,用来更好的包装Git来管理android代码。更多Repo信息参见Version Control. http://source.android.com/source/version-control.html
下面按步骤安装,初始化,以及配置Repo:
首先保证有一个bin/目录在用户主目录,并且包含在path路径:
$ mkdir ~/bin
$ PATH=~/bin:$PATH
第二步下载Repo脚本并且保证它可以执行:
$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
curl -k 可以避免ssl验证错误
$ chmod a+x ~/bin/repo
注意对于版本1.17,repo的SHA-1校验码是 ddd79b6d5a7807e911b524cb223bc3544b661c28

2).初始化一个Repo客户端
安装好Repo后,设置你的客户端来访问android源码仓库:
首先创建一个空的文件夹,用来保存工作目录:
$ mkdir WORKING_DIRECTORY
$ cd WORKING_DIRECTORY
第二步运行repo init 来下载最新的Repo以及bug补丁.你必须指定manifest的URL,这个manifest指明各个代码仓库位于android源码将会放在本工作目录.
$ repo init -u https://android.googlesource.com/platform/manifest
如果下载一个分支而不是主分支代码,使用 -b:
$ repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r3
当提示时候,配置用户名字和email地址.
初始化完成后目录里面有.repo文件夹,包含了manifest.

3).获取代码文件
$ repo sync

4).使用授权
默认代码访问限制一个IP对应一个匿名用户,如果共享IP的用户可以使用授权访问,
第一步创建密码https://android.googlesource.com/new-password,并且保存到~/.netrc
第二步强制使用授权访问代码,在URL加入/a/目录
$ repo init -u https://android.googlesource.com/a/platform/manifest

5).网络问题
如果使用代理访问,可能需要明确设置代理:
$ export HTTP_PROXY=http://<proxy_user_id>:<proxy_password>@<proxy_server>:<proxy_port>
$ export HTTPS_PROXY=http://<proxy_user_id>:<proxy_password>@<proxy_server>:<proxy_port>
若Linux系统用户出现不能下载,可能需要设置TCP/IP 缓存和非多线程下载:
$ sudo sysctl -w net.ipv4.tcp_window_scaling=0
$ repo sync -j1
cygwin环境下如果SSL链接有问题:安装keys
$ cd /usr/ssl/certs
$ curl http://curl.haxx.se/ca/cacert.pem | awk ‘split_after==1{n++;split_after=0} /—–END CERTIFICATE—–/ {split_after=1} {print > “cert” n “.pem”}’
$ c_rehash

6).使用本地镜像
首先创建镜像:
$ export PATH=~/bin:$PATH
$ mkdir -p /usr/local/aosp/mirror
$ cd /usr/local/aosp/mirror
$ repo init -u https://android.googlesource.com/mirror/manifest –mirror
$ repo sync
第二步使用镜像:
$ mkdir -p /usr/local/aosp/master
$ cd /usr/local/aosp/master
$ repo init -u /usr/local/aosp/mirror/platform/manifest.git -b android-4.1.1_r3
$ repo sync

注意更新要先更新镜像:
$ cd /usr/local/aosp/mirror
$ repo sync
$ cd /usr/local/aosp/master
$ repo sync

可以把镜像放在局域网,通过NFS,SSH,Git来访问.

7).验证Git Tags
$ gpg –import
copy and paste key below,最后ctrl + D结束.
—–BEGIN PGP PUBLIC KEY BLOCK—–
Version: GnuPG v1.4.2.2 (GNU/Linux)

mQGiBEnnWD4RBACt9/h4v9xnnGDou13y3dvOx6/t43LPPIxeJ8eX9WB+8LLuROSV
lFhpHawsVAcFlmi7f7jdSRF+OvtZL9ShPKdLfwBJMNkU66/TZmPewS4m782ndtw7
8tR1cXb197Ob8kOfQB3A9yk2XZ4ei4ZC3i6wVdqHLRxABdncwu5hOF9KXwCgkxMD
u4PVgChaAJzTYJ1EG+UYBIUEAJmfearb0qRAN7dEoff0FeXsEaUA6U90sEoVks0Z
wNj96SA8BL+a1OoEUUfpMhiHyLuQSftxisJxTh+2QclzDviDyaTrkANjdYY7p2cq
/HMdOY7LJlHaqtXmZxXjjtw5Uc2QG8UY8aziU3IE9nTjSwCXeJnuyvoizl9/I1S5
jU5SA/9WwIps4SC84ielIXiGWEqq6i6/sk4I9q1YemZF2XVVKnmI1F4iCMtNKsR4
MGSa1gA8s4iQbsKNWPgp7M3a51JCVCu6l/8zTpA+uUGapw4tWCp4o0dpIvDPBEa9
b/aF/ygcR8mh5hgUfpF9IpXdknOsbKCvM9lSSfRciETykZc4wrRCVGhlIEFuZHJv
aWQgT3BlbiBTb3VyY2UgUHJvamVjdCA8aW5pdGlhbC1jb250cmlidXRpb25AYW5k
cm9pZC5jb20+iGAEExECACAFAknnWD4CGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIX
gAAKCRDorT+BmrEOeNr+AJ42Xy6tEW7r3KzrJxnRX8mij9z8tgCdFfQYiHpYngkI
2t09Ed+9Bm4gmEO5Ag0ESedYRBAIAKVW1JcMBWvV/0Bo9WiByJ9WJ5swMN36/vAl
QN4mWRhfzDOk/Rosdb0csAO/l8Kz0gKQPOfObtyYjvI8JMC3rmi+LIvSUT9806Up
hisyEmmHv6U8gUb/xHLIanXGxwhYzjgeuAXVCsv+EvoPIHbY4L/KvP5x+oCJIDbk
C2b1TvVk9PryzmE4BPIQL/NtgR1oLWm/uWR9zRUFtBnE411aMAN3qnAHBBMZzKMX
LWBGWE0znfRrnczI5p49i2YZJAjyX1P2WzmScK49CV82dzLo71MnrF6fj+Udtb5+
OgTg7Cow+8PRaTkJEW5Y2JIZpnRUq0CYxAmHYX79EMKHDSThf/8AAwUIAJPWsB/M
pK+KMs/s3r6nJrnYLTfdZhtmQXimpoDMJg1zxmL8UfNUKiQZ6esoAWtDgpqt7Y7s
KZ8laHRARonte394hidZzM5nb6hQvpPjt2OlPRsyqVxw4c/KsjADtAuKW9/d8phb
N8bTyOJo856qg4oOEzKG9eeF7oaZTYBy33BTL0408sEBxiMior6b8LrZrAhkqDjA
vUXRwm/fFKgpsOysxC6xi553CxBUCH2omNV6Ka1LNMwzSp9ILz8jEGqmUtkBszwo
G1S8fXgE0Lq3cdDM/GJ4QXP/p6LiwNF99faDMTV3+2SAOGvytOX6KjKVzKOSsfJQ
hN0DlsIw8hqJc0WISQQYEQIACQUCSedYRAIbDAAKCRDorT+BmrEOeCUOAJ9qmR0l
EXzeoxcdoafxqf6gZlJZlACgkWF7wi2YLW3Oa+jv2QSTlrx4KLM=
=Wi5D
—–END PGP PUBLIC KEY BLOCK—–
在导入keys后就可以这样验证git tags
$ git tag -v TAG_NAME

8).使用ccache和分布式编译

9).注意froyo以及较早版本使用java 1.5,gingerbread以及以后版本使用java 1.6编译.

1.6410的fs的编译makefile什么原理?
源码根目录下只有Makefile文件,内容是include build/core/main.mk,所以make命令首先进入build/core/main.mk.

l 初始化相关的参数设置(buildspec.mk、envsetup.mk、config.mk)
l 检测编译环境和目标环境
l 决定目标product
l 读取product的配置信息及目标平台信息
l 清除输出目录
l 检查版本号
l 读取Board的配置
l 读取所有Module的配置
l 根据配置产生必要的规则(build/core/Makefile)
l 生成image

在build/core/main.mk里面安排了以下主要编译流程:
1).初始化参数设置
-include $(BUILD_SYSTEM)/config.mk
——————————————config.mk——————————————
其中设置了源文件的一系列路径,包括头文件、库文件、服务、API已经编译工具的路径。(前36行)
从40行开始,定义一些编译模块的生成规则:
这里面除了第一个CLEAR_VARS外,其他都对应的一种模块的生成规则,每一个module都会来include其中某个来生成目标模块。
例如:Camera模块的makefile里(Android.mk)就包含了其中的一种生成规则BUILD_PACKAGE:也就是Camera会按照package.mk里的生成规则去生成目标模块。
回到config.mk,接着会尝试读取buildspec.mk的设置:
-include $(TOPDIR)buildspec.mk
如同注释所说,会尝试查找buildspec.mk,如果文件不存在会自动使用环境变量的设置,如果仍然未定义,会按arm默认的设置去build。
这里的buildspec.mk可以自己创建,也可以将原先build/下的buildspec.mk.default直接命名为buildspec.mk并移到根目录。
实际上,buildspec.mk配置都被屏蔽了,我们可以根据需要直接打开和修改一些变量。在这里我们可以加入自己的目标产品信息:
ifndef TARGET_PRODUCT
TARGET_PRODUCT:=mobot
endif
以及输出目录设置:OUT_DIR:=$(TOPDIR)mobot
2).读取Product的设定
回到config.mk,接着进行全局变量设置,进入envsetup.mk:
include $(BUILD_SYSTEM)/envsetup.mk
——————————————envsetup.mk——————————————
里面的大部分函数都在build/envsetup.sh中定义。
include $(BUILD_SYSTEM)/version_defaults.mk
首先,设置版本信息,(11行)在build/core/version_defaults.mk中具体定义平台版本、SDK版本、Product版本,我们可以将BUILD_NUMBER作为我们产品mobot的version信息,当然,也可以自定义一个版本变量。
回到envsetup.mk,接着设置默认目标产品(generic),这里由于我们在buildspec.mk里设置过TARGET_PRODUCT,事实上这个变量值为mobot。
include $(BUILD_SYSTEM)/product_config.mk
然后读取product的设置(41行),具体实现在build/core/product_config.mk中,
include $(BUILD_SYSTEM)/product.mk
进而进入product.mk,
define _find-android-products-files
$(shell test -d device && find device -maxdepth 6 -name AndroidProducts.mk) \
$(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk) \
$(SRC_TARGET_DIR)/product/AndroidProducts.mk
endef
从build/target/product/AndroidProducts.mk中读出PRODUCT_MAKEFILES,这些makefile各自独立定义product,而我们的产品mobot也应添加一个makefile文件mobot.mk。在mobot.mk中我们可以加入所需编译的PRODUCT_PACKAGES。
回到envsetup.mk,下面为HOST配置信息及输出目录,最后打印相关信息:
ifneq ($(PRINT_BUILD_CONFIG),)
$(info ============================================)
$(info PLATFORM_VERSION_CODENAME=$(PLATFORM_VERSION_CODENAME))
$(info PLATFORM_VERSION=$(PLATFORM_VERSION))
$(info TARGET_PRODUCT=$(TARGET_PRODUCT))
$(info TARGET_BUILD_VARIANT=$(TARGET_BUILD_VARIANT))
$(info TARGET_SIMULATOR=$(TARGET_SIMULATOR))
$(info TARGET_BUILD_TYPE=$(TARGET_BUILD_TYPE))
$(info TARGET_BUILD_APPS=$(TARGET_BUILD_APPS))
$(info TARGET_ARCH=$(TARGET_ARCH))
$(info HOST_ARCH=$(HOST_ARCH))
$(info HOST_OS=$(HOST_OS))
$(info HOST_BUILD_TYPE=$(HOST_BUILD_TYPE))
$(info BUILD_ID=$(BUILD_ID))
$(info ============================================)
endif
3).读取BoardConfig
接着回到config.mk,(114行)这里会搜索所有的BoardConfig.mk,主要有以下两个地方:
# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
# or under vendor/*/$(TARGET_DEVICE). Search in both places, but
# make sure only one exists.
# Real boards should always be associated with an OEM vendor.
board_config_mk := \
$(strip $(wildcard \
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
device/*/$(TARGET_DEVICE)/BoardConfig.mk \
vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
))
这里的TARGET_DEVICE就是mobot,就是说为了定义我们自己的产品mobot,我们要在build/target/board下添加一个自己的目录mobot用来加载自己的board配置。
在BoardConfig.mk中会决定是否编译bootloader、kernel等信息。
4).读取所有Module
结束全局变量配置后,回到main.mk,马上对编译工具及版本进行检查,错误便中断编译。
# Bring in standard build system definitions.
include $(BUILD_SYSTEM)/definitions.mk
142行,包含文件definitions.mk,这里面定义了许多变量和函数供main.mk使用。main.mk第446行,这里会去读取所有的Android.mk文件:
ifneq ($(ONE_SHOT_MAKEFILE),)
# We’ve probably been invoked by the “mm” shell function
# with a subdirectory’s makefile.
include $(ONE_SHOT_MAKEFILE)
这个ONE_SHOT_MAKEFILE是在前面提到的envsetup.sh的mm(envsetup.mk)函数中赋值的:
ONE_SHOT_MAKEFILE=$M make -C $T files $@
而M=$(findmakefile),最终实现在:envsetup.sh
function findmakefile()
{
TOPFILE=build/core/envsetup.mk
# We redirect cd to /dev/null in case it’s aliased to
# a command that prints something as a side-effect
# (like pushd)
local HERE=$PWD
T=
while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != “/” \) ]; do
T=$PWD
if [ -f “$T/Android.mk” ]; then
echo $T/Android.mk
cd $HERE > /dev/null
return
fi
cd .. > /dev/null
done
cd $HERE > /dev/null
}
回到main.mk,最终将遍历查找到的所有子目录下的Android.mk的路径保存到subdir_makefiles变量里(main.mk里的470行):
# Change CUSTOM_MODULES to include only modules that were
# defined by this makefile; this will install all of those
# modules as a side-effect. Do this after including ONE_SHOT_MAKEFILE
# so that the modules will be installed in the same place they
# would have been with a normal make.
CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS)))
FULL_BUILD :=
# Stub out the notice targets, which probably aren’t defined
# when using ONE_SHOT_MAKEFILE.
NOTICE-HOST-%: ;
NOTICE-TARGET-%: ;

else # ONE_SHOT_MAKEFILE

#
# Include all of the makefiles in the system
#

# Can’t use first-makefiles-under here because
# –mindepth=2 makes the prunes not work.
subdir_makefiles := \
$(shell build/tools/findleaves.py –prune=out –prune=.repo –prune=.git $(subdirs) Android.mk)

include $(subdir_makefiles)
endif # ONE_SHOT_MAKEFILE

# ——————————————————————-
# All module makefiles have been included at this point.
# ——————————————————————-
我们在package/apps下每个模块根目录都能看到Android.mk,里面会去定义当前本地模块的Tag:LOCAL_MODULE_TAGS,Android会通过这个Tag来决定哪些本地模块会编译进系统,通过PRODUCT和LOCAL_MODULE_TAGS来决定哪些应用包会编译进系统。(前面说过,你也能通过buildspec.mk来制定你要编译进系统的模块)
这个过程在mian.mk的445行开始,最后需要编译的模块路径打包到ALL_DEFAULT_INSTALLED_MODULES(602行):
# build/core/Makefile contains extra stuff that we don’t want to pollute this
# top-level makefile with. It expects that ALL_DEFAULT_INSTALLED_MODULES
# contains everything that’s built during the current make, but it also further
# extends ALL_DEFAULT_INSTALLED_MODULES.
ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)
include $(BUILD_SYSTEM)/Makefile
modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
ALL_DEFAULT_INSTALLED_MODULES :=

5).产生相应的Rules,生成image
include $(BUILD_SYSTEM)/Makefile
所有需要配置的准备工作都已完成,下面该决定如何生成image输出文件了,这一过程实际上在build/core/Makefile中处理的。
这里定义各种img的生成方式,包括ramdisk.img、userdata.img、system.img、update.zip、recover.img等。具体对应的rules可以参考下图:
http://p.blog.csdn.net/images/p_blog_csdn_net/yili_xie/EntryImages/20091214/make%20goals.png
当Make include所有的文件,完成对所有make文件的解析以后就会寻找生成对应目标的规则,依次生成它的依赖,直到所有满足的模块被编译好,然后使用相应的工具打包成相应的img。
————————————————————————————————————————-
谷歌Android 4.0.3源代码结构分析
2012-04-13 11:16

下面为大家分析一下谷歌Android 4.0.3源代码结构,如下所示:
Android/abi (abi相关代码。ABI:applicationbinary interface,应用程序二进制接口)

Android/bionic (bionic C库)

Android/bootable (启动引导相关代码)

Android/build (存放系统编译规则及generic等基础开发配置包)

Android/cts (Android兼容性测试套件标准)

Android/dalvik (dalvik JAVA虚拟机)

Android/development(应用程序开发相关)

Android/device (设备相关代码)

Android/docs (介绍开源的相关文档)

Android/external (android使用的一些开源的模组)

Android/frameworks (核心框架——java及C++语言,是Android应用程序的框架。)

Android/hardware (主要是硬件适配层HAL代码)

Android/libcore (核心库相关)

Android/ndk (ndk相关代码。AndroidNDK(Android NativeDevelopment Kit)是一系列的开发工具,允许程序开发人员在Android应用程序中嵌入C/C++语言编写的非托管代码。)

Android/out (编译完成后的代码输出与此目录)

Android/packages (应用程序包)

Android/prebuilt (x86和arm架构下预编译的一些资源)

Android/sdk (sdk及模拟器)

Android/system (文件系统、应用及组件——C语言)

Android/Makefile

Android/v8.log
以上就是谷歌Android 4.0.3源代码结构的分析

android2.3中dalvik分析(较android2.1)

1libcore目录的调整

/dalvik/libcore 整个目录调整到根目录,即2.3中libcore与dalvik处于同一级目录。

2 支持多核处理器

ifeq ($(TARGET_CPU_SMP),true)

target_smp_flag := -DANDROID_SMP=1

3 针对ARM架构默认打开JIT

/gingerbread/build/core/combo/TARGET_linux-arm.mk中

ifeq ($(strip $(WITH_JIT)),)

WITH_JIT := true

endif

4 针对ARM架构,在打开JIT的情况下,android会编译出3个*.so

libdvm_assert.so

LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \

-DWITH_JIT_TUNING $(target_smp_flag)

libdvm_sv.so

LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \

-DWITH_SELF_VERIFICATION $(target_smp_flag)

libdvm_interp.so

WITH_JIT := false

5 解释器C文件的调整LOCAL_SRC_FILES:\

增加:

+Atomic.c.arm \

+TestCompability.c \

+alloc/CardTable.c\

+ alloc/Verify.c\

+alloc/Visit.c \

减少:

-alloc/HeapSource.c \

-alloc/MarkSweep.c.arm \

-native/dalvik_system_SamplingProfi

-native/SystemThread.c \

更改:

C-analysis/DexOptimize.c TO analysis/DexPrepare.c

C-analysis/ReduceConstants.c TO analysis/Optimize.c

C-test/AtomicSpeed.c TO test/AtomicTest.c.arm

6 虚拟机编译器源码的调整compiler(左边为2.3)

ifeq ($(WITH_JIT),true) | ifeq ($(WITH_JIT),true)

LOCAL_CFLAGS += -DWITH_JIT | LOCAL_CFLAGS += -DWITH_JIT

LOCAL_SRC_FILES += \ | LOCAL_SRC_FILES += \

| ../dexdump/OpCodeNames.c \

compiler/Compiler.c \ | compiler/Compiler.c \

compiler/Frontend.c \ | compiler/Frontend.c \

compiler/Utility.c \ | compiler/Utility.c \

compiler/InlineTransformation.c \ |

compiler/IntermediateRep.c \ | compiler/IntermediateRep.c \

compiler/Dataflow.c \ |

compiler/Loop.c \ |

compiler/Ralloc.c \ |

interp/Jit.c | interp/Jit.c

endif | endif

7 解释器指令的增加

下面的内容可以说是android升级后针对dalvik部分最大的改动,因为它增加了14条解释器指令。也就是说,由2.3编译也来的*.apk将不向下兼容了。我们来看看增加了哪14条指令:

OP_IGET_VOLATILE = 0xe3,

OP_IPUT_VOLATILE = 0xe4,

OP_SGET_VOLATILE = 0xe5,

OP_SPUT_VOLATILE = 0xe6,

OP_IGET_OBJECT_VOLATILE = 0xe7,

OP_IGET_WIDE_VOLATILE = 0xe8,

OP_IPUT_WIDE_VOLATILE = 0xe9,

OP_SGET_WIDE_VOLATILE = 0xea,

OP_SPUT_WIDE_VOLATILE = 0xeb,

OP_BREAKPOINT = 0xec,

OP_BREAKPOINT = 0xec,

OP_IPUT_OBJECT_VOLATILE = 0xfc,

OP_SGET_OBJECT_VOLATILE = 0xfd,

OP_SPUT_OBJECT_VOLATILE = 0xfe,

如果给一款新的CPU移植DALVIK,从2.1到2.3的升级,即使不开JIT,上述14条解释器指令也需要相应进行优化。
——————————————————————————————————————————–

2.移植2.2系统,2.3系统,4.0系统,通过bcompare比较.

android_fs.tar
*.gitignore

.\jdk1.6.0_23
.\cts
.\out
.\rootfs_dir
.\sdk
.git
.\ndk
.\dalvik
.\bionic
.\bootable
.\development
.\device
.\libcore
.\prebuilt
.\packages
.\system

比较2.3.4和2.3.3-R1
bionic-基本c库-
2.3.4多出来的文件:
bionic\libc\kernel\common\linux\usb\f_accessory.h
bionic\libc\kernel\common\linux\s3c_fimg2d2x.h
bionic\libc\kernel\common\linux\s3c_g2d.h
2.3.3-R1多出来的文件:
两者不同的文件:

bootable-启动bootloader-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:

build-编译脚本-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:
“”” Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds
build\tools\releasetools\common.py
build\tools\releasetools\edify_generator.py
build\tools\releasetools\img_from_target_files
build\tools\releasetools\ota_from_target_files
before doing other work.”””

build\tools\droiddoc\templates\assets\android-developer-reference.js
build\core\tasks\sdk-addon.mk
build\core\build_id.mk
build\core\java_library.mk
build\core\main.mk
build\core\Makefile
build\core\package.mk
build\core\prebuilt.mk
build\core\version_defaults.mk

dalvik-虚拟机-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:

development-开发者目录-
2.3.4多出来的文件:
development\build\tools\mk_sdk_repo_xml.sh
development\build\tools\sdk_repo.mk
development\ndk\platforms\android-3\include\linux\s3c_fimg2d2x.h
2.3.3-R1多出来的文件:
两者不同的文件:
development\build\tools\windows_sdk.mk
development\samples\source.properties
development\tools\mkstubs\*.*

device-第三方厂家-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:

external-开源库-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:

framework-框架-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:

hardware-硬件HAL-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:

libcore-虚拟机核心-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:
libcore\luni\
libcore\support\

packages-应用-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:

prebuild-预编译资源-
2.3.4多出来的文件:
prebuild\common\
prebuild\sdk\
2.3.3-R1多出来的文件:
两者不同的文件:

system-文件系统-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:
system\core\rootdir\ueventd.rc
system\core\libnetutils\dhcp_utils.c
system\core\include\private\android_filesystem_config.h
system\bluetooth\bluedroid\bluetooth.c

vender-设备厂家自定义驱动HAL-
2.3.4多出来的文件:
2.3.3-R1多出来的文件:
两者不同的文件:
////////////////////////////////////////////////////////////////////////
比较2.3.4和2.3.4-R0.9
bionic-基本c库-
2.3.4多出来的文件:
bionic\libc\kernel\common\linux\s3c_fimg2d2x.h
bionic\libc\kernel\common\linux\s3c_g2d.h
2.3.4-R0.9多出来的文件:
两者不同的文件:

bootable-启动bootloader-
2.3.4多出来的文件:
2.3.4-R0.9多出来的文件:
两者不同的文件:

build-编译脚本-
2.3.4多出来的文件:
2.3.4-R0.9多出来的文件:
两者不同的文件:
build\core\main.mk

dalvik-虚拟机-
2.3.4多出来的文件:
2.3.4-R0.9多出来的文件:
两者不同的文件:

development-开发者目录-
2.3.4多出来的文件:
development\ndk\platforms\android-3\include\linux\s3c_fimg2d2x.h
2.3.4-R0.9多出来的文件:
两者不同的文件:
device-第三方厂家-
2.3.4多出来的文件:
2.3.4-R0.9多出来的文件:
两者不同的文件:
device\samsung\*.mk里面LOCAL_MODULE_TAGS := eng LOCAL_MODULE_TAGS := optional

external-开源库-
2.3.4多出来的文件:
新模块alsa tslib
2.3.4-R0.9多出来的文件:
两者不同的文件:
Android.mk 里面的 LOCAL_LDFLAGS += -m32
framework-框架-
2.3.4多出来的文件:
2个新服务:LedManager PwmManager
2.3.4-R0.9多出来的文件:
两者不同的文件:

hardware-硬件HAL-
2.3.4多出来的文件:
4个新服务:LedManager PwmManager alsa camera
2.3.4-R0.9多出来的文件:
两者不同的文件:

libcore-虚拟机核心-
2.3.4多出来的文件:
2.3.4-R0.9多出来的文件:
两者不同的文件:

packages-应用-
2.3.4多出来的文件:
2个新应用:LedManager PwmManager
2.3.4-R0.9多出来的文件:
两者不同的文件:

prebuild-预编译资源-
2.3.4多出来的文件:
prebuild\common\
prebuild\sdk\
2.3.4-R0.9多出来的文件:
两者不同的文件:

system-文件系统-
2.3.4多出来的文件:
2.3.4-R0.9多出来的文件:
两者不同的文件:
system\bluetooth\bluedroid\bluetooth.c

vender-设备厂家自定义驱动HAL-
2.3.4多出来的文件:
2.3.4-R0.9多出来的文件:
两者不同的文件:

问题0.认真比较不同?

问题1.packages的list在那里mk文件?

问题2.git clone kernel

问题3.camera 和audio的部分作为突破口

问题4.仅仅更新kernel,比如2.6.26->3.0
问题5.128Mb的uboot比较一下

kernel目录tree?
hal目录以及原理?
arm的cpu指令与寄存器?

编译链接流程?启动流程?移植流程?
翻译readme.txt
pdf

笔记
移动硬盘的培训资料

///////////////////////////////////////////
编译uboot:
1.make smdk6410_config
进入./Makefle
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
……
smdk6410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s3c64xx smdk6410 samsung s3c6410
意思是./mkconfig smdk6410 arm s3c64xx smdk6410 samsung s3c6410
对应 $0 $1 $2 $3 $4 $5 $6
对应 Parameters Target Architecture CPU Board [VENDER] [SOC]
所以 Target=smdk6410 Architecture=arm CPU=s3c64xx Board=smdk6410 VENDER=samsung SOC=s3c6410
进入./mkconfig
[ “${BOARD_NAME}” ] || BOARD_NAME=”$1″
意思是 BOARD_NAME=smdk6410
……
cd ./include
rm -f asm
ln -s asm-$2 asm
意思是 ln -s asm-arm asm //建立链接文件asm指向asm-arm目录
……
if [ -z “$6” -o “$6” = “NULL” ] ; then
ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi
意思是 ln -s arch-s3c6410 asm-arm/arch //建立链接文件asm-arm/arch指向arch-s3c6410目录
……
# create link for s3c64xx SoC
if [ “$3” = “s3c64xx” ] ; then
rm -f regs.h
ln -s $6.h regs.h
rm -f asm-$2/arch
ln -s arch-$3 asm-$2/arch
fi
if [ “$2” = “arm” ] ; then
rm -f asm-$2/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi
意思是 ln -s s3c6410.h regs.h //建立链接文件regs.h指向./include/s3c6410.h
意思是 ln -s arch-s3c64xx asm-arm/arch //建立链接文件asm-arm/arch指向arch-s3c64xx目录
意思是 ln -s proc-armv asm-arm/proc //建立链接文件asm-arm/proc指向proc-armv目录
……
#
# Create include file for Make
#
echo “ARCH = $2” > config.mk
echo “CPU = $3” >> config.mk
echo “BOARD = $4” >> config.mk

[ “$5” ] && [ “$5” != “NULL” ] && echo “VENDOR = $5” >> config.mk

[ “$6” ] && [ “$6” != “NULL” ] && echo “SOC = $6” >> config.mk
意思是创建./include/config.mk内容是
ARCH = arm
CPU = s3c64xx
BOARD = smdk6410
VENDOR = samsung
SOC = s3c6410
……
#
# Create board specific header file
#
if [ “$APPEND” = “yes” ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo “/* Automatically generated – do not edit */” >>config.h
echo “#include <configs/$1.h>” >>config.h
意思是创建./include/config.h内容是
#include <configs/smdk6410.h>

进入configs/smdk6410.h主要配置宏:
#define MEMORY_BASE_ADDRESS 0x50000000 //sdram实际开始物理地址
……
/*
* Architecture magic and machine type
*/
#define MACH_TYPE 1626
#define UBOOT_MAGIC (0x43090000 | MACH_TYPE)
……

#define CFG_LOAD_ADDR MEMORY_BASE_ADDRESS /* default load address */
……
/* this must be included AFTER the definition of CONFIG_COMMANDS (if any) */
#include <cmd_confdefs.h>

#define CONFIG_BOOTDELAY 1
#define CONFIG_BOOTARGS “root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc nconsole=tty1 console=ttySAC0,115200″
……
#define CONFIG_NR_DRAM_BANKS 1 /* we have 2 bank of DRAM */
#define PHYS_SDRAM_1 MEMORY_BASE_ADDRESS /* SDRAM Bank #1 */ //sdram实际开始物理地址
//#define PHYS_SDRAM_1_SIZE 0x08000000 /* 64 MB */
#define PHYS_SDRAM_1_SIZE 0x10000000 //sdram大小256MB

#define CFG_FLASH_BASE 0x00000000 //flash实际开始物理地址
……
#define CFG_ENV_ADDR 0
#define CFG_ENV_SIZE 0x80000 /* Total Size of Environment Sector */

……
/*
* Size of malloc() pool
*/
//jkeqiang change it to avoid stack flow
#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 512*1024)
//#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 1024*1024)
#define CFG_GBL_DATA_SIZE 128 /* size in bytes reserved for initial data */

#define CFG_STACK_SIZE 512*1024
……
/*
* SMDK6400 board specific data
*/

#define CONFIG_IDENT_STRING ” for SMDK6410″

/* total memory required by uboot */
#define CFG_UBOOT_SIZE (2*1024*1024)

/* base address for uboot */
#ifdef CONFIG_ENABLE_MMU
#define CFG_UBOOT_BASE 0xc7e00000
#else
#define CFG_UBOOT_BASE 0x57e00000
#endif
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0x7e00000

#define CFG_ENV_OFFSET 0x00080000
……
/* Settings as above boot configuration */
#if defined(CONFIG_BOOT_NAND)
#define CFG_ENV_IS_IN_NAND
//jkeqiang close it
//#define CFG_NAND_LARGEPAGE_SAVEENV
#define CFG_NAND_HWECC
//#define CFG_NAND_FLASH_BBT

#define CONFIG_BOOTCOMMAND “nand read 0xc0008000 0x100000 0x500000;bootm 0xc0008000”
2.make
Makefile的默认入口是: all
进入./Makefile
……
# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export ARCH CPU BOARD VENDOR SOC
意思是include ./include/config.mk //前面make smdk6410_config最后创建的
内容是
ARCH = arm
CPU = s3c64xx
BOARD = smdk6410
VENDOR = samsung
SOC = s3c6410
……
# load other configuration
include $(TOPDIR)/config.mk
进入 ./config.mk
……
ifdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
ifdef BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
……
ifeq ($(CONFIG_NAND_U_BOOT),y)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
else
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif
其中BOARDDIR=samsung/smdk6410,所以sinclude ./board/samsung/smdk6410/config.mk
进入./board/samsung/smdk6410/config.mk
#
# SMDK6410 has a 128 MB SDR SDRAM
#
# 5000’0000 to 5800’0000
#
#
# Linux-Kernel is expected to be at 5000’8000, entry 5000’8000
# optionally with a ramdisk at 5080’0000
#
# we load ourself to 57e0’0000 without MMU
# with MMU, load address is changed to 0xc7e0_0000
#
# download area is 5000’0000
#
ifndef TEXT_BASE
TEXT_BASE = 0xc7e00000
endif
如果norflash启动链接脚本是./board/samsung/smdk6410/u-boot.lds
进入./board/samsung/smdk6410/u-boot.lds
OUTPUT_FORMAT(“elf32-littlearm”, “elf32-littlearm”, “elf32-littlearm”)
/*OUTPUT_FORMAT(“elf32-arm”, “elf32-arm”, “elf32-arm”)*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;

. = ALIGN(4);
.text :
{
cpu/s3c64xx/start.o (.text)
cpu/s3c64xx/s3c6410/cpu_init.o (.text)
cpu/s3c64xx/onenand_cp.o (.text)
cpu/s3c64xx/nand_cp.o (.text)
cpu/s3c64xx/movi.o (.text)
*(.text)
lib_arm/div0.o
}

. = ALIGN(4);
.rodata : { *(.rodata) }

. = ALIGN(4);
.data : { *(.data) }

. = ALIGN(4);
.got : { *(.got) }

__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

. = ALIGN(4);
.mmudata : { *(.mmudata) }

. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}

……
回到./Makefile
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e ‘s/.*\(__u_boot_cmd_.*\)/-u\1/p’|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
–start-group $(__LIBS) –end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
意思是根据前面的链接脚本LDSCRIPT和链接标志LDFLAGS链接.
///////////////////////////////////////////
uboot启动流程:
1.根据./board/samsung/smdk6410/u-boot.lds,首先进入start.s
#include <config.h> //意思是include “./include/config.h”,内容是#include <configs/smdk6410.h>
……
#include <regs.h> //意思是include 链接文件regs.h指向./include/s3c6410.h目录
……
_TEXT_BASE:
.word TEXT_BASE //链接标志LDFLAGS里面的TEXT_BASE = 0xc7e00000,这是uboot拷贝到sdram中的地址

……
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE //意思=MEMORY_BASE_ADDRESS + 0x7e00000=0x50000000 + 0x7e00000=0x57e00000
……
.globl _armboot_start
_armboot_start:
.word _start //第一条指令的运行地址
……
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start //初始值为0,无初始值的全局变量,静态变量放在BSS段,BSS开始

.globl _bss_end
_bss_end:
.word _end ////初始值为0,无初始值的全局变量,静态变量放在BSS段,BSS结束
……
/* Peri port setup */
ldr r0, =0x70000000
orr r0, r0, #0x13
mcr p15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff)
———————————————————————————————————————
意思是是为了给256M的内存在MMU开启的时候把0x70000000作为重映射的基地址
c15协处理器寄存器在s3c6410上有特殊作用,它是外部内存端口映射寄存器,32位,在开关MMU的时候发生作用,且优先级最高
这里的0x70000000为外部端口的基地址,0x13的二进制为0x10011,0x10011的意思为256M,代表映射的
大小为256M,0x10010为128M。假如你没开MMU,PHY和Peri port映射的地址将相同。通过下面的内容后,我们知道我们原来uboot
代码是放置到0x57e00000的,现在便只能通过0x57e00000+0x70000000虚拟地址来访问uboot起始地址了。
———————————————————————————————————————
……
/*
* Go setup Memory and board specific bits prior to relocation.
*/
bl lowlevel_init /* go setup pll,mux,memory */
进入./board\samsung\smdk6410\lowlevel_init.S
……
#ifdef CONFIG_ENABLE_MMU

/*
* MMU Table for SMDK6400
*/

/* form a first-level section entry */
.macro FL_SECTION_ENTRY base,ap,d,c,b
.word (\base << 20) | (\ap << 10) | \
(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
.endm
.section .mmudata, “a”
.align 14
// the following alignment creates the mmu table at address 0x4000.
.globl mmu_table
mmu_table:
.set __base,0
// 1:1 mapping for debugging
.rept 0xA00
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr

// access is not allowed.
.rept 0xC00 – 0xA00
.word 0x00000000
.endr

// 256MB for SDRAM 0xC0000000 -> 0x50000000 ,256MB内存地址映射规律是开始地址为0x0xC0000000 ->对应实际物理地址 0x50000000
.set __base, 0x500
.rept 0xD00 – 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr

// access is not allowed.
.rept 0x1000 – 0xD00
.word 0x00000000
.endr

#endif
……
回到start.s
#ifdef CONFIG_BOOT_NAND
mov r0, #0x1000
bl copy_from_nand //如果从nand flash启动
#endif
……
/*
* copy U-Boot to SDRAM and jump to ram (from NAND or OneNAND)
* r0: size to be compared
* Load 1’st 2blocks to RAM because U-boot’s size is larger than 1block(128k) size
*/
.globl copy_from_nand
copy_from_nand:
mov r10, lr /* save return address */

mov r9, r0
/* get ready to call C functions */
ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
sub sp, sp, #12
mov fp, #0 /* no previous frame, so fp=0 */
mov r9, #0x1000
bl copy_uboot_to_ram //copy_uboot_to_ram是个c语言写的函数,在Nand_cp.c (cpu\s3c64xx)文件中
源码如下所示:
——————————————————————————————————————————-
copy_uboot_to_ram的源码:
int copy_uboot_to_ram (void)
{
int large_block = 0;
int i;
vu_char id;
NAND_ENABLE_CE(); 使能NAND
—————————————————————————————————————————-
其中有:
#define NAND_ENABLE_CE()(NFCONT_REG &= ~(1 << 1))
#define NFCONT_REG __REG(ELFIN_NAND_BASE+NFCONT_OFFSET)
#define __REG(x) (*(vu_long *)(x))
#define ELFIN_NAND_BASE0x70200000
#define NFCONT_OFFSET 0x04
—————————————————————————————————————————-
NFCMD_REG = NAND_CMD_READID;
NFADDR_REG = 0x00;
—————————————————————————–
其中有:
#define NFCMD_REG __REG(ELFIN_NAND_BASE+NFCMMD_OFFSET)
#define NAND_CMD_READID0x90
90 ID : Access command = 90H(NAND手册)
————————————————————————
#define NFADDR_REG __REG(ELFIN_NAND_BASE+NFADDR_OFFSET)
———————————————————————————————————————————-
/* wait for a while */
for (i=0; i<200; i++);
——————————————————————————————————————————-
id = NFDATA8_REG;
id = NFDATA8_REG;
——————————————————-
其中有:
#define NFDATA8_REG __REGb(ELFIN_NAND_BASE+NFDATA_OFFSET)
——————————————————————————————————————————-
if (id > 0x80)
large_block = 1;
if(id == 0xd5)
large_block = 2; 对读出了ID进行判断,得出page(页)大小
——————————————————————————————————————————-

/* read NAND Block.
* 128KB ->240KB because of U-Boot size increase. by scsuh
* So, read 0x3c000 bytes not 0x20000(128KB).
*/
//内存分布图-MMU使能 //内存大小为256MB,0xc0000000 – 0xd0000000,对应物理地址0x50000000 – 0x60000000
//0xd0000000 //内存最高位置地址
//…….
//0xc8000000 //内存中间128MB位置地址
//12字节保留
//sp寄存器=0xc7fffff4 //ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE – 0xc) //0xc7e00000 + (2*1024*1024) – 0xc,因为CONFIG_ENABLE_MMU 所以CFG_UBOOT_BASE = 0xc7e00000 , CFG_UBOOT_SIZE = (2*1024*1024)
//stack区域,CFG_STACK_SIZE=512*1024
//MALLOC区域,CFG_MALLOC_LEN=(0x80000 + 512*1024)
//gd_t的结构体
//bd_t的结构体
//……
//0xc7e00000 + 240kb //uboot最大地址
//uboot的bss段
//uboot的mmu页表段
//uboot的cmd命令段
//uboot的got段
//uboot的数据段
//uboot的代码段
//0xc7e00000 //TEXT_BASE,uboot开始地址,内存中间126MB位置地址
//……
//0xc0000100 //指定向内核传参的地址,对应物理地址0x50000100
//……
//0xc0000000 //内存最低地址
return nandll_read_blocks(CFG_PHY_UBOOT_BASE, 0x3c000, large_block); //这时候mmu没开,使用物理地址CFG_PHY_UBOOT_BASE=0x57e00000,uboot大小0x3c000=240kb
}
上面这个函数的内容,下篇博客在说。
……
回到start.s
……
ldr r0, =0x0c000000 //可是这里的r0为什么是0x0c000000呢?这要从s3c6410的启动方式说起,当从NAND启动时,会复制uboot源码前4K到内部RAM,内部SRAM 的地址范围是0x0C00_0000~0x0FFF_FFFF,但是实际存储仅4KB。该区域能被读和写,当NAND 闪存启动被选择时能映射。
ldr r1, _TEXT_PHY_BASE //=CFG_PHY_UBOOT_BASE=MEMORY_BASE_ADDRESS + 0x7e00000=0x50000000 + 0x7e00000=0x57e00000
1: ldr r3, [r0], #4 //LDR Rd,[Rn],#0x04 ; Rn的值用做传输数据的存储地址。在数据传送后,将偏移量0x04 与Rn相加,结果写回到Rn中。Rn不允许是R15
ldr r4, [r1], #4
teq r3, r4
bne compare_failed /* not matched */
subs r9, r9, #4 //这里movr9, #0x1000这个是在上面被赋值的,即r9=4K,即这里要比较前4K的代码
//既0x57e00000+4k与0x0c000000+4k,目前还是物理地址,没有mmu使能.
bne 1b

4: mov lr, r10 /* all is OK */
mov pc, lr //成功就可以返回了
……
after_copy:
#ifdef CONFIG_ENABLE_MMU
enable_mmu:
/* enable domain access */
ldr r5, =0x0000ffff
mcr p15, 0, r5, c3, c0, 0 @ load domain access register //代码的含义为设置高8个域无访问权限,低8个域为管理者权限.协处理器c3的作用是存储的保护和控制,用在MMU中为内存的域访问控制c3为32位寄存器,每两位为一个访问控制特权,0x00代表没有访问权限,这时候访问将失效;0x01为客户类型,将根据地址变换条目中的访问控制位决定是否允许特定内存访问;0x10是保留的,暂时没有使用,0x11为管理者权限,不考虑地址变换条目中的权限控制位,将不会访问内存失效。

/* Set the TTB register */
ldr r0, _mmu_table_base //lowlevel_init.S 里面内存管理单元对物理内存地址到虚拟内存地址的映射表
ldr r1, =CFG_PHY_UBOOT_BASE ////=MEMORY_BASE_ADDRESS + 0x7e00000=0x50000000 + 0x7e00000=0x57e00000
ldr r2, =0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15, 0, r1, c2, c0, 0 //接着下面通过mcr p15, 0, r1, c2, c0, 0指令给c2赋值,c2用于保存页表基地址。所谓页表基地址即是虚实转换的内存页表的首地址。这里r1的值赋值给了c2,r1的值为0x57exxxxx,c2高14位是储存页表的基地址

/* Enable the MMU */ //使能内存管理单元,内存地址开始使用映射
mmu_on:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1 /* Set CR_M to enable MMU */
mcr p15, 0, r0, c1, c0, 0
nop
nop
nop
nop
#endif

skip_hw_init:
/* Set up the stack */
stack_setup:
#ifdef CONFIG_MEMORY_UPPER_CODE //如果栈地址从低向高使用
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE – 0xc) //0xc7e00000 + (2*1024*1024) – 0xc,因为CONFIG_ENABLE_MMU 所以CFG_UBOOT_BASE = 0xc7e00000 , CFG_UBOOT_SIZE = (2*1024*1024)
#else //如果栈地址从高向低使用
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot 为代码段开始地址,值为0xc7e00000 */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area 代码段下面留出一段内存实现malloc,值为 (0x80000 + 512*1024) */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo 再留一段内存保存全局数据 ,值为 128 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) //IRQ,FIQ模式的栈
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack 最后留出12字节的内存给abort异常,往下的内存就都是栈了 */

#endif

clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */

clbss_l:
str r2, [r0] /* clear loop… */
add r0, r0, #4
cmp r0, r1
ble clbss_l

ldr pc, _start_armboot //进入uboot第二阶段

_start_armboot:
.word start_armboot
进入第二阶段./lib_arm/board.c里面,start_armboot函数
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
#ifndef CFG_NO_FLASH
ulong size;
#endif

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif

#if defined(CONFIG_BOOT_MOVINAND)
uint *magic = (uint *) (PHYS_SDRAM_1);//#define PHYS_SDRAM_1 MEMORY_BASE_ADDRESS /* SDRAM Bank #1 */ //sdram实际开始物理地址0x50000000
#endif

/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base;

gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE – CFG_MALLOC_LEN – CFG_STACK_SIZE – sizeof(gd_t);
//见s3c6410 uboot代码分析《一》的内存图0xc7e00000 + (2*1024*1024) – (0x80000 + 512*1024) -512*1024 -sizeof(gd_t)
//因为我们定义了CONFIG_MEMORY_UPPER_CODE宏,所以gd = (gd_t*)gd_base,由gd_base的值我们知道,malloc区域、stack区域、bdinfo数据在内存的位置是放在upper of uboot。
#ifdef CONFIG_USE_IRQ
gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
gd = (gd_t*)gd_base;
//#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r8”),全局数据地址放在r8寄存器
#else
gd = (gd_t*)(_armboot_start – CFG_MALLOC_LEN – sizeof(gd_t));
#endif

/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__(“”: : :”memory”);

memset ((void*)gd, 0, sizeof (gd_t));
//内存分布图-MMU使能 //内存大小为256MB,0xc0000000 – 0xd0000000,对应物理地址0x50000000 – 0x60000000
//0xd0000000 //内存最高位置地址
//…….
//0xc8000000 //内存中间128MB位置地址
//12字节保留
//sp寄存器=0xc7fffff4 //ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE – 0xc) //0xc7e00000 + (2*1024*1024) – 0xc,因为CONFIG_ENABLE_MMU 所以CFG_UBOOT_BASE = 0xc7e00000 , CFG_UBOOT_SIZE = (2*1024*1024)
//stack区域,CFG_STACK_SIZE=512*1024
//MALLOC区域,CFG_MALLOC_LEN=(0x80000 + 512*1024)
//gd_t的结构体
//bd_t的结构体
//……
//0xc7e00000 + 240kb //uboot最大地址
//uboot的bss段
//uboot的mmu页表段
//uboot的cmd命令段
//uboot的got段
//uboot的数据段
//uboot的代码段
//0xc7e00000 //TEXT_BASE,uboot开始地址,内存中间126MB位置地址
//……
//0xc0000100 //指定向内核传参的地址,对应物理地址0x50000100
//……
//0xc0000000 //内存最低地址
gd->bd = (bd_t*)((char*)gd – sizeof(bd_t));//gd->bd指针指向数据类型为bd_t的结构体,bd_t结构体记录开发板的参数,例如串口波特率、ip地址、机器类型、启动参数、环境变量位置等。
memset (gd->bd, 0, sizeof (bd_t));

monitor_flash_len = _bss_start – _armboot_start;

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
———————————————————-
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */ //因为我们没有定义CONFIG_USE_IRQ,所以这个函数直接返回0
board_init, /* basic board dependent setup */ //函数内首先执行dm9000_pre_init()函数,73-74行一个是记录机器类型,一个是指定向内核传参的地址。gd->bd->bi_arch_number = MACH_TYPE;gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100)=0x50000100;
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */ //因为没有定义ENV_IS_EMBEDDED,所有只是执行了142-143,把环境变量default_environment[0]的首地址赋值给gd->env_addr。
//default_environment[0]的大小必须小于CFG_ENV_SIZE, default_environment[0]={“bootargs=” CONFIG_BOOTARGS “bootcmd=” CONFIG_BOOTCOMMAND “ramboot=” CONFIG_RAMBOOTCOMMAND “nfsboot=” CONFIG_NFSBOOTCOMMAND “bootdelay=” MK_STR(CONFIG_BOOTDELAY) “baudrate=” MK_STR(CONFIG_BAUDRATE) “loads_echo=” MK_STR(CONFIG_LOADS_ECHO) “ethaddr=” MK_STR(CONFIG_ETHADDR) “eth1addr=” MK_STR(CONFIG_ETH1ADDR) “eth2addr=” MK_STR(CONFIG_ETH2ADDR) “eth3addr=” MK_STR(CONFIG_ETH3ADDR) “ipaddr=” MK_STR(CONFIG_IPADDR) “serverip=” MK_STR(CONFIG_SERVERIP) “autoload=” CFG_AUTOLOAD “preboot=” CONFIG_PREBOOT “rootpath=” MK_STR(CONFIG_ROOTPATH) “gatewayip=” MK_STR(CONFIG_GATEWAYIP) “netmask=” MK_STR(CONFIG_NETMASK) “hostname=” MK_STR(CONFIG_HOSTNAME) “bootfile=” MK_STR(CONFIG_BOOTFILE) “loadaddr=” MK_STR(CONFIG_LOADADDR) }
init_baudrate, /* initialze baudrate settings */ //使用getenv_r函数在gd->env_addr 里找baudrate关键字,找到后把“=”号后面的值赋值给gd->baudrate,然后再放到gd->bd->bi_baudrate里面。simple_strtoul是uboot实现的字符串转UL类型。
serial_init, /* serial communications setup */ // serial_init:什么都没做,保持默认的8位数据、无奇偶校验、1 停止位、无开始位。
console_init_f, /* stage 1 init of console */ //console_init_f:gd->have_console = 1就这一句话
display_banner, /* say that we are here */ //串口打印uboot信息,就是uboot启动的时候我们看到的信息,这里使用的是printf,但是我们追进去后,关注的函数
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */ //打印CPU信息,CPU型号和速度 CPU:…
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */ //打印开发板信息 BOARD:…
#endif
dram_init, /* configure available RAM banks */ // 就2句话 gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
display_dram_config, //因为没有定义DEBUG,所以打印DRAM:256M
NULL,
};
—————————————————————

#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */

#ifdef CONFIG_VFD //(因为CONFIG_LCD、CONFIG_VFD没有定义,所以跳过这一部分)
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE – 1)) & ~(PAGE_SIZE – 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */

#ifdef CONFIG_LCD//(因为CONFIG_LCD、CONFIG_VFD没有定义,所以跳过这一部分)
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE – 1)) & ~(PAGE_SIZE – 1);
size = lcd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */

/* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ //我们定义了UPPER_CODE,所以执行第一个mem_malloc_init。这个函数的作用是记录堆栈空间的起始地址、结束地址、当前地址。
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE – CFG_MALLOC_LEN – CFG_STACK_SIZE);
//栈空间起始地址mem_malloc_start = 0xc7e00000 + (2*1024*1024) – (0x80000 + 512*1024) -512*1024
//栈空间结束地址mem_malloc_end = 0xc7e00000 + (2*1024*1024) -512*1024
//栈空间当前地址mem_malloc_brk = mem_malloc_start;
#else
mem_malloc_init (_armboot_start – CFG_MALLOC_LEN);
#endif

#if defined(CONFIG_SMDK6400) || defined(CONFIG_SMDK6410) || defined(CONFIG_SMDK6430) || defined(CONFIG_SMDK2450) || defined(CONFIG_SMDK2416)

#if defined(CONFIG_NAND)
puts (“NAND: “);
nand_init(); /* go init the NAND */
#endif

#if defined(CONFIG_ONENAND)
puts (“OneNAND: “);
onenand_init(); /* go init the One-NAND */
#endif

#if defined(CONFIG_BOOT_MOVINAND)
puts (“SD/MMC: “);

if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) {
printf(“Boot up for burning\n”);
} else {
movi_set_capacity();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
movi_init();
}
#endif

#else

#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts (“NAND: “);
nand_init(); /* go init the NAND */ //因为没有定义CFG_NAND_LEGACY,所以nand_init在drivers/nand/nand.c中
#endif

#endif

#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif

/* initialize environment */
env_relocate ();
//env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
//memcpy (env_ptr->data,default_environment,sizeof(default_environment));
//gd->env_addr = (ulong)&(env_ptr->data);把内存中可操作的环境变量记录在全局变量中
//内存分布图-MMU使能 //内存大小为256MB,0xc0000000 – 0xd0000000,对应物理地址0x50000000 – 0x60000000
//0xd0000000 //内存最高位置地址
//…….
//0xc8000000 //内存中间128MB位置地址
//12字节保留
//sp寄存器=0xc7fffff4 //ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE – 0xc) //0xc7e00000 + (2*1024*1024) – 0xc,因为CONFIG_ENABLE_MMU 所以CFG_UBOOT_BASE = 0xc7e00000 , CFG_UBOOT_SIZE = (2*1024*1024)
//stack区域,CFG_STACK_SIZE=512*1024
//MALLOC区域,CFG_MALLOC_LEN=(0x80000 + 512*1024),其中CFG_ENV_SIZE=0x80000大小内存是env_ptr指向的环境变量
//gd_t的结构体
//bd_t的结构体
//……
//0xc7e00000 + 240kb //uboot最大地址
//uboot的bss段
//uboot的mmu页表段
//uboot的cmd命令段
//uboot的got段
//uboot的数据段
//uboot的代码段
//0xc7e00000 //TEXT_BASE,uboot开始地址,内存中间126MB位置地址
//……
//0xc0000100 //指定向内核传参的地址,对应物理地址0x50000100
//……
//0xc0000000 //内存最低地址
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */

//网络初始化分为IP地址初始化和MAC地址初始化
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr (“ipaddr”);//=#define CONFIG_IPADDR 192.168.1.20

/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];

i = getenv_r (“ethaddr”, tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;

for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0; //=#define CONFIG_ETHADDR 00:40:5c:26:0a:5b
if (s)
s = (*e) ? e + 1 : e;
}

#ifdef CONFIG_HAS_ETH1
i = getenv_r (“eth1addr”, tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;

for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
#endif
}

devices_init (); /* get the devices list going. */ //函数完成的主要功能是把设备放入list列表。这种方法是被推荐的,因为下次在查找设备的时候会通过这个列表来查询设备,方便统一管理。这个设备列表的接口的个二级指针变量devlist(list_t类型->二级指针)。

#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */

jumptable_init (); //为gd-jt全局变量定义了一些独立的函数接口,方便调用。

console_init_r (); /* fully init console as a device */ //让我们的输入stdin和输出stdout,stderr都指向它serial

#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif

/* enable exceptions */
enable_interrupts (); //作用就是清除cpsr中的[7]位。这位的含义是中断使能/禁止位,拥有中断最高优先级,这里代码用于开启中断。

/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
//cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv (“ethaddr”)) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

/* Initialize from environment */
if ((s = getenv (“loadaddr”)) != NULL) {
load_addr = simple_strtoul (s, NULL, 16); //#define CFG_LOAD_ADDR MEMORY_BASE_ADDRESS /* default load address */
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv (“bootfile”)) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif /* CFG_CMD_NET */

#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts (“Net: “);
#endif
eth_initialize(gd->bd);
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}

/* NOTREACHED – no way out of command loop except booting */
}
下面进入main_loop()
void main_loop (void)
{
#ifndef CFG_HUSH_PARSER
static char lastcommand[CFG_CBSIZE] = { 0, }; //CFG_HUSH_PARSER没有定义,所以会定义全局变量lastcommand[CFG_CBSIZE],CFG_CBSIZE的值为256,用于记录console buffer size;
int len;
int rc = 1;
int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay; //我们定义了CONFIG_BOOTDELAY在头文件中,默认是3,我改成了1。后面会定义指针s和int型bootdelay。用于uboot判断无任何按键按下,就执行CONFIG_BOOTCOMMAND宏所对应的命令,这个命令通常用于加载启动操作系统;
#endif
#ifdef CONFIG_PREBOOT
char *p;
#endif
…….

#ifdef CONFIG_AUTO_COMPLETE
install_auto_complete(); //其实CONFIG_AUTO_COMPLETE宏我也没有定义,但是这个东西挺好用的,可以帮助我们自动补齐命令
#endif

…….

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv (“bootdelay”); //我们定义了CONFIG_BOOTDELAY,所以391行s的值将保存bootdelay环境变量的值得地址
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; //392行把地址的值给bootdelay

debug (“### main_loop entered: bootdelay=%d\n\n”, bootdelay);
……

s = getenv (“bootcmd”); //s保存bootcmd等于号后面字符串的地址;=”nand read 0xc0008000 0x100000 0x500000;bootm 0xc0008000″

debug (“### main_loop: bootcmd=\”%s\”\n”, s ? s : “<UNDEFINED>”);
/***************************************************************************
* Watch for ‘delay’ seconds for autoboot stop or autoboot delay string.
* returns: 0 – no key string, allow autoboot
* 1 – got key string, abort
*/
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
……

# ifndef CFG_HUSH_PARSER
run_command (s, 0); //自动启动kernel,s=”nand read 0xc0008000 0x100000 0x500000;bootm 0xc0008000″
# else
……
# endif
……
}
…….
}
进入run_command() in./common/main.c
……
if ((argc = parse_line (finaltoken, argv)) == 0) { //比如”bootm 0xc0008000″就是argc=2,argv[0]=bootm,argv[1]=0xc0008000
rc = -1; /* no command at all */
continue;
}
/* Look up command in command table */
if ((cmdtp = find_cmd(argv[0])) == NULL) { //比如”bootm 0xc0008000″就是cmdtp=pointer of U_BOOT_CMD(bootm, CFG_MAXARGS, 1, do_bootm,……
printf (“Unknown command ‘%s’ – try ‘help’\n”, argv[0]);
rc = -1; /* give up after bad command */
continue;
}
……
/* OK – call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { //比如”bootm 0xc0008000″就是cmdtp->cmd=do_bootm() in ./common/Cmd_bootm.c,flag=0,argc=2,argv[0]=bootm,argv[1]=0xc0008000
rc = -1;
}
……
进入do_bootm() in ./common/Cmd_bootm.c
……
s = getenv (“verify”);
verify = (s && (*s == ‘n’)) ? 0 : 1; //如果环境变量有”verify”并且值为”n”则verify=0,否则verify=1.这里结果是verify=1

if (argc < 2) {
addr = load_addr; //ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */ //=MEMORY_BASE_ADDRESS=0x50000000
} else {
addr = simple_strtoul(argv[1], NULL, 16); //”bootm 0xc0008000″就是 0xc0008000
}
…….
data = addr + sizeof(image_header_t); //后移64字节uImage头部信息, 0xc0008000 + 0x40 = 0xc0008040
…….
len_ptr = (ulong *)data; //后移64字节uImage头部信息, 0xc0008000 + 0x40 = 0xc0008040
…….
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify); //flag=0,argc=2,argv[0]=bootm,argv[1]=0xc0008000, addr=0xc0008000, len_ptr=0xc0008040,verify=1
进入do_bootm_linux() in ./lib_arm/Armlinux.c
……
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv (“bootargs”); //commandline=”root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc nconsole=tty1 console=ttySAC0,115200″
#endif
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); //uImage一般是0xc0008040,zImage是0xc0008000
…….
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd); //params = (struct tag *) bd->bi_boot_params;其中gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100)=0x50000100;
…….
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd); // params->u.mem.start = bd->bi_dram[i].start=PHYS_SDRAM_1=0x50000000; params->u.mem.size = bd->bi_dram[i].size=PHYS_SDRAM_1_SIZE=0x10000000;gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline); // strcpy (params->u.cmdline.cmdline, p);=”root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc nconsole=tty1 console=ttySAC0,115200″
#endif

……
setup_end_tag (bd);
#endif
…….
theKernel (0, bd->bi_arch_number, bd->bi_boot_params); ////一般是0xc0008040,gd->bd->bi_arch_number = MACH_TYPE;gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100)=0x50000100;
///////////////////////////////////////////
kernel源码结构:
D:\android2.3_kernel_v1.01(ok6410的kernel )与F:\kernel_tegra_2636\tegra(google下载的kernel_tegra源码)比较:

1.arch/这是体系相关目录,每个cpu对应一个子目录,比如arch/arm
移植需要修改的目录:arch/arm/
2.block/这是块设备的通用函数
移植需要修改的目录:没有
3.crypto/这是常用加密,散列算法,以及一些crc和压缩算法
移植需要修改的目录:没有
4.Document/这是文档目录
移植需要修改的目录:没有-不参与编译
5.drivers/这是所有设备的驱动,每个子目录对应一类设备驱动程序
移植需要修改的目录:
drivers/char
drivers/crypto
drivers/hid
drivers/i2c
drivers/input
drivers/leds
drivers/media
drivers/mfd
drivers/misc
drivers/mmc
drivers/mtd
drivers/net
drivers/power
drivers/regulator
drivers/rtc
drivers/serial
drivers/spi
drivers/usb
drivers/video
drivers/wl
drivers/watchdog
6.firmware/这是固件
移植需要修改的目录:没有
7.fs/这是支持的文件系统的代码,每个子目录对应一种文件系统
移植需要修改的目录:
fs/cramfs
fs/fuse
fs/partitions
fs/yaffs2
8.include/这是内核头文件,有基本头文件incldue/linux/,有驱动头文件include/meida,include/mtd,include/net,有体系相关头文件include/asm-arm
移植需要修改的目录:
include/linux
include/media
include/mtd
9.init/这是内核初始化代码,其中main.c的start_kernel()是内核引导后第一个函数,这也是启动流程第一个c代码,前面是汇编代码.
移植需要修改的目录:没有
10.ipc/这是进程间通信代码
移植需要修改的目录:没有
11.kernel/这是内核管理核心通用代码,与cpu体系相关位于arch/***/kernel
移植需要修改的目录:kernel\pm_qos_params.c
12.lib/这是内核使用的库代码,与cpu体系相关位于arch/***/lib
移植需要修改的目录:没有
13.mm/这是内存管理代码,与cpu体系相关位于arch/***/mm
移植需要修改的目录:没有
14.net/网络支持代码,每个子目录对应网络的一个方面
移植需要修改的目录:
net/bluetooth
net/bridge
net/core
net/ipv4
net/netfilter
15.samples/这是示例代码-不参与编译
16.scripts/这是配置,编译内核的脚本文件
移植需要修改的目录:没有-不参与编译
17.security/这是安全,密钥相关代码
移植需要修改的目录:没有
18.sound/这是音频设备的驱动程序
移植需要修改的目录:sound/s3c24xx/
19.tools/这是工具目录
移植需要修改的目录:没有-不参与编译
20.usr/这是用于制作initrd的镜像的目录,可以值为内核启动后挂接的第一个文件系统.
移植需要修改的目录:没有
21.virt/这是基于kernel的虚拟机驱动代码
移植需要修改的目录:没有-不参与编译
///////////////////////////////////////////
kernel的Makefile
1../Makefile 顶层Makefile是所有Makefile 的核心,从总体控制内核的编译,链接.他决定根目录下那些目录参与编译.
上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
2../.config 配置文件,在配置内核make menuconfig时候自动生成,所有Makefile (包括顶层Makefile 和子目录Makefile )依赖.config决定使用那些文件.
3.arch/$(ARCH)/Makefile 对应cpu体系的Makefile ,他决定arch/$(ARCH)/下那些文件,目录参与编译,并且提供规则生成特定格式内核映像.
4.scripts/Makefile.* 通用规则和脚本
5.子目录下的Makefile 被上层Makefile 调用来编译所在目录下文件

4个变量init-y,core-y,drivers-y,net-y所列出的目录的每个子目录下都会生成一个build-in.o
1个变量libs-y所列出的目录的每个子目录下都会生成一个lib.a,除非该文件同时包含在obj-y则编译到build-in.o
最后head-y变量代表的.o和build-in.o,lib.a一起链接为内核映像文件vmlinux
变量obj-y表示文件编译进build-in.o(使用$(LD)-r命令)
变量obj-m表示文件编译进可加载模块.ko
变量lib-y表示文件编译进lib.a,除非该文件同时包含在obj-y则编译到build-in.o

全局编译选项CFLAGS(c文件编译选项),AFLAGS(汇编文件编译选项),LDFLAGS(链接选项),ARFLAGS(库文件选项)
局部编译选项EXTRA_CFLAGS(c文件编译选项),EXTRA_AFLAGS(汇编文件编译选项),EXTRA_LDFLAGS(链接选项),EXTRA_ARFLAGS(库文件选项)
单独编译选项CFLAGS_文件名(c文件编译选项),AFLAGS_文件名(汇编文件编译选项),LDFLAGS_文件名(链接选项),ARFLAGS_文件名(库文件选项)

编译完成后,zImage放在/arch/arm/boot/
///////////////////////////////////////////
链接工具命名:
arch-vendor-(os-)abi

1、arm-none-linux-gnueabi (ARM architecture, no vendor, linux OS, and the gnueabi ABI)
用于编译ARM架构的u-boot、linux内核、linux应用等
2、arm-none-eabi
用于编译ARM架构的裸机系统(包括linux的 boot、kernel)
3、arm-eabi
Android ARM 编译器
arm-none-linux-gnueabi-gcc 只是告诉你支持eabi的功能,你当然也可以把这个名字改成arm-linux-gcc执行了,
但是arm-linux-gcc的话是不可以直接改成arm-none-linux-gnueabi-gcc的,因为我们不知道arm-linux-gcc是什么形式的。
现在的arm linux toolchain大部分都是arm-none-linux-gnueabi-gcc类型的,但是有的人家为了方便,就软链接成arm-linux-gcc。呵呵

export $PATH=/usr/local/arm/arm-none-linux-gnueabi/bin:$PATH

kernel配置
ReadMe翻译
– 确保没有.o文件和相关文件存在:使用下面命令清楚上次编译Make sure you have no stale .o files and dependencies lying around:
cd linux
make mrproper
下载kernel源代码是干净的You should now have the sources correctly installed.
设置编译输出目录BUILD directory for the kernel:
当编译时候所有输出文件默认和源码在一起,这时候可以使用选项”make O=output/dir”指定目录来输出编译文件,包括.config;
比如:
kernel source code: /usr/src/linux-2.6.N
build directory: /home/name/build/kernel
在shell执行下面:
cd /usr/src/linux-2.6.N
make O=/home/name/build/kernel menuconfig
make O=/home/name/build/kernel
sudo make O=/home/name/build/kernel modules_install install

注意如果’O=output/dir’选项被使用,后面的所有make目录都要使用’O=output/dir’

配置内核CONFIGURING the kernel:
如果使用复制过来的配置文件,使用”make oldconfig”, 就会询问当前kernel的新选项配置;

– 配置命令有这些:
“make config” Plain text interface. //文本界面配置
“make menuconfig” Text based color menus, radiolists & dialogs. //菜单界面配置
“make xconfig” X windows (Qt) based configuration tool. //x界面配置
“make gconfig” X windows (Gtk) based configuration tool. //gtk界面配置
“make oldconfig” Default all questions based on the contents of //使用存在的./.config并且询问新选项配置
your existing ./.config file and asking about
new config symbols.
“make silentoldconfig”
Like above, but avoids cluttering the screen
with questions already answered.
Additionally updates the dependencies.
“make defconfig” Create a ./.config file by using the default //#make S3C2410_defconfig, S3C2410_defconfig,在Makefile中有一个入口, 也存在同名的一个文件:S3C2410_defconfig
symbol values from either arch/$ARCH/defconfig
or arch/$ARCH/configs/${PLATFORM}_defconfig,
depending on the architecture.
“make ${PLATFORM}_defconfig” //#make S3C2410_defconfig, S3C2410_defconfig,在Makefile中有一个入口, 也存在同名的一个文件:S3C2410_defconfig
Create a ./.config file by using the default
symbol values from
arch/$ARCH/configs/${PLATFORM}_defconfig.
Use “make help” to get a list of all available
platforms of your architecture.
“make allyesconfig”
Create a ./.config file by setting symbol
values to ‘y’ as much as possible.
“make allmodconfig”
Create a ./.config file by setting symbol
values to ‘m’ as much as possible.
“make allnoconfig” Create a ./.config file by setting symbol
values to ‘n’ as much as possible.
“make randconfig” Create a ./.config file by setting symbol
values to random values.

编译内核COMPILING the kernel:
– 使用”make”创建压缩的内核映像.
– 使用make V=1 all查看编译过程,使用”V=2″查看编译重新编译的原因.默认是”V=0″

所有make命令
$ make help
Cleaning targets:
clean – Remove most generated files but keep the config and
enough build support to build external modules
mrproper – Remove all generated files + config + various backup files 删除所有生成的文件
distclean – mrproper + remove editor backup and patch files 删除所有生成、备份、补丁文件

Configuration targets:
config – Update current config utilising a line-oriented program 基于文本的命令行工具
nconfig – Update current config utilising a ncurses menu based program
menuconfig – Update current config utilising a menu based program 基于ncurse库编写的图形化工具
xconfig – Update current config utilising a QT based front-end 基于X11的图形工具
gconfig – Update current config utilising a GTK based front-end 基于gtk+的图形工具
oldconfig – Update current config utilising a provided .config as base
localmodconfig – Update current config disabling modules not loaded
localyesconfig – Update current config converting local mods to core
silentoldconfig – Same as oldconfig, but quietly, additionally update deps
defconfig – New config with default from ARCH supplied defconfig
– #make S3C2410_defconfig, S3C2410_defconfig,在Makefile中有一个入口, 也存在同名的一个文件:S3C2410_defconfig
savedefconfig – Save current config as ./defconfig (minimal config)
allnoconfig – New config where all options are answered with no
allyesconfig – New config where all options are accepted with yes
allmodconfig – New config selecting modules when possible
alldefconfig – New config with all symbols set to default
randconfig – New config with random answer to all options
listnewconfig – List new options
oldnoconfig – Same as silentoldconfig but set new symbols to n (unset)

Other generic targets:
all – Build all targets marked with [*]
* vmlinux – Build the bare kernel
* modules – Build all modules
modules_install – Install all modules to INSTALL_MOD_PATH (default: /)
firmware_install- Install all firmware to INSTALL_FW_PATH
(default: $(INSTALL_MOD_PATH)/lib/firmware)
dir/ – Build all files in dir and below
dir/file.[oisS] – Build specified target only
dir/file.lst – Build specified mixed source/assembly target only
(requires a recent binutils and recent build (System.map))
dir/file.ko – Build module including final link
modules_prepare – Set up for building external modules
tags/TAGS – Generate tags file for editors
cscope – Generate cscope index
kernelrelease – Output the release version string
kernelversion – Output the version stored in Makefile
headers_install – Install sanitised kernel headers to INSTALL_HDR_PATH
(default: /cygdrive/f/kernel_tegra_2636_menu/tegra/usr)

Static analysers
checkstack – Generate a list of stack hogs
namespacecheck – Name space analysis on compiled kernel
versioncheck – Sanity check on version.h usage
includecheck – Check for duplicate included header files
export_report – List the usages of all exported symbols
headers_check – Sanity check on exported headers
headerdep – Detect inclusion cycles in headers
coccicheck – Check with Coccinelle.

Kernel packaging:
rpm-pkg – Build both source and binary RPM kernel packages
binrpm-pkg – Build only the binary kernel package
deb-pkg – Build the kernel as an deb package
tar-pkg – Build the kernel as an uncompressed tarball
targz-pkg – Build the kernel as a gzip compressed tarball
tarbz2-pkg – Build the kernel as a bzip2 compressed tarball
perf-tar-src-pkg – Build perf-2.6.36.tar source tarball
perf-targz-src-pkg – Build perf-2.6.36.tar.gz source tarball
perf-tarbz2-src-pkg – Build perf-2.6.36.tar.bz2 source tarball

Documentation targets:
Linux kernel internal documentation in different formats:
htmldocs – HTML
pdfdocs – PDF
psdocs – Postscript
xmldocs – XML DocBook
mandocs – man pages
installmandocs – install man pages generated by mandocs
cleandocs – clean all generated DocBook files

Architecture specific targets (x86):
* bzImage – Compressed kernel image (arch/x86/boot/bzImage)
install – Install kernel using
(your) ~/bin/installkernel or
(distribution) /sbin/installkernel or
install to $(INSTALL_PATH) and run lilo
fdimage – Create 1.4MB boot floppy image (arch/x86/boot/fdimage)
fdimage144 – Create 1.4MB boot floppy image (arch/x86/boot/fdimage)
fdimage288 – Create 2.8MB boot floppy image (arch/x86/boot/fdimage)
isoimage – Create a boot CD-ROM image (arch/x86/boot/image.iso)
bzdisk/fdimage*/isoimage also accept:
FDARGS=”…” arguments for the booted kernel
FDINITRD=file initrd for the booted kernel

i386_defconfig – Build for i386
x86_64_defconfig – Build for x86_64

make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build
make V=2 [targets] 2 => give reason for rebuild of target
make O=dir [targets] Locate all output files in “dir”, including .config //指定目录为编译输出目录
make C=1 [targets] Check all c source with $CHECK (sparse by default)
make C=2 [targets] Force check of all c source with $CHECK

Execute “make” or “make all” to build all targets marked with [*]
For further info see the ./README file

配置界面最下面,有如下2行菜单:
Load an Alternate Configuration File
Save an Alternate Configuration File
前者用于加载某个配置文件,后者用于将当前配置文件保存到某个配置文件.注意如果不使用这2菜单,配置的加载文件和输出文件默认值根目录
下的.config文件; 如果加载了其它文件(比如A),然后在A基础上修改,最后保存退出时,这些改动会自动保存到A中,而不是根目录的.config;
当然我们可以先使用菜单加载A,然后修改,最后使用菜单保存到根目录的.config;

config
配置完成后,生成./.config

后面make all的时候会生成include/config/*,
include/generated/*
scripts/下面部分文件
include/config/auto.conf只是把./.config文件的注释去掉,并且工具根目录的Makefile 定义的新变量增加进来.
所以包含include/config/auto.conf就是间接包含./.config;

启动流程
fs
///////////////////////////////////////////
kernel的编译和链接
Makefile的默认入口是: all
首先进入顶层Makefile :
……
# Objects we will link into vmlinux / subdirs we need to visit
init-y := init/ //上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
drivers-y := drivers/ sound/ firmware/ //上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
net-y := net/ //上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
libs-y := lib/ //上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
core-y := usr/ //上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
…….
# Read in config
-include include/config/auto.conf //include/config/auto.conf只是吧./.config文件的注释去掉,并且工具根目录的Makefile 定义的新变量增加进来.所以包含include/config/auto.conf就是间接包含./.config;
…….
//下面是arch/目录下对应cpu体系arch/$(ARCH)/目录
include $(srctree)/arch/$(SRCARCH)/Makefile //上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
进入arch/$(ARCH)/makefile
…….
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o //有MMU的cpu则MMUEXT为空
…….
# If we have a machine-specific directory, then include it in the build. //扩展core-y,drivers-y,libs-y的编译范围,只要是cpu体系相关内容
core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y += $(machdirs) $(platdirs)
core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/
core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)
core-$(CONFIG_VFP) += arch/arm/vfp/

drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/

libs-y := arch/arm/lib/ $(libs-y)
…….
# Default target when executing plain make //zlmage映像的入口代码是自引导程序。自引导程序包含一些初始化代码,所以它是体系结构相关的,这个目录是arch/arm/boot。
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := xipImage
else
KBUILD_IMAGE := zImage
endif

all: $(KBUILD_IMAGE)

boot := arch/arm/boot

archprepare:
$(Q)$(MAKE) $(build)=arch/arm/tools include/generated/mach-types.h

# Convert bzImage to zImage
bzImage: zImage

zImage Image xipImage bootpImage uImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

zinstall install: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
…….
回到顶层Makefile ./Makefile
…….
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ //上面21个目录除了include目录和不包含kernel代码的5个目录scripts目录,Documents目录,virt目录,usr目录,tools目录,samples目录,其它15个目录都参与kernel编译.
…….
//$(patsubst,pattern,replacement,text)表示寻找”text”中符合格式”pattern”的字,用”replacement”替换.
init-y := $(patsubst %/, %/built-in.o, $(init-y))
core-y := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)

# Build vmlinux
# —————————————————————————
# vmlinux is built from the objects selected by $(vmlinux-init) and
# $(vmlinux-main). Most are built-in.o files from top-level directories
# in the kernel tree, others are specified in arch/$(ARCH)/Makefile.
# Ordering when linking is important, and $(vmlinux-init) must be first.
#
# vmlinux
# ^
# |
# +-< $(vmlinux-init)
# | +–< init/version.o + more
# |
# +–< $(vmlinux-main)
# | +–< driver/built-in.o mm/built-in.o + more
# |
# +-< kallsyms.o (see description in CONFIG_KALLSYMS section)
#
# vmlinux version (uname -v) cannot be updated during normal
# descending-into-subdirs phase since we do not yet know if we need to
# update vmlinux.
# Therefore this step is delayed until just before final link of vmlinux –
# except in the kallsyms case where it is done just before adding the
# symbols to the kernel.
#
# System.map is generated to document addresses of all kernel symbols

vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds //链接脚本由arch/$(ARCH)/kernel/vmlinux.lds.s生成
…….
进入arch/$(SRCARCH)/kernel/vmlinux.lds内容;
…….
OUTPUT_ARCH(arm)
ENTRY(stext)
jiffies = jiffies_64;
SECTIONS
{
. = 0xC0000000 + 0x00008000;
.init : { /* Init code and data */
_stext = .;
_sinittext = .;
*(.head.text)
*(.init.text) *(.cpuinit.text) *(.meminit.text)
_einittext = .;
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
. = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
__con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
__security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .;
. = ALIGN((1 << 12)); __initramfs_start = .; *(.init.ramfs) __initramfs_end = .;
__init_begin = _stext;
*(.init.data) *(.cpuinit.data) *(.meminit.data) . = ALIGN(8); __ctors_start = .; *(.ctors) __ctors_end = .; *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata)
}
. = ALIGN((1 << 12)); .data..percpu : AT(ADDR(.data..percpu) – 0) { __per_cpu_load = .; __per_cpu_start = .; *(.data..percpu..first) *(.data..percpu..page_aligned) *(.data..percpu) *(.data..percpu..shared_aligned) __per_cpu_end = .; }
. = ALIGN((1 << 12));
__init_end = .;
/*
* unwind exit sections must be discarded before the rest of the
* unwind sections get included.
*/
/DISCARD/ : {
*(.ARM.exidx.exit.text)
*(.ARM.extab.exit.text)
*(.ARM.exidx.cpuexit.text)
*(.ARM.extab.cpuexit.text)
}
.text : { /* Real text segment */
_text = .; /* Text and read-only data */
__exception_text_start = .;
*(.exception.text)
__exception_text_end = .;
. = ALIGN(8); *(.text.hot) *(.text) *(.ref.text) *(.devinit.text) *(.devexit.text) *(.text.unlikely)
. = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;
. = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;
. = ALIGN(8); __kprobes_text_start = .; *(.kprobes.text) __kprobes_text_end = .;
*(.fixup)
*(.gnu.warning)
*(.rodata)
*(.rodata.*)
*(.glue_7)
*(.glue_7t)
*(.got) /* Global offset table */
}
. = ALIGN(((1 << 12))); .rodata : AT(ADDR(.rodata) – 0) { __start_rodata = .; *(.rodata) *(.rodata.*) *(__vermagic) *(__markers_strings) *(__tracepoints_strings) } .rodata1 : AT(ADDR(.rodata1) – 0) { *(.rodata1) } .pci_fixup : AT(ADDR(.pci_fixup) – 0) { __start_pci_fixups_early = .; *(.pci_fixup_early) __end_pci_fixups_early = .; __start_pci_fixups_header = .; *(.pci_fixup_header) __end_pci_fixups_header = .; __start_pci_fixups_final = .; *(.pci_fixup_final) __end_pci_fixups_final = .; __start_pci_fixups_enable = .; *(.pci_fixup_enable) __end_pci_fixups_enable = .; __start_pci_fixups_resume = .; *(.pci_fixup_resume) __end_pci_fixups_resume = .; __start_pci_fixups_resume_early = .; *(.pci_fixup_resume_early) __end_pci_fixups_resume_early = .; __start_pci_fixups_suspend = .; *(.pci_fixup_suspend) __end_pci_fixups_suspend = .; } .builtin_fw : AT(ADDR(.builtin_fw) – 0) { __start_builtin_fw = .; *(.builtin_fw) __end_builtin_fw = .; } .rio_ops : AT(ADDR(.rio_ops) – 0) { __start_rio_switch_ops = .; *(.rio_switch_ops) __end_rio_switch_ops = .; } __ksymtab : AT(ADDR(__ksymtab) – 0) { __start___ksymtab = .; *(__ksymtab) __stop___ksymtab = .; } __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) – 0) { __start___ksymtab_gpl = .; *(__ksymtab_gpl) __stop___ksymtab_gpl = .; } __ksymtab_unused : AT(ADDR(__ksymtab_unused) – 0) { __start___ksymtab_unused = .; *(__ksymtab_unused) __stop___ksymtab_unused = .; } __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) – 0) { __start___ksymtab_unused_gpl = .; *(__ksymtab_unused_gpl) __stop___ksymtab_unused_gpl = .; } __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) – 0) { __start___ksymtab_gpl_future = .; *(__ksymtab_gpl_future) __stop___ksymtab_gpl_future = .; } __kcrctab : AT(ADDR(__kcrctab) – 0) { __start___kcrctab = .; *(__kcrctab) __stop___kcrctab = .; } __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) – 0) { __start___kcrctab_gpl = .; *(__kcrctab_gpl) __stop___kcrctab_gpl = .; } __kcrctab_unused : AT(ADDR(__kcrctab_unused) – 0) { __start___kcrctab_unused = .; *(__kcrctab_unused) __stop___kcrctab_unused = .; } __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) – 0) { __start___kcrctab_unused_gpl = .; *(__kcrctab_unused_gpl) __stop___kcrctab_unused_gpl = .; } __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) – 0) { __start___kcrctab_gpl_future = .; *(__kcrctab_gpl_future) __stop___kcrctab_gpl_future = .; } __ksymtab_strings : AT(ADDR(__ksymtab_strings) – 0) { *(__ksymtab_strings) } __init_rodata : AT(ADDR(__init_rodata) – 0) { *(.ref.rodata) *(.devinit.rodata) *(.devexit.rodata) } __param : AT(ADDR(__param) – 0) { __start___param = .; *(__param) __stop___param = .; . = ALIGN(((1 << 12))); __end_rodata = .; } . = ALIGN(((1 << 12)));
_etext = .; /* End of text and rodata section */
/*
* Stack unwinding tables
*/
. = ALIGN(8);
.ARM.unwind_idx : {
__start_unwind_idx = .;
*(.ARM.exidx*)
__stop_unwind_idx = .;
}
.ARM.unwind_tab : {
__start_unwind_tab = .;
*(.ARM.extab*)
__stop_unwind_tab = .;
}
. = ALIGN(8192);
__data_loc = .;
.data : AT(__data_loc) {
_data = .; /* address in memory */
_sdata = .;
/*
* first, the init task union, aligned
* to an 8192 byte boundary.
*/
. = ALIGN(8192); *(.data..init_task)
. = ALIGN((1 << 12)); __nosave_begin = .; *(.data..nosave) . = ALIGN((1 << 12)); __nosave_end = .;
. = ALIGN(32); *(.data..cacheline_aligned)
/*
* The exception fixup table (might need resorting at runtime)
*/
. = ALIGN(32);
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
/*
* and the usual data section
*/
*(.data) *(.ref.data) *(.devinit.data) *(.devexit.data) . = ALIGN(32); __start___tracepoints = .; *(__tracepoints) __stop___tracepoints = .; . = ALIGN(8); __start___verbose = .; *(__verbose) __stop___verbose = .; __start___trace_bprintk_fmt = .; *(__trace_printk_fmt) __stop___trace_bprintk_fmt = .; . = ALIGN(32); __start_ftrace_events = .; *(_ftrace_events) __stop_ftrace_events = .; . = ALIGN(32);
CONSTRUCTORS
_edata = .;
}
_edata_loc = __data_loc + SIZEOF(.data);
. = ALIGN(0); __bss_start = .; . = ALIGN(0); .sbss : AT(ADDR(.sbss) – 0) { *(.sbss) *(.scommon) } . = ALIGN(0); .bss : AT(ADDR(.bss) – 0) { *(.bss..page_aligned) *(.dynbss) *(.bss) *(COMMON) } . = ALIGN(0); __bss_stop = .;
_end = .;
.stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) }
.comment 0 : { *(.comment) }
/* Default discards */
/DISCARD/ : { *(.exit.text) *(.cpuexit.text) *(.memexit.text) *(.exit.data) *(.cpuexit.data) *(.cpuexit.rodata) *(.memexit.data) *(.memexit.rodata) *(.exitcall.exit) *(.discard) *(.discard.*) }
}
/*
* These must never be empty
* If you have to comment these two assert statements out, your
* binutils is too old (for other reasons as well)
*/
ASSERT((__proc_info_end – __proc_info_begin), “missing CPU support”)
ASSERT((__arch_info_end – __arch_info_begin), “no machine record defined”)

编译完成后,zImage放在/arch/arm/boot/
vmlinux:
可独立运行的、未压缩的内核
System.map:
符号表
arch/<arch>/boot/zImage :
zlib压缩过的内核
ARM的默认内核镜像

///////////////////////////////////////////
kernel的启动流程:
启动过程从内核映像入口开始执行
解压vmlinux并且转换到虚拟地址空间
调用统一的内核启动函数start_kernel(),从而启动整个linux系统。
zlmage映像的入口代码是自引导程序。自引导程序包含一些初始化代码,所以它是体系结构相关的,这个目录是arch/arm/boot。
那么第一条指令所在的文件是自引导程序中的head.S。
进入arch/arm/boot/head.s
…….
//decompress_kernel 函数则是C语言写的,在arch\arm\boot\compressed文件中实现
bl decompress_kernel
//Call_kernel 子程序完成启动vmlinux的任务,它负责关闭CACHE,在R2寄存器中恢复系统平台号,然后跳转到vmlinux的入口。这样控制权就完全交给vmlinux执行了。
call_kernel: bl cache_clean_flush
bl cache_off
mov r0, #0 @ must be zero
mov r1, r7 @ restore architecture number
mov r2, r8 @ restore atags pointer
mov pc, r4 @ call kernel
…….
进入arch/arm/kernel/head$(MMUEXT).o //arch/arm/kernel/head.s有MMU的cpu则MMUEXT为空
/*
* Kernel startup entry point.
* —————————
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We’re trying to keep crap to a minimum; DO NOT add any machine specific
* crap here – that’s what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
这一段汇编代码的主要作用,是检查cpu id,architecture number,初始化页表、cpu、bbs等操作,并跳到start_kernel函数。它在执行前,处理器的状态应满足:
r0 – should be 0
r1 – unique architecture number-机器类型ID
MMU – off
I-cache – on or off
D-cache – off
……
__HEAD
ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode //确保进入管理(svc)模式
@ and irqs disabled //并且禁止中断
mrc p15, 0, r9, c0, c0 @ get processor id //读取CPU ID存入r9
bl __lookup_processor_type @ r5=procinfo r9=cpuid //检查CPU ID
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error ‘p’
bl __lookup_machine_type @ r5=machinfo //检查机器类型ID
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error ‘a’
说明检查CPU ID需要进入arch/arm/kernel/head-common.s,并且对应CPU ID的源码arch/arm/mm/proc-v6.S要编译进kernel,因为CONFIG_CPU_V6=y,ARM11—ARMv6KZ—ARM1176JZ(F)-S
arch/arm/mm/proc-v6.S部分内容
…………
.type cpu_v6_name, #object
cpu_v6_name:
.asciz “ARMv6-compatible processor”
.size cpu_v6_name, . – cpu_v6_name
………….
.section “.proc.info.init”, #alloc, #execinstr

/*
* Match any ARMv6 processor core.
*/
.type __v6_proc_info, #object
__v6_proc_info:
.long 0x0007b000
.long 0x0007f000
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ | \
PMD_FLAGS
.long PMD_TYPE_SECT | \
PMD_SECT_XN | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __v6_setup
.long cpu_arch_name
.long cpu_elf_name
/* See also feat_v6_fixup() for HWCAP_TLS */
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA|HWCAP_TLS
.long cpu_v6_name
.long v6_processor_functions
.long v6wbi_tlb_fns
.long v6_user_fns
.long v6_cache_fns
.size __v6_proc_info, . – __v6_proc_info

.type __pj4_v6_proc_info, #object
__pj4_v6_proc_info:
.long 0x560f5810
.long 0xff0ffff0
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ | \
PMD_FLAGS
.long PMD_TYPE_SECT | \
PMD_SECT_XN | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __v6_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_TLS
.long cpu_pj4_name
.long v6_processor_functions
.long v6wbi_tlb_fns
.long v6_user_fns
.long v6_cache_fns
.size __pj4_v6_proc_info, . – __pj4_v6_proc_info
……..
.section “.proc.info.init”由arch/arm/kernel/vmlinux.lds链接脚本中:
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
所以arch/arm/kernel/head-common.s里面的__lookup_processor_type就是比较__proc_info_begin—__proc_info_end是否符合的CPU ID.
__lookup_machine_type也要进入arch/arm/kernel/head-common.s,并且对应机器类型 ID的源码arch\arm\mach-s3c64xx\mach-smdk6410.c要编译进kernel.
arch\arm\mach-s3c64xx\mach-smdk6410.c部分内容:
MACHINE_START(SMDK6410, “SMDK6410”)
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C64XX_PA_SDRAM + 0x100,

.init_irq = s3c6410_init_irq,
.map_io = smdk6410_map_io,
.init_machine = smdk6410_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END

其中宏定义在arch/arm/include/asm/mach/Arch.h
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(“.arch.info.init”))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,

#define MACHINE_END \
};
……..
回到arch/arm/kernel/head.s,下面
bl __create_page_tables创建一级页表.
经过一系列的初始化过程,打开MMU
……..
引导最后
ldr r13, __switch_data @ address to jump to after
跳转到arch/arm/kernel/head-common.s里面经过清除bss后
…….
b start_kernel //跳转start_kernel( )函数负责初始化内核子系统,

进入第二阶段,进入/init/main.c的start_kernel( )函数
………
setup_arch(&command_line); //位于arch/arm/kernel/setup.c
进入arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;

unwind_init();

setup_processor(); //处理器相关设置,他会调用lookup_processor_type函数获取proc_info_list结构,源码arch/arm/mm/proc-v6.S
mdesc = setup_machine(machine_arch_type); //他会调用lookup_machine_type函数获取machine_type结构,arch\arm\mach-s3c64xx\mach-smdk6410.c
machine_name = mdesc->name;
……..
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
else if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params); //boot_params=S3C64XX_PA_SDRAM + 0x100,其中#define S3C64XX_PA_SDRAM (0x50000000)
……..

if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags); //
}

init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = (unsigned long) _end;

/* parse_early_param needs a boot_command_line */
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); //=”root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc nconsole=tty1 console=ttySAC0,115200″

/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;

parse_early_param();
……
源码arch\arm\mach-s3c64xx\mach-smdk6410.c的smdk6410_map_io会在start_kernel()->setup_arch()->paging_init()->devicemaps_init()调用到.所以修改开发板晶振频率
……
…….
}
回到/init/main.c的start_kernel( )函数
…….
parse_early_param();
parse_args(“Booting kernel”, static_command_line, __start___param,
__stop___param – __start___param,
&unknown_bootoption);//=”root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc nconsole=tty1 console=ttySAC0,115200″
比如console=会在/kernel/printk.c的console_setup()处理

在start_kernel()的最后,最后调用rest-init(),启动一个动作init的内核线程,系统创建第一个内核核心线程继续初始化。
而内核核心线程init()主要是来进行
1.负责完成个挂根文件系统
populate_rootfs()
Linux能够在内存中虚拟磁盘文件系统,叫作ramdisk。
preparenamespace()
它负责为Linux系统挂接一个根文件系统。

2.初始化设备驱动
初始化内核设备驱动程序,然后才能访问设备,做系统真正想要做的任务。
do_basic_setup函数
在_initcall_start 和_initcall_end之间的函数指针数组
会在rest_init()->kernel_init()->do_basic_setup()->do_initcalls()->customize_machine()->smdk6410_machine_init()调用.
do_initcalls调用上面的函数指针数组完成驱动程序初始化
当do_basic_setup()函数返回init(),init()又打开了/dev/console设备,
重定向三个标准的输入输出文件stdin、stdout和stderr到控制台

3.启动用户空间的init进程等重要文件
Linux系统在挂接根文件系统之后,要执行文件系统的中的应用程序。
最后,搜索文件系统中的init程序(或者由init=命令行参数指定的程序),
并使用 execve()系统调用加载执行用户空间的init程序。到此内核init()函数结束,内核的引导部分也到此结束了
Init进程是通过执行根文件系统中init程序启动的。
run_init_process函数
run_init_process()函数通过execve函数执行应用程序,execute()又调用do_execve ()
Execve函数调用do_execve()执行用户空间程序。如果执行失败,则返回;如果执行成功,就不能返回。


邮件
ppt
txt

///////////////////////////////////////////
kernel的移植
1.
ARCH ?= arm
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:”%”=%)

2.需要确保对应CPU ID的源码arch/arm/mm/proc-v6.S要编译进kernel,所以arch/arm/mm/的Makefile 里面的CONFIG_CPU_V6=y,
就是SystemType->ARM system type->ARCH_S3C64XX

3.对应机器类型 ID的源码arch\arm\mach-s3c64xx\mach-smdk6410.c要编译进kernel,所以CONFIG_MACH_SMDK6410=y,
就是SystemType->SMDK6410

4.源码arch\arm\mach-s3c64xx\mach-smdk6410.c的smdk6410_map_io会在start_kernel()->setup_arch()->paging_init()->devicemaps_init()调用到.所以修改开发板晶振频率

5.源码arch\arm\mach-s3c64xx\mach-smdk6410.c上网smdk_default_nand_part描述了flash分区,
设备驱动会在rest_init()->kernel_init()->do_basic_setup()->do_initcalls()->customize_machine()->smdk6410_machine_init()调用

5.fs支持
/*
* Configuring Nandflash on SMDK6410
*/
struct mtd_partition ok6410_nand_part[] = {
{
.name = “Bootloader”,
.offset = 0,
.size = (1 * SZ_1M),
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = “Kernel”,
.offset = (1 * SZ_1M),
.size = (5*SZ_1M) ,
.mask_flags = MTD_CAP_NANDFLASH,
},

{
.name = “User”,
.offset = (6 * SZ_1M),
.size = (200*SZ_1M) ,
},
{
.name = “File System”,
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
1.arch/这是体系相关目录,每个cpu对应一个子目录,比如arch/arm
移植需要修改的目录:arch/arm/
5.drivers/这是所有设备的驱动,每个子目录对应一类设备驱动程序
移植需要修改的目录:
drivers/char/adc.c , pwm.c , s3c6410_leds.c
drivers/input\touchscreen\s3c-ts.c
drivers/media\video\samsung\*.*
drivers/mtd\nand\s3c_nand.c , nand_ids.c , nand_base.c
drivers/net/dm9000.c , dm9000_con201.c
drivers/serial/samsung.c
drivers/usb/3g_devices.c , 3g_model.h , option.c , pl2303.c
drivers/video/samsung/*.*
7.fs/这是支持的文件系统的代码,每个子目录对应一种文件系统
移植需要修改的目录:
\fs\cramfs\inode.c
\fs\fuse\file.c
\fs\yaffs2\*.*
8.include/这是内核头文件,有基本头文件incldue/linux/,有驱动头文件include/meida,include/mtd,include/net,有体系相关头文件include/asm-arm
移植需要修改的目录:
\include\linux\akm8975.h
\linux\cramfs_fs_sb.h
\include\linux\version.h
\include\media\v4l2-dev.h
\include\mtd\mtd-abi.h
18.sound/这是音频设备的驱动程序
移植需要修改的目录:
\sound\soc\s3c24xx\s3c-ac97.c
\sound\soc\s3c24xx\smdk_wm9713.c


mini2410的pdf
ppt
txt
找一个能编译ok的kernel,比如先make deconfig,使用samsung的kernel
make menuconfig 与 make oldconfig的区别?
subsystemcall与moduleinit?
probe?

Linux驱动中,probe函数何时被调用
最近看到linux的设备驱动模型,关于Kobject、Kset等还不是很清淅。看到了struct device_driver这个结构时,想到一个问题:它的初始化函数到底在哪里调用呢?以前搞PCI驱动时用pci驱动注册函数就可以调用它,搞s3c2410驱动时只要在mach-smdk2410.c中的struct platform_device *smdk2410_devices {}中加入设备也会调用。但从来就没有想过具体的驱动注册并调用probe的过程。

于是打开SourceInsight追踪了一下:

从driver_register看起:
int driver_register(struct device_driver * drv)
{
klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
init_completion(&drv->unloaded);
return bus_add_driver(drv);
}

klist_init与init_completion没去管它,可能是2.6的这个设备模型要做的一些工作。直觉告诉我要去bus_add_driver。

bus_add_driver中:
都是些Kobject 与 klist 、attr等。还是与设备模型有关的。但是其中有一句:
driver_attach(drv);
单听名字就很像:
void driver_attach(struct device_driver * drv)
{
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
这个熟悉,遍历总线上的设备并设用__driver_attach。
在__driver_attach中又主要是这样:
driver_probe_device(drv, dev);
跑到driver_probe_device中去看看:
有一段很重要:
if (drv->bus->match && !drv->bus->match(dev, drv))
goto Done;
明显,是调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。
继承执行的话:
if (drv->probe) {
ret = drv->probe(dev);
if (ret) {
dev->driver = NULL;
goto ProbeFailed;
}
只要probe存在则调用之。至此就完成了probe的调用。

这个过程链的关键还是在drv->bus->match ,因为其余的地方出错的话就是注册失败,而只要注册不失败且match返回1,那么就铁定会调用驱程的probe了。你可以注册一个总线类型和总线,并在match中总是返回 1, 会发现,只要struct device_driver中的bus类型正确时,probe函数总是被调用.
PCI设备有自己的总线模型,估计在它的match中就有一个判断的条件。

static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *pci_drv = to_pci_driver(drv);
const struct pci_device_id *found_id;

found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;

return 0;
}

再往下跟踪就知道主要是根据我们熟悉的id_table来的。
——————————-另解———————————————————————————————–
从driver_register看起,此处我的这里是:

int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING “Driver ‘%s’ needs updating – please use bus_type methods\n”, drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);
}

klist_init不相关,不用管他,具体再去看bus_add_driver:
int bus_add_driver(struct device_driver *drv)
{
//1.先kobject_set_name(&drv->kobj, “%s”, drv->name);
//2.再kobject_register(&drv->kobj)
//3.然后调用了:driver_attach(drv)
}
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
真正起作用的是__driver_attach:

static int __driver_attach(struct device * dev, void * data)
{

if (!dev->driver)
driver_probe_device(drv, dev);

}
int driver_probe_device(struct device_driver * drv, struct device * dev)
{

//1.先是判断bus是否match:
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
//2.再具体执行probe:
ret = really_probe(dev, drv);

}

really_probe才是我们要找的函数:

static int really_probe(struct device *dev, struct device_driver *drv)
{

//1.先是调用的驱动所属总线的probe函数:
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;

} else if (drv->probe) {
//2.再调用你的驱动中的probe函数:
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}

}

其中,drv->probe(dev),才是真正调用你的驱动实现的具体的probe函数。
也就是对应此文标题所问的,probe函数此时被调用。
声明:以下主要内容参考自:
关于struct device_driver结构中的probe探测函数的调用
http://blog.chinaunix.net/u2/71164/showart.php?id=1361188

宏定义__define_initcall(level,fn)对于内核的初始化很重要,他指示编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序放在一个section中。在内核初始化段,do_initcalls() 将按顺序从该section中以函数指针的形式取出这些函数的起始地址,来依次完成相应的初始化。于内核某些部分的初始化需要依赖于其他某些部分的初始化的完成,因此这个顺序排列常常很重要。
下面将从__define_initcall(level,fn) 宏定义的代码分析入手,依次分析名称为initcall.init的section的结构,最后分析内核初始化函数do_initcalls()是怎样利用宏定义__define_initcall(level,fn)及其相关的衍生的7个宏宏定义,来实现内核某些部分的顺序初始化的。
1、分析 __define_initcall(level,fn) 宏定义
1) 这个宏的定义位于inlclude\linux\init.h中:
#define __define_initcall(level,fn) \
static initcall_t __initcall_##fn \
__attribute__((__section__(“.initcall” level “.init”))) \
= fn
其中 initcall_t 是个函数指针类型:
typedef int (*initcall_t)(void);
而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。所以这个宏定义的的含义是:1) 声明一个名称为__initcall_##fn的函数指针(其中##表示替换连接,);2) 将这个函数指针初始化为fn;3) 编译的时候需要把这个函数指针变量放置到名称为 “.initcall” level “.init”的section中(比如level=”1″,代表这个section的名称是 “.initcall1.init”)。
2) 举例:__define_initcall(6, pci_init)
上述宏调用的含义是:1) 声明一个函数指针__initcall_pic_init = pci_init;且 2) 这个指针变量__initcall_pic_init 需要放置到名称为 .initcall6.init的section中( 其实质就是将 这个函数pic_init的首地址放置到了这个section中)。
3) 这个宏一般并不直接使用,而是被定义成下述其他更简单的7个衍生宏
这些衍生宏宏的定义也位于 inlclude\linux\Init.h 中:
#define core_initcall(fn) __define_initcall(“1”,fn)
#define postcore_initcall(fn) __define_initcall(“2”,fn)
#define arch_initcall(fn) __define_initcall(“3”,fn)
#define subsys_initcall(fn) __define_initcall(“4”,fn)
#define fs_initcall(fn) __define_initcall(“5”,fn)
#define device_initcall(fn) __define_initcall(“6”,fn)
#define late_initcall(fn) __define_initcall(“7”,fn)
因此通过宏 core_initcall() 来声明的函数指针,将放置到名称为.initcall1.init的section中,而通过宏 postcore_initcall() 来声明的函数指针,将放置到名称为.initcall2.init的section中,依次类推。
4) 举例:device_initcall(pci_init)
解释同上 1-2)。
2、和初始化调用有关section–initcall.init被分成了7个子section
1) 他们依次是.initcall1.init、.initcall2.init、…、.initcall7.init
2) 按照先后顺序依次排列
3) 他们的定义在文档vmlinux.lds.S中
例如 对于i386+,在i386\kernel\vmlinux.lds.S中有:
__initcall_start = .;
.initcall.init : {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
而在makefile 中有

LDFLAGS_vmlinux += -T arch/$(ARCH)/kernel/vmlinux.lds.s
4) 在这7个section总的开始位置被标识为__initcall_start,而在结尾被标识为__initcall_end。
3、 内核初始化函数do_basic_setup(): do_initcalls() 将从.initcall.init中,也就是这7个section中依次取出任何的函数指针,并调用这些函数指针所指向的函数,来完成内核的一些相关的初始化。
这个函数的定义位于init\main.c中:
extern initcall_t __initcall_start, __initcall_end;
static void __init do_initcalls(void)
{
initcall_t *call;
….
for (call = &__initcall_start; call
***********************************************************************

假如您希望某个初始化函数在内核初始化阶段就被调用,那么您就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏来把这个初始化函数fn的起始地址按照初始化的顺序放置到相关的section 中。 内核初始化时的do_initcalls()将从这个section中按顺序找到这些函数来执行。
module_init解析
2012-06-25 14:19 67人阅读 评论(0) 收藏 举报
像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
其中,init.h 定义了驱动的初始化和退出相关的函数,kernel.h 定义了经常用到的函数原型及宏定义,module.h 定义了内核模块相关的函数、变量及宏。

几乎每个linux驱动都有个module_init(与module_exit的定义在Init.h (\include\linux) 中)。没错,驱动的加载就靠它。为什么需要这样一个宏?原因是按照一般的编程想法,各部分的初始化函数会在一个固定的函数里调用比如:

void init(void)

{

init_a();

init_b();

}

如果再加入一个初始化函数呢,那么在init_b()后面再加一行:init_c();这样确实能完成我们的功能,但这样有一定的问题,就是不能独立的添加初始化函数,每次添加一个新的函数都要修改init函数。可以采用另一种方式来处理这个问题,只要用一个宏来修饰一下:

void init_a(void)

{

}

__initlist(init_a, 1);

它是怎么样通过这个宏来实现初始化函数列表的呢?先来看__initlist的定义:

#define __init __attribute__((unused, __section__(“.initlist”)))

#define __initlist(fn, lvl) \
static initlist_t __init_##fn __init = { \
magic: INIT_MAGIC, \
callback: fn, \
level: lvl }

请注意:__section__(“.initlist”),这个属性起什么作用呢?它告诉连接器这个变量存放在.initlist区段,如果所有的初始化函数都是用这个宏,那么每个函数会有对应的一个initlist_t结构体变量存放在.initlist区段,也就是说我们可以在.initlist区段找到所有初始化函数的指针。怎么找到.initlist区段的地址呢?

extern u32 __initlist_start;
extern u32 __initlist_end;

这两个变量起作用了,__initlist_start是.initlist区段的开始,__initlist_end是结束,通过这两个变量我们就可以访问到所有的初始化函数了。这两个变量在那定义的呢?在一个连接器脚本文件里

. = ALIGN(4);
.initlist : {
__initlist_start = .;
*(.initlist)
__initlist_end = .;
}
这两个变量的值正好定义在.initlist区段的开始和结束地址,所以我们能通过这两个变量访问到所有的初始化函数。

与此类似,内核中也是用到这种方法,所以我们写驱动的时候比较独立,不用我们自己添加代码在一个固定的地方来调用我们自己的初始化函数和退出函数,连接器已经为我们做好了。先来分析一下module_init。定义如下:

#define module_init(x) __initcall(x); //include\linux\init.h

#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn) __define_initcall(“6”,fn,6)

#define __define_initcall(level,fn,id) \

static initcall_t __initcall_##fn##id __used \

__attribute__((__section__(“.initcall” level “.init”))) = fn

如果某驱动想以func作为该驱动的入口,则可以如下声明:module_init(func);被上面的宏处理过后,变成__initcall_func6 __used加入到内核映像的”.initcall”区。内核的加载的时候,会搜索”.initcall”中的所有条目,并按优先级加载它们,普通驱动程序的优先级是6。其它模块优先级列出如下:值越小,越先加载。

#define pure_initcall(fn) __define_initcall(“0”,fn,0)

#define core_initcall(fn) __define_initcall(“1”,fn,1)

#define core_initcall_sync(fn) __define_initcall(“1s”,fn,1s)

#define postcore_initcall(fn) __define_initcall(“2”,fn,2)

#define postcore_initcall_sync(fn) __define_initcall(“2s”,fn,2s)

#define arch_initcall(fn) __define_initcall(“3”,fn,3)

#define arch_initcall_sync(fn) __define_initcall(“3s”,fn,3s)

#define subsys_initcall(fn) __define_initcall(“4”,fn,4)

#define subsys_initcall_sync(fn) __define_initcall(“4s”,fn,4s)

#define fs_initcall(fn) __define_initcall(“5”,fn,5)

#define fs_initcall_sync(fn) __define_initcall(“5s”,fn,5s)

#define rootfs_initcall(fn) __define_initcall(“rootfs”,fn,rootfs)

#define device_initcall(fn) __define_initcall(“6”,fn,6)

#define device_initcall_sync(fn) __define_initcall(“6s”,fn,6s)

#define late_initcall(fn) __define_initcall(“7”,fn,7)

#define late_initcall_sync(fn) __define_initcall(“7s”,fn,7s)

可以看到,被声明为pure_initcall的最先加载。

module_init除了初始化加载之外,还有后期释放内存的作用。linux kernel中有很大一部分代码是设备驱动代码,这些驱动代码都有初始化和反初始化函数,这些代码一般都只执行一次,为了有更有效的利用内存,这些代码所占用的内存可以释放出来。

linux就是这样做的,对只需要初始化运行一次的函数都加上__init属性,__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,module_exit的参数卸载时同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作,显然__init和__exit对动态加载的模块是无效的,只支持完全编译进内核。

在kernel初始化后期,释放所有这些函数代码所占的内存空间。连接器把带__init属性的函数放在同一个section里,在用完以后,把整个section释放掉。当函数初始化完成后这个区域可以被清除掉以节约系统内存。Kenrel启动时看到的消息“Freeing unused kernel memory: xxxk freed”同它有关。

我们看源码,init/main.c中start_kernel是进入kernel()的第一个c函数,在这个函数的最后一行是rest_init();

static void rest_init(void)
{
…..

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
unlock_kernel();
cpu_idle();

…..
}
创建了一个内核线程,主函数kernel_init末尾有个函数:

/*
* Ok, we have completed the initial bootup, and
* we’re essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
init_post();

这个init_post中的第一句就是free_initmem();就是用来释放初始化代码和数据的。

void free_initmem(void)
{
if (!machine_is_integrator() && !machine_is_cintegrator()) {
free_area((unsigned long)(&__init_begin),
(unsigned long)(&__init_end),
“init”);
}
}

接下来就是kernel内存管理的事了。

其中do_basic_setup()在init_post前调用,完成初始化

在Linux底下写过driver模块的对这个宏一定不会陌生。module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(obj-y)。

1.在MODULE没有定义这种情况下,module_init定义如下:
#define module_init(x) __initcall(x);
因为
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(“6”,fn,6)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(“.initcall” level “.init”))) = fn
所以,module_init(x)最终展开为:
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(“.initcall” level “.init”))) = fn
更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于:
static initcall_t __initcall_gpio_init_6 __used __attribute__((__section__(“.initcall6.init”))) = gpio_init;
就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。
这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为”.initcall6.init”节中。接下来结合vmlinux.lds中的
.initcall.init : AT(ADDR(.initcall.init) – (0xc0000000 -0x00000000)) {
__initcall_start = .;
*(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
__initcall_end = .;
}
以及do_initcalls:
static void __init do_initcalls(void)
{
initcall_t *call;
for (call = __initcall_start; call < __initcall_end; call++)
do_one_initcall(*call);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
那么就不难理解阁下模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。
//第二中情况在Android中没用到
2.在MODULE被定义的情况下(大部分可动态加载的driver模块都属于此, obj-m),module_init定义如下:
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
这段宏定义关键点是后面一句,通过alias将initfn变名为init_module。前面那个__inittest的定义其实是种技巧,用来对initfn进行某种静态的类型检查,如果阁下将模块初始化函数定义成,比如,void gpio_init(void)或者是int gpio_init(int),那么在编译时都会有类似下面的warning:
GPIO/fsl-gpio.c: In function ‘__inittest’:
GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type
通过module_init将模块初始化函数统一别名为init_module,这样以后insmod时候,在系统内部会调用sys_init_module()去找到init_module函数的入口地址。
如果objdump -t gpio.ko,就会发现init_module和gpio_init位于相同的地址偏移处。简言之,这种情况下模块的初始化函数在insmod时候被调用

drivers\media\video\samsung\fimc\Makefile
obj-$(CONFIG_VIDEO_FIMC) += s3c_fimc_core.o s3c_fimc_v4l2.o s3c_fimc_cfg.o
obj-$(CONFIG_ARCH_S3C64XX) += s3c_fimc3x_regs.o
obj-$(CONFIG_OV965X) += ov965x.o

平台总线对应的设备是cpu的cameraInterface控制器, 对应的driver是S3c_fimc_XXX,在driver 的probe里面会向v4l2添加一个

设备以及相关的文件操作函数(open,close,read,write)和ioctrl操作函数.
driver有内存通过copy_to_user给用户提供的memoryHeap

i2c控制器作为平台总线的一个设备,对应驱动是i2c-s3c2410,在驱动的probe里面会添加i2c总线上的board(实现声明的i2c设备

)

i2c设备对应的驱动是ov965x.o,在驱动里面会向i2c总线注册,寻找匹配的设备并且调用自己的probe
drivers\i2c\busses\Makefile
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
drivers\i2c\Makefile
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o

i2c驱动包括i2c核心,i2c总线驱动,i2c设备驱动:
i2c总线驱动是对适配器的实现包括了(i2c适配器数据结构)i2c_adapter,(i2c适配器的algorithm数据结构)i2c_algorithm,(控制i2c适配器产生通信信号)函数
i2c设备驱动包含数据结构i2c_driver和i2c_client, 所有i2c设备显示在/sys/bus/i2c/

其中i2c-core.c实现i2c核心.
其中i2c-dev.c实现适配器设备文件

sound\Makefile
# This one must be compilable even if sound is configured out
obj-$(CONFIG_AC97_BUS) += ac97_bus.o

sound\soc\Makefile
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
sound\soc\s3c24xx\Makefile
obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
obj-$(CONFIG_SND_S3C_SOC_AC97) += snd-soc-s3c-ac97.o
obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
alsa声卡/dev/snd/pcmC0D0p的open打开流程
soc_probe
==> snd_soc_register_card或者snd_soc_register_dai或者snd_soc_register_platform或者snd_soc_register_codec
==> snd_soc_instantiate_cards
==> snd_soc_instantiate_card
==> codec_dev->probe
==> uda134x_soc_probe
==> snd_soc_new_pcms
==> soc_new_pcm
==> snd_pcm_new

snd_soc_new_pcms
snd_card_create
snd_ctl_create
snd_ctl_dev_register创建”controlC%i”声卡对应的控制节点,
他的fops为snd_ctl_f_ops

snd_pcm_dev_register
snd_register_device_for_dev
创建”pcmC%iD%ip”或者”pcmC%iD%ic”设备节点,它的fops为snd_pcm_f_ops[2]

snd_pcm_f_ops[2]
==> snd_pcm_playback_open或者snd_pcm_capture_open
==> snd_pcm_open或者oss的snd_pcm_oss_f_reg.snd_pcm_oss_open,这就对应/dev/dsp了
==> snd_pcm_open_file或者oss的snd_pcm_oss_open_file
==> snd_pcm_open_substream
==> soc_pcm_open

/* ASoC PCM operations */
static struct snd_pcm_ops soc_pcm_ops = {
.open = soc_pcm_open,
.close = soc_codec_close,
.hw_params = soc_pcm_hw_params,
.hw_free = soc_pcm_hw_free,
.prepare = soc_pcm_prepare,
.trigger = soc_pcm_trigger,
};
==> soc_pcm_open
cpu_dai->ops->startup // 无
platform->pcm_ops->open // s3c_dma_open 分配DMA空间
codec_dai->ops->startup // uda134x_startup
card_dai_link->ops->startup // s3c24xx_uda134x_startup 获取clock时钟源

// cpu_dai单元
struct snd_soc_dai s3c24xx_i2s_dai = {
.name = “s3c24xx-i2s”,
.id = 0,
.probe = s3c24xx_i2s_probe,
.suspend = s3c24xx_i2s_suspend,
.resume = s3c24xx_i2s_resume,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = S3C24XX_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = S3C24XX_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
.ops = &s3c24xx_i2s_dai_ops,
};
//platform单元
struct snd_soc_platform s3c24xx_soc_platform = {
.name = “s3c24xx-audio”,
.pcm_ops = &s3c_dma_ops,
.pcm_new = s3c_dma_new,
.pcm_free = s3c_dma_free_dma_buffers,
};

static struct snd_pcm_ops s3c_dma_ops = {
.open = s3c_dma_open,
.close = s3c_dma_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = s3c_dma_hw_params,
.hw_free = s3c_dma_hw_free,
.prepare = s3c_dma_prepare,
.trigger = s3c_dma_trigger,
.pointer = s3c_dma_pointer,
.mmap = s3c_dma_mmap,
};
// codec_dai单元
struct snd_soc_dai uda134x_dai = {
.name = “UDA134X”,
/* playback capabilities */
.playback = {
.stream_name = “Playback”,
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* capture capabilities */
.capture = {
.stream_name = “Capture”,
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* pcm operations */
.ops = &uda134x_dai_ops,
};

//card_dai_link单元
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
.name = “UDA134X”,
.stream_name = “UDA134X”,
.codec_dai = &uda134x_dai,
.cpu_dai = &s3c24xx_i2s_dai,
.ops = &s3c24xx_uda134x_ops,
};