写在前面

哈哈,写东西前总喜欢先扯蛋,赶时间的直接无视这段吧。前段时间照着x_project成功的将手上的一块基于nuc972的板子成功移植上了最新的u-boot,相关细节可以参考我的这篇博客。

那篇博客的最后我给自己设立了几个后续要完善的功能,是选择继续模仿着搬砖呢,还是先把一些东西看明白些呢,显然我开始写这篇文章的时候是选择了后者,哈哈。

“慢下来,享受技术” —— 蜗窝科技。

依葫芦画瓢后的疑问

一直以来我大多是处于一种依葫芦画瓢的搬砖状态,同样的下面这部分code展示了我画的瓢,nuc972在u-boot上的串口驱动。

#include <common.h>
#include <asm/io.h>
#include <dm.h>
#include <serial.h>#include "serial_nuc970.h"static int nuc970_serial_setbrg(struct udevice *dev, int baudrate)
{......
}static int nuc970_serial_getc(struct udevice *dev)
{......
}static int nuc970_serial_putc(struct udevice *dev, const char c)
{......
}static int nuc970_serial_pending(struct udevice *dev, bool input)
{......
}static const struct dm_serial_ops nuc970_serial_ops = {.putc = nuc970_serial_putc,.pending = nuc970_serial_pending,.getc = nuc970_serial_getc,.setbrg = nuc970_serial_setbrg,
};static int nuc970_serial_probe(struct udevice *dev)
{......
}U_BOOT_DRIVER(nuc970_serial) = {.name = "nuc970_serial",.id = UCLASS_SERIAL,.ops = &nuc970_serial_ops,.probe = nuc970_serial_probe,.flags = DM_FLAG_PRE_RELOC,
};U_BOOT_DEVICE(nuc970_serial) = { .name = "nuc970_serial",
};

为什么code里写的是nuc970,不是说好了是nuc972的吗,因为我是从公版bsp里面沿用下来的,不用纠结,哈哈

那问题来了,device是怎么遇上driver的呢?这个问题似乎是终极问题,要回答它要先弄明白很多东西,那这篇文章我们先来看看宏U_BOOT_DRIVERU_BOOT_DEVICE多做了些什么事,它们是怎么被用起来的。

一切问题的答案多会在code中找到,下面开始代码走起,哈哈

链接器为我们做的那些事

1、脱下宏的外衣

U_BOOT_DRIVERU_BOOT_DEVICE展开得到如下:

struct driver _u_boot_list_2_driver_2_nuc970_serial __aligned(4) \
__attribute__((unused,  section(".u_boot_list_2_driver_2_nuc970_serial"))) = {.name = "nuc970_serial",.id = UCLASS_SERIAL,.ops = &nuc970_serial_ops,.of_match = nuc970_serial_ids,.probe = nuc970_serial_probe,.flags = DM_FLAG_PRE_RELOC,
};struct driver_info _u_boot_list_2_driver_info_2_nuc970_serial __aligned(4) \
__attribute__((unused,  section(".u_boot_list_2_driver_info_2_nuc970_serial"))) = { .name = "nuc970_serial",
};

从上面我们可以看到声明他们的时候对它们做了如下要求:

  • 要求它们存放的时候4字节对齐,这通常是为了更方便的访问处理它们;
  • 要求它们存放在一个各自独有的段里面。

那问题又来了,要求他们存放在各自的段里面那肯定是要在链接脚本里体现的啊,让我们赶紧来看下链接脚本。

让我们打开./arch/arm/cpu/u-boot.lds文件,仔细的看我们会看到一段这样的描述:

.............................. = ALIGN(4);.u_boot_list : {KEEP(*(SORT(.u_boot_list*)));}.............................

没错所有以.u_boot_list开头的段多将在这里存放,还有两个奇怪的符号是要做啥呢,赶紧查查,这里是关键的地方千万不要放过。

google一番后了解到KEEP关键字是为了保证所有的段多被加进来,不要被链接器自作聪明的把某些它认为没有的段舍弃,事实上我们确实是定义了一些会让它看起来没用的段,这个我们后面会提到;而SORT关键字如其名字就是根据段名字串进行排序然后存放,它将起到关键作用,为了配合它我们把段名作了些手脚,你看前面的段名展开后这么长,哈哈

