Linux PCI和PCIe总线

Linux PCI和PCIe总线LinuxPCI和PCIe总线

大家好,又见面了,我是你们的朋友全栈君。

1 PCIe中断
– PCI/PCIe设备中断都是level触发,并且请求信号为低电平有效
– PCI总线一般只有INTA#到INTD#的4个中断引脚,所以PCI多功能设备的func一般不会超过4个,但是共享中断除外

2 IOMMU
2.1 ARM SMMU
iommus = <&apps_smmu 0x300 0>:中间的数字0x300就是设备的Stream ID,执行DMA请求时,SMMU根据Stream ID找到设备在DDR中的STE(Stream Table Entry)。PCI的Stream ID等于BDF号。
SSID:Substream ID。ste.S1CDMax为0表示STE只支持一个CD(Context Descriptor),不需要使用SSID,如果S1CDMax不为0,那么支持的CD数目是2 ^ S1CDMax。当往STE中写入S1DSS = SSID0时就禁止了SSID功能。
TBU: Translation Buffer Unit for TLB
TCU: Translation Control Unit for Page Table Walks

2.2 x86 IOMMU
When a PCI device is assigned, KVM/QEMU call intel_iommu_page_mapping() to build VT-d DTE (Device Table Entry) to map the entire guest memory.

Kernel parameter intel_iommu=pt to set up pass through mode in context mapping entry. This disables DMAR in Linux kernel; but KVM still runs on VT-d and interrupt remapping still works.

In this mode, kernel uses swiotlb for DMA API functions but other VT-d functionalities are enabled for KVM. KVM always uses multi level translation page table in VT-d. By default, pass though mode is disabled in kernel.

PCIe PASID capability ID is equal to 0x1B (PCI_EXT_CAP_ID_PASID).

1)在虚拟化场景下,直通设备的中断是无法直接投递到Guest中的,而是由IOMMU截获中断,先将其中断映射到host的某个中断上,然后再重定向(由VMM写VMCS寄存器中的32 bits VM-entry interruption-information字段)到Guest内部。
2)IOMMU IRTE(Interrupt Remapping Table Entry,128bit)中的Destination ID字段指明中断要投递的CPU的APIC ID信息,vector字段指明中断号,VMM会为每个中断源分配一个IRTE,并且把guest分配的vector号填入到IRTE的vector域。
3)在进入guest执行前,kvm是关中断的,在VM-Exit完全恢复了host上下文后,才开中断。关中断是在vcpu_enter_guest函数中调用了local_irq_disable,开中断是在这个函数从kvm_x86_ops->run返回后(即VM-Exit后)调用local_irq_enable。
4)guest vcpu执行时,物理中断发生,导致VM-Exit,但是此时是关中断的,所以硬件不会响应中断,中断处于pending,在开中断后,硬件发现pending中断并开始响应,此时已经在host上下文中,IDT(Interrupt Descriptor Table)已经指向host的IDT,物理中断由host handler来处理。

3 Linux x86 PCIe调试
3.1 PCIe设备分类
– RC,BDF为00:00.0
– bridge就像hub,一般是个多功能的设备,传递数据需要仲裁,比较慢
– switch就像交换机,PCIe规范中引入,比较快
– endpoint,x86主板上内置设备的总线号一般为0,而外挂EP的总线号一般从1开始
Figure 3-1 Type0 Header

Linux PCI和PCIe总线

Figure 3-2 Type1 Header

Linux PCI和PCIe总线

3.2 基本概念
– PCIe QOS:TC(Traffic Class),TC的值从0到7,值越大,优先级越高,类似于支持AVB的EtherSwitch,因为PCIe设计之初主要是针对于音视频应用;一个TC对应一个VC buffer(Virtual Channel),如果只有一个VC buffer,那么设置的TC值无效
– PCIe超过256字节的配置空间需要找到基地址,在MMCFG中,偏移44字节(0x2c),长度为8个字节,而MCFG可以通过acpidump找到
– PCIe的domain在内核代码中叫segment,可以通过pci_domain_nr()获得
– dev号(也叫slot)和func号一般通过宏PCI_DEVFN()合并成一个字节
– 因为PCI规范允许单个系统拥有高达256个总线,所以总线编号是8位。但对于大型系统而言,这是不够的,所以,引入了域的概念,每个PCI域可以拥有最多256个总线,每个总线上可支持32个设备,所以设备号是5位,而每个设备上最多可有8种功能,所以功能号是3位
– I210一般连接在pcieport的Lane0

