From 8cd7d436705bff6300901a77a9eb1727491f2234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Tue, 9 Jul 2013 23:31:58 +0200 Subject: [PATCH] PCI: implemented reading, configuring and enabling MSI-X. --- .../pci/arch/x86/pci_arch_info.cpp | 1 + .../bus_managers/pci/arch/x86/pci_arch_info.h | 1 + .../bus_managers/pci/arch/x86/pci_msi.cpp | 274 ++++++++++++++++++ .../bus_managers/pci/arch/x86/pci_msi.h | 33 +++ 4 files changed, 309 insertions(+) diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.cpp index f27393a1d0..3071c12245 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.cpp @@ -9,4 +9,5 @@ void pci_read_arch_info(PCIDev *dev) { pci_read_msi_info(dev); + pci_read_msix_info(dev); } diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.h b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.h index 544eec9a65..4822dbe007 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.h +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.h @@ -9,6 +9,7 @@ typedef struct pci_arch_info { msi_info msi; + msix_info msix; } pci_arch_info; diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.cpp index cbfe5efb04..590551bff3 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2013, Jérôme Duval, korli@users.berlios.de. * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved. * Distributed under the terms of the MIT License. */ @@ -225,3 +226,276 @@ pci_read_msi_info(PCIDev *device) info->data_value = 0; info->address_value = 0; } + + +uint8 +pci_get_msix_count(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return 0; + + uint8 bus; + uint8 domain; + if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) + return 0; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return 0; + + msix_info *info = &device->arch_info.msix; + if (!info->msix_capable) + return 0; + + return info->message_count; +} + + +status_t +pci_configure_msix(uint8 virtualBus, uint8 _device, uint8 function, + uint8 count, uint8 *startVector) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + if (count == 0 || startVector == NULL) + return B_BAD_VALUE; + + uint8 bus; + uint8 domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msix_info *info = &device->arch_info.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; + area_id area = map_physical_memory("msi table map", + device->info.u.h0.base_registers[info->table_bar], + tableSize + info->table_offset, + B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_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) { + area = map_physical_memory("msi pba map", + device->info.u.h0.base_registers[info->pba_bar], + tableSize + info->pba_offset, + B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_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; + + 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 + gPCI->WriteConfig(device, PCI_command, 2, + gPCI->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 %d vectors\n", count); + return B_OK; +} + + +status_t +pci_unconfigure_msix(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + uint8 bus; + uint8 domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msix_info *info = &device->arch_info.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; + gPCI->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_enable_msix(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + uint8 bus; + uint8 domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msix_info *info = &device->arch_info.msix; + if (!info->msix_capable) + return B_UNSUPPORTED; + + if (info->configured_count == 0) + return B_NO_INIT; + + // ensure the pinned interrupt is disabled + gPCI->WriteConfig(device, PCI_command, 2, + gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_int_disable); + + // enable msi-x generation + info->control_value |= PCI_msix_control_enable; + gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2, + info->control_value); + + dprintf("msi-x enabled: 0x%04" B_PRIx32 "\n", + gPCI->ReadConfig(device, info->capability_offset + PCI_msix_control, 2)); + return B_OK; +} + + +status_t +pci_disable_msix(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + uint8 bus; + uint8 domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msix_info *info = &device->arch_info.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; + gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2, + info->control_value); + + return B_OK; +} + + +void +pci_read_msix_info(PCIDev *device) +{ + if (!msi_supported()) + return; + + msix_info *info = &device->arch_info.msix; + info->msix_capable = false; + status_t result = gPCI->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 = gPCI->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 = gPCI->ReadConfig(device->domain, device->bus, + device->device, device->function, + info->capability_offset + PCI_msix_table, 4); + uint32 pba_value = gPCI->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; +} + diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.h b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.h index 849e3b25df..286d030f1a 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.h +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.h @@ -1,12 +1,15 @@ /* + * Copyright 2013, Jérôme Duval, korli@users.berlios.de. * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _PCI_x86_MSI_H #define _PCI_x86_MSI_H +#include #include + class PCIDev; // Message Signaled Interrupts @@ -30,4 +33,34 @@ status_t pci_enable_msi(uint8 virtualBus, uint8 device, uint8 function); status_t pci_disable_msi(uint8 virtualBus, uint8 device, uint8 function); void pci_read_msi_info(PCIDev *device); + +// Message Signaled Interrupts +typedef struct msix_info { + bool msix_capable; + uint8 capability_offset; + uint8 message_count; + uint8 table_bar; + uint32 table_offset; + area_id table_area_id; + addr_t table_address; + uint8 pba_bar; + uint32 pba_offset; + area_id pba_area_id; + addr_t pba_address; + uint8 configured_count; + uint8 start_vector; + uint16 control_value; + uint16 data_value; + uint64 address_value; +} msix_info; + + +uint8 pci_get_msix_count(uint8 virtualBus, uint8 _device, uint8 function); +status_t pci_configure_msix(uint8 virtualBus, uint8 _device, uint8 function, + uint8 count, uint8 *startVector); +status_t pci_unconfigure_msix(uint8 virtualBus, uint8 device, uint8 function); +status_t pci_enable_msix(uint8 virtualBus, uint8 _device, uint8 function); +status_t pci_disable_msix(uint8 virtualBus, uint8 _device, uint8 function); +void pci_read_msix_info(PCIDev *device); + #endif // _PCI_x86_MSI_H