通过前面的窥探,我们大概知道了用宏U_BOOT_DRIVERU_BOOT_DEVICE声明的变量将被分配到自己一个特有的段下,在链接的时候被组织到一起,那它们是怎么被用起来的呢,还是回到代码里来看看

2、回到起点

让从打开器件驱动模型后执行的第一个函数initf_dm()开始一路往下看去,一路展开如下:

initf_dm()
{dm_init_and_scan(){dm_init(){device_bind_by_name(&root_info){lists_driver_lookup_name("root_driver"){struct driver *drv = ll_entry_start(struct driver, driver);const int n_ents = ll_entry_count(struct driver, driver);for (entry = drv; entry != drv + n_ents; entry++) {if (!strcmp(name, entry->name))return entry;}}}}}
}

这里让我们先把注意力放到这句话上struct driver *drv = ll_entry_start(struct driver, driver);可以看到它又是个宏定义,同样来自于头文件Linker_lists.h让我们把它展开来一探究竟,如下:

struct driver *drv = ({ static char start[0] __aligned(4) __attribute__((unused, section(".u_boot_list_2_driver_1")));(driver *)&start;
});

static char start[0]是什么鬼,0个元素的字符数组,那就是不占用空间咯,还把它强加到了.u_boot_list_2_driver_1命名的段上,原来它就是那个会让编译器看起来没有用的段,而在链接脚本里给它强加上KEEP关键字,我们再结合这个段名和(driver *)&start;我想你应该能明白了是怎么一回事了吧,哈哈

有start那会不会有end的呢?让我们在Linker_lists.h找下,果然有一宏叫ll_entry_end那就让我们假设有这样一句代码来展开试试,如下:

struct driver *end_drv = ll_entry_start(struct driver, driver);

struct driver *end_drv = ({ static char end[0] __aligned(4) __attribute__((unused, section(".u_boot_list_2_driver_3")));(driver *)&end;
});

让我们来关注下它的段名.u_boot_list_2_driver_3取的多么的优雅,结合前面我们定义的nuc972的串口驱动,然后再结合链接脚本里SORT关键字的那么一处理,我想你大概明白它们是怎么被组织到一块的了吧,让我们来列列大概如下:

.................................u_boot_list_2_driver_1.u_boot_list_2_driver_2_nuc970_serial.u_boot_list_2_driver_3
................................

哈哈,列好队了,随时待命。可以看出所有driver的结构体变量多被组织到这里,而因为段u_boot_list_2_driver_1u_boot_list_2_driver_3的存在会给我提供一个找到它们的一个起始地址和结束地址。

小结

总结要简单,总结要简单,总结要简单,那在这篇文章里我们总结一句话,如下:

使用Linker_lists.h文件提供的宏声明的相同类型的变量将被链接器安排到一起并被排序,同时它还提供了让我们找到它们的方法

哈哈,怎么变成分析Linker_lists.h文件的作用了,说好的分析器件驱动模型的呢,不急后面我们慢慢看下去,这里先把它搞清楚;其实这些内容在Linker_lists.h已经注释的非常清楚了,点赞,而这里是从code来一步步看清它的。

