想为babyos2实现网络功能,第一步就是得驱动网卡。选了个比较古老但general的网卡RTL8139,看了一些介绍,发现得先搞PCI。
1.Enumerating PCI Buses
类定义:
/*
* guzhoudiaoke@126.com
* 2018-03-01
*/
#include "types.h"
class pci_device_bar_t {
public:
void init(uint32 addr_reg_val, uint32 len_reg_val);
void dump();
enum type_e {
TYPE_INVALID = -1,
TYPE_MEM = 0,
TYPE_IO,
};
public:
uint32 m_type;
uint32 m_base_addr;
uint32 m_length;
};
class pci_device_t {
public:
void init(uint16 vendor_id, uint16 device_id, uint32 class_code, uint8 revision, bool multi_function);
void dump();
public:
uint16 m_vendor_id;
uint16 m_device_id;
uint32 m_class_code;
bool m_multi_function;
uint8 m_interrupt_line;
uint8 m_revision;
pci_device_bar_t m_bar[6];
};
class pci_t {
public:
void init();
private:
void enum_buses();
void check_device(uint8 bus, uint8 device);
uint32 read(uint32 bus, uint32 device, uint32 function, uint32 addr);
void write(uint32 bus, uint32 device, uint32 function, uint32 addr, uint32 val);
private:
list_t<pci_device_t> m_devices;
};
enum:
void pci_t::check_device(uint8 bus, uint8 device)
{
uint8 function = 0;
uint32 val = read(bus, device, function, PCI_CONFIG_VENDOR);
uint16 vendor_id = val & 0xffff;
uint16 device_id = val >> 16;
if (vendor_id == 0xffff) {
return;
}
val = read(bus, device, function, PCI_CONFIG_HDR_TYPE);
uint8 header_type = ((val >> 16));
pci_device_t pci_device;
pci_device.init(vendor_id, device_id, (header_type & 0x80));
val = read(bus, device, function, PCI_CONFIG_CLASS_REV);
uint32 classcode = val >> 8;
uint8 revision = val & 0xFF;
for (int bar = 0; bar < 6; bar++) {
int reg = PCI_CONFIG_BASE_ADDR0 + (bar*4);
val = read(bus, device, function, reg);
write(bus, device, function, reg, 0xffffffff);
uint32 len = read(bus, device, function, reg);
write(bus, device, function, reg, val);
if (len != 0 && len != 0xffffffff) {
pci_device.m_bar[bar].init(val, len);
}
}
val = read(bus, device, function, PCI_CONFIG_INTR);
if ((val & 0xff) > 0 && (val & 0xff) < 32) {
uint32 irq = val & 0xff;
pci_device.m_interrupt_line = irq;
}
console()->kprintf(YELLOW, "pci device at bus: %u, device: %u\n", bus, device);
pci_device.dump();
}
void pci_t::enum_buses()
{
for (uint16 bus = 0; bus < 256; bus++) {
for (uint8 device = 0; device < 32; device++) {
check_device(bus, device);
}
}
}
暂时不考虑效率问题,采用暴力遍历方式,遍历256条bus,每个bus遍历32个device,通过读config space 获取vendor id,判断该位置是否有一个设备。
2.Configuration Space Access Mechanism:
Two 32-bit I/O locations are used, the first location (0xCF8) is named CONFIG_ADDRESS, and the second (0xCFC) is called CONFIG_DATA. CONFIG_ADDRESS specifies the configuration address that is required to be accesses, while accesses to CONFIG_DATA will actually generate the configuration access and will transfer the data to or from the CONFIG_DATA register.
其中CONFIG_ADDRESS结构:
读写代码:
uint32 pci_t::read(uint32 bus, uint32 device, uint32 function, uint32 addr)
{
outl(PCI_CONFIG_ADDR, ((uint32) 0x80000000 | (bus << 16) | (device << 11) | (function << 8) | addr));
return inl(PCI_CONFIG_DATA);
}
void pci_t::write(uint32 bus, uint32 device, uint32 function, uint32 addr, uint32 val)
{
outl(PCI_CONFIG_ADDR, ((uint32) 0x80000000 | (bus << 16) | (device << 11) | (function << 8) | addr));
outl(PCI_CONFIG_DATA, val);
}
3.header_type:
8bit, bit 7 表示是否是multi function设备,bit 6-0表示 header type
当header type == 0时:
4.base address:
Base address Registers (or BARs) can be used to hold memory addresses used by the device, or offsets for port addresses. Typically, memory address BARs need to be located in physical ram while I/O space BARs can reside at any memory address (even beyond physical memory). To distinguish between them, you can check the value of the lowest bit. The following tables describe the two types of BARs:
void pci_device_bar_t::init(uint32 addr_reg_val, uint32 len_reg_val)
{
if (addr_reg_val == 0xffffffff) {
addr_reg_val = 0;
}
if (addr_reg_val & 1) {
m_type = TYPE_IO;
m_base_addr = addr_reg_val & PCI_BASE_ADDR_IO_MASK;
m_length = ~(len_reg_val & PCI_BASE_ADDR_IO_MASK) + 1;
}
else {
m_type = TYPE_MEM;
m_base_addr = addr_reg_val & PCI_BASE_ADDR_MEM_MASK;
m_length = ~(len_reg_val & PCI_BASE_ADDR_MEM_MASK) + 1;
}
}
5.Class Codes
The Class Code, Subclass, and Prog IF registers are used to identify the device’s type, the device’s function, and the device’s register-level programming interface, respectively.
6.运行结果:
从结果看,共找到4个设备, 分别是:
class code 06, subclass 00, prog if 00, Host Bridge
class code 06, subclass 01, prog if 00, ISA Bridge
class code 03, subclass 00, prog if 00, VGA-Compatible Controller
class code 02, subclass 00, prog if 00, Ethernet Controller
其中Ethernet Controller就是想要找的一台网卡,
它的vendor id为 0x10ec,表示Realtek瑞昱,device id 为0x8139。
当然qemu的默认网卡不是这个,是特意配置的这个网卡:
qemu-system-i386 -hda baby_hd.img -hdb fs.img -m size=128M -net nic,model=rtl8139 -smp 2
PCI比较复杂,本着够用的原则,暂时不准备深究,先暂时只学习这些,后面用到着再继续学习。
后面将会搞下RTL8139网卡。