admin管理员组

文章数量:1534855

2024年1月15日发(作者:)

PCIE 设备扫描的过程

初步了解完PCI总线标准之后,我们接下来正式开始PCIe设备的漫游之旅。从我们按下PC的电源按钮开始,BIOS就接管系统控制权开始工作,它会先进行一些内存和设备的初始化工作(当然,也包括我们的PCI设备),由于商业上的原因,Phoenix等厂商的BIOS代码需要授权协议,在此,我们以另外一个款开源BIOS(openbios)为例,来剖析BIOS中,我们的PCIe设备是如何被找到以及初始化的。

PCI设备的扫描是基于深度优先搜索算法(DFS:Depth First Search),也就是说,下级分支最多的PCI桥将最先完成其子设备的扫描。下面我们以图片来具体说明,BIOS是如何一步步完成PCI 设备扫描的。

第一步:

PCI Host 主桥扫描Bus 0上的设备(在一个处理器系统中,一般将与HOST主桥直接相连的PCI总线被命名为PCI Bus 0),系统首先会忽略Bus 0上的D1,D2等不会挂接PCI桥的设备,主桥发现Bridge 1后,将Bridge1 下面的PCI Bus定为 Bus 1,系统将初始化Bridge 1的配置空间,并将该桥的Primary Bus

Number 和 Secondary Bus Number寄存器分别设置成0和1,以表明

Bridge1 的上游总线是0,下游总线是1,由于还无法确定Bridge1下挂载设备的具体情况,系统先暂时将Subordinate Bus Number设为0xFF。如下图所示:

第二步:

系统开始扫描Bus 1,将会发现Bridge 2。系统将Bridge 2下面的PCI Bus定为Bus 2,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成1和2,和上一步一样暂时把Bridge 2 的Subordinate Bus

Number设为0xFF。如下图所示:

第三步:

系统继续扫描Bus 2,将会发现Bridge 4。系统将Bridge 4下面的PCI Bus定为Bus 3,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成2和3,此后

系统继续扫描后发现Bus 3 下面已经没有任何Bridge了,意味着该PCI总线下已经没有任何挂载下游总线了,因此Bridge 4的Subordinate Bus Number的值已经可以确定为3了。

如下图所示:

第四步:

完成Bus 3的扫描后,系统返回到Bus 2继续扫描,发现Bus 2下面已经没有其他Bridge了。此时Bridge 2的Subordinate Bus Number的值也已经可以确定为3了。如下图所示:

第五步:

完成Bus 2的扫描后,系统返回到Bus1继续扫描,会发现Bridge 3,系统将Bridge 3下面的PCI Bus定为Bus 4。并将Bridge 4的Primary Bus Number

和 Secondary Bus Number寄存器分别设置成1和4,此后系统继续扫描后发现Bus 4 下面已经没有任何Bridge了,意味着该PCI总线下已经没有挂载任何下游总线了,因此Bridge 3 的Subordinate Bus Number的值已经可以确定为4了。如下图所示:

第六步:

完成Bus 4的扫描后,系统返回到Bus 1继续扫描, 发现Bus 1下面已经没有其他Bridge了。此时Bridge 1的Subordinate Bus Number的值已经可以确定为4,系统返回Bus 0继续扫描(Bus 0下如果有其他它Bridge,将重复上述的步骤进行扫描)。至此,本例中的整个PCI的设备扫描已经完成了。最终的设备和总线的扫描结果如下图所示。

了解了上面PCI设备扫描的大概流程,我们接下来看看Bios代码中具体是如何实现这些扫描的。

一般来说,我们可以通过两个寄存器来访问PCI的配置空间(寄存器CONFIG_ADDRESS与CONFIG_DATA),在x86体系下,这两个寄存器分别对应0xCF8和0xCFC端口,对配置空间的访问都是通过对这两个寄存器的读写来实现先。CONFIG_ADDRESS寄存器的具体位组成如下图所示:

Bus Number : 总线号(8 bit),范围0--255。

Device Number: 设备号(5 bit),范围0--31。

Function Number: 功能号(3 bit),范围0--7。

