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."); } }