【我所认知的BIOS】->PCI enumerate

本文探讨了PCI桥接的工作原理及其在总线访问中的作用,重点介绍了Type0和Type1访问方式的区别,并深入分析了PCI总线编号的分配规则及过程。此外,还讨论了PCI资源分配和桥接地址解码机制。

PCI enumerate

ByLightseed

10/06/2010

这篇文章本是很久以前写的,最近感觉比较懒惰今天才贴出来。近两年开始EFI越来越流行了,可以说逐渐有颠覆legacy bios的趋势。处于对知识的渴望和追求我也最近对EFI有了一些简单的了解,我打算在后续的文章中做一个关于EFI的专题,基于EDK来写blog应该不错。毕竟它是开源的嘛,如果以AMI的EFI来写文章我估计可能会涉及到商业机密的问题。希望能像网上那个《linux的那些事儿》那样做成一个一个的小专题。也希望如果有兴趣的朋友或者BIOSER能够和我联系,咱们一起研究哈。期待您的加入。。。先说说我的打算,PCI,IDE等这些比较重要的东西都各自做一个小专题。至于其他的嘛暂时还没想到。形式以code为例子直接加注或者说明。

另外之前有关于ACPI的文章,但是看了看,真的觉得没啥好说的,都是架构上的一些东西。估计要说的话,肯能也就要结合到实际的代码,我再斟酌斟酌用什么形式来表达出来会比较好。

1PCI bridge的工作原理

PCI的架构中,引入了PCI bridge的概念来扩展使用更多的PCI设备。其他的基本东西就不多讲了,开门见山先分析在PCI bridge处理总线上访问的原理。

1.1PCI总线的两种方式

PCI总线上有两种访问方式,分别是Type0Type1。且bridge会通过比对自己的secondary bus numbersubordinate bus number后做出后续的反应是type0还是type1的配置。

1.2详细说说type1

当总线上CPU发出了一个访问PCI的动作,(假设说CPU用的是type0的方式,经过bridge1后被其配置成了type1)那么PCI bridge会去做比对,这时就出现了三种情况:

①如果此总线序号不在bridgesecondary bus numbersubordinate bus number之间则忽略掉它。

②如果此总线序号与bridgesecondary bus number相同则将其转换成类型0 配置命令。

③如果此总线序号位于bridgesecondary bus numbersubordinate bus number之间则将它不作改变的传递到二级总线接口中。

总之就是通过这中方式找到PCI总线上的对应的PCI设备,然后访问之~

2PCI 在枚举过程

2.1PCIbus number必须遵循的一个规则

在我们当前的主板上面PCI to PCI bridge是基本上都会有的,整个PCI系统要能够正确地被解析访问到,PCIbus number必须遵循一个规则:

All PCI buses located behind a PCI-PCI bridge must reside between the seondary bus number and the subordinate bus number.

有了这个规则,那么不管是BIOS还是linux在分配PCI bus number的时候就有规可循了。

2.2PCI bus number的分配过程详解

算法:不管是在Award bios还是在linux上都是深度优先。

概念:

Primary Bus Numberthe bus number of the PCI bus which the primary interface of the bridge is connected.

Secondary bus numberthe bus number of the PCI bus which the secondary interface of the bridge is connected.

Subordinate bus numberthe bus number of the highest numbered PCI bus which is behind (or subordinate to) the bridge.

假设的PCI结构图1

1 假设的某平台上的PCI设备连接图


2.2.1PCI bus number初始化step1

当在BUS 0#上第一个被扫到的PCI2PCI bridge1的时候(事实上也只有一个),primary bus number被填成0,鉴于深度优先的原则,那么这个bridge下的那条bus对应的bus number就应该是BUS 1#,为了能够继续往后面扫PCI device(不漏掉PCI bridge)所以把subordinate bus number设置为FDH。同样方式识别到了PCI2PCI bridge2。直到到了PCI2PCI bridge3的时候,发现在bridge3下下面已经没有其他的PCI bridge了。因此,才在PCI2PCI bridge3的相应寄存器里面填上,

