searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

virtio设备capability的初始化

2023-05-23 07:30:46
93
0
在使用lspci -vvs XX:XX.X 查看virtio设备信息的时候,经常会看到下面的capability
本篇介绍下virtio的这类capability初始化的过程。
 
下面是软件定义的virtio capability的通用的结构体。
上面看到的各个virtio的capability,比如CommonCfg、Notify、ISR、DeviceCfg其结构都是下面这样。
/* This is the PCI capability header: */
struct virtio_pci_cap {
        __u8 cap_vndr;          /* Generic PCI field: PCI_CAP_ID_VNDR */
        __u8 cap_next;          /* Generic PCI field: next ptr. */
        __u8 cap_len;           /* Generic PCI field: capability length */
        __u8 cfg_type;          /* Identifies the structure. */
        __u8 bar;               /* Where to find it. */
        __u8 padding[3];        /* Pad to full dword. */
        __le32 offset;          /* Offset within bar. */ 
        __le32 length;          /* Length of the structure, in bytes. */
};      
 
查看一个virtio_net设备的pci配置空间(前256字节),当做示例,已经用红圈圈出来对应上面结构各个成员的字节内容。
可以更直观的看到,作为一个pci设备,当前virtio设备的一个capability在pci配置空间里的内容。
 
上面这个图里,capability的起始查找位置为0x34,指向0x40的capability。0x40的capability的第二个字节为指向下一个cap的指针,这里是0x70。0x70的下一个cap是0xb0,0xb0的下一个cap是0x48,即指向上面cap。
 
 
这里的capability内容,只是一个概述,简单的描述了一些信息。该capability的具体内容,则是根据这个概述信息,在BAR空间有一段属于该capability的对应空间,里面存有该capability的具体各项内容。比如CommonCfg的内容如下。
 
/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */
struct virtio_pci_common_cfg {
        /* About the whole device. */
        __le32 device_feature_select;   /* read-write */
        __le32 device_feature;          /* read-only */
        __le32 guest_feature_select;    /* read-write */
        __le32 guest_feature;           /* read-write */
        __le16 msix_config;             /* read-write */
        __le16 num_queues;              /* read-only */
        __u8 device_status;             /* read-write */
        __u8 config_generation;         /* read-only */

        /* About a specific virtqueue. */
        __le16 queue_select;            /* read-write */
        __le16 queue_size;              /* read-write, power of 2. */
        __le16 queue_msix_vector;       /* read-write */
        __le16 queue_enable;            /* read-write */
        __le16 queue_notify_off;        /* read-only */
        __le32 queue_desc_lo;           /* read-write */
        __le32 queue_desc_hi;           /* read-write */
        __le32 queue_avail_lo;          /* read-write */
        __le32 queue_avail_hi;          /* read-write */
        __le32 queue_used_lo;           /* read-write */
        __le32 queue_used_hi;           /* read-write */
};   

 

下面是virtio驱动代码初始化capability的部分代码和过程
下面的函数,迭代读取pci配置空间中各个capability,根据VIRTIO_PCI_CAP_COMMON_CFG查找commoncfg的capability是否存在,并返回在pci配置空间中的偏移
    common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG,
                                        IORESOURCE_IO | IORESOURCE_MEM,
                                        &vp_dev->modern_bars);
这里返回的common为commoncfg capability的偏移,实际为0x48。对应下面lspci -vvs看到的偏移[48],即上面cap开始的位置
    Capabilities: [48] Vendor Specific Information: VirtIO: CommonCfg

下面函数根据上面的capability偏移,从pci配置空间中对应位置,读取该capability的offset和length。即上面virtio_pci_cap中的offset和length
vp_dev->common = map_capability(pci_dev, common,        // common 0x48, 即下面的off
                                sizeof(struct virtio_pci_common_cfg), 4,
                                0, sizeof(struct virtio_pci_common_cfg),
                                NULL);  
        pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, offset),
                             &offset);
        pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, length),
                              &length);
        根据读取到的offset和length,从该capability所在的bar映射的空间,进行相应偏移,映射length长度的内存空间给p,并返回p给vp_dev->common 
        vp_dev->common即驱动定义的commoncfg结构的起始地址,驱动直接访问该结构,便是直接访问硬件后端bar空间相应commoncfg内容                                            
        p = pci_iomap_range(dev, bar, offset, length);                                    

 

virtio_pci_find_capability迭代查找cap的的具体流程
virtio_pci_find_capability
{
        int pos;

        for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR);
                        // __pci_bus_find_cap_start    
                            // return PCI_CAPABILITY_LIST;    0x34
                        // pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);  找到0x34指向的下一个cap的pci配置空间的偏移                                
             pos > 0;
             pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) {    // 迭代找当前cap指向的下一个cap在配置空间的偏移
                u8 type, bar;
                获取当前cap的type
                pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap,
                                                         cfg_type),
                                     &type);
                pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap,
                                                         bar),
                                     &bar);

                /* Ignore structures with reserved BAR values */
                if (bar > 0x5) 
                        continue;
                         
                if (type == cfg_type) {
                        if (pci_resource_len(dev, bar) &&
                            pci_resource_flags(dev, bar) & ioresource_types) {
                                *bars |= (1 << bar);
                                return pos;
                        }
                }
        }
        return 0;
}
0条评论
0 / 1000
h****n
6文章数
1粉丝数
h****n
6 文章 | 1 粉丝
原创