Register Number: 寄存器号(6 bit),范围0--63 (配置空间一共256个字节,分割成64个4字节的寄存器,从0--63编号)。

每个PCI设备可根据上图所示的四个信息:Bus Number, Device Number, Function Number,Register Number 来进行具体设备的定位并对其配置空间访问。当我们要访问PCI设备的配置空间时,先根据以上格式设置CONFIG_ADDRESS寄存器,然后再读取CONFIG_DATA寄存器即可得到相应的配置空间寄存器的值。

因此,BIOS中PCI配置空间的读写可以封装成下面的函数:

[cpp] view plain copy

1. static inline uint32_t pci_config_read32(pci_addr dev, uint8_t reg)

2. {

3. outl(dev | reg, 0xcf8);

4. return inl(0xcfc | reg);

5. }

6.

7. static inline void pci_config_write32(pci_addr dev, uint8_t reg,

uint32_t val)

8. {

9. outl(dev | reg, 0xcf8);

10. outl(val, 0xcfc);

11. }

总体来说。该BIOS扫描过程中调用如下几个主要的函数:

ob_pci_init ----> ob_scan_pci_bus ----> pci_find_device ---->

ob_pci_configure

下面我们来具体看看代码,首先BIOS执行ob_pci_init(void)函数

[cpp] view plain copy

1. int ob_pci_init(void)

2. {

3. int bus;

4. unsigned long mem_base, io_base;

5. char *path;

6.

7. #ifdef CONFIG_DEBUG_PCI

8. printk("Initializing ");

9. #endif

10.

11. /* brute force bus scan */

12.

13. /* Find all PCI bridges */

14.

15. //获取系统指定的memeory与I/O空间的范围,分配给PCIe设备。

16. mem_base = arch->mem_base;

17. /* I/O ports under 0x400 are used by devices mapped at fixed

18. location. */

19. io_base = arch->io_base + 0x400;

20. path = strdup("");

21.

22. /*遍历256条总线*/

23. for (bus = 0; bus<0x100; bus++) {

24. ob_scan_pci_bus(bus, &mem_base, &io_base, &path);

25. }

26. free(path);

27. return 0;

28. }

总线扫描具体实现:

[cpp] view plain copy

1. static void ob_scan_pci_bus(int bus, unsigned long *mem_base,

2. unsigned long *io_base, char **path)

3. {

4. int devnum, fn, is_multi, vid, did;

5. unsigned int htype;

6. pci_addr addr;

7. pci_config_t config;

8. const pci_dev_t *pci_dev;

9. uint32_t ccode;

10. uint8_t class, subclass, iface, rev;

11.

12. activate_device("/");

13. for (devnum = 0; devnum < 32; devnum++) {

14. is_multi = 0;

15. for (fn = 0; fn==0 || (is_multi && fn<8); fn++) {

16. #ifdef CONFIG_XBOX

17. if (pci_xbox_blacklisted (bus, devnum, fn))

18. continue;

19. #endif

20. addr = PCI_ADDR(bus, devnum, fn); /*获取设备配置空间地址*/

21. vid = pci_config_read16(addr, PCI_VENDOR_ID); /*获取Vendor ID*/

22. did = pci_config_read16(addr, PCI_DEVICE_ID); /*获取Device

ID*/

23.

24. if (vid==0xffff || vid==0)

25. continue;

26.

27. ccode = pci_config_read16(addr, PCI_CLASS_DEVICE);

28. class = ccode >> 8;

29. subclass = ccode;

30. iface = pci_config_read8(addr, PCI_CLASS_PROG);

31. rev = pci_config_read8(addr, PCI_REVISION_ID);

32.

33. pci_dev = pci_find_device(class, subclass, iface,/*具体设备查找以及初始化*/

34. vid, did);

35.

36. #ifdef CONFIG_DEBUG_PCI

37. printk("%x:%x.%x - %x:%x - ", bus, devnum, fn,

38. vid, did);

39. #endif

40. htype = pci_config_read8(addr, PCI_HEADER_TYPE);

41. if (fn == 0)

42. is_multi = htype & 0x80;

43.

44. if (pci_dev == NULL || pci_dev->name == NULL)

45. snprintf(, sizeof(),

46. "%s/pci%x,%x", *path, vid, did);

47. else

48. snprintf(, sizeof(),

49. "%s/%s", *path, pci_dev->name);

50. #ifdef CONFIG_DEBUG_PCI

51. printk("%s - ", );

52. #endif

53. = addr & 0x00FFFFFF;

54.

55. REGISTER_NAMED_NODE(ob_pci_node, );

56.

57. activate_device();

58.

59. ob_pci_configure(addr, &config, mem_base, io_base);

/*配置设备的配置空间*/

60. ob_pci_add_properties(addr, pci_dev, &config);

61.

62. if (class == PCI_BASE_CLASS_BRIDGE &&

63. (subclass == PCI_SUBCLASS_BRIDGE_HOST ||

64. subclass == PCI_SUBCLASS_BRIDGE_PCI)) {

65. /* host or bridge */

66. free(*path);

67. *path = strdup();

68. }

69.

70. }

71. }

72. device_end();

73. }

