前言
在加载驱动时,内核版本与驱动版本是必须严格匹配的,否则会出现驱动加载失败的情况。当驱动与内核版本不匹配时,最快的方法自然是更换内核或驱动。但面对一些特殊情况时,不得不修改内核版本号强行匹配驱动版本,本文将就该问题进行处理。
内核版本号是如何生成的
在kernel根目录的Makefile中可以查到,内核版本号(KERNELRELEASE)储存在include/config/kernel.release中,会在编译内核时生成。可以通过cat命令查看内核版本号。
cat include/config/kernel.release
4.4.77-gb1fd820b-dirty
那么kernel.release这个文件是如何生成的呢?
在Makefile中,使用路径include/config/kernel.release作为搜索关键词可以找到kernel.release的生成过程。
# Store (new) KERNELRELEASE string in include/config/kernel.release
include/config/kernel.release: include/config/auto.conf FORCE
$(call filechk,kernel.release)
define filechk_kernel.release
#这段代码的目的是在生成 kernel.release 文件时,使用内核版本号和执行 setlocalversion 脚本的结果作为文件的内容。
echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
endef
可知,(KERNELRELEASE)由两部分组成,分别是变量$(KERNELVERSION)与setlocalversion 脚本的执行结果。
在Makefile中搜索KERNELVERSION变量,可以发现该变量是在Makefile最开头定义的,因此修改如下变量就可以修改KERNELVERSION
VERSION = 4
PATCHLEVEL = 4
SUBLEVEL = 77
EXTRAVERSION =
NAME = Blurry Fish Butt
#......
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
# localversion* files in the build and source directory
res="$(collect_files localversion*)"
if test ! "$srctree" -ef .; then
res="$res$(collect_files "$srctree"/localversion*)"
fi
# CONFIG_LOCALVERSION and LOCALVERSION (if set)
res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}"
# scm version string if not at a tagged commit
if test "$CONFIG_LOCALVERSION_AUTO" = "y"; then
# full scm version string
res="$res$(scm_version)"
else
# append a plus sign if the repository is not in a clean
# annotated or signed tagged state (as git describe only
# looks at signed or annotated tags - git tag -a/-s) and
# LOCALVERSION= is not specified
if test "${LOCALVERSION+set}" != "set"; then
scm=$(scm_version --short)
res="$res${scm:++}"
fi
fi
与变量CONFIG_LOCALVERSION_AUTO,是否定义LOCALVERSION有关
CONFIG_LOCALVERSION_AUTO:也许在deconfig里面?
LOCALVERSION:盲猜是自定义的本地个性化版本号,这里先不管
setlocalversion的版本号生成分几种情况:
- 如果 CONFIG_LOCALVERSION_AUTO=y, 则会在最终的LOCALVERSION后追加一段由版本控制工具git或者svn生成的版本信息, 对于git, 如果打了tag,则会使用tag, 否则, 使用字母‘g’再加上最新的commit ID的前7位, 例如 “4.4.77-gb1fd820b-dirty”, 若存在未被追踪的修改, 则还会添加” -dirty”字样
- 如果 CONFIG_LOCALVERSION_AUTO=n, 且未指定LOCALVERSION变量则会在最终的LOCALVERSION后追加‘+’, 例如 “3.10.49+”
# Check for git and a git repo.
if test -z "$(git rev-parse --show-cdup 2>/dev/null)" &&
head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
#tips:git rev-parse --verify --short HEAD:获取最新commit id的简短结果
# If we are at a tagged commit (like "v2.6.30-rc6"), we ignore
# it, because this version is defined in the top level Makefile.
if [ -z "`git describe --exact-match 2>/dev/null`" ]; then
# If only the short version is requested, don't bother
# running further git commands
if $short; then
echo "+"
return
fi
# If we are past a tagged commit (like
# "v2.6.30-rc5-302-g72357d5"), we pretty print it.
if atag="`git describe 2>/dev/null`"; then
echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
# If we don't have a tag at all we print -g{commitish}.
else
printf '%s%s' -g $head
fi
fi
# Is this git on svn?
if git config --get svn-remote.svn.url >/dev/null; then
printf -- '-svn%s' "`git svn find-rev $head`"
fi
# Check for uncommitted changes
if git diff-index --name-only HEAD | grep -qv "^scripts/package"; then
printf '%s' -dirty
fi
# All done with git
return
fi
源码:
#!/bin/sh
#
# This scripts adds local version information from the version
# control systems git, mercurial (hg) and subversion (svn).
#
# If something goes wrong, send a mail the kernel build mailinglist
# (see MAINTAINERS) and CC Nico Schottelius
# <nico-linuxsetlocalversion -at- schottelius.org>.
#
#
usage() {
echo "Usage: $0 [--save-scmversion] [srctree]" >&2
exit 1
}
scm_only=false
srctree=.
if test "$1" = "--save-scmversion"; then
scm_only=true
shift
fi
if test $# -gt 0; then
srctree=$1
shift
fi
if test $# -gt 0 -o ! -d "$srctree"; then
usage
fi
scm_version()
{
local short
short=false
cd "$srctree"
if test -e .scmversion; then
cat .scmversion
return
fi
if test "$1" = "--short"; then
short=true
fi
# Check for git and a git repo.
if test -z "$(git rev-parse --show-cdup 2>/dev/null)" &&
head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
# If we are at a tagged commit (like "v2.6.30-rc6"), we ignore
# it, because this version is defined in the top level Makefile.
if [ -z "`git describe --exact-match 2>/dev/null`" ]; then
# If only the short version is requested, don't bother
# running further git commands
if $short; then
echo "+"
return
fi
# If we are past a tagged commit (like
# "v2.6.30-rc5-302-g72357d5"), we pretty print it.
if atag="`git describe 2>/dev/null`"; then
echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
# If we don't have a tag at all we print -g{commitish}.
else
printf '%s%s' -g $head
fi
fi
# Is this git on svn?
if git config --get svn-remote.svn.url >/dev/null; then
printf -- '-svn%s' "`git svn find-rev $head`"
fi
# Check for uncommitted changes
if git diff-index --name-only HEAD | grep -qv "^scripts/package"; then
printf '%s' -dirty
fi
# All done with git
return
fi
# Check for mercurial and a mercurial repo.
if test -d .hg && hgid=`hg id 2>/dev/null`; then
# Do we have an tagged version? If so, latesttagdistance == 1
if [ "`hg log -r . --template '{latesttagdistance}'`" == "1" ]; then
id=`hg log -r . --template '{latesttag}'`
printf '%s%s' -hg "$id"
else
tag=`printf '%s' "$hgid" | cut -d' ' -f2`
if [ -z "$tag" -o "$tag" = tip ]; then
id=`printf '%s' "$hgid" | sed 's/[+ ].*//'`
printf '%s%s' -hg "$id"
fi
fi
# Are there uncommitted changes?
# These are represented by + after the changeset id.
case "$hgid" in
*+|*+\ *) printf '%s' -dirty ;;
esac
# All done with mercurial
return
fi
# Check for svn and a svn repo.
if rev=`LANG= LC_ALL= LC_MESSAGES=C svn info 2>/dev/null | grep '^Last Changed Rev'`; then
rev=`echo $rev | awk '{print $NF}'`
printf -- '-svn%s' "$rev"
# All done with svn
return
fi
}
collect_files()
{
local file res
for file; do
case "$file" in
*\~*)
continue
;;
esac
if test -e "$file"; then
res="$res$(cat "$file")"
fi
done
echo "$res"
}
if $scm_only; then
if test ! -e .scmversion; then
res=$(scm_version)
echo "$res" >.scmversion
fi
exit
fi
if test -e include/config/auto.conf; then
. include/config/auto.conf
else
echo "Error: kernelrelease not valid - run 'make prepare' to update it"
exit 1
fi
# localversion* files in the build and source directory
res="$(collect_files localversion*)"
if test ! "$srctree" -ef .; then
res="$res$(collect_files "$srctree"/localversion*)"
fi
# CONFIG_LOCALVERSION and LOCALVERSION (if set)
res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}"
# scm version string if not at a tagged commit
if test "$CONFIG_LOCALVERSION_AUTO" = "y"; then
# full scm version string
res="$res$(scm_version)"
else
# append a plus sign if the repository is not in a clean
# annotated or signed tagged state (as git describe only
# looks at signed or annotated tags - git tag -a/-s) and
# LOCALVERSION= is not specified
if test "${LOCALVERSION+set}" != "set"; then
scm=$(scm_version --short)
res="$res${scm:++}"
fi
fi
echo "$res"
驱动与内核版本检查
当通过insmod或modprobe加载核外驱动ko时,会进行模块版本检测。当kernel的版本与驱动的vermagic不同时,系统会报错,提示无法加载模块。
modinfo [.ko]/
使用modprobe命令加载模块时, 提供了两个选项, 用于在加载模块时忽略vermagic或者CRC检查, 强制加载一个模块
不建议 怕崩溃
–force-vermagic : 忽略vermagic检查
–force-modversion : 忽略CRC检查
-f : 相当于同时使用 –force-vermagic 和 –force-modversion