199 lines
6.0 KiB
D
199 lines
6.0 KiB
D
module hulk.pci;
|
|
|
|
import hulk.cpu;
|
|
import hulk.klog;
|
|
import hulk.hurl;
|
|
import hulk.hurl.a1;
|
|
import hulk.range;
|
|
|
|
struct Pci
|
|
{
|
|
/* IO address for PCI configuration address. */
|
|
enum IO_ADDR_CONFIG_ADDRESS = 0xCF8u;
|
|
|
|
/* IO address for PCI configuration data. */
|
|
enum IO_ADDR_CONFIG_DATA = 0xCFCu;
|
|
|
|
struct Address
|
|
{
|
|
/** Bus number (0-255). */
|
|
ubyte bus_nr;
|
|
|
|
/** Device number (0-31). */
|
|
ubyte device_nr;
|
|
|
|
/** Function number (0-7). */
|
|
ubyte function_nr;
|
|
}
|
|
|
|
struct Device
|
|
{
|
|
Address address;
|
|
ushort vendor_id;
|
|
ushort device_id;
|
|
ubyte class_id;
|
|
ubyte subclass_id;
|
|
ubyte if_id;
|
|
bool multifunction;
|
|
ubyte header_type;
|
|
Range[6] memory_ranges;
|
|
|
|
void initialize(Address address, ushort vendor_id, ushort device_id)
|
|
{
|
|
this.address = address;
|
|
this.vendor_id = vendor_id;
|
|
this.device_id = device_id;
|
|
uint reg2 = read_config_register(address, 2u);
|
|
class_id = (reg2 >> 24u) & 0xFFu;
|
|
subclass_id = (reg2 >> 16u) & 0xFFu;
|
|
if_id = (reg2 >> 8u) & 0xFFu;
|
|
Klog.writefln("Found PCI device %04x:%04x (%02x:%02x:%02x) at %02u:%02u.%u",
|
|
vendor_id, device_id,
|
|
class_id, subclass_id, if_id,
|
|
address.bus_nr, address.device_nr, address.function_nr);
|
|
uint reg3 = read_config_register(address, 3u);
|
|
header_type = (reg3 >> 16u) & 0x7Fu;
|
|
multifunction = (address.function_nr == 0u) && ((reg3 & (1u << 23u)) != 0u);
|
|
|
|
if (header_type == 0u)
|
|
{
|
|
map_memory_regions();
|
|
}
|
|
}
|
|
|
|
private void map_memory_regions()
|
|
{
|
|
size_t range_index;
|
|
uint[2] r;
|
|
uint[2] s;
|
|
for (uint reg_no = 4u; reg_no <= 9u; reg_no++)
|
|
{
|
|
/* Read the BAR. */
|
|
r[0] = read_config_register(address, reg_no);
|
|
/* Skip zeroed BARs. */
|
|
if (r[0] == 0u)
|
|
{
|
|
continue;
|
|
}
|
|
/* Skip I/O BARs. */
|
|
if ((r[0] & 1u) != 0u)
|
|
{
|
|
continue;
|
|
}
|
|
bool is_64bit;
|
|
uint bar_type = (r[0] >> 1u) & 0x3u;
|
|
if (bar_type == 0u)
|
|
{
|
|
/* 32-bit region. */
|
|
r[1] = 0u;
|
|
}
|
|
else if ((bar_type == 2u) && (reg_no < 9u))
|
|
{
|
|
/* 64-bit region. */
|
|
is_64bit = true;
|
|
r[1] = read_config_register(address, reg_no + 1);
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Determine the region length. */
|
|
write_config_register(address, reg_no, 0xFFFF_FFFFu);
|
|
s[0] = read_config_register(address, reg_no);
|
|
write_config_register(address, reg_no, r[0]);
|
|
if (is_64bit)
|
|
{
|
|
write_config_register(address, reg_no + 1, 0xFFFF_FFFFu);
|
|
s[1] = read_config_register(address, reg_no + 1);
|
|
write_config_register(address, reg_no + 1, r[1]);
|
|
}
|
|
else
|
|
{
|
|
s[1] = 0xFFFF_FFFFu;
|
|
}
|
|
ulong mm_region_address = (cast(ulong)r[0] & 0xFFFF_FFF0u) | (cast(ulong)r[1] << 32u);
|
|
ulong length = ~((cast(ulong)s[0] & 0xFFFF_FFF0u) | (cast(ulong)s[1] << 32u)) + 1u;
|
|
ulong flags = (r[0] & 0x8) != 0u ? PT_WRITE_THROUGH : 0u;
|
|
Hurl.identity_map_range(mm_region_address, length, flags);
|
|
memory_ranges[range_index].address = mm_region_address;
|
|
memory_ranges[range_index].length = length;
|
|
range_index++;
|
|
if (is_64bit)
|
|
{
|
|
reg_no++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SerialBusController
|
|
{
|
|
enum ID = 0x0Cu;
|
|
|
|
struct USBController
|
|
{
|
|
enum ID = 0x03u;
|
|
|
|
struct XHCIController
|
|
{
|
|
enum ID = 0x30u;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static uint read_config_register(Address address, uint register_id)
|
|
{
|
|
uint cfg_addr = 0x8000_0000u |
|
|
(address.bus_nr << 16u) |
|
|
(address.device_nr << 11u) |
|
|
(address.function_nr << 8u) |
|
|
(register_id << 2u);
|
|
out32(IO_ADDR_CONFIG_ADDRESS, cfg_addr);
|
|
return in32(IO_ADDR_CONFIG_DATA);
|
|
}
|
|
|
|
private static void write_config_register(Address address, uint register_id, uint value)
|
|
{
|
|
uint cfg_addr = 0x8000_0000u |
|
|
(address.bus_nr << 16u) |
|
|
(address.device_nr << 11u) |
|
|
(address.function_nr << 8u) |
|
|
(register_id << 2u);
|
|
out32(IO_ADDR_CONFIG_ADDRESS, cfg_addr);
|
|
out32(IO_ADDR_CONFIG_DATA, value);
|
|
}
|
|
|
|
private static void scan(Address address)
|
|
{
|
|
ulong reg0 = read_config_register(address, 0u);
|
|
if (reg0 != 0xFFFFFFFFu)
|
|
{
|
|
ushort vendor_id = reg0 & 0xFFFFu;
|
|
ushort device_id = (reg0 >> 16u) & 0xFFFFu;
|
|
Device * device = A1.allocate!Device();
|
|
device.initialize(address, vendor_id, device_id);
|
|
if (device.multifunction)
|
|
{
|
|
for (address.function_nr = 1u; address.function_nr < 8u; address.function_nr++)
|
|
{
|
|
scan(address);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void initialize()
|
|
{
|
|
Klog.writefln("Scanning PCI devices...");
|
|
for (uint bus_nr = 0u; bus_nr < 256u; bus_nr++)
|
|
{
|
|
for (ubyte device_nr = 0u; device_nr < 32u; device_nr++)
|
|
{
|
|
scan(Address(cast(ubyte)bus_nr, device_nr, 0u));
|
|
}
|
|
}
|
|
Klog.writefln("PCI scan complete.");
|
|
}
|
|
}
|