具体某条总线上的设备扫描由以下函数实现:

[cpp] view plain copy

1.

const pci_dev_t *pci_find_device 

(uint8_t class, uint8_t subclass,

2. uint8_t iface, uint16_t vendor,

3. uint16_t product)

4. {

5. int (*config_cb)(const pci_config_t *config);

6. const pci_class_t *pclass;

7. const pci_subclass_t *psubclass;

8. const pci_iface_t *piface;

9. const pci_dev_t *dev;

10. const void *private;

11. pci_dev_t *new;

12. const char *name, *type;

13.

14. name = "unknown";

15. type = "unknown";

16. config_cb = NULL;

17. private = NULL;

18.

19. if (class == 0x00 && subclass == 0x01) {

20. /* Special hack for old style VGA devices */

21. class = 0x03;

22. subclass = 0x00;

23. } else if (class == 0xFF) {

24. /* Special case for misc devices */

25. dev = misc_pci;

26. goto find_device;

27. }

28. if (class > (sizeof(pci_classes) / sizeof(pci_class_t))) {

29. name = "invalid PCI device";

30. type = "invalid";

31. goto bad_device;

32. }

33. pclass = &pci_classes[class];

34. name = pclass->name;

35. type = pclass->type;

36. for (psubclass = pclass->subc; ; psubclass++) {

37. if (psubclass->subclass == 0xFF)

38. goto bad_device;

39. if (psubclass->subclass == subclass) {

40. if (psubclass->name != NULL)

41. name = psubclass->name;

42. if (psubclass->type != NULL)

43. type = psubclass->type;

44. if (psubclass->config_cb != NULL) {

45. config_cb = psubclass->config_cb;

46. }

47. if (psubclass->private != NULL)

48. private = psubclass->private;

49. if (psubclass->iface != NULL)

50. break;

51. dev = psubclass->devices;

52. goto find_device;

53. }

54. }

55. for (piface = psubclass->iface; ; piface++) {

56. if (piface->iface == 0xFF) {

57. dev = psubclass->devices;

58. break;

59. }

60. if (piface->iface == iface) {

61. if (piface->name != NULL)

62. name = piface->name;

63. if (piface->type != NULL)

64. type = piface->type;

65. if (piface->config_cb != NULL) {

66. config_cb = piface->config_cb;

67. }

68. if (piface->private != NULL)

69. private = piface->private;

70. dev = piface->devices;

71. break;

72. }

73. }

74. find_device:

75. if (dev == NULL)

76. goto bad_device;

77. for (;; dev++) {

78. if (dev->vendor == 0xFFFF && dev->product == 0xFFFF) {

79. goto bad_device;

80. }

81. if (dev->vendor == vendor && dev->product == product) {

82. if (dev->name != NULL)

83. name = dev->name;

84. if (dev->type != NULL)

85. type = dev->type;

86. if (dev->config_cb != NULL) {

87. config_cb = dev->config_cb;

88. }

89. if (dev->private != NULL)

90. private = dev->private;

91. new = malloc(sizeof(pci_dev_t));

92. if (new == NULL)

93. return NULL;

94. new->vendor = vendor;

95. new->product = product;

96. new->type = type;

97. new->name = name;

98. new->model = dev->model;

99. new->compat = dev->compat;

100.

101.

new->acells = dev->acells;

new->scells = dev->scells;

102.

103.

104.

105.

106.

107.

108.

109.

110.

new->icells = dev->icells;

new->config_cb = config_cb;

new->private = private;

return new;

}

}

bad_device:

printk("Cannot manage '%s' PCI device type '%s':n %x %x

(%x %x %x)n",

111.

112.

113.

114.

115.

116.

117.

118.

119.

120.

name, type, vendor, product, class, subclass, iface);

return NULL;

}