3.3 LTSSM状态的查询
– PCIESTS1 offset:328h
– PCIe的LTSSM控制寄存器一般位于bridge的配置空间中(x86或者synopsys)或者RC的私有的寄存器(qcom)
– 读取EP的上一级bridge的config space的0x328(假如EP直接连在RC的port上,读取RC私有的寄存器),就可以获得下一级EP的LTSSM状态。譬如读取bridge(00:13.0)的下一级EP状态:peeknpoke b r 0x00 13 0 328

CONFIG_PCI_MMCONFIG=y
MMCONFIG: PCIe Memory-Mapped Config

QNX读取桥配置空间0x328的方法:
pci-tool -D 0x5ada -vvvvv
pci-tool -d 0:19:2 –read=CFG:0x328

3.4 LTSSM链路训练结果
通过访问PCIe桥的配置寄存器获得
Link Capabilities:配置空间0x4c
Link Control and Link Status:配置空间0x50

3.5 Linux pcibios_init
x86 BIOS专门提供了针对PCI总线的操作,这些操作里就包括了总线枚举的整个过程,Linux kernel中的宏CONFIG_PCI_BIOS。在系统加电以后自检时,就会完成对PCI总线的枚举,之后Linux对PCI配置空间的访问都是通过BIOS调用的形式进行,提供有这些功能和服务的BIOS就称之为PCI BIOS 。需要注意的是Linux x86_64是不采用PCI BIOS访问PCI配置空间的,而是内核实现了直接访问PCI配置空间的函数(CONFIG_PCI_DIRECT)。

pcibios_init()的第一个功能是在内存中找到BIOS程序的代码(参考函数pci_find_bios),然后将调用BIOS例程的读写PCI配置空间的代码封装成函数赋值给pci_ops。

pci_ops里面的函数指针都是用来读写PCI配置空间的,把要读写的值和设备号告诉这些函数,在这些函数中调用了BIOS例程,并把这些值当作参数传给BIOS例程,BIOS再根据设备号和要读写的值来进行操作。所以Linux x86驱动程序中pci_read_config_byte()最终调用的是pci_bios_read_config_byte()。

zcat /proc/config.gz | grep PCI

3.6 PCIe Reset
– Cold Reset: PCI A15 pin RST
– Warm Reset (LTSSM)
– Hot Reset (LTSSM): PCIe Type1 Header config space offset 0x3E 16-bit Bridge Control register bit6 Secondary Bus Reset, bit6 can retrigger LTSSM Link Training from Rx.Detect
– FLR (Function Level Reset): find Synopsys PCIe Capability ID 0x10, read 12 bytes from Capability ID byte, index 8 and 9 is Device Control Register, bit15 is FLR, Linux FLR path is /sys/bus/pci/devices/BDF/reset

3.7 x86 MIPI60
Blackhawk USB560v2

4 ARM PCIe
4.1 MSM RC
drivers/pci/host/pci-msm.c
qcom平台上每个RC属于一个domain(PCIe规范叫segment),并且每个RC只连接一个EP。
Figure 4-1 qcom RC拓扑图

Linux PCI和PCIe总线

4.2 MSM ep_pcie
msm/ep_pcie
ep_pcie_enumeration()

5 PCI用户空间编程 – libpci
5.1 Android libpci库
external/pciutils

5.2 libpci判断一个PCI设备是不是PCIe
capability ID参考:include/uapi/linux/pci_regs.h
参数ptr是配置空间偏移地址0x34指向的一个字节(first capability list entry)。
static bool pci_is_pcie(struct pci_dev *pdev,
        unsigned char ptr)
{

    unsigned int value;
    unsigned int next_ptr;
    unsigned int cap_id;

    next_ptr = ptr;
    if (0 == ptr)
        return false;

    do {

        value = pci_read_long(pdev, next_ptr);
        next_ptr = (value >> 8) & 0xff;
        cap_id = value & 0xff;
        /* PCI Express Capability Structure */
        if (0x10 == cap_id)
            return true;
    } while (next_ptr);

    return false;
}

