一,前言
对于通用的流程,我需要形成闭环的代码理解,验证我理解的准确性。于是我选择用调试的方法来看数据流,用qemu来仿真,vsocde来调试,但是不能仿真am335。所以用了qemu支持仿真的v9。
二,过程记录
1,编译uboot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
rm -rf ./v9
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- O=v9 vexpress_ca9x4_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- O=v9
2,编译qemu的arm版本,进入qemu目录
mkdir build && cd build
../configure --target-list=arm-softmmu --audio-drv-list= --enable-debug
make
3,仿真uboot 前提
export PATH=/work/tools/qemu-2.8.0/build/arm-softmmu:$PATH
进入路径/work/v9/u-boot-2023.10/v9,输入命令仿真uboot的命令
qemu-system-arm -M vexpress-a9 -kernel u-boot -nographic -m 512M
qemu退出仿真的方法:先按ctrl+a,然后松口按一个x。
4,通过gdb调试qemu开发板 前提
export PATH=/work/tools/qemu-2.8.0/build/arm-softmmu:$PATH
进入uboot文件所在目录,通过启动参数来启动gdb server
qemu-system-arm -M vexpress-a9 -kernel u-boot -nographic -m 512M --gdb tcp::1234 -S
Client
arm-linux-gnueabihf-gdb /work/v9/u-boot-2023.10/v9/u-boot
然后输入
target remote localhost:1234
5,通过vscode调试qemu开发板 前提
export PATH=/work/tools/qemu-2.8.0/build/arm-softmmu:$PATH
进入uboot文件所在目录,通过启动参数来启动gdb server
qemu-system-arm -M vexpress-a9 -kernel u-boot -nographic -m 512M --gdb tcp::1234 -S
客户端创建.vscode文件夹再创建launch.json文件,最后按F5运行客户端调试即可。
{
"version":"0.2.0",
"configurations":[
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "/work/v9/u-boot-2023.10/v9/u-boot",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"miDebuggerPath": "/work/tools/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gdb",
"miDebuggerServerAddress":"localhost:1234",
/*"preLaunchTask":"build",*/
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
6,uboot中会重新搬运,所以符号表就会不匹配,若要打断点就不起作用了。需要调试搬运后的代码,就需用在gdb中重新加载符号表。这个重定向地址编译后会变化的
-exec b relocate_code //进入此断点后,就可以重新加载uboot了
-exec add-symbol-file /work/v9/u-boot-2023.10/v9/u-boot 0x7ff60000 //重新加载后,则可打正常断点了
-exec b board_init_r
7,关于bind路径 我调试的设备对象为mmc,bind的路径
board_init_r(\common\board_r.c:818)
initcall_run_list(\include\initcall.h:46)
dm_init_and_scan(\drivers\core\root.c:433)
dm_scan(\drivers\core\root.c:409)
dm_extended_scan(\drivers\core\root.c:330)
dm_scan_fdt(\drivers\core\root.c:309)
dm_scan_fdt_node(\drivers\core\root.c:288) ----1
lists_bind_fdt(\drivers\core\lists.c:253)
device_bind_with_driver_data(\drivers\core\device.c:244)
device_bind_common(\drivers\core\device.c:178)
simple_bus_post_bind(\drivers\core\simple-bus.c:57)
dm_scan_fdt_node(\drivers\core\root.c:288) ----2
lists_bind_fdt(\drivers\core\lists.c:253)
device_bind_with_driver_data(\drivers\core\device.c:244)
device_bind_common(\drivers\core\device.c:178)
simple_bus_post_bind(\drivers\core\simple-bus.c:57)
dm_scan_fdt_node(\drivers\core\root.c:288) ----3
lists_bind_fdt(\drivers\core\lists.c:253)
device_bind_with_driver_data(\drivers\core\device.c:244)
device_bind_common(\drivers\core\device.c:178)
simple_bus_post_bind(\drivers\core\simple-bus.c:57)
dm_scan_fdt_node(\drivers\core\root.c:288) ----4
lists_bind_fdt(\drivers\core\lists.c:253)
device_bind_with_driver_data(\drivers\core\device.c:244)
device_bind_common(\drivers\core\device.c:168)
arm_pl180_mmc_bind(\drivers\mmc\arm_pl180_mmci.c:499)
lists_bind_fdt是匹配的关键函数,首先在driver中遍历,然后和node的compatible字段进行比较
for (i = 0; i < compat_length; i += strlen(compat) + 1) {
...
id = NULL;
for (entry = driver; entry != driver + n_ents; entry++) {
if (drv) {
if (drv != entry)
continue;
if (!entry->of_match)
break;
}
ret = driver_check_compatible(entry->of_match, &id, compat);
if (!ret)
break;
}
if (entry == driver + n_ents)
continue;
...
ret = device_bind_with_driver_data(parent, entry, name,
id ? id->data : 0, node,
&dev);
...
}
比较是通过driver_check_compatible函数,若strcmp为0说明找到了,然后返回0,就break,退出driver的遍历了。若driver全部扫描完了还没找到,最后退出时候判断entry等于最后一个driver,那么就continue,不会向下再走到device_bind_with_driver_data了,重新开始循环compat_length的node字符串内容。否则会进入device_bind_common来创建device设备且绑定driver。
static int driver_check_compatible(const struct udevice_id *of_match,
const struct udevice_id **of_idp,
const char *compat)
{
if (!of_match)
return -ENOENT;
while (of_match->compatible) {
if (!strcmp(of_match->compatible, compat)) {
*of_idp = of_match;
return 0;
}
of_match++;
}
return -ENOENT;
}
我由于是打断点进行子类bind函数,所以父类都是compatible mach的。所以会看到4个相同的调用路径。 分析主要函数,发现dm_scan_fdt_node会被重复调用4次后进入bind函数,原因是node的父类和子类关系图可以看出node mmc在第4层。
smb@4000000
motherboard
iofpga@7,00000000
mmci@5000
dm_scan_fdt_node主要就是在父类node中从第一个子类node开始扫描,所以第一个scan的node是smb对象,然后是motherboard依次遍历扫描到了mmci@5000进行了bind调用。
for (node = ofnode_first_subnode(parent_node);
ofnode_valid(node);
node = ofnode_next_subnode(node)) {
const char *node_name = ofnode_get_name(node);
......
err = lists_bind_fdt(parent, node, NULL, NULL, pre_reloc_only);
......
}
只要是UCLASS_SIMPLE_BUS类的,没有bind函数,但是都有post_bind
UCLASS_DRIVER(simple_bus) = {
.id = UCLASS_SIMPLE_BUS,
.name = "simple_bus",
.post_bind = simple_bus_post_bind,
.per_device_plat_auto = sizeof(struct simple_bus_plat),
};
而post_bind的函数simple_bus_post_bind主要就是继续遍历下层node进行驱动绑定,调用的函数依然是dm_scan_fdt_node,所以看上去像是归递的效果来寻找子类。
static int simple_bus_post_bind(struct udevice *dev)
{
return dm_scan_fdt_dev(dev);
}
int dm_scan_fdt_dev(struct udevice *dev)
{
return dm_scan_fdt_node(dev, dev_ofnode(dev),gd->flags & GD_FLG_RELOC ? false : true);
}
8,关于probe的流程 这个看起来比较少,扫描uclass中的device,通过seq来匹配。device_probe完成后设置DM_FLAG_ACTIVATED标志,device_probe先会处理父类设备进行probe,device_probe(dev->parent)。然后才是自己probe,ret = drv->probe(dev)。probe就是真正的通过driver来初始化设备了。
board_init_r
initcall_run_list
initr_mmc
mmc_initialize
mmc_probe
uclass_get_device_by_seq
uclass_get_device_tail
device_probe
arm_pl180_mmc_probe
三,小结
本次主要学习了gdb的symbol文件加载命令,深入学习了基于dm的数据流,这样等于形成一个代码理解的闭环,之前我看着就点乱,感觉会重复probe,原来有DM_FLAG_ACTIVATED标志。之前node的遍历方式不太清楚,现在知道是先按node来遍历compatible,然后在driver list中搜进行字符串匹配,至于匹配后就会创建device对象,然后调用prebind,bind,postbind这类钩子函数。而probe是初始化device当然要在bind后面,而且probe先进行父类设备再进行子类设备。今天看的这个system_bus的回调函数,学习到一招,就是post_bind的用途,这样可以看出bus和mmc虽然都匹配了,但是后面调用的路径不同。通过system_bus还形成了post_bind的子node归递查询,非常棒的设计。
标签:node,core,uboot,vscode,bind,drivers,device,qemu,arm From: https://blog.51cto.com/AppleCai/7978205