u-boot器件驱动模型(DeviceDrivers)之链接器的秘密相关推荐

  1. 详解Linux2.6内核中基于platform机制的驱动模型 (经典)

    [摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了Platform总线的基本概念,接着介绍了platform device和platform dri ...

  2. 详解Linux2.6内核中基于platform机制的驱动模型

    原文地址:详解Linux2.6内核中基于platform机制的驱动模型 作者:nacichan [摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了P ...

  3. 从串口驱动的移植看linux2.6内核中的驱动模型 platform device platform driver【转】...

    转自:http://blog.csdn.net/bonnshore/article/details/7979705 写在前面的话: 博主新开了个人站点:你也可以在这里看到这篇文章,点击打开链接 本文是 ...

  4. EDK2设备驱动模型

    UEFI设备驱动模型 1. UEFI Drivers UEFI Drivers是UEFI Image的一种,UEFI Drivers与UEFI Applications的区别: Objects man ...

  5. 从串口驱动到Linux驱动模型,想转Linux的必会!

    关注.星标公众号,直达精彩内容 ID:技术让梦想更伟大 整理:李肖遥 本文通过对Linux下串口驱动的分析.由最上层的C库.到操作系统系统调用层的封装.再到tty子系统的核心.再到一系列线路规程.再到 ...

  6. 嵌入式驱动解析:从串口驱动到Linux驱动模型

    本文通过对Linux下串口驱动的分析.由最上层的C库.到操作系统系统调用层的封装.再到tty子系统的核心.再到一系列线路规程.再到最底层的硬件操作. 对Linux中的tty子系统进行简要的说明.从理论 ...

  7. 从串口驱动到Linux驱动模型

    大学的时候,帮朋友写的操作系统调研的作业,最近整理过去的文档时候偶然发现,遂作为博客发出来. 从串口驱动到Linux的tty子系统驱动模型简要分析 基于ARM920T核心 Samsung的S3C244 ...

  8. 设备驱动模型:总线-设备-驱动

    1 设备驱动模型简介 参考 以下内容: Linux 笔记: https://xuesong.blog.csdn.net/article/details/109522945?spm=1001.2014. ...

  9. linux设备驱动模型 - device/bus/driver

    在linux驱动模型中,为了便于管理各种设备,我们把不同设备分别挂在他们对应的总线上,设备对应的驱动程序也在总线上找,这样就提出了deivce-bus-driver的模型,硬件上有许多设备总线,那么我 ...

最新文章

  1. 一杯茶的时间,上手Zabbix
  2. 【实战分享】漫谈 gRPC的选型
  3. 数组和指针、数组指针和指针数组
  4. Apsara Stack 技术百科 | 可运营的行业云,让云上资源跑起来
  5. 从RCNN到SSD,深度学习目标检测算法盘点
  6. 编程范式:命令式编程(Imperative)、声明式编程(Declarative)和函数式编程(Functional)...
  7. [20151014]关于result cache.txt
  8. Atitit postgre sql json使用法 目录 1.1.1. 插入数据 1 2. json数据的常见操作 1 1.1.1.插入数据 插入数据可以直接以json格式插入: insert
  9. element-ui的分页,添加首页、尾页,跳转按钮
  10. 360浏览器怎么开java_360安全浏览器怎么打开javascript
  11. 黑马程序员_面向对象简介
  12. 视频异常事件检测Object-centric Auto-encoders and Dummy Anomalies for Abnormal Event Detection in Video
  13. 文法二义性与语言二义性
  14. 利用管道检测技术成果对城市地下空洞进行筛查分析的探讨
  15. 相似度算法--莱文斯坦距离加入同义词逻辑
  16. 如何优雅的用 Nginx 在公网上快速搭建一个加密数据通道
  17. 从0糖到0防腐剂,元气森林缘何偏向“虎山行”?
  18. python爬取58手机号_爬取58上的手机号码信息
  19. 固有频率约束下桁架优化的动态算术优化算法(Matlab代码实现)
  20. Augustus安装小记

热门文章

  1. 计算机系统与配置要求,电脑系统以及Adobe Audition的版本配置要求-喜马拉雅
  2. 苹果6s最大屏幕尺寸_苹果 iPhone 12 Pro DXOMARK 屏幕评分 87 分,最大问题是黄色色偏 - 苹果,iPhone...
  3. 安装域、对域进行管理
  4. 谷歌插件学习必备准备知识代码提示插件调试
  5. 7.5 《丰田模式》阅读笔记和感悟
  6. 足球联赛赛程表思路(转)
  7. Python爬取的微信好友信息里我看到了自律 | CSDN博文精选
  8. Android Studio创建app问题: Install repository and sync project等
  9. 谷歌人工智能影响挑战的受益者 | 硅谷洞察
  10. 前端面试题之浏览器原理篇