Primary BusNo.=2

Secondar BusNo.=3

Subordinate BusNo.=3

然后分别填好bridge2Subordinate bus number = 3

见如图2

2 PCI bus number初始化1

2.2.2 PCI bus number初始化step2

PCI2PCI bridge2后面的PCI bus number分配好了后,在bus 1#上面又被扫到了PCI2PCI bridge4,用同样的算法可以确定在这个bridge后面的PCI bus number了。

此时,PCI2PCI bridge4的相应寄存器被初始化成:

Primary BusNo.=2

Secondar BusNo.=3

Subordinate BusNo.=0xFD

见图3

3

2.2.3 PCI bus number初始化step2

PCI2PCI bridge5后面已经没有挂PCI设备的时候,此时整个PCI系统上的所有bus number都可以被确定了。在初始化PCI2PCI bridge5的三个寄存器后,

Primary BusNo.=4

Secondar BusNo.=5

Subordinate BusNo.=5

然后分别回填PCI2PCI bridge4PCI2PCI bridge1Subordinate bus number寄存器。

见图4

4 PCI bus number的完整分配过程

3PCI的资源分配

资源的问题,就不对赘述了。只是这里提一下,是因为在分配bus number的过程中,BIOS其实就已经把每个PCI设备需要的resources都已经记录下来了。然后在bus number分配好了后,后续就好进行资源分配了。

4PCI bridgeaddress decode

4.1Subtractive decode

如果bridge支持subtractive decode,当在primary面没有找到另外的相应设备,则4 个周期的超时之后,认为在当前总线上没有找到相应设备。产生从primary 面到secondary 面的响应。如果此bridge不支持subtractive decode的功能,则bridge不会响应并且不会传输数据,而是调用中断提示错误。

这个功能,主要是针对PCI to ISA bridge的。如果这个功能不打开的话,那么PCI to ISA bridge下面的ISA设备就不能正常work。因为一般情况下前面4KIO空间系统都是没有做任何操作的。这个和PCIIO操作不一样。所以需要这么一种模式来做。比如ITE8888

4.2Positive decode

如果数据处理中找到相应的设备地址在“bridge windows”中的话,那么bridge使用positive decode立即响应,从primary面到secondary面正向译码。

4.3Negative decode