virtio设备capability的初始化

2023-05-23 07:30:46
93
0
在使用lspci -vvs XX:XX.X 查看virtio设备信息的时候,经常会看到下面的capability
本篇介绍下virtio的这类capability初始化的过程。
 
下面是软件定义的virtio capability的通用的结构体。
上面看到的各个virtio的capability,比如CommonCfg、Notify、ISR、DeviceCfg其结构都是下面这样。
/* This is the PCI capability header: */
struct virtio_pci_cap {
        __u8 cap_vndr;          /* Generic PCI field: PCI_CAP_ID_VNDR */
        __u8 cap_next;          /* Generic PCI field: next ptr. */
        __u8 cap_len;           /* Generic PCI field: capability length */
        __u8 cfg_type;          /* Identifies the structure. */
        __u8 bar;               /* Where to find it. */
        __u8 padding[3];        /* Pad to full dword. */
        __le32 offset;          /* Offset within bar. */ 
        __le32 length;          /* Length of the structure, in bytes. */
};      
 
查看一个virtio_net设备的pci配置空间(前256字节),当做示例,已经用红圈圈出来对应上面结构各个成员的字节内容。
可以更直观的看到,作为一个pci设备,当前virtio设备的一个capability在pci配置空间里的内容。
 
上面这个图里,capability的起始查找位置为0x34,指向0x40的capability。0x40的capability的第二个字节为指向下一个cap的指针,这里是0x70。0x70的下一个cap是0xb0,0xb0的下一个cap是0x48,即指向上面cap。
 
 
这里的capability内容,只是一个概述,简单的描述了一些信息。该capability的具体内容,则是根据这个概述信息,在BAR空间有一段属于该capability的对应空间,里面存有该capability的具体各项内容。比如CommonCfg的内容如下。
 
/* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */
struct virtio_pci_common_cfg {
        /* About the whole device. */
        __le32 device_feature_select;   /* read-write */
        __le32 device_feature;          /* read-only */
        __le32 guest_feature_select;    /* read-write */
        __le32 guest_feature;           /* read-write */
        __le16 msix_config;             /* read-write */
        __le16 num_queues;              /* read-only */
        __u8 device_status;             /* read-write */
        __u8 config_generation;         /* read-only */

        /* About a specific virtqueue. */
        __le16 queue_select;            /* read-write */
        __le16 queue_size;              /* read-write, power of 2. */
        __le16 queue_msix_vector;       /* read-write */
        __le16 queue_enable;            /* read-write */
        __le16 queue_notify_off;        /* read-only */
        __le32 queue_desc_lo;           /* read-write */
        __le32 queue_desc_hi;           /* read-write */
        __le32 queue_avail_lo;          /* read-write */
        __le32 queue_avail_hi;          /* read-write */
        __le32 queue_used_lo;           /* read-write */
        __le32 queue_used_hi;           /* read-write */
};   

 

下面是virtio驱动代码初始化capability的部分代码和过程
下面的函数,迭代读取pci配置空间中各个capability,根据VIRTIO_PCI_CAP_COMMON_CFG查找commoncfg的capability是否存在,并返回在pci配置空间中的偏移
    common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG,
                                        IORESOURCE_IO | IORESOURCE_MEM,
                                        &vp_dev->modern_bars);
这里返回的common为commoncfg capability的偏移,实际为0x48。对应下面lspci -vvs看到的偏移[48],即上面cap开始的位置
    Capabilities: [48] Vendor Specific Information: VirtIO: CommonCfg

下面函数根据上面的capability偏移,从pci配置空间中对应位置,读取该capability的offset和length。即上面virtio_pci_cap中的offset和length
vp_dev->common = map_capability(pci_dev, common,        // common 0x48, 即下面的off
                                sizeof(struct virtio_pci_common_cfg), 4,
                                0, sizeof(struct virtio_pci_common_cfg),
                                NULL);  
        pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, offset),
                             &offset);
        pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, length),
                              &length);
        根据读取到的offset和length,从该capability所在的bar映射的空间,进行相应偏移,映射length长度的内存空间给p,并返回p给vp_dev->common 
        vp_dev->common即驱动定义的commoncfg结构的起始地址,驱动直接访问该结构,便是直接访问硬件后端bar空间相应commoncfg内容                                            
        p = pci_iomap_range(dev, bar, offset, length);                                    

 

virtio_pci_find_capability迭代查找cap的的具体流程
virtio_pci_find_capability
{
        int pos;

        for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR);
                        // __pci_bus_find_cap_start    
                            // return PCI_CAPABILITY_LIST;    0x34
                        // pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);  找到0x34指向的下一个cap的pci配置空间的偏移                                
             pos > 0;
             pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) {    // 迭代找当前cap指向的下一个cap在配置空间的偏移
                u8 type, bar;
                获取当前cap的type
                pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap,
                                                         cfg_type),
                                     &type);
                pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap,
                                                         bar),
                                     &bar);

                /* Ignore structures with reserved BAR values */
                if (bar > 0x5) 
                        continue;
                         
                if (type == cfg_type) {
                        if (pci_resource_len(dev, bar) &&
                            pci_resource_flags(dev, bar) & ioresource_types) {
                                *bars |= (1 << bar);
                                return pos;
                        }
                }
        }
        return 0;
}
文章来自个人专栏
系统问题调试
5 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0