【微知】Mellanox驱动中的iseg是什么?如何指定哪些PCIe设备绑定到驱动?如何获取固件Firmware版本?(Initialization segment)

快速回忆

pci-ids 快速查询Mellanox PCIe的device的网址

PCIe设备id表:mlx5_core_pci_table
iseg是Initialization segment的意思,初始化字段,处于bar空间,BAR0的前几个字段,偏移是BAR0的0开始的。

背景

Mellanox网卡驱动主要是PCIe的驱动,驱动mlx5_core.ko主要负责PCIe驱动的管理,他会注册一个PCIe驱动到Linux内核的PCIe框架中,然后如果有PCIe设备热插拔或者rescan触发热插后就会调用。本文主要分析mlx5_core.ko如何利用PCIe的配置空间来获取他的Firmware版本号。

触发时机和关联key

首先,PCIe设备在BIOS阶段会被扫描枚举到PCIe设备并且放在ACPI(Advanced Configuration and Power Interface,高级配置与电源接口)模块中的acpi_pci中管理,并且会给PCIe设备根据bar空间的内容预留物理内存的地址空间。BIOS加载Linux系统会将相关资源信息高速Linux系统,并且PCIe设备就会在PCIe框架中创建对应设备,如果此时存在与之绑定的驱动,就会触发加载PCIe驱动的probe函数。其中PCIe驱动是根据pci_register_driver的时候注册的驱动描述结构中的id_table(pci_device_id的结构)来决定该驱动绑定什么PCIe设备的。这里决定绑定的PCIe设备主要是通过PCIe设备的vendorID和deviceID。 这两个信息在PCIe设备中会存储在PCIe的配置空间中,在BIOS扫描阶段会读取这些配置信息并且存下来。

pci_device_id的结构:

struct pci_device_id {
	__u32 vendor, device;		/* Vendor and device ID or PCI_ANY_ID*/
	__u32 subvendor, subdevice;	/* Subsystem ID's or PCI_ANY_ID */
	__u32 class, class_mask;	/* (class,subclass,prog-if) triplet */
	kernel_ulong_t driver_data;	/* Data private to the driver */
	__u32 override_only;
};

比如Mellanox的驱动注册几个关键信息:

#ifndef PCI_VENDOR_ID_MELLANOX
#define PCI_VENDOR_ID_MELLANOX		0x15b3
#endif

#define PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX	0x1015

static const struct pci_device_id mlx5_core_pci_table[] = {
...
	{ PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX) },
...
}

static struct pci_driver mlx5_core_driver = {
	.name           = KBUILD_MODNAME,
	.id_table       = mlx5_core_pci_table,
	.probe          = probe_one,
...
}

pci_register_driver(&mlx5_core_driver);

可以看到这里pci注册驱动的结构中,就会有id_table,用来表示这个驱动支持哪些vendor和那些device。这里的PCIe的vendorID需要再PCI sig去注册获取。这里可以查询:pcisig
也可以这里查询:PCIe vendor search
也可以这里查询:pci-ids
比如Mellanox查询出来的:https://admin.pci-ids.ucw.cz/read/PC/15b3
在这里插入图片描述

获取bar 0空间的物理地址

dev->bar_addr = pci_resource_start(pdev, 0); #获取PCIe设备的资源初始地址,bar0的。pci_resource_start 函数用于获取 PCI 设备资源的起始地址。
dev->iseg_base = dev->bar_addr;  #iseg是Initialization segment的意思,初始化字段,处于bar空间,BAR0的前几个字段,偏移是BAR0的0开始的。
dev->iseg = ioremap(dev->iseg_base, sizeof(*dev->iseg)); #获取到物理地址后使用ioremap将IO地址空间映射到内核的虚拟地址空间。 后面驱动访问虚拟地址空间,会被转化为物理地址,并且是ioaddr,系统的host bridge访问该地址的时候发现是ioaddr,不会直接访问DDR,而是访问对应的设备。

根据iseg的虚拟地址直接访问获取系统信息

firmware version如何获取的?
在这里插入图片描述

	mlx5_core_info(dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev),
		       fw_rev_min(dev), fw_rev_sub(dev));

static inline u16 fw_rev_maj(struct mlx5_core_dev *dev)
{
	return ioread32be(&dev->iseg->fw_rev) & 0xffff;
}

static inline u16 fw_rev_min(struct mlx5_core_dev *dev)
{
	return ioread32be(&dev->iseg->fw_rev) >> 16;
}

static inline u16 fw_rev_sub(struct mlx5_core_dev *dev)
{
	return ioread32be(&dev->iseg->cmdif_rev_fw_sub) & 0xffff;
}

可以看到获取网卡的固件版本是通过访问iseg的fw_rev的地址的值,可以看到这个地址的值是在BAR0的第一个4Bytes里面。并且

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值