babyos2(34) PCI Config space and Enumerating PCI buses

想为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网卡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值