6 x86 GPIO
PCH(Platform Controller Hub)上大部分设备可以通过PCIe或IO方式访问,但PCH上部分设备需要访问PCH的私有空间,这部分空间通过P2SB(Primary to SideBand)的SBREG_BAR寄存器映射到内存空间,这段空间被称为PCR(PCH Private Configuration Space Register)。每个设备对应一个PortID,PortID表示设备在PCR空间的偏移量,在加上寄存器偏移就可以获取寄存器的地址。

x86 GPIO寄存器位于PCH的私有空间。GPIO被分组,每组对应一个PCR的PortID。GPIO community和PortID的对应关系如下所示。
SouthWest: 0xC0
NorthWest: 0xC4
North: 0xC5
West: 0xC7

7 Windows PCIe工具软件
Mindshare的Arbor
Teledyne LeCroy的TeleScan PE

8 Abbreviations
ATU:Address Translation Unit
BDF:Bus,Device,Function
MEI:Intel Management Engine Interface;一个独立的子系统,使用ARC处理器,OS是Minix 3,固件整合到BIOS中,通过PCI桥片在x86端访问ARC的local端
overhead:开销,包头包尾等由协议层而不是应用层添加的字节,也就是说,一个PCIe包中除了payload之外的附加字节(ACK、CRC等)都叫overhead
P2SB:x86 Primary to Sideband
PCIe bifurcation:分叉
RC:Root Complex,执行存储器域地址到PCIe域地址的翻译,ATU被配置好后,CPU将要访问的地址发给ATU,ATU翻译后生成TLP包发给对应的Endpoint
TLP:Transaction Layer Packet,TLP中包含BDF号或者要寻址的内存和IO地址及其范围
VMCS:Virtual Machine Control Structure

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/149028.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • mysql数据库压缩备份_Mysql备份压缩及恢复数据库方法总结

    mysql数据库压缩备份_Mysql备份压缩及恢复数据库方法总结一般情况我们通过mysqldump来备份MySQL数据库,并上传至其它备份机器。如果数据库比较大,在备份传输的时候可能会慢,所以我们尽量让备份的文件小一些。在写自动备份脚本时,最好把备份结果直接压缩,恢复时也可以直接由压缩备份恢复。下面介绍如何使用bzip2和gzip进行压缩mysql备份文件。备份并用bzip压缩:代码如下mysqldump|bzip2>outputfile.sql…

  • 微信小程序必用接口「建议收藏」

    微信小程序必用接口获取openiduni-app示例获取openidopenid是微信用户的一个唯一的标识,只针对当前的微信号有效。微信开发时,用户使用小程序需要授权,这时就要用到openid进行绑定这个用户。可用于永久标记一个用户,同时也是微信JSAPI支付的必传参数。一般都是将code值传到后端去获取openid,因为在前端可能会被抓包或爬取到你的appid和secret,不安全,如果放在后端获取openid,除非你的服务器被攻击了,不然就是安全的。下面的实例是在前端直接获取的,这个明白后,可

  • linux查看80端口占用情况_centos如何查看端口是否被占用

    linux查看80端口占用情况_centos如何查看端口是否被占用前言平常使用linux,我们经常需要查看哪个服务占用了哪个端口,接下来就为大家介绍了2种Linux查看端口占用情况可以使用lsof和netstat命令。1.lsof-i:端口号用

  • java volatile 修饰符

    java volatile 修饰符小编参考了添加链接描述这篇博客,在此对博主进行感谢!volatile修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。新建MainDemopublicclassMainDemo{//privatestaticinta=0;privatestaticvolatileinta=0;pri

  • DDoS攻击工具HOIC分析

    DDoS攻击工具HOIC分析本文是绿盟科技安全+技术刊物中的文章,文章对拒绝服务攻击工具—”HighOrbitIonCannon”的技术性分析。HOIC是一款用RealBasic开发可移植的多平台拒绝服务攻击工具,该工具虽然对使用者的水平…

  • S3服务器规格和性能指标,云服务器s3与s4

    S3服务器规格和性能指标,云服务器s3与s4云服务器s3与s4内容精选换一换创建一台或多台云服务器。V1.1版本创建云服务器的接口兼容了V1版本创建云服务器(按需)的功能,同时合入新功能,支持创建包年/包月的弹性云服务器。本接口为异步接口,当前创建云服务器请求下发成功后会返回job_id,此时创建云服务器并没有立即完成,需要通过调用查询任务的执行状态查询job状态,当Job状态为SUCCESS时代表云服务器创在申请SAPS/4HAN…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号