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

本文详细介绍了PCI bridge的工作原理,包括PCI总线的Type0和Type1访问方式,以及PCI枚举过程中bus number的分配规则。在枚举过程中,遵循所有位于PCI-PCI bridge后的总线必须在secondary bus number和subordinate bus number之间的原则。通过具体的步骤阐述了PCI bus number的初始化过程,为理解PCI系统提供了清晰的指导。

PCI enumerate

By Lightseed   

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 numbe

<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(); // 释放已创建的套接字 ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值