配置具体设备的配置空间

static void ob_pci_configure(pci_addr addr, pci_config_t

*config, unsigned long *mem_base,

121. unsigned long *io_base)

122.

123.

124.

125.

126.

127.

128.

129.

130.

131.

132.

133.

134.

3;

135.

136.

137.

138.

139.

140.

141.

142.

{

uint32_t smask, omask, amask, size, reloc, min_align;

unsigned long base;

pci_addr config_addr;

int reg;

uint8_t irq_pin, irq_line;

/*配置中断引脚与中断编号*/

irq_pin = pci_config_read8(addr, PCI_INTERRUPT_PIN);

if (irq_pin) {

config->irq_pin = irq_pin;

irq_pin = (((config->dev >> 11) & 0x1F) + irq_pin - 1) &

irq_line = arch->irqs[irq_pin];

pci_config_write8(addr, PCI_INTERRUPT_LINE, irq_line);

config->irq_line = irq_line;

} else

config->irq_line = -1;

/*配置memory空间和I/O空间*/

omask = 0x00000000;

143.

144.

145.

146.

147.

148.

149.

150.

151.

152.

153.

154.

155.

156.

157.

158.

for (reg = 0; reg < 7; reg++) {

config->assigned[reg] = 0x00000000;

config->sizes[reg] = 0x00000000;

if ((omask & 0x0000000f) == 0x4) {

/* 64 bits memory mapping */

continue;

}

if (reg == 6)

config_addr = PCI_ROM_ADDRESS;

else

config_addr = PCI_BASE_ADDR_0 + reg * 4;

config->regions[reg] = pci_config_read32(addr,

config_addr);

159.

160.

161.

162.

163.

/* get region size */

pci_config_write32(addr, config_addr, 0xffffffff);

smask = pci_config_read32(addr, config_addr);

164.

165.

166.

167.

168.

169.

170.

171.

172.

173.

174.

175.

176.

177.

178.

179.

180.

181.

182.

183.

184.

185.

if (smask == 0x00000000 || smask == 0xffffffff)

continue;

if (smask & 0x00000001 && reg != 6) {

/* I/O space */

base = *io_base;

min_align = 1 << 7;

amask = 0x00000001;

pci_config_write16(addr, PCI_COMMAND,

pci_config_read16(addr,

PCI_COMMAND) |

PCI_COMMAND_IO);

} else {

/* Memory Space */

base = *mem_base;

min_align = 1 << 16;

amask = 0x0000000F;

if (reg == 6) {

smask |= 1; /* ROM */

}

pci_config_write16(addr, PCI_COMMAND,

pci_config_read16(addr,

186.

187.

188.

189.

190.

191.

192.

193.

194.

195.

196.

197.

198.

199.

200.

201.

202.

203.

204.

205.

206.

207.

PCI_COMMAND) |

PCI_COMMAND_MEMORY);

}

omask = smask & amask;

smask &= ~amask;

size = (~smask) + 1;

config->sizes[reg] = size;

reloc = base;

if (size < min_align)

size = min_align;

reloc = (reloc + size -1) & ~(size - 1);

if (*io_base == base) {

*io_base = reloc + size;

reloc -= arch->io_base;

} else {

*mem_base = reloc + size;

}

pci_config_write32(addr, config_addr, reloc | omask);

config->assigned[reg] = reloc | omask;

}

}

208. 通过以上这些步骤,Bios就完成了所有PCI设备的扫描,并且为每个设备分配好了系统资源。

209.

210.

211.

  

本文标签: 设备扫描配置空间总线