前言
bootargs是uboot向内核传递参数时使用的,本次我们要验证的是bootargs向内核启动后加载的模块传递的参数,真正的跨过山和大海。跟着我的脚步,来一次bootargs之旅。
这是一个综合性,系统性很强的实例验证,要做这个实验,
首先能够进入uboot命令行。
其次相关的系统内核源码。
第三,具备编译一个模块的环境。
一 编写模块
这是一个platform驱动。可以接受参数。分别是int,charp,bool三种类型。且支持设备树。
源码gebageba.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/of.h>
#define MYDEBUG(format,...) printk("%s - %d:"format"\n",\
__func__,__LINE__,##__VA_ARGS__)
static int int_para = 10086;
module_param_named(int_para_name,int_para,int, 0644);
static char* char100 = "who are you";
module_param_named(char100_name,char100,charp,0644);
static bool isboy = true;
MODULE_PARM_DESC(isboy, "Yes,yes,A boy,not a bird man");
module_param_named(isboy_name, isboy, bool, 0644);
static int isp_dev_probe(struct platform_device *pdev)
{
MYDEBUG("int_para = %d",int_para);
MYDEBUG("char100 = %s",char100);
MYDEBUG("isboy = %d",isboy);
return 0;
}
static int isp_dev_remove(struct platform_device *pdev)
{
MYDEBUG("bye bai");
return 0;
}
static const struct of_device_id isp_dt_ids[] = {
{ .compatible = "helloworld", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, isp_dt_ids);
static struct platform_driver isp_platform_driver = {
.driver = {
.name = "gebageba",
.of_match_table = of_match_ptr(isp_dt_ids),
},
.probe = isp_dev_probe,
.remove = isp_dev_remove,
};
module_platform_driver(isp_platform_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lkmao");
MODULE_DESCRIPTION("Balabala ... Balala ... Balala");
二 配置设备树
在设备树根节点下添加如下所示的内容,这么正式的代码,相信读者一定知道是哪一行,没错就是hello22,为什么是这个名字,当然不是因为第22条军规,主要是我自己在设备树里测试代码太多了,给一个垃圾名字,让看见的人明白,这是个垃圾。可以随便删。
/ {
/*略*/
hello22{
compatible = "helloworld";
};
/*略*/
};
三 设置Kconfig和Makefile
1 在drivers目录添加子目录,并修改Makefile
在内核的driver目录创建文件夹
lkmao@ubuntu:~/imx/linux/linux-imx/drivers$ mkdir gebageba
lkmao@ubuntu:~/imx/linux/linux-imx/drivers$
修改drivers目录的Makefile,添加gebageba这个子目录,修改方式,在drivers/Makefile文件最底下,添加如下内容。
obj-y += gebageba/
2 创建并编写自己的Makefile
下面的操作是在新的gebageba目录操作的,然后创建Kconfig和Makefile文件
lkmao@ubuntu:~/imx/linux/linux-imx/drivers/gebageba$ touch Kconfig Makefile
lkmao@ubuntu:~/imx/linux/linux-imx/drivers/gebageba$ ls -lsh
总用量 0
0 -rw-rw-r-- 1 lkmao lkmao 0 12月 17 16:19 Kconfig
0 -rw-rw-r-- 1 lkmao lkmao 0 12月 17 16:19 Makefile
lkmao@ubuntu:~/imx/linux/linux-imx/drivers/gebageba$
编写Makefile文件
obj-m = gebageba.c
3 创建并编写自己的Kconfig
然后,就是让make menuconfig时,能够找到我们的新驱动,并能够配置它:修改drivers/Kconfig文件,最后一行endmenu前面添加source "drivers/gebageba/Kconfig",如下图所示:
修改我们自己的drivers/gebageba/Kconfig,写的还是挺正规的。
config LKMAO_GEBAGEBA
tristate "this is a module test: parameter send form uboot to this module"
default M
help
This macro very good.
保存,执行make menuconfig,如下图所示,有个绿色的加号,以前还真没注意到。
选项走过来以后绿色的加号就不见了。
Y变成*,M变成M,N表示不选,我们输入M,当做模块。配置完,在.config文件中搜索新的配置项,如下所示,配置成功了。
lkmao@ubuntu:~/imx/linux/linux-imx$ cat .config | grep GEBAGEBA
CONFIG_LKMAO_GEBAGEBA=m
lkmao@ubuntu:~/imx/linux/linux-imx$
修改drivers/gebageba/Makefile文件,让它可配置:
obj-$(CONFIG_LKMAO_GEBAGEBA) = gebageba.o
make modules_install INSTALL_MOD_PATH=tmp,
输出结果中看到,我们的模块被编译了。
三 设置bootargs
模块名字叫gebageba。参数名字叫啥了?忘记了,没关系使用modinfo + 模块名字 命令查看:
lkmao@ubuntu:~/imx/linux/linux-imx/drivers/gebageba$ modinfo gebageba.ko
filename: /home/lkmao/imx/linux/linux-imx/drivers/gebageba/gebageba.ko
license: GPL
srcversion: 73C63980927DB0B2865878D
alias: of:N*T*Chelloworld*
depends:
intree: Y
vermagic: 4.1.15 SMP preempt mod_unload modversions ARMv7 p2v8
parm: int_para_name:int
parm: char100_name:charp
parm: isboy:Yes,yes,A boy,not a bird man
parm: isboy_name:bool
lkmao@ubuntu:~/imx/linux/linux-imx/drivers/gebageba$
为了最大程度让实验具备可复制性,我们只修改int类型的参数:在uboot中修改bootargs参数
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw gebageba.int_para_name=10010'
修改完毕后,记得使用saveenv保存新参数
四 验证模块和设备树
下面的操作,具体情况具体分析,总之就是想办法把模块送到板子中。在这里还是有很多学问的,作为初学者,能避开的难点就避开。除非是真的很聪明,很天才。反正我是比较蠢的那种。
lkmao@ubuntu:~/imx/linux/linux-imx$ sudo scp tmp/modules.tar root@192.168.0.33:/lib/modules
The authenticity of host '192.168.0.33 (192.168.0.33)' can't be established.
RSA key fingerprint is SHA256:yyVGvCUidWhwOAXI2+q+vlb9lAgyr0URCK6WBr/uaqY.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.0.33' (RSA) to the list of known hosts.
modules.tar 100% 4040KB 4.0MB/s 00:01
lkmao@ubuntu:~/imx/linux/linux-imx$
如下所示:我们的模块已经站在哪里等着了。
root@hehe:/lib/modules/4.1.15/kernel/drivers/gebageba# ls
gebageba.ko
root@hehe:/lib/modules/4.1.15/kernel/drivers/gebageba#
然后就是看看设备树是否准备好,设备树在哪里,不是在flash里吗?在根文件系统中有对应的虚拟文件系统,就是目录/sys/firmware/devicetree。我们新加的节点是hello22。如下所示,设备树也就绪了。
root@hehe:/sys/firmware/devicetree/base/hello22# cat compatible ;echo
helloworld
root@hehe:/sys/firmware/devicetree/base/hello22# cat name ;echo
hello22
root@hehe:/sys/firmware/devicetree/base/hello22# pwd
/sys/firmware/devicetree/base/hello22
root@hehe:/sys/firmware/devicetree/base/hello22#
五 开始测试
下图所示,记住这三个_name结尾的字符串,这三就是模块参数在外部看到的名字。
1 修改int类型数据
就是在原有的bootargs的基础上添加gebageba.int_para_name=10010,gebageba是模块名字,int_para_name是参数名字,10010是新设置的值。
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw gebageba.int_para_name=10010'
从前面的代码可知,默认值是10086.
启动后,值变成10010,就算测试成功。下图为测试结果
好了,这应该就算测试成功了,下面的内容可以不用看了。
2 修改charp类型数据 传输带空格的参数有错误
就是在原有的bootargs的基础上添加gebageba.char100_name="Ku lu,Ku lu",gebageba是模块名字,char100_name是参数名字,"Ku lu,Ku lu"是新设置的值。
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw gebageba.char100_name="Ku lu,Ku lu"'
从前面的代码可知,默认值是who are you。
下图为测试结果,结果只有一个Ku,难道是因为空格
这里需要看下内核源码的parse_args函数怎么实现的。
Params.c (kernel)文件
char *parse_args(const char *doing,
char *args,
const struct kernel_param *params,
unsigned num,
s16 min_level,
s16 max_level,
int (*unknown)(char *param, char *val, const char *doing))
{
char *param, *val;
/* Chew leading spaces */
args = skip_spaces(args);
if (*args)
pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args);
while (*args) {
int ret;
int irq_was_disabled;
args = next_arg(args, ¶m, &val);
/* Stop at -- */
if (!val && strcmp(param, "--") == 0)
return args;
irq_was_disabled = irqs_disabled();
ret = parse_one(param, val, doing, params, num,
min_level, max_level, unknown);
if (irq_was_disabled && !irqs_disabled())
pr_warn("%s: option '%s' enabled irq's!\n",
doing, param);
switch (ret) {
case -ENOENT:
pr_err("%s: Unknown parameter `%s'\n", doing, param);
return ERR_PTR(ret);
case -ENOSPC:
pr_err("%s: `%s' too large for parameter `%s'\n",
doing, val ?: "", param);
return ERR_PTR(ret);
case 0:
break;
default:
pr_err("%s: `%s' invalid for parameter `%s'\n",
doing, val ?: "", param);
return ERR_PTR(ret);
}
}
/* All parsed OK. */
return NULL;
}
在这个函数中调用了skip_spaces。
/**
* skip_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
*
* Returns a pointer to the first non-whitespace character in @str.
*/
char *skip_spaces(const char *str)
{
while (isspace(*str))
++str;
return (char *)str;
}
可能不是这里,总之,就是不能出现空格,修改booargs如下所示将空格换成\x20
=> setenv bootargs "console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw gebageba.char100_name='Ku\\x20lu,Ku\\x20lu'"
=> printenv bootargs
bootargs=console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw gebageba.char100_name=Ku\x20lu,Ku\x20lu
=>
显然,这并非我们想要的结果;传参文档位于Documentation/kernel-parameters.txt,我们去研究研究。还真有,
Double-quotes can be used to protect spaces in values, e.g.:
param="spaces in here"
这里介绍了rootwait的作用:
rootwait [KNL] Wait (indefinitely) for root device to show up.
Useful for devices that are detected asynchronously
(e.g. USB and MMC devices).
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw gebageba.char100_name=! gebageba.char100_name=Ku" "lu,Ku" "lu'
卸载模块,然后手动加载模块传递参数,这样是没问题的。
root@hehe:~# rmmod gebageba
[ 21.994268] isp_dev_remove - 32:bye bai
root@hehe:~# modprobe gebageba char100_name='ku'
[ 44.434610] isp_dev_probe - 25:int_para = 10010
[ 44.439174] isp_dev_probe - 26:char100 = ku
[ 44.447188] isp_dev_probe - 27:isboy = 1
root@hehe:~# rmmod gebageba
[ 48.250783] isp_dev_remove - 32:bye bai
root@hehe:~# modprobe gebageba char100_name='ku la'
[ 51.494024] isp_dev_probe - 25:int_para = 10010
[ 51.498586] isp_dev_probe - 26:char100 = ku la
[ 51.503037] isp_dev_probe - 27:isboy = 1
root@hehe:~# rmmod gebageba
[ 100.860819] isp_dev_remove - 32:bye bai
root@hehe:~#
root@hehe:~# modprobe gebageba char100_name="ku la"
[ 110.336387] isp_dev_probe - 25:int_para = 10010
[ 110.340949] isp_dev_probe - 26:char100 = ku la
[ 110.349143] isp_dev_probe - 27:isboy = 1
root@hehe:~#
暂时搁置
六 使用转义字符输出空格
首先看下转义字符表:
测试代码:
#include <stdio.h>
int main()
{
printf("hello\x20world\n");
}
测试结果:没问题\x20确实被转义为空格了。
lkmao@ubuntu:~/imx/linux/linux-imx/drivers/gebageba$ ./a.out
hello world
lkmao@ubuntu:~/imx/linux/linux-imx/drivers/gebageba$