0%

QEMU PCI 设备学习笔记

xio3130_upstream 设备初始化过程

函数调用链:

1
type_init(xio3130_upstream_register_types) -> type_register_static(&xio3130_upstream_info); -> xio3130_upstream_class_init(ObjectClass *klass, void *data)

pci 设备主要有两个成员变量–DeviceClass、PCIDeviceClass

初始化这个两个成员变量的过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void xio3130_upstream_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

k->is_bridge = true;
k->config_write = xio3130_upstream_write_config;
k->realize = xio3130_upstream_realize;
k->exit = xio3130_upstream_exitfn;
k->vendor_id = PCI_VENDOR_ID_TI;
k->device_id = PCI_DEVICE_ID_TI_XIO3130U;
k->revision = XIO3130_REVISION;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->desc = "TI X3130 Upstream Port of PCI Express Switch";
dc->reset = xio3130_upstream_reset;
dc->vmsd = &vmstate_xio3130_upstream;
}

xio3130_upstream 有一个父类————PCIEPort,从

1
2
3
static const TypeInfo xio3130_upstream_info = {
.name = "x3130-upstream",
.parent = TYPE_PCIE_PORT,

就可以看出来。同理可以看出来 PCIEPort的父类是PCIBridge,PCIBridge的父类是抽象类PCIDevice。

PCI 设备寄存器初始化过程

pci 设备类的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//pci 物理设备的空间
struct PCIDevice {
DeviceState qdev;
bool partially_hotplugged;

/* PCI config space */
uint8_t *config;

/* Used to enable config checks on load. Note that writable bits are
* never checked even if set in cmask. */
uint8_t *cmask;

/* Used to implement R/W bytes */
uint8_t *wmask;

/* Used to implement RW1C(Write 1 to Clear) bytes */
uint8_t *w1cmask;

/* Used to allocate config space for capabilities. */
uint8_t *used;

/* the following fields are read only */
int32_t devfn;
/* Cached device to fetch requester ID from, to avoid the PCI
* tree walking every time we invoke PCI request (e.g.,
* MSI). For conventional PCI root complex, this field is
* meaningless. */
PCIReqIDCache requester_id_cache;
char name[64];
PCIIORegion io_regions[PCI_NUM_REGIONS];
AddressSpace bus_master_as;
MemoryRegion bus_master_container_region;
MemoryRegion bus_master_enable_region;

/* do not access the following fields */
PCIConfigReadFunc *config_read;
PCIConfigWriteFunc *config_write;

/* Legacy PCI VGA regions */
MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS];
bool has_vga;

/* Current IRQ levels. Used internally by the generic PCI code. */
uint8_t irq_state;

/* Capability bits */
uint32_t cap_present;

/* Offset of MSI-X capability in config space */
uint8_t msix_cap;

/* MSI-X entries */
int msix_entries_nr;

/* Space to store MSIX table & pending bit array */
uint8_t *msix_table;
uint8_t *msix_pba;
/* MemoryRegion container for msix exclusive BAR setup */
MemoryRegion msix_exclusive_bar;
/* Memory Regions for MSIX table and pending bit entries. */
MemoryRegion msix_table_mmio;
MemoryRegion msix_pba_mmio;
/* Reference-count for entries actually in use by driver. */
unsigned *msix_entry_used;
/* MSIX function mask set or MSIX disabled */
bool msix_function_masked;
/* Version id needed for VMState */
int32_t version_id;

/* Offset of MSI capability in config space */
uint8_t msi_cap;

/* PCI Express */
PCIExpressDevice exp;

/* SHPC */
SHPCDevice *shpc;

/* Location of option rom */
char *romfile;
bool has_rom;
MemoryRegion rom;
uint32_t rom_bar;

/* INTx routing notifier */
PCIINTxRoutingNotifier intx_routing_notifier;

/* MSI-X notifiers */
MSIVectorUseNotifier msix_vector_use_notifier;
MSIVectorReleaseNotifier msix_vector_release_notifier;
MSIVectorPollNotifier msix_vector_poll_notifier;

/* ID of standby device in net_failover pair */
char *failover_pair_id;
};

// pci 设备这个类的结构体
typedef struct PCIDeviceClass {
DeviceClass parent_class;

void (*realize)(PCIDevice *dev, Error **errp);
PCIUnregisterFunc *exit;
PCIConfigReadFunc *config_read;
PCIConfigWriteFunc *config_write;

uint16_t vendor_id;
uint16_t device_id;
uint8_t revision;
uint16_t class_id;
uint16_t subsystem_vendor_id; /* only for header type = 0 */
uint16_t subsystem_id; /* only for header type = 0 */

/*
* pci-to-pci bridge or normal device.
* This doesn't mean pci host switch.
* When card bus bridge is supported, this would be enhanced.
*/
bool is_bridge;

/* rom bar */
const char *romfile;
} PCIDeviceClass;

static const TypeInfo pci_device_type_info = {
.name = TYPE_PCI_DEVICE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(PCIDevice),
.abstract = true,
.class_size = sizeof(PCIDeviceClass),
.class_init = pci_device_class_init,
.class_base_init = pci_device_class_base_init,
};

instance_size 和 class_size 的区别在于它们分别定义了对象和类的内存大小。对象通常存储设备的状态和属性,而类则包含了类特有的方法和类变量。

pci_device_class_init 函数中注册了实例化函数,当需要实例化pci设备时就会分配一个instance_size大小的空间。

pci bridge 写配置

pci_bridge_write_config 调用了 pci.c 里面的 pci_default_write_config实现设备网桥设备的读写。pci_default_write_config函数内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int l)
{
int i, was_irq_disabled = pci_irq_disabled(d);
uint32_t val = val_in;

for (i = 0; i < l; val >>= 8, ++i) {
uint8_t wmask = d->wmask[addr + i];
uint8_t w1cmask = d->w1cmask[addr + i];
assert(!(wmask & w1cmask));
d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
}
if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) ||
range_covers_byte(addr, l, PCI_COMMAND))
pci_update_mappings(d);

if (range_covers_byte(addr, l, PCI_COMMAND)) {
pci_update_irq_disabled(d, was_irq_disabled);
memory_region_set_enabled(&d->bus_master_enable_region,
pci_get_word(d->config + PCI_COMMAND)
& PCI_COMMAND_MASTER);
}

msi_write_config(d, addr, val_in, l);
msix_write_config(d, addr, val_in, l);
}

d->config是pci设备配置空间,为了方便,这里贴出地址空间的图片可以对应上。

-------------本文结束再接再厉-------------

本文标题:QEMU PCI 设备学习笔记

文章作者:lx

发布时间:2024年06月26日 - 19:06

最后更新:2024年06月26日 - 19:06

原始链接:http://example.com/2024/06/26/QEMU-PCI-%E8%AE%BE%E5%A4%87%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。