Skip to content

Commit

Permalink
WIP virtio working on getting MSI-x working
Browse files Browse the repository at this point in the history
  • Loading branch information
travisg committed Jun 3, 2024
1 parent 1f6119a commit 07ab278
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 53 deletions.
18 changes: 18 additions & 0 deletions dev/bus/pci/bus_mgr/bus_mgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,24 @@ status_t pci_bus_mgr_allocate_msi(const pci_location_t loc, size_t num_requested
return d->allocate_msi(num_requested, irqbase);
}

status_t pci_bus_mgr_allocate_msix(const pci_location_t loc, size_t num_requested, uint *irqbase) {
char str[14];
LTRACEF("%s num_request %zu\n", pci_loc_string(loc, str), num_requested);

*irqbase = 0;

device *d = lookup_device_by_loc(loc);
if (!d) {
return ERR_NOT_FOUND;
}

if (!d->has_msix()) {
return ERR_NO_RESOURCES;
}

return d->allocate_msix(num_requested, irqbase);
}

status_t pci_bus_mgr_allocate_irq(const pci_location_t loc, uint *irqbase) {
char str[14];
LTRACEF("%s\n", pci_loc_string(loc, str));
Expand Down
152 changes: 152 additions & 0 deletions dev/bus/pci/bus_mgr/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include "device.h"
#include "arch/mmu.h"

#include <sys/types.h>
#include <lk/cpp.h>
Expand All @@ -22,6 +23,10 @@
#include <assert.h>
#include <platform/interrupts.h>

#if WITH_KERNEL_VM
#include <kernel/vm.h>
#endif

#define LOCAL_TRACE 0

#include "bus_mgr.h"
Expand Down Expand Up @@ -358,6 +363,153 @@ status_t device::allocate_msi(size_t num_requested, uint *msi_base) {
return NO_ERROR;
}

status_t device::allocate_msix(size_t num_requested, uint *msi_base) {
LTRACE_ENTRY;

// for the moment, only deal with 1
DEBUG_ASSERT(num_requested == 1);

if (!has_msix()) {
return ERR_NOT_SUPPORTED;
}

DEBUG_ASSERT(msix_cap_ && msix_cap_->is_msix());

// program it into the capability
const uint16_t cap_offset = msix_cap_->config_offset;

// read the table size and address out of the capability
uint16_t control;
status_t err = pci_read_config_half(loc(), cap_offset + 2, &control);
if (err != NO_ERROR) {
return err;
}
const uint32_t table_count = (control & 0x3f) + 1;
TRACEF("control word %#x table count %u\n", control, table_count);
uint32_t table_offset, pba_offset;
err = pci_read_config_word(loc(), cap_offset + 4, &table_offset);
if (err != NO_ERROR) {
return err;
}
err = pci_read_config_word(loc(), cap_offset + 8, &pba_offset);
if (err != NO_ERROR) {
return err;
}

// does the device support enough vectors?
if (num_requested > table_count) {
return ERR_NO_RESOURCES;
}

// ask the platform for interrupts
uint vector_base;
err = platform_allocate_interrupts(num_requested, 0, true, &vector_base);
if (err != NO_ERROR) {
return err;
}

// Compute what BARs we need to map and where
struct mapping {
explicit mapping(uint32_t offset_bar_word) {
bar = offset_bar_word & 0x3;
offset = offset_bar_word & ~0x3;
length = static_cast<size_t>(offset_bar_word) * 16;
}

uint8_t bar;
size_t offset;
size_t length;
};

mapping table_map(table_offset);
mapping pba_map(pba_offset);
TRACEF("table offset %#zx, bar %u\n", table_map.offset, table_map.bar);
TRACEF("pba offset %#zx, bar %u\n", pba_map.offset, pba_map.bar);

auto map_it = [this, &err](mapping &map, void **ptr, bool readonly) -> status_t {
#if WITH_KERNEL_VM
const auto &bar = bars_[map.bar];
if (!bar.valid || bar.io) {
printf("msi-x bar is not valid\n");
return ERR_INVALID_ARGS;
}

paddr_t base = ROUNDDOWN(map.offset, PAGE_SIZE);
size_t length = ROUNDUP(map.length + map.offset - base, PAGE_SIZE);
base += bar.addr;

err = vmm_alloc_physical(vmm_get_kernel_aspace(), "pci msix var", length, ptr, 0,
base, /* vmm_flags */ 0,
ARCH_MMU_FLAG_UNCACHED_DEVICE | (readonly ? ARCH_MMU_FLAG_PERM_RO : 0));
if (err != NO_ERROR) {
printf("error mapping msi-x bar\n");
return err;
}
TRACEF("msi-x bar mapped at %p\n", *ptr);
#else
// no need to map, it's already available at the physical address
if (sizeof(void *) < 8 && (bars[i].addr + bars[i].size) > UINT32_MAX) {
TRACEF("aborting due to 64bit BAR on 32bit arch\n");
return ERR_NO_MEMORY;
}
bar_map.vaddr = (uint8_t *)(uintptr_t)bars[i].addr;
#endif
return NO_ERROR;
};

err = map_it(table_map, &msix_table_map, false);
if (err != NO_ERROR) {
return err;
}
err = map_it(pba_map, &msix_pba_map, true);
if (err != NO_ERROR) {
return err;
}

// compute the table pointers
msix_table_ptr = (volatile uint32_t *)((uintptr_t)msix_table_map + (table_map.offset - ROUNDDOWN(table_map.offset, PAGE_SIZE)));
msix_pba_ptr = (volatile uint32_t *)((uintptr_t)msix_pba_map + (pba_map.offset - ROUNDDOWN(pba_map.offset, PAGE_SIZE)));

TRACEF("msix table %p, pba table %p\n", msix_table_ptr, msix_pba_ptr);

// compute the MSI message to construct
uint64_t msi_address = 0;
uint16_t msi_data = 0;
err = platform_compute_msi_values(vector_base, 0, true, &msi_address, &msi_data);
if (err != NO_ERROR) {
// TODO: return the allocated msi
return err;
}

// Mask all of the vectors
for (size_t i = 0; i < table_count; i++) {
msix_table_ptr[i * 4] = 0;
msix_table_ptr[i * 4 + 1] = 0;
msix_table_ptr[i * 4 + 2] = 0;
msix_table_ptr[i * 4 + 3] = 1; // masked
}

// write the requested vectors
for (size_t i = 0; i < num_requested; i++) {
msix_table_ptr[i * 4] = msi_address;
msix_table_ptr[i * 4 + 1] = msi_address >> 32;
msix_table_ptr[i * 4 + 2] = msi_data;
msix_table_ptr[i * 4 + 3] = 0; // not masked
}

// set up the control register and enable it
control |= (1<<15); // MSI-X enable, no functions masked
pci_write_config_half(loc(), cap_offset + 2, control);

// write it back to the pci config in the interrupt line offset
pci_write_config_byte(loc(), PCI_CONFIG_INTERRUPT_LINE, vector_base);

// pass back the allocated irq to the caller
*msi_base = vector_base;

return NO_ERROR;
}

status_t device::load_bars() {
size_t num_bars;

Expand Down
8 changes: 8 additions & 0 deletions dev/bus/pci/bus_mgr/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class device {

status_t allocate_irq(uint *irq);
status_t allocate_msi(size_t num_requested, uint *msi_base);
status_t allocate_msix(size_t num_requested, uint *msi_base);
status_t load_config();
status_t load_bars();

Expand Down Expand Up @@ -112,6 +113,13 @@ class device {
list_node capability_list_ = LIST_INITIAL_VALUE(capability_list_);
capability *msi_cap_ = nullptr;
capability *msix_cap_ = nullptr;

// MSI-X saved details
uint32_t msix_table_size = {};
void *msix_table_map = nullptr;
void *msix_pba_map = nullptr;
volatile uint32_t *msix_table_ptr = nullptr;
volatile uint32_t *msix_pba_ptr = nullptr;
};

struct capability {
Expand Down
3 changes: 3 additions & 0 deletions dev/bus/pci/include/dev/bus/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ status_t pci_bus_mgr_read_bars(const pci_location_t loc, pci_bar_t bar[6]);
// try to allocate one or more msi vectors for this device
status_t pci_bus_mgr_allocate_msi(const pci_location_t loc, size_t num_requested, uint *irqbase);

// try to allocate one or more msi-x vectors for this device
status_t pci_bus_mgr_allocate_msix(const pci_location_t loc, size_t num_requested, uint *irqbase);

// allocate a regular irq for this device and return it in irqbase
status_t pci_bus_mgr_allocate_irq(const pci_location_t loc, uint *irqbase);

Expand Down
8 changes: 6 additions & 2 deletions dev/virtio/include/dev/virtio.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@

__BEGIN_CDECLS

/* detect a virtio mmio hardware block
* returns number of devices found */
// Detect a virtio mmio hardware block.
// Returns number of devices found.
int virtio_mmio_detect(void *ptr, uint count, const uint irqs[], size_t stride);

// Scan pci bus for virtio devices. Only implemented if PCI is present.
// Returns number of devices found.
int virtio_pci_init(void);

__END_CDECLS

Loading

0 comments on commit 07ab278

Please sign in to comment.