/* * Copyright 2003-2008, Marcus Overhagen. All rights reserved. * Copyright 2005-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * * Distributed under the terms of the MIT License. */ #include #include #define __HAIKU_PCI_BUS_MANAGER_TESTING 1 #include #include #if defined(__i386__) || defined(__x86_64__) #include #endif #include "util/kernel_cpp.h" #include "pci_fixup.h" #include "pci_info.h" #include "pci_private.h" #include "pci.h" #define TRACE_CAP(x...) dprintf(x) #define FLOW(x...) //#define FLOW(x...) dprintf(x) PCI *gPCI; // #pragma mark bus manager exports long pci_get_nth_pci_info(long index, pci_info *outInfo) { return gPCI->GetNthInfo(index, outInfo); } uint32 pci_read_config(uint8 virtualBus, uint8 device, uint8 function, uint16 offset, uint8 size) { uint8 bus; uint8 domain; uint32 value; if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return 0xffffffff; if (gPCI->ReadConfig(domain, bus, device, function, offset, size, &value) != B_OK) return 0xffffffff; return value; } void pci_write_config(uint8 virtualBus, uint8 device, uint8 function, uint16 offset, uint8 size, uint32 value) { uint8 bus; uint8 domain; if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return; gPCI->WriteConfig(domain, bus, device, function, offset, size, value); } phys_addr_t pci_ram_address(phys_addr_t childAdr) { phys_addr_t hostAdr = 0; #if defined(__i386__) || defined(__x86_64__) hostAdr = childAdr; #else uint8 domain; pci_resource_range range; if (gPCI->LookupRange(B_IO_MEMORY, childAdr, domain, range) >= B_OK) hostAdr = childAdr - range.pci_address + range.host_address; #endif //dprintf("pci_ram_address(%#" B_PRIx64 ") -> %#" B_PRIx64 "\n", childAdr, hostAdr); return hostAdr; } status_t pci_find_capability(uint8 virtualBus, uint8 device, uint8 function, uint8 capID, uint8 *offset) { uint8 bus; uint8 domain; if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; return gPCI->FindCapability(domain, bus, device, function, capID, offset); } status_t pci_find_extended_capability(uint8 virtualBus, uint8 device, uint8 function, uint16 capID, uint16 *offset) { uint8 bus; uint8 domain; if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; return gPCI->FindExtendedCapability(domain, bus, device, function, capID, offset); } status_t pci_reserve_device(uchar virtualBus, uchar device, uchar function, const char *driverName, void *nodeCookie) { status_t status; uint8 bus; uint8 domain; TRACE(("pci_reserve_device(%d, %d, %d, %s)\n", virtualBus, device, function, driverName)); /* * we add 2 nodes to the PCI devices, one with constant attributes, * so adding for another driver fails, and a subnode with the * driver-provided informations. */ if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; //TRACE(("%s(%d [%d:%d], %d, %d, %s, %p)\n", __FUNCTION__, virtualBus, // domain, bus, device, function, driverName, nodeCookie)); device_attr matchThis[] = { // info about device {B_DEVICE_BUS, B_STRING_TYPE, {.string = "pci"}}, // location on PCI bus {B_PCI_DEVICE_DOMAIN, B_UINT8_TYPE, {.ui8 = domain}}, {B_PCI_DEVICE_BUS, B_UINT8_TYPE, {.ui8 = bus}}, {B_PCI_DEVICE_DEVICE, B_UINT8_TYPE, {.ui8 = device}}, {B_PCI_DEVICE_FUNCTION, B_UINT8_TYPE, {.ui8 = function}}, {NULL} }; device_attr legacyAttrs[] = { // info about device {B_DEVICE_BUS, B_STRING_TYPE, {.string = "legacy_driver"}}, {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Legacy Driver Reservation"}}, {NULL} }; device_attr drvAttrs[] = { // info about device {B_DEVICE_BUS, B_STRING_TYPE, {.string = "legacy_driver"}}, {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = driverName}}, {"legacy_driver", B_STRING_TYPE, {.string = driverName}}, {"legacy_driver_cookie", B_UINT64_TYPE, {.ui64 = (uint64)nodeCookie}}, {NULL} }; device_node *node, *legacy; status = B_DEVICE_NOT_FOUND; device_node *root_pci_node = gPCI->_GetDomainData(domain)->root_node; node = NULL; if (gDeviceManager->get_next_child_node(root_pci_node, matchThis, &node) < B_OK) { goto err1; } // common API for all legacy modules ? //status = legacy_driver_register(node, driverName, nodeCookie, PCI_LEGACY_DRIVER_MODULE_NAME); status = gDeviceManager->register_node(node, PCI_LEGACY_DRIVER_MODULE_NAME, legacyAttrs, NULL, &legacy); if (status < B_OK) goto err2; status = gDeviceManager->register_node(legacy, PCI_LEGACY_DRIVER_MODULE_NAME, drvAttrs, NULL, NULL); if (status < B_OK) goto err3; gDeviceManager->put_node(node); return B_OK; err3: gDeviceManager->unregister_node(legacy); err2: gDeviceManager->put_node(node); err1: TRACE(("pci_reserve_device for driver %s failed: %s\n", driverName, strerror(status))); return status; } status_t pci_unreserve_device(uchar virtualBus, uchar device, uchar function, const char *driverName, void *nodeCookie) { status_t status; uint8 bus; uint8 domain; TRACE(("pci_unreserve_device(%d, %d, %d, %s)\n", virtualBus, device, function, driverName)); if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; //TRACE(("%s(%d [%d:%d], %d, %d, %s, %p)\n", __FUNCTION__, virtualBus, // domain, bus, device, function, driverName, nodeCookie)); device_attr matchThis[] = { // info about device {B_DEVICE_BUS, B_STRING_TYPE, {.string = "pci"}}, // location on PCI bus {B_PCI_DEVICE_DOMAIN, B_UINT8_TYPE, {.ui8 = domain}}, {B_PCI_DEVICE_BUS, B_UINT8_TYPE, {.ui8 = bus}}, {B_PCI_DEVICE_DEVICE, B_UINT8_TYPE, {.ui8 = device}}, {B_PCI_DEVICE_FUNCTION, B_UINT8_TYPE, {.ui8 = function}}, {NULL} }; device_attr legacyAttrs[] = { // info about device {B_DEVICE_BUS, B_STRING_TYPE, {.string = "legacy_driver"}}, {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Legacy Driver Reservation"}}, {NULL} }; device_attr drvAttrs[] = { // info about device {B_DEVICE_BUS, B_STRING_TYPE, {.string = "legacy_driver"}}, {"legacy_driver", B_STRING_TYPE, {.string = driverName}}, {"legacy_driver_cookie", B_UINT64_TYPE, {.ui64 = (uint64)nodeCookie}}, {NULL} }; device_node *pci, *node, *legacy, *drv; status = B_DEVICE_NOT_FOUND; pci = gPCI->_GetDomainData(domain)->root_node; node = NULL; if (gDeviceManager->get_next_child_node(pci, matchThis, &node) < B_OK) goto err1; // common API for all legacy modules ? //status = legacy_driver_unregister(node, driverName, nodeCookie); legacy = NULL; if (gDeviceManager->get_next_child_node(node, legacyAttrs, &legacy) < B_OK) goto err2; drv = NULL; if (gDeviceManager->get_next_child_node(legacy, drvAttrs, &drv) < B_OK) goto err3; gDeviceManager->put_node(drv); status = gDeviceManager->unregister_node(drv); //dprintf("unreg:drv:%s\n", strerror(status)); gDeviceManager->put_node(legacy); status = gDeviceManager->unregister_node(legacy); //dprintf("unreg:legacy:%s\n", strerror(status)); // we'll get EBUSY here anyway... gDeviceManager->put_node(node); return B_OK; err3: gDeviceManager->put_node(legacy); err2: gDeviceManager->put_node(node); err1: TRACE(("pci_unreserve_device for driver %s failed: %s\n", driverName, strerror(status))); return status; } status_t pci_update_interrupt_line(uchar virtualBus, uchar device, uchar function, uchar newInterruptLineValue) { uint8 bus; uint8 domain; if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; return gPCI->UpdateInterruptLine(domain, bus, device, function, newInterruptLineValue); } status_t pci_get_powerstate(uchar virtualBus, uint8 device, uint8 function, uint8* state) { uint8 bus; uint8 domain; if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; return gPCI->GetPowerstate(domain, bus, device, function, state); } status_t pci_set_powerstate(uchar virtualBus, uint8 device, uint8 function, uint8 newState) { uint8 bus; uint8 domain; if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; return gPCI->SetPowerstate(domain, bus, device, function, newState); } // used by pci_info.cpp print_info_basic() void __pci_resolve_virtual_bus(uint8 virtualBus, uint8 *domain, uint8 *bus) { if (gPCI->ResolveVirtualBus(virtualBus, domain, bus) < B_OK) panic("ResolveVirtualBus failed"); } // #pragma mark kernel debugger commands static int display_io(int argc, char **argv) { int32 displayWidth; int32 itemSize; int32 num = 1; int address; int i = 1, j; switch (argc) { case 3: num = atoi(argv[2]); case 2: address = strtoul(argv[1], NULL, 0); break; default: kprintf("usage: %s
[num]\n", argv[0]); return 0; } // build the format string if (strcmp(argv[0], "inb") == 0 || strcmp(argv[0], "in8") == 0) { itemSize = 1; displayWidth = 16; } else if (strcmp(argv[0], "ins") == 0 || strcmp(argv[0], "in16") == 0) { itemSize = 2; displayWidth = 8; } else if (strcmp(argv[0], "inw") == 0 || strcmp(argv[0], "in32") == 0) { itemSize = 4; displayWidth = 4; } else { kprintf("display_io called in an invalid way!\n"); return 0; } for (i = 0; i < num; i++) { if ((i % displayWidth) == 0) { int32 displayed = min_c(displayWidth, (num-i)) * itemSize; if (i != 0) kprintf("\n"); kprintf("[0x%" B_PRIx32 "] ", address + i * itemSize); if (num > displayWidth) { // make sure the spacing in the last line is correct for (j = displayed; j < displayWidth * itemSize; j++) kprintf(" "); } kprintf(" "); } switch (itemSize) { case 1: kprintf(" %02" B_PRIx8, pci_read_io_8(address + i * itemSize)); break; case 2: kprintf(" %04" B_PRIx16, pci_read_io_16(address + i * itemSize)); break; case 4: kprintf(" %08" B_PRIx32, pci_read_io_32(address + i * itemSize)); break; } } kprintf("\n"); return 0; } static int write_io(int argc, char **argv) { int32 itemSize; uint32 value; int address; int i = 1; if (argc < 3) { kprintf("usage: %s
[value [...]]\n", argv[0]); return 0; } address = strtoul(argv[1], NULL, 0); if (strcmp(argv[0], "outb") == 0 || strcmp(argv[0], "out8") == 0) { itemSize = 1; } else if (strcmp(argv[0], "outs") == 0 || strcmp(argv[0], "out16") == 0) { itemSize = 2; } else if (strcmp(argv[0], "outw") == 0 || strcmp(argv[0], "out32") == 0) { itemSize = 4; } else { kprintf("write_io called in an invalid way!\n"); return 0; } // skip cmd name and address argv += 2; argc -= 2; for (i = 0; i < argc; i++) { value = strtoul(argv[i], NULL, 0); switch (itemSize) { case 1: pci_write_io_8(address + i * itemSize, value); break; case 2: pci_write_io_16(address + i * itemSize, value); break; case 4: pci_write_io_32(address + i * itemSize, value); break; } } return 0; } static int pcistatus(int argc, char **argv) { for (uint32 domain = 0; ; domain++) { domain_data *data = gPCI->_GetDomainData(domain); if (data == NULL) break; gPCI->ClearDeviceStatus(data->bus, true); } return 0; } static int pcirefresh(int argc, char **argv) { gPCI->RefreshDeviceInfo(); pci_print_info(); return 0; } // #pragma mark bus manager init/uninit status_t pci_init(void) { gPCI = new PCI; add_debugger_command("inw", &display_io, "dump io words (32-bit)"); add_debugger_command("in32", &display_io, "dump io words (32-bit)"); add_debugger_command("ins", &display_io, "dump io shorts (16-bit)"); add_debugger_command("in16", &display_io, "dump io shorts (16-bit)"); add_debugger_command("inb", &display_io, "dump io bytes (8-bit)"); add_debugger_command("in8", &display_io, "dump io bytes (8-bit)"); add_debugger_command("outw", &write_io, "write io words (32-bit)"); add_debugger_command("out32", &write_io, "write io words (32-bit)"); add_debugger_command("outs", &write_io, "write io shorts (16-bit)"); add_debugger_command("out16", &write_io, "write io shorts (16-bit)"); add_debugger_command("outb", &write_io, "write io bytes (8-bit)"); add_debugger_command("out8", &write_io, "write io bytes (8-bit)"); add_debugger_command("pcistatus", &pcistatus, "dump and clear pci device status registers"); add_debugger_command("pcirefresh", &pcirefresh, "refresh and print all pci_info"); return B_OK; } void pci_uninit(void) { delete gPCI; remove_debugger_command("outw", &write_io); remove_debugger_command("out32", &write_io); remove_debugger_command("outs", &write_io); remove_debugger_command("out16", &write_io); remove_debugger_command("outb", &write_io); remove_debugger_command("out8", &write_io); remove_debugger_command("inw", &display_io); remove_debugger_command("in32", &display_io); remove_debugger_command("ins", &display_io); remove_debugger_command("in16", &display_io); remove_debugger_command("inb", &display_io); remove_debugger_command("in8", &display_io); remove_debugger_command("pcistatus", &pcistatus); remove_debugger_command("pcirefresh", &pcirefresh); } // #pragma mark PCI class PCI::PCI() : fDomainCount(0), fBusEnumeration(false), fVirtualBusMap(), fNextVirtualBus(0) { #if defined(__POWERPC__) || defined(__M68K__) fBusEnumeration = true; #endif } void PCI::InitBus(PCIBus *bus) { if (fBusEnumeration) { _EnumerateBus(bus->domain, 0); } if (1) { _FixupDevices(bus->domain, 0); } _DiscoverBus(bus); _ConfigureBridges(bus); ClearDeviceStatus(bus, false); _RefreshDeviceInfo(bus); } PCI::~PCI() { } status_t PCI::_CreateVirtualBus(uint8 domain, uint8 bus, uint8 *virtualBus) { #if defined(__i386__) || defined(__x86_64__) // IA32 doesn't use domains if (domain) panic("PCI::CreateVirtualBus domain != 0"); *virtualBus = bus; return B_OK; #else if (fNextVirtualBus > 0xff) panic("PCI::CreateVirtualBus: virtual bus number space exhausted"); uint16 value = domain << 8 | bus; for (VirtualBusMap::Iterator it = fVirtualBusMap.Begin(); it != fVirtualBusMap.End(); ++it) { if (it->Value() == value) { *virtualBus = it->Key(); FLOW("PCI::CreateVirtualBus: domain %d, bus %d already in map => " "virtualBus %d\n", domain, bus, *virtualBus); return B_OK; } } *virtualBus = fNextVirtualBus++; FLOW("PCI::CreateVirtualBus: domain %d, bus %d => virtualBus %d\n", domain, bus, *virtualBus); return fVirtualBusMap.Insert(*virtualBus, value); #endif } status_t PCI::ResolveVirtualBus(uint8 virtualBus, uint8 *domain, uint8 *bus) { #if defined(__i386__) || defined(__x86_64__) // IA32 doesn't use domains *bus = virtualBus; *domain = 0; return B_OK; #else if (virtualBus >= fNextVirtualBus) return B_ERROR; uint16 value = fVirtualBusMap.Get(virtualBus); *domain = value >> 8; *bus = value & 0xff; return B_OK; #endif } status_t PCI::AddController(pci_controller_module_info *controller, void *controllerCookie, device_node *rootNode, domain_data **domainData) { if (fDomainCount == MAX_PCI_DOMAINS) return B_ERROR; uint8 domain = fDomainCount; domain_data& data = fDomainData[domain]; data.controller = controller; data.controller_cookie = controllerCookie; data.root_node = rootNode; data.bus = new(std::nothrow) PCIBus {.domain = domain}; if (data.bus == NULL) return B_NO_MEMORY; // initialized later to avoid call back into controller at this point data.max_bus_devices = -1; fDomainCount++; InitDomainData(data); InitBus(data.bus); if (data.controller->finalize != NULL) data.controller->finalize(data.controller_cookie); _RefreshDeviceInfo(data.bus); pci_print_info(); *domainData = &data; return B_OK; } status_t PCI::LookupRange(uint32 type, phys_addr_t pciAddr, uint8 &domain, pci_resource_range &range, uint8 **mappedAdr) { for (uint8 curDomain = 0; curDomain < fDomainCount; curDomain++) { const auto &ranges = fDomainData[curDomain].ranges; for (int32 i = 0; i < ranges.Count(); i++) { const pci_resource_range curRange = ranges[i]; if (curRange.type != type) continue; if (pciAddr >= curRange.pci_address && pciAddr < (curRange.pci_address + curRange.size)) { domain = curDomain; range = curRange; #if !(defined(__i386__) || defined(__x86_64__)) if (type == B_IO_PORT && mappedAdr != NULL) *mappedAdr = fDomainData[curDomain].io_port_adr; #endif return B_OK; } } } return B_ENTRY_NOT_FOUND; } void PCI::InitDomainData(domain_data &data) { int32 count; status_t status; pci_controller_module_info *ctrl = data.controller; void *ctrlCookie = data.controller_cookie; status = ctrl->get_max_bus_devices(ctrlCookie, &count); data.max_bus_devices = (status == B_OK) ? count : 0; pci_resource_range range; for (uint32 j = 0; ctrl->get_range(ctrlCookie, j, &range) >= B_OK; j++) data.ranges.Add(range); #if !(defined(__i386__) || defined(__x86_64__)) for (int32 i = 0; i < data.ranges.Count(); i++) { pci_resource_range &ioPortRange = data.ranges[i]; if (ioPortRange.type != B_IO_PORT) continue; if (ioPortRange.size > 0) { data.io_port_area = map_physical_memory("PCI IO Ports", ioPortRange.host_address, ioPortRange.size, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&data.io_port_adr); if (data.io_port_area < B_OK) data.io_port_adr = NULL; // TODO: Map other IO ports (if any.) break; } } #endif } domain_data * PCI::_GetDomainData(uint8 domain) { if (domain >= fDomainCount) return NULL; return &fDomainData[domain]; } inline int PCI::_NumFunctions(uint8 domain, uint8 bus, uint8 device) { uint8 type = ReadConfig(domain, bus, device, 0, PCI_header_type, 1); return (type & PCI_multifunction) != 0 ? 8 : 1; } status_t PCI::GetNthInfo(long index, pci_info *outInfo) { long currentIndex = 0; for (uint32 domain = 0; domain < fDomainCount; domain++) { if (_GetNthInfo(fDomainData[domain].bus, ¤tIndex, index, outInfo) >= B_OK) return B_OK; } return B_ERROR; } status_t PCI::_GetNthInfo(PCIBus *bus, long *currentIndex, long wantIndex, pci_info *outInfo) { // maps tree structure to linear indexed view PCIDev *dev = bus->child; while (dev) { if (*currentIndex == wantIndex) { *outInfo = dev->info; return B_OK; } *currentIndex += 1; if (dev->child && B_OK == _GetNthInfo(dev->child, currentIndex, wantIndex, outInfo)) return B_OK; dev = dev->next; } return B_ERROR; } void PCI::_EnumerateBus(uint8 domain, uint8 bus, uint8 *subordinateBus) { TRACE(("PCI: EnumerateBus: domain %u, bus %u\n", domain, bus)); int maxBusDevices = _GetDomainData(domain)->max_bus_devices; // step 1: disable all bridges on this bus for (int dev = 0; dev < maxBusDevices; dev++) { uint16 vendor_id = ReadConfig(domain, bus, dev, 0, PCI_vendor_id, 2); if (vendor_id == 0xffff) continue; int numFunctions = _NumFunctions(domain, bus, dev); for (int function = 0; function < numFunctions; function++) { uint16 device_id = ReadConfig(domain, bus, dev, function, PCI_device_id, 2); if (device_id == 0xffff) continue; uint8 baseClass = ReadConfig(domain, bus, dev, function, PCI_class_base, 1); uint8 subClass = ReadConfig(domain, bus, dev, function, PCI_class_sub, 1); if (baseClass != PCI_bridge || subClass != PCI_pci) continue; // skip incorrectly configured devices uint8 headerType = ReadConfig(domain, bus, dev, function, PCI_header_type, 1) & PCI_header_type_mask; if (headerType != PCI_header_type_PCI_to_PCI_bridge) continue; TRACE(("PCI: found PCI-PCI bridge: domain %u, bus %u, dev %u, func %u\n", domain, bus, dev, function)); TRACE(("PCI: original settings: pcicmd %04" B_PRIx32 ", primary-bus " "%" B_PRIu32 ", secondary-bus %" B_PRIu32 ", subordinate-bus " "%" B_PRIu32 "\n", ReadConfig(domain, bus, dev, function, PCI_command, 2), ReadConfig(domain, bus, dev, function, PCI_primary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_secondary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_subordinate_bus, 1))); // disable decoding uint16 pcicmd; pcicmd = ReadConfig(domain, bus, dev, function, PCI_command, 2); pcicmd &= ~(PCI_command_io | PCI_command_memory | PCI_command_master); WriteConfig(domain, bus, dev, function, PCI_command, 2, pcicmd); // disable busses WriteConfig(domain, bus, dev, function, PCI_primary_bus, 1, 0); WriteConfig(domain, bus, dev, function, PCI_secondary_bus, 1, 0); WriteConfig(domain, bus, dev, function, PCI_subordinate_bus, 1, 0); TRACE(("PCI: disabled settings: pcicmd %04" B_PRIx32 ", primary-bus " "%" B_PRIu32 ", secondary-bus %" B_PRIu32 ", subordinate-bus " "%" B_PRIu32 "\n", ReadConfig(domain, bus, dev, function, PCI_command, 2), ReadConfig(domain, bus, dev, function, PCI_primary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_secondary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_subordinate_bus, 1))); } } uint8 lastUsedBusNumber = bus; // step 2: assign busses to all bridges, and enable them again for (int dev = 0; dev < maxBusDevices; dev++) { uint16 vendor_id = ReadConfig(domain, bus, dev, 0, PCI_vendor_id, 2); if (vendor_id == 0xffff) continue; int numFunctions = _NumFunctions(domain, bus, dev); for (int function = 0; function < numFunctions; function++) { uint16 deviceID = ReadConfig(domain, bus, dev, function, PCI_device_id, 2); if (deviceID == 0xffff) continue; uint8 baseClass = ReadConfig(domain, bus, dev, function, PCI_class_base, 1); uint8 subClass = ReadConfig(domain, bus, dev, function, PCI_class_sub, 1); if (baseClass != PCI_bridge || subClass != PCI_pci) continue; // skip incorrectly configured devices uint8 headerType = ReadConfig(domain, bus, dev, function, PCI_header_type, 1) & PCI_header_type_mask; if (headerType != PCI_header_type_PCI_to_PCI_bridge) continue; TRACE(("PCI: configuring PCI-PCI bridge: domain %u, bus %u, dev %u, func %u\n", domain, bus, dev, function)); // open Scheunentor for enumerating the bus behind the bridge WriteConfig(domain, bus, dev, function, PCI_primary_bus, 1, bus); WriteConfig(domain, bus, dev, function, PCI_secondary_bus, 1, lastUsedBusNumber + 1); WriteConfig(domain, bus, dev, function, PCI_subordinate_bus, 1, 255); // enable decoding (too early here?) uint16 pcicmd; pcicmd = ReadConfig(domain, bus, dev, function, PCI_command, 2); pcicmd |= PCI_command_io | PCI_command_memory | PCI_command_master; WriteConfig(domain, bus, dev, function, PCI_command, 2, pcicmd); TRACE(("PCI: probing settings: pcicmd %04" B_PRIx32 ", primary-bus " "%" B_PRIu32 ", secondary-bus %" B_PRIu32 ", subordinate-bus " "%" B_PRIu32 "\n", ReadConfig(domain, bus, dev, function, PCI_command, 2), ReadConfig(domain, bus, dev, function, PCI_primary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_secondary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_subordinate_bus, 1))); // enumerate bus _EnumerateBus(domain, lastUsedBusNumber + 1, &lastUsedBusNumber); // close Scheunentor WriteConfig(domain, bus, dev, function, PCI_subordinate_bus, 1, lastUsedBusNumber); TRACE(("PCI: configured settings: pcicmd %04" B_PRIx32 ", primary-bus " "%" B_PRIu32 ", secondary-bus %" B_PRIu32 ", subordinate-bus " "%" B_PRIu32 "\n", ReadConfig(domain, bus, dev, function, PCI_command, 2), ReadConfig(domain, bus, dev, function, PCI_primary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_secondary_bus, 1), ReadConfig(domain, bus, dev, function, PCI_subordinate_bus, 1))); } } if (subordinateBus) *subordinateBus = lastUsedBusNumber; TRACE(("PCI: EnumerateBus done: domain %u, bus %u, last used bus number %u\n", domain, bus, lastUsedBusNumber)); } void PCI::_FixupDevices(uint8 domain, uint8 bus) { FLOW("PCI: FixupDevices domain %u, bus %u\n", domain, bus); int maxBusDevices = _GetDomainData(domain)->max_bus_devices; static int recursed = 0; if (recursed++ > 10) { // guard against buggy chipsets // XXX: is there any official limit ? dprintf("PCI: FixupDevices: too many recursions (buggy chipset?)\n"); recursed--; return; } for (int dev = 0; dev < maxBusDevices; dev++) { uint16 vendorId = ReadConfig(domain, bus, dev, 0, PCI_vendor_id, 2); if (vendorId == 0xffff) continue; int numFunctions = _NumFunctions(domain, bus, dev); for (int function = 0; function < numFunctions; function++) { uint16 deviceId = ReadConfig(domain, bus, dev, function, PCI_device_id, 2); if (deviceId == 0xffff) continue; pci_fixup_device(this, domain, bus, dev, function); uint8 baseClass = ReadConfig(domain, bus, dev, function, PCI_class_base, 1); if (baseClass != PCI_bridge) continue; uint8 subClass = ReadConfig(domain, bus, dev, function, PCI_class_sub, 1); if (subClass != PCI_pci) continue; // some FIC motherboards have a buggy BIOS... // make sure the header type is correct for a bridge, uint8 headerType = ReadConfig(domain, bus, dev, function, PCI_header_type, 1) & PCI_header_type_mask; if (headerType != PCI_header_type_PCI_to_PCI_bridge) { dprintf("PCI: dom %u, bus %u, dev %2u, func %u, PCI bridge" " class but wrong header type 0x%02x, ignoring.\n", domain, bus, dev, function, headerType); continue; } int busBehindBridge = ReadConfig(domain, bus, dev, function, PCI_secondary_bus, 1); TRACE(("PCI: FixupDevices: checking bus %d behind %04x:%04x\n", busBehindBridge, vendorId, deviceId)); _FixupDevices(domain, busBehindBridge); } } recursed--; } void PCI::_ConfigureBridges(PCIBus *bus) { for (PCIDev *dev = bus->child; dev; dev = dev->next) { if (dev->info.class_base == PCI_bridge && dev->info.class_sub == PCI_pci && (dev->info.header_type & PCI_header_type_mask) == PCI_header_type_PCI_to_PCI_bridge) { uint16 bridgeControlOld = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control, 2); uint16 bridgeControlNew = bridgeControlOld; // Enable: Parity Error Response, SERR, Master Abort Mode, Discard // Timer SERR // Clear: Discard Timer Status bridgeControlNew |= PCI_bridge_parity_error_response | PCI_bridge_serr | PCI_bridge_master_abort | PCI_bridge_discard_timer_status | PCI_bridge_discard_timer_serr; // Set discard timer to 2^15 PCI clocks bridgeControlNew &= ~(PCI_bridge_primary_discard_timeout | PCI_bridge_secondary_discard_timeout); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control, 2, bridgeControlNew); bridgeControlNew = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control, 2); dprintf("PCI: dom %u, bus %u, dev %2u, func %u, changed PCI bridge" " control from 0x%04x to 0x%04x\n", dev->domain, dev->bus, dev->device, dev->function, bridgeControlOld, bridgeControlNew); } if (dev->child) _ConfigureBridges(dev->child); } } void PCI::ClearDeviceStatus(PCIBus *bus, bool dumpStatus) { for (PCIDev *dev = bus->child; dev; dev = dev->next) { // Clear and dump PCI device status uint16 status = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_status, 2); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_status, 2, status); if (dumpStatus) { kprintf("domain %u, bus %u, dev %2u, func %u, PCI device status " "0x%04x\n", dev->domain, dev->bus, dev->device, dev->function, status); if (status & PCI_status_parity_error_detected) kprintf(" Detected Parity Error\n"); if (status & PCI_status_serr_signalled) kprintf(" Signalled System Error\n"); if (status & PCI_status_master_abort_received) kprintf(" Received Master-Abort\n"); if (status & PCI_status_target_abort_received) kprintf(" Received Target-Abort\n"); if (status & PCI_status_target_abort_signalled) kprintf(" Signalled Target-Abort\n"); if (status & PCI_status_parity_signalled) kprintf(" Master Data Parity Error\n"); } if (dev->info.class_base == PCI_bridge && dev->info.class_sub == PCI_pci) { // Clear and dump PCI bridge secondary status uint16 secondaryStatus = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_status, 2); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_status, 2, secondaryStatus); if (dumpStatus) { kprintf("domain %u, bus %u, dev %2u, func %u, PCI bridge " "secondary status 0x%04x\n", dev->domain, dev->bus, dev->device, dev->function, secondaryStatus); if (secondaryStatus & PCI_status_parity_error_detected) kprintf(" Detected Parity Error\n"); if (secondaryStatus & PCI_status_serr_signalled) kprintf(" Received System Error\n"); if (secondaryStatus & PCI_status_master_abort_received) kprintf(" Received Master-Abort\n"); if (secondaryStatus & PCI_status_target_abort_received) kprintf(" Received Target-Abort\n"); if (secondaryStatus & PCI_status_target_abort_signalled) kprintf(" Signalled Target-Abort\n"); if (secondaryStatus & PCI_status_parity_signalled) kprintf(" Data Parity Reported\n"); } // Clear and dump the discard-timer error bit located in bridge-control register uint16 bridgeControl = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control, 2); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control, 2, bridgeControl); if (dumpStatus) { kprintf("domain %u, bus %u, dev %2u, func %u, PCI bridge " "control 0x%04x\n", dev->domain, dev->bus, dev->device, dev->function, bridgeControl); if (bridgeControl & PCI_bridge_discard_timer_status) { kprintf(" bridge-control: Discard Timer Error\n"); } } } if (dev->child) ClearDeviceStatus(dev->child, dumpStatus); } } void PCI::_DiscoverBus(PCIBus *bus) { FLOW("PCI: DiscoverBus, domain %u, bus %u\n", bus->domain, bus->bus); int maxBusDevices = _GetDomainData(bus->domain)->max_bus_devices; static int recursed = 0; if (recursed++ > 10) { // guard against buggy chipsets // XXX: is there any official limit ? dprintf("PCI: DiscoverBus: too many recursions (buggy chipset?)\n"); recursed--; return; } for (int dev = 0; dev < maxBusDevices; dev++) { uint16 vendorID = ReadConfig(bus->domain, bus->bus, dev, 0, PCI_vendor_id, 2); if (vendorID == 0xffff) continue; int numFunctions = _NumFunctions(bus->domain, bus->bus, dev); for (int function = 0; function < numFunctions; function++) _DiscoverDevice(bus, dev, function); } recursed--; } void PCI::_DiscoverDevice(PCIBus *bus, uint8 dev, uint8 function) { FLOW("PCI: DiscoverDevice, domain %u, bus %u, dev %u, func %u\n", bus->domain, bus->bus, dev, function); uint16 deviceID = ReadConfig(bus->domain, bus->bus, dev, function, PCI_device_id, 2); if (deviceID == 0xffff) return; PCIDev *newDev = _CreateDevice(bus, dev, function); uint8 baseClass = ReadConfig(bus->domain, bus->bus, dev, function, PCI_class_base, 1); uint8 subClass = ReadConfig(bus->domain, bus->bus, dev, function, PCI_class_sub, 1); uint8 headerType = ReadConfig(bus->domain, bus->bus, dev, function, PCI_header_type, 1) & PCI_header_type_mask; if (baseClass == PCI_bridge && subClass == PCI_pci && headerType == PCI_header_type_PCI_to_PCI_bridge) { uint8 secondaryBus = ReadConfig(bus->domain, bus->bus, dev, function, PCI_secondary_bus, 1); PCIBus *newBus = _CreateBus(newDev, bus->domain, secondaryBus); _DiscoverBus(newBus); } } PCIBus * PCI::_CreateBus(PCIDev *parent, uint8 domain, uint8 bus) { PCIBus *newBus = new(std::nothrow) PCIBus; if (newBus == NULL) return NULL; newBus->parent = parent; newBus->child = NULL; newBus->domain = domain; newBus->bus = bus; // append parent->child = newBus; return newBus; } PCIDev * PCI::_CreateDevice(PCIBus *parent, uint8 device, uint8 function) { FLOW("PCI: CreateDevice, domain %u, bus %u, dev %u, func %u:\n", parent->domain, parent->bus, device, function); PCIDev *newDev = new(std::nothrow) PCIDev; if (newDev == NULL) return NULL; newDev->next = NULL; newDev->parent = parent; newDev->child = NULL; newDev->domain = parent->domain; newDev->bus = parent->bus; newDev->device = device; newDev->function = function; memset(&newDev->info, 0, sizeof(newDev->info)); _ReadBasicInfo(newDev); FLOW("PCI: CreateDevice, vendor 0x%04x, device 0x%04x, class_base 0x%02x, " "class_sub 0x%02x\n", newDev->info.vendor_id, newDev->info.device_id, newDev->info.class_base, newDev->info.class_sub); // append if (parent->child == NULL) { parent->child = newDev; } else { PCIDev *sub = parent->child; while (sub->next) sub = sub->next; sub->next = newDev; } return newDev; } uint64 PCI::_BarSize(uint64 bits) { if (!bits) return 0; uint64 size = 1; while ((bits & size) == 0) size <<= 1; return size; } size_t PCI::_GetBarInfo(PCIDev *dev, uint8 offset, uint32 &_ramAddress, uint32 &_pciAddress, uint32 &_size, uint8 &flags, uint32 *_highRAMAddress, uint32 *_highPCIAddress, uint32 *_highSize) { uint64 pciAddress = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4, 0xffffffff); uint64 size = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4, pciAddress); uint32 mask = PCI_address_memory_32_mask; bool is64bit = false; if ((pciAddress & PCI_address_space) != 0) mask = PCI_address_io_mask; else { is64bit = (pciAddress & PCI_address_type) == PCI_address_type_64; if (is64bit && _highRAMAddress != NULL) { uint64 highPCIAddress = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, offset + 4, 4); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, offset + 4, 4, 0xffffffff); uint64 highSize = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, offset + 4, 4); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, offset + 4, 4, highPCIAddress); pciAddress |= highPCIAddress << 32; size |= highSize << 32; } } flags = (uint32)pciAddress & ~mask; pciAddress &= ((uint64)0xffffffff << 32) | mask; size &= ((uint64)0xffffffff << 32) | mask; size = _BarSize(size); uint64 ramAddress = pci_ram_address(pciAddress); _ramAddress = ramAddress; _pciAddress = pciAddress; _size = size; if (!is64bit) return 1; if (_highRAMAddress == NULL || _highPCIAddress == NULL || _highSize == NULL) panic("64 bit PCI BAR but no space to store high values\n"); else { *_highRAMAddress = ramAddress >> 32; *_highPCIAddress = pciAddress >> 32; *_highSize = size >> 32; } return 2; } void PCI::_GetRomBarInfo(PCIDev *dev, uint8 offset, uint32 &_address, uint32 *_size, uint8 *_flags) { uint32 oldValue = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4, 0xfffffffe); // LSB must be 0 uint32 newValue = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, offset, 4, oldValue); _address = oldValue & PCI_rom_address_mask; if (_size != NULL) *_size = _BarSize(newValue & PCI_rom_address_mask); if (_flags != NULL) *_flags = newValue & 0xf; } void PCI::_ReadBasicInfo(PCIDev *dev) { uint8 virtualBus; if (_CreateVirtualBus(dev->domain, dev->bus, &virtualBus) != B_OK) { dprintf("PCI: CreateVirtualBus failed, domain %u, bus %u\n", dev->domain, dev->bus); return; } dev->info.vendor_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_vendor_id, 2); dev->info.device_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_device_id, 2); dev->info.bus = virtualBus; dev->info.device = dev->device; dev->info.function = dev->function; dev->info.revision = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_revision, 1); dev->info.class_api = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_class_api, 1); dev->info.class_sub = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_class_sub, 1); dev->info.class_base = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_class_base, 1); dev->info.line_size = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_line_size, 1); dev->info.latency = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_latency, 1); // BeOS does not mask off the multifunction bit, developer must use // (header_type & PCI_header_type_mask) dev->info.header_type = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_header_type, 1); dev->info.bist = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bist, 1); dev->info.reserved = 0; } void PCI::_ReadHeaderInfo(PCIDev *dev) { switch (dev->info.header_type & PCI_header_type_mask) { case PCI_header_type_generic: { // disable PCI device address decoding (io and memory) while BARs // are modified uint16 pcicmd = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_command, 2); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_command, 2, pcicmd & ~(PCI_command_io | PCI_command_memory)); // get BAR size infos _GetRomBarInfo(dev, PCI_rom_base, dev->info.u.h0.rom_base_pci, &dev->info.u.h0.rom_size); for (int i = 0; i < 6;) { i += _GetBarInfo(dev, PCI_base_registers + 4 * i, dev->info.u.h0.base_registers[i], dev->info.u.h0.base_registers_pci[i], dev->info.u.h0.base_register_sizes[i], dev->info.u.h0.base_register_flags[i], i < 5 ? &dev->info.u.h0.base_registers[i + 1] : NULL, i < 5 ? &dev->info.u.h0.base_registers_pci[i + 1] : NULL, i < 5 ? &dev->info.u.h0.base_register_sizes[i + 1] : NULL); } // restore PCI device address decoding WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_command, 2, pcicmd); dev->info.u.h0.rom_base = (uint32)pci_ram_address( dev->info.u.h0.rom_base_pci); dev->info.u.h0.cardbus_cis = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_cardbus_cis, 4); dev->info.u.h0.subsystem_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_subsystem_id, 2); dev->info.u.h0.subsystem_vendor_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_subsystem_vendor_id, 2); dev->info.u.h0.interrupt_line = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_interrupt_line, 1); dev->info.u.h0.interrupt_pin = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_interrupt_pin, 1); dev->info.u.h0.min_grant = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_min_grant, 1); dev->info.u.h0.max_latency = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_max_latency, 1); break; } case PCI_header_type_PCI_to_PCI_bridge: { // disable PCI device address decoding (io and memory) while BARs // are modified uint16 pcicmd = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_command, 2); WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_command, 2, pcicmd & ~(PCI_command_io | PCI_command_memory)); _GetRomBarInfo(dev, PCI_bridge_rom_base, dev->info.u.h1.rom_base_pci); for (int i = 0; i < 2;) { i += _GetBarInfo(dev, PCI_base_registers + 4 * i, dev->info.u.h1.base_registers[i], dev->info.u.h1.base_registers_pci[i], dev->info.u.h1.base_register_sizes[i], dev->info.u.h1.base_register_flags[i], i < 1 ? &dev->info.u.h1.base_registers[i + 1] : NULL, i < 1 ? &dev->info.u.h1.base_registers_pci[i + 1] : NULL, i < 1 ? &dev->info.u.h1.base_register_sizes[i + 1] : NULL); } // restore PCI device address decoding WriteConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_command, 2, pcicmd); dev->info.u.h1.rom_base = (uint32)pci_ram_address( dev->info.u.h1.rom_base_pci); dev->info.u.h1.primary_bus = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_primary_bus, 1); dev->info.u.h1.secondary_bus = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_bus, 1); dev->info.u.h1.subordinate_bus = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_subordinate_bus, 1); dev->info.u.h1.secondary_latency = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_latency, 1); dev->info.u.h1.io_base = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_base, 1); dev->info.u.h1.io_limit = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_limit, 1); dev->info.u.h1.secondary_status = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_status, 2); dev->info.u.h1.memory_base = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_memory_base, 2); dev->info.u.h1.memory_limit = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_memory_limit, 2); dev->info.u.h1.prefetchable_memory_base = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_prefetchable_memory_base, 2); dev->info.u.h1.prefetchable_memory_limit = ReadConfig( dev->domain, dev->bus, dev->device, dev->function, PCI_prefetchable_memory_limit, 2); dev->info.u.h1.prefetchable_memory_base_upper32 = ReadConfig( dev->domain, dev->bus, dev->device, dev->function, PCI_prefetchable_memory_base_upper32, 4); dev->info.u.h1.prefetchable_memory_limit_upper32 = ReadConfig( dev->domain, dev->bus, dev->device, dev->function, PCI_prefetchable_memory_limit_upper32, 4); dev->info.u.h1.io_base_upper16 = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_base_upper16, 2); dev->info.u.h1.io_limit_upper16 = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_limit_upper16, 2); dev->info.u.h1.interrupt_line = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_interrupt_line, 1); dev->info.u.h1.interrupt_pin = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_interrupt_pin, 1); dev->info.u.h1.bridge_control = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control, 2); dev->info.u.h1.subsystem_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_sub_device_id_1, 2); dev->info.u.h1.subsystem_vendor_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_sub_vendor_id_1, 2); break; } case PCI_header_type_cardbus: { // for testing only, not final: dev->info.u.h2.subsystem_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_sub_device_id_2, 2); dev->info.u.h2.subsystem_vendor_id = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_sub_vendor_id_2, 2); dev->info.u.h2.primary_bus = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_primary_bus_2, 1); dev->info.u.h2.secondary_bus = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_bus_2, 1); dev->info.u.h2.subordinate_bus = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_subordinate_bus_2, 1); dev->info.u.h2.secondary_latency = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_latency_2, 1); dev->info.u.h2.reserved = 0; dev->info.u.h2.memory_base = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_memory_base0_2, 4); dev->info.u.h2.memory_limit = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_memory_limit0_2, 4); dev->info.u.h2.memory_base_upper32 = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_memory_base1_2, 4); dev->info.u.h2.memory_limit_upper32 = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_memory_limit1_2, 4); dev->info.u.h2.io_base = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_base0_2, 4); dev->info.u.h2.io_limit = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_limit0_2, 4); dev->info.u.h2.io_base_upper32 = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_base1_2, 4); dev->info.u.h2.io_limit_upper32 = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_io_limit1_2, 4); dev->info.u.h2.secondary_status = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_secondary_status_2, 2); dev->info.u.h2.bridge_control = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_bridge_control_2, 2); break; } default: TRACE(("PCI: Header type unknown (0x%02x)\n", dev->info.header_type)); break; } } void PCI::RefreshDeviceInfo() { for (uint32 domain = 0; domain < fDomainCount; domain++) _RefreshDeviceInfo(fDomainData[domain].bus); } void PCI::_RefreshDeviceInfo(PCIBus *bus) { for (PCIDev *dev = bus->child; dev; dev = dev->next) { _ReadBasicInfo(dev); _ReadHeaderInfo(dev); _ReadMSIInfo(dev); _ReadMSIXInfo(dev); _ReadHtMappingInfo(dev); if (dev->child) _RefreshDeviceInfo(dev->child); } } status_t PCI::ReadConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, uint16 offset, uint8 size, uint32 *value) { domain_data *info = _GetDomainData(domain); if (!info) return B_ERROR; if (device > (info->max_bus_devices - 1) || function > 7 || (size != 1 && size != 2 && size != 4) || (size == 2 && (offset & 3) == 3) || (size == 4 && (offset & 3) != 0)) { dprintf("PCI: can't read config for domain %d, bus %u, device %u, function %u, offset %u, size %u\n", domain, bus, device, function, offset, size); return B_ERROR; } return (*info->controller->read_pci_config)(info->controller_cookie, bus, device, function, offset, size, value); } uint32 PCI::ReadConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, uint16 offset, uint8 size) { uint32 value; if (ReadConfig(domain, bus, device, function, offset, size, &value) != B_OK) return 0xffffffff; return value; } uint32 PCI::ReadConfig(PCIDev *device, uint16 offset, uint8 size) { uint32 value; if (ReadConfig(device->domain, device->bus, device->device, device->function, offset, size, &value) != B_OK) return 0xffffffff; return value; } status_t PCI::WriteConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, uint16 offset, uint8 size, uint32 value) { domain_data *info = _GetDomainData(domain); if (!info) return B_ERROR; if (device > (info->max_bus_devices - 1) || function > 7 || (size != 1 && size != 2 && size != 4) || (size == 2 && (offset & 3) == 3) || (size == 4 && (offset & 3) != 0)) { dprintf("PCI: can't write config for domain %d, bus %u, device %u, function %u, offset %u, size %u\n", domain, bus, device, function, offset, size); return B_ERROR; } return (*info->controller->write_pci_config)(info->controller_cookie, bus, device, function, offset, size, value); } status_t PCI::WriteConfig(PCIDev *device, uint16 offset, uint8 size, uint32 value) { return WriteConfig(device->domain, device->bus, device->device, device->function, offset, size, value); } status_t PCI::FindCapability(uint8 domain, uint8 bus, uint8 device, uint8 function, uint8 capID, uint8 *offset) { uint16 status = ReadConfig(domain, bus, device, function, PCI_status, 2); if (!(status & PCI_status_capabilities)) { FLOW("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x " "not supported\n", bus, device, function, capID); return B_ERROR; } uint8 headerType = ReadConfig(domain, bus, device, function, PCI_header_type, 1); uint8 capPointer; switch (headerType & PCI_header_type_mask) { case PCI_header_type_generic: case PCI_header_type_PCI_to_PCI_bridge: capPointer = PCI_capabilities_ptr; break; case PCI_header_type_cardbus: capPointer = PCI_capabilities_ptr_2; break; default: TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability " "%#02x unknown header type\n", bus, device, function, capID); return B_ERROR; } capPointer = ReadConfig(domain, bus, device, function, capPointer, 1); capPointer &= ~3; if (capPointer == 0) { TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x " "empty list\n", bus, device, function, capID); return B_NAME_NOT_FOUND; } for (int i = 0; i < 48; i++) { if (ReadConfig(domain, bus, device, function, capPointer, 1) == capID) { if (offset != NULL) *offset = capPointer; return B_OK; } capPointer = ReadConfig(domain, bus, device, function, capPointer + 1, 1); capPointer &= ~3; if (capPointer == 0) return B_NAME_NOT_FOUND; } TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x circular list\n", bus, device, function, capID); return B_ERROR; } status_t PCI::FindCapability(PCIDev *device, uint8 capID, uint8 *offset) { return FindCapability(device->domain, device->bus, device->device, device->function, capID, offset); } status_t PCI::FindExtendedCapability(uint8 domain, uint8 bus, uint8 device, uint8 function, uint16 capID, uint16 *offset) { if (FindCapability(domain, bus, device, function, PCI_cap_id_pcie) != B_OK) { FLOW("PCI:FindExtendedCapability ERROR %u:%u:%u capability %#02x " "not supported\n", bus, device, function, capID); return B_ERROR; } uint16 capPointer = PCI_extended_capability; uint32 capability = ReadConfig(domain, bus, device, function, capPointer, 4); if (capability == 0 || capability == 0xffffffff) return B_NAME_NOT_FOUND; for (int i = 0; i < 48; i++) { if (PCI_extcap_id(capability) == capID) { if (offset != NULL) *offset = capPointer; return B_OK; } capPointer = PCI_extcap_next_ptr(capability) & ~3; if (capPointer < PCI_extended_capability) return B_NAME_NOT_FOUND; capability = ReadConfig(domain, bus, device, function, capPointer, 4); } TRACE_CAP("PCI:FindExtendedCapability ERROR %u:%u:%u capability %#04x " "circular list\n", bus, device, function, capID); return B_ERROR; } status_t PCI::FindExtendedCapability(PCIDev *device, uint16 capID, uint16 *offset) { return FindExtendedCapability(device->domain, device->bus, device->device, device->function, capID, offset); } status_t PCI::FindHTCapability(uint8 domain, uint8 bus, uint8 device, uint8 function, uint16 capID, uint8 *offset) { uint8 capPointer; // consider the passed offset as the current ht capability block pointer // when it's non zero if (offset != NULL && *offset != 0) { capPointer = ReadConfig(domain, bus, device, function, *offset + 1, 1); } else if (FindCapability(domain, bus, device, function, PCI_cap_id_ht, &capPointer) != B_OK) { FLOW("PCI:FindHTCapability ERROR %u:%u:%u capability %#02x " "not supported\n", bus, device, function, capID); return B_NAME_NOT_FOUND; } uint16 mask = PCI_ht_command_cap_mask_5_bits; if (capID == PCI_ht_command_cap_slave || capID == PCI_ht_command_cap_host) mask = PCI_ht_command_cap_mask_3_bits; for (int i = 0; i < 48; i++) { capPointer &= ~3; if (capPointer == 0) return B_NAME_NOT_FOUND; uint8 capability = ReadConfig(domain, bus, device, function, capPointer, 1); if (capability == PCI_cap_id_ht) { if ((ReadConfig(domain, bus, device, function, capPointer + PCI_ht_command, 2) & mask) == capID) { if (offset != NULL) *offset = capPointer; return B_OK; } } capPointer = ReadConfig(domain, bus, device, function, capPointer + 1, 1); } TRACE_CAP("PCI:FindHTCapability ERROR %u:%u:%u capability %#04x " "circular list\n", bus, device, function, capID); return B_ERROR; } status_t PCI::FindHTCapability(PCIDev *device, uint16 capID, uint8 *offset) { return FindHTCapability(device->domain, device->bus, device->device, device->function, capID, offset); } PCIDev * PCI::FindDevice(uint8 domain, uint8 bus, uint8 device, uint8 function) { if (domain >= fDomainCount) return NULL; return _FindDevice(fDomainData[domain].bus, domain, bus, device, function); } PCIDev * PCI::_FindDevice(PCIBus *current, uint8 domain, uint8 bus, uint8 device, uint8 function) { if (current->domain == domain) { // search device on this bus for (PCIDev *child = current->child; child != NULL; child = child->next) { if (child->bus == bus && child->device == device && child->function == function) return child; if (child->child != NULL) { // search child busses PCIDev *found = _FindDevice(child->child, domain, bus, device, function); if (found != NULL) return found; } } } return NULL; } status_t PCI::UpdateInterruptLine(uint8 domain, uint8 bus, uint8 _device, uint8 function, uint8 newInterruptLineValue) { PCIDev *device = FindDevice(domain, bus, _device, function); if (device == NULL) return B_ERROR; pci_info &info = device->info; switch (info.header_type & PCI_header_type_mask) { case PCI_header_type_generic: info.u.h0.interrupt_line = newInterruptLineValue; break; case PCI_header_type_PCI_to_PCI_bridge: info.u.h1.interrupt_line = newInterruptLineValue; break; default: return B_ERROR; } return WriteConfig(device, PCI_interrupt_line, 1, newInterruptLineValue); } uint8 PCI::GetPowerstate(PCIDev *device) { uint8 capabilityOffset; status_t res = FindCapability(device, PCI_cap_id_pm, &capabilityOffset); if (res == B_OK) { uint32 state = ReadConfig(device, capabilityOffset + PCI_pm_status, 2); return (state & PCI_pm_mask); } return PCI_pm_state_d0; } void PCI::SetPowerstate(PCIDev *device, uint8 newState) { uint8 capabilityOffset; status_t res = FindCapability(device, PCI_cap_id_pm, &capabilityOffset); if (res == B_OK) { uint32 state = ReadConfig(device, capabilityOffset + PCI_pm_status, 2); if ((state & PCI_pm_mask) != newState) { WriteConfig(device, capabilityOffset + PCI_pm_status, 2, (state & ~PCI_pm_mask) | newState); if ((state & PCI_pm_mask) == PCI_pm_state_d3) snooze(10); } } } status_t PCI::GetPowerstate(uint8 domain, uint8 bus, uint8 _device, uint8 function, uint8* state) { PCIDev *device = FindDevice(domain, bus, _device, function); if (device == NULL) return B_ERROR; *state = GetPowerstate(device); return B_OK; } status_t PCI::SetPowerstate(uint8 domain, uint8 bus, uint8 _device, uint8 function, uint8 newState) { PCIDev *device = FindDevice(domain, bus, _device, function); if (device == NULL) return B_ERROR; SetPowerstate(device, newState); return B_OK; } //#pragma mark - MSI uint32 PCI::GetMSICount(PCIDev *device) { if (!msi_supported()) return 0; msi_info *info = &device->msi; if (!info->msi_capable) return 0; return info->message_count; } status_t PCI::ConfigureMSI(PCIDev *device, uint32 count, uint32 *startVector) { if (!msi_supported()) return B_UNSUPPORTED; if (count == 0 || startVector == NULL) return B_BAD_VALUE; msi_info *info = &device->msi; if (!info->msi_capable) return B_UNSUPPORTED; if (count > 32 || count > info->message_count || ((count - 1) & count) != 0 /* needs to be a power of 2 */) { return B_BAD_VALUE; } if (info->configured_count != 0) return B_BUSY; status_t result = msi_allocate_vectors(count, &info->start_vector, &info->address_value, &info->data_value); if (result != B_OK) return result; uint8 offset = info->capability_offset; WriteConfig(device, offset + PCI_msi_address, 4, info->address_value & 0xffffffff); if (info->control_value & PCI_msi_control_64bit) { WriteConfig(device, offset + PCI_msi_address_high, 4, info->address_value >> 32); WriteConfig(device, offset + PCI_msi_data_64bit, 2, info->data_value); } else WriteConfig(device, offset + PCI_msi_data, 2, info->data_value); info->control_value &= ~PCI_msi_control_mme_mask; info->control_value |= (ffs(count) - 1) << 4; WriteConfig(device, offset + PCI_msi_control, 2, info->control_value); info->configured_count = count; *startVector = info->start_vector; return B_OK; } status_t PCI::UnconfigureMSI(PCIDev *device) { if (!msi_supported()) return B_UNSUPPORTED; // try MSI-X status_t result = _UnconfigureMSIX(device); if (result != B_UNSUPPORTED && result != B_NO_INIT) return result; msi_info *info = &device->msi; if (!info->msi_capable) return B_UNSUPPORTED; if (info->configured_count == 0) return B_NO_INIT; msi_free_vectors(info->configured_count, info->start_vector); info->control_value &= ~PCI_msi_control_mme_mask; WriteConfig(device, info->capability_offset + PCI_msi_control, 2, info->control_value); info->configured_count = 0; info->address_value = 0; info->data_value = 0; return B_OK; } status_t PCI::EnableMSI(PCIDev *device) { if (!msi_supported()) return B_UNSUPPORTED; msi_info *info = &device->msi; if (!info->msi_capable) return B_UNSUPPORTED; if (info->configured_count == 0) return B_NO_INIT; // ensure the pinned interrupt is disabled WriteConfig(device, PCI_command, 2, ReadConfig(device, PCI_command, 2) | PCI_command_int_disable); // enable msi generation info->control_value |= PCI_msi_control_enable; WriteConfig(device, info->capability_offset + PCI_msi_control, 2, info->control_value); // enable HT msi mapping (if applicable) _HtMSIMap(device, info->address_value); dprintf("msi enabled: 0x%04" B_PRIx32 "\n", ReadConfig(device, info->capability_offset + PCI_msi_control, 2)); return B_OK; } status_t PCI::DisableMSI(PCIDev *device) { if (!msi_supported()) return B_UNSUPPORTED; // try MSI-X status_t result = _DisableMSIX(device); if (result != B_UNSUPPORTED && result != B_NO_INIT) return result; msi_info *info = &device->msi; if (!info->msi_capable) return B_UNSUPPORTED; if (info->configured_count == 0) return B_NO_INIT; // disable HT msi mapping (if applicable) _HtMSIMap(device, 0); // disable msi generation info->control_value &= ~PCI_msi_control_enable; WriteConfig(device, info->capability_offset + PCI_msi_control, 2, info->control_value); return B_OK; } uint32 PCI::GetMSIXCount(PCIDev *device) { if (!msi_supported()) return 0; msix_info *info = &device->msix; if (!info->msix_capable) return 0; return info->message_count; } status_t PCI::ConfigureMSIX(PCIDev *device, uint32 count, uint32 *startVector) { if (!msi_supported()) return B_UNSUPPORTED; if (count == 0 || startVector == NULL) return B_BAD_VALUE; msix_info *info = &device->msix; if (!info->msix_capable) return B_UNSUPPORTED; if (count > 32 || count > info->message_count) { return B_BAD_VALUE; } if (info->configured_count != 0) return B_BUSY; // map the table bar size_t tableSize = info->message_count * 16; addr_t address; phys_addr_t barAddr = device->info.u.h0.base_registers[info->table_bar]; uchar flags = device->info.u.h0.base_register_flags[info->table_bar]; if ((flags & PCI_address_type) == PCI_address_type_64) { barAddr |= (uint64)device->info.u.h0.base_registers[ info->table_bar + 1] << 32; } area_id area = map_physical_memory("msi table map", barAddr, tableSize + info->table_offset, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&address); if (area < 0) return area; info->table_area_id = area; info->table_address = address + info->table_offset; // and the pba bar if necessary if (info->table_bar != info->pba_bar) { barAddr = device->info.u.h0.base_registers[info->pba_bar]; flags = device->info.u.h0.base_register_flags[info->pba_bar]; if ((flags & PCI_address_type) == PCI_address_type_64) { barAddr |= (uint64)device->info.u.h0.base_registers[ info->pba_bar + 1] << 32; } area = map_physical_memory("msi pba map", barAddr, tableSize + info->pba_offset, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&address); if (area < 0) { delete_area(info->table_area_id); info->table_area_id = -1; return area; } info->pba_area_id = area; } else info->pba_area_id = -1; info->pba_address = address + info->pba_offset; status_t result = msi_allocate_vectors(count, &info->start_vector, &info->address_value, &info->data_value); if (result != B_OK) { delete_area(info->pba_area_id); delete_area(info->table_area_id); info->pba_area_id = -1; info->table_area_id = -1; return result; } // ensure the memory i/o is enabled WriteConfig(device, PCI_command, 2, ReadConfig(device, PCI_command, 2) | PCI_command_memory); uint32 data_value = info->data_value; for (uint32 index = 0; index < count; index++) { volatile uint32 *entry = (uint32*)(info->table_address + 16 * index); *(entry + 3) |= PCI_msix_vctrl_mask; *entry++ = info->address_value & 0xffffffff; *entry++ = info->address_value >> 32; *entry++ = data_value++; *entry &= ~PCI_msix_vctrl_mask; } info->configured_count = count; *startVector = info->start_vector; dprintf("msix configured for %" B_PRIu32 " vectors\n", count); return B_OK; } status_t PCI::EnableMSIX(PCIDev *device) { if (!msi_supported()) return B_UNSUPPORTED; msix_info *info = &device->msix; if (!info->msix_capable) return B_UNSUPPORTED; if (info->configured_count == 0) return B_NO_INIT; // ensure the pinned interrupt is disabled WriteConfig(device, PCI_command, 2, ReadConfig(device, PCI_command, 2) | PCI_command_int_disable); // enable msi-x generation info->control_value |= PCI_msix_control_enable; WriteConfig(device, info->capability_offset + PCI_msix_control, 2, info->control_value); // enable HT msi mapping (if applicable) _HtMSIMap(device, info->address_value); dprintf("msi-x enabled: 0x%04" B_PRIx32 "\n", ReadConfig(device, info->capability_offset + PCI_msix_control, 2)); return B_OK; } void PCI::_HtMSIMap(PCIDev *device, uint64 address) { ht_mapping_info *info = &device->ht_mapping; if (!info->ht_mapping_capable) return; bool enabled = (info->control_value & PCI_ht_command_msi_enable) != 0; if ((address != 0) != enabled) { if (enabled) { info->control_value &= ~PCI_ht_command_msi_enable; } else { if ((address >> 20) != (info->address_value >> 20)) return; dprintf("ht msi mapping enabled\n"); info->control_value |= PCI_ht_command_msi_enable; } WriteConfig(device, info->capability_offset + PCI_ht_command, 2, info->control_value); } } void PCI::_ReadMSIInfo(PCIDev *device) { if (!msi_supported()) return; msi_info *info = &device->msi; info->msi_capable = false; status_t result = FindCapability(device->domain, device->bus, device->device, device->function, PCI_cap_id_msi, &info->capability_offset); if (result != B_OK) return; info->msi_capable = true; info->control_value = ReadConfig(device->domain, device->bus, device->device, device->function, info->capability_offset + PCI_msi_control, 2); info->message_count = 1 << ((info->control_value & PCI_msi_control_mmc_mask) >> 1); info->configured_count = 0; info->data_value = 0; info->address_value = 0; } void PCI::_ReadMSIXInfo(PCIDev *device) { if (!msi_supported()) return; msix_info *info = &device->msix; info->msix_capable = false; status_t result = FindCapability(device->domain, device->bus, device->device, device->function, PCI_cap_id_msix, &info->capability_offset); if (result != B_OK) return; info->msix_capable = true; info->control_value = ReadConfig(device->domain, device->bus, device->device, device->function, info->capability_offset + PCI_msix_control, 2); info->message_count = (info->control_value & PCI_msix_control_table_size) + 1; info->configured_count = 0; info->data_value = 0; info->address_value = 0; info->table_area_id = -1; info->pba_area_id = -1; uint32 table_value = ReadConfig(device->domain, device->bus, device->device, device->function, info->capability_offset + PCI_msix_table, 4); uint32 pba_value = ReadConfig(device->domain, device->bus, device->device, device->function, info->capability_offset + PCI_msix_pba, 4); info->table_bar = table_value & PCI_msix_bir_mask; info->table_offset = table_value & PCI_msix_offset_mask; info->pba_bar = pba_value & PCI_msix_bir_mask; info->pba_offset = pba_value & PCI_msix_offset_mask; } void PCI::_ReadHtMappingInfo(PCIDev *device) { if (!msi_supported()) return; ht_mapping_info *info = &device->ht_mapping; info->ht_mapping_capable = false; uint8 offset = 0; if (FindHTCapability(device, PCI_ht_command_cap_msi_mapping, &offset) == B_OK) { info->control_value = ReadConfig(device, offset + PCI_ht_command, 2); info->capability_offset = offset; info->ht_mapping_capable = true; if ((info->control_value & PCI_ht_command_msi_fixed) != 0) { #if defined(__i386__) || defined(__x86_64__) info->address_value = MSI_ADDRESS_BASE; #else // TODO: investigate what should be set here for non-x86 dprintf("PCI_ht_command_msi_fixed flag unimplemented\n"); info->address_value = 0; #endif } else { info->address_value = ReadConfig(device, offset + PCI_ht_msi_address_high, 4); info->address_value <<= 32; info->address_value |= ReadConfig(device, offset + PCI_ht_msi_address_low, 4); } dprintf("found an ht msi mapping at %#" B_PRIx64 "\n", info->address_value); } } status_t PCI::_UnconfigureMSIX(PCIDev *device) { msix_info *info = &device->msix; if (!info->msix_capable) return B_UNSUPPORTED; if (info->configured_count == 0) return B_NO_INIT; // disable msi-x generation info->control_value &= ~PCI_msix_control_enable; WriteConfig(device, info->capability_offset + PCI_msix_control, 2, info->control_value); msi_free_vectors(info->configured_count, info->start_vector); for (uint8 index = 0; index < info->configured_count; index++) { volatile uint32 *entry = (uint32*)(info->table_address + 16 * index); if ((*(entry + 3) & PCI_msix_vctrl_mask) == 0) *(entry + 3) |= PCI_msix_vctrl_mask; } if (info->pba_area_id != -1) delete_area(info->pba_area_id); if (info->table_area_id != -1) delete_area(info->table_area_id); info->pba_area_id= -1; info->table_area_id = -1; info->configured_count = 0; info->address_value = 0; info->data_value = 0; return B_OK; } status_t PCI::_DisableMSIX(PCIDev *device) { msix_info *info = &device->msix; if (!info->msix_capable) return B_UNSUPPORTED; if (info->configured_count == 0) return B_NO_INIT; // disable HT msi mapping (if applicable) _HtMSIMap(device, 0); // disable msi-x generation info->control_value &= ~PCI_msix_control_enable; gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2, info->control_value); return B_OK; }