bridge的正向译码窗口外从secondary面到primary 面的地址译码。这种译码发生在bridgesecondary面的设备要访问primary面的设备的时候,而传输的地址数据不在window中定义。如果地址在window 中范围内,则允许同一总线中的对应设备处理(transaction---peer-to-peer transactions 而不经过bridge。如图5

5 Address decoding types

图6 Secondary side bridge rules

4.4PCI-PCI Bridge的类型

系统中支持3 种类型的p2p bridge。这些类型是:

只支持 subtractivepci-pci bridge

②只支持 positivep2p bridge

Subtractive类型p2p bridge

Subtractive-only PCI-to-PCI bridge

这种类型的bridge 没有windows 的概念的pci-pci bridge。只能实现通过subtractive decode方式访问pci bridge secondary侧的地址资源。不支持传输(transactions)的开始和终止都在secondary侧的pci总线上。点对点(peer to peer)的传输(transactions)是不支持的。

注:此种类型的bridgepci-pci bridgespec中没有定义。

Positive-only PCI-to-PCI bridge

这种类型的bridge只能通过positive decodes 来实现源自桥的primary侧的传输。Subtractive decode是不支持的。由于此种桥具有bridge window,所以能实现点对点(peer to peer)的传输(transactions)。同时还能支持negative decode由于此类桥不支持subtractive decodes ,所以positive-only p2p bridge 不能寻址secondary的设备。

Subtractive PCI-to-PCI bridge

这种类型的bridge具有bridge windows的功能,而且支持positive subtractive decode。如下图。因为此类桥有positive decode window,可以通过window 实现在桥的secondary侧的peer-peer transactions。同时也可以实现negative decode过程。由于这类bridge支持subtractive decodes,那么就能支持桥的secondary侧的legacy设备的寻址。

Reference

1. PCI-to-PCI Bridge Architecture Specification, revision 1.1

2http://tldp.org/LDP/tlk/dd/pci.html#pci-pci-config-eg-4

<think> 我们需要分析这段dnsmasq代码的功能。首先,代码分为两个主要部分:当OPT_NOWILD选项启用时执行的部分和未启用时的部分。 1. 首先调用enumerate_interfaces()函数,如果失败则报错退出。 2. 如果设置了OPT_NOWILD(即不使用通配符绑定),则: a. 调用create_bound_listeners(1)创建绑定的监听器。 b. 遍历daemon->if_names链表,检查每个接口是否被使用(used标志)。如果有接口没有被使用,则报错退出。 c. 在Linux网络环境下,对于DHCP和DHCP6,进行bindtodevice操作,将套接字绑定到特定设备。 3. 如果没有设置OPT_NOWILD,则调用create_wildcard_listeners()创建通配符监听器。 具体代码解析如下: ```c if (!enumerate_interfaces()) // 枚举系统网络接口,如果失败则退出 die(_("failed to find list of interfaces: %s"), NULL, EC_MISC); if (option_bool(OPT_NOWILD)) // 如果设置了OPT_NOWILD选项(即不使用通配符监听) { // 创建绑定到特定接口的监听器(例如,只绑定到特定IP地址) create_bound_listeners(1); // 遍历配置的接口列表,检查每个接口是否在系统中存在(即是否被enumerate_interfaces标记为已使用) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) if (if_tmp->name && !if_tmp->used) // 如果配置了接口名,但没有被使用(即系统中不存在) die(_("unknown interface %s"), if_tmp->name, EC_BADNET); // 报错退出 // 以下部分根据编译条件进行 #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP) // 在Linux网络环境下并且启用了DHCP if (daemon->dhcp) { // 将DHCP套接字绑定到特定设备(避免从其他接口接收数据包) bindtodevice(daemon->dhcpfd); if (daemon->enable_pxe) // 如果启用了PXE bindtodevice(daemon->pxefd); // 绑定PXE套接字 } #endif #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6) if (daemon->dhcp6) bindtodevice(daemon->dhcp6fd); // 绑定DHCPv6套接字 #endif } else // 没有设置OPT_NOWILD,则使用通配符监听 { // 创建通配符监听器(例如绑定到INADDR_ANY) create_wildcard_listeners(); } ``` ### 功能说明: 1. **enumerate_interfaces()**:该函数用于获取当前系统的网络接口信息,并更新daemon结构中的相关接口列表。如果失败,说明无法获取接口列表,程序无法继续运行。 2. **OPT_NOWILD选项**:该选项控制dnsmasq是否绑定到特定的接口。当设置此选项时,dnsmasq将只监听指定的接口,而不是所有接口(通配符绑定)。 3. **create_bound_listeners(1)**:创建绑定到特定地址的监听套接字。参数1可能表示IPv4(或者特定含义,需结合源码)。 4. **接口检查循环**:遍历配置文件中指定的接口(daemon->if_names链表),确保每个配置的接口都存在于系统中(即被enumerate_interfaces标记为used)。如果某个接口不存在,则报错退出。 5. **bindtodevice**:在Linux环境下,将套接字绑定到特定的网络设备(接口),这样该套接字就只会接收来自该设备的数据包。这用于确保DHCP/DHCPv6/PXE服务只在指定接口上响应。 6. **create_wildcard_listeners**:创建通配符监听套接字,绑定到0.0.0.0(IPv4)和::(IPv6),监听所有接口。 ### 设计目的: - **安全性**:避免监听不必要的接口,减少攻击面。 - **精确控制**:在多接口系统上,只对指定接口提供服务。 - **错误预防**:配置的接口不存在时立即报错,防止服务在部分接口上无法工作。 ### 注意事项: - 只有在Linux网络环境下,并且编译时启用了DHCP/DHCPv6的情况下,才会执行bindtodevice。 - 使用bindtodevice需要root权限,并且是Linux特有的功能(通过SO_BINDTODEVICE套接字选项实现)。 §§相关问题§§ 1. `enumerate_interfaces`函数具体如何获取系统接口信息? 2. 为什么在绑定设备(bindtodevice)之前需要检查接口存在性? 3. `create_bound_listeners`和`create_wildcard_listeners`内部实现的主要区别是什么? 4. 在哪些场景下应该启用OPT_NOWILD选项? 5. 如果配置文件中指定了一个未存在的接口,为什么必须终止程序而不是忽略? </think> 这段代码来自 dnsmasq 的网络接口初始化流程,主要完成网络接口绑定和监听器的创建。以下是详细解析: ### **代码功能解析** ```c // 1. 枚举系统网络接口 if (!enumerate_interfaces()) die(_("failed to find list of interfaces: %s"), NULL, EC_MISC); // 2. 根据 OPT_NOWILD 选项选择绑定模式 if (option_bool(OPT_NOWILD)) { // 非通配符模式(精确绑定) // 创建绑定到具体接口的监听器 create_bound_listeners(1); // 3. 验证配置的接口是否存在 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) if (if_tmp->name && !if_tmp->used) // 配置了但未找到的接口 die(_("unknown interface %s"), if_tmp->name, EC_BADNET); /** 4. Linux 专用绑定操作 **/ #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP) if (daemon->dhcp) { bindtodevice(daemon->dhcpfd); // 绑定DHCPv4套接字到设备 if (daemon->enable_pxe) bindtodevice(daemon->pxefd); // 绑定PXE启动套接字 } #endif #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6) if (daemon->dhcp6) bindtodevice(daemon->dhcp6fd); // 绑定DHCPv6套接字 #endif } else { // 通配符模式 // 5. 创建通配监听器 create_wildcard_listeners(); } ``` ### **核心功能说明** 1. **接口枚举 (`enumerate_interfaces`)** - 扫描系统网络接口(如 eth0, wlan0) - 填充 `daemon->if_names` 链表并标记 `used` 标志 - 失败则立即终止程序(防止无效配置) 2. **绑定模式选择** | 模式 | 特点 | 适用场景 | |-------------------|-------------------------------|----------------------| | `OPT_NOWILD` (非通配) | 精确绑定指定接口 | 多网卡服务器/安全加固 | | 通配模式 | 监听所有接口 (`INADDR_ANY`) | 简单网络环境 | 3. **接口验证循环** - 遍历配置的接口列表(`daemon->if_names`) - 检查每个接口是否被枚举到(`used`标记) - 未找到的接口报错退出(避免"静默失败") 4. **Linux 设备绑定 (`bindtodevice`)** - 使用 `SO_BINDTODEVICE` 套接字选项 - 实现效果: ```c setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); ``` - 好处:避免数据包跨接口路由 5. **监听器创建区别** ```mermaid graph TB A[监听器创建] --> B[非通配模式] A --> C[通配模式] B --> D[create_bound_listeners] D --> E[为每个指定接口创建独立套接字] C --> F[create_wildcard_listeners] F --> G[创建单个套接字绑定0.0.0.0] ``` ### **错误处理设计** 1. **严格校验** - 接口枚举失败 → 立即终止 (`EC_MISC`) - 配置接口不存在 → 终止 (`EC_BADNET`) 2. **条件编译** - `HAVE_LINUX_NETWORK`:仅Linux支持设备绑定 - `HAVE_DHCP`/`HAVE_DHCP6`:按需编译功能 3. **资源清理** 所有 `die()` 调用前会自动触发: ```c cleanup_bound_listeners(); // 释放已创建的套接字 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值