msi-x for virtio block device

This commit is contained in:
uosfz 2026-06-24 22:14:19 +02:00
parent 255e24fa6b
commit 513fb8a813
Signed by: uosfz
SSH key fingerprint: SHA256:FlktuluyhTQg3jHZNLKwxOOC5hbfrUXM0tz3IA3lGJo
5 changed files with 20 additions and 31 deletions

View file

@ -150,7 +150,6 @@ struct msix_table_entry {
uint32_t control;
};
// only uses first table entry
bool msix_cap_configure(struct bdf bdf, uint8_t cap_offset, uint64_t addr, uint32_t msg,
struct msix_table_entry **table_out, uint64_t **pba_out);
bool msix_cap_configure(struct bdf bdf, uint8_t cap_offset, uint64_t addr, uint32_t msg);
#endif

View file

@ -91,7 +91,6 @@ struct virtio_blk_context {
struct virtq_avail *avail;
struct virtq_used *used;
volatile le16 *queue0_notify_location;
volatile uint8_t *interrupt_byte;
struct queued_op *request_head;
};
#define VIRTIO_BLK_NOTIFY(ctx) do { (ctx).queue0_notify_location->value = 0; } while (0)

View file

@ -178,4 +178,6 @@ void virtio_descriptor_fill(struct virtq_desc *desc, uint64_t addr, uint32_t len
// TODO interface without making it device visible, for bulk submission
void virtio_avail_queue_add(struct virtq_avail *avail, uint16_t desc_idx);
#define VIRTIO_MSI_NO_VECTOR 0xffff
#endif

View file

@ -262,9 +262,9 @@ msi_cap_configure(struct bdf bdf, uint8_t cap_offset, unsigned icount, uint64_t
#define MSIX_VECTOR_ENABLE(entry) do { entry.control &= ~1u; } while (0)
bool msix_cap_configure(struct bdf bdf, uint8_t cap_offset, uint64_t addr, uint32_t msg,
struct msix_table_entry **table_out, uint64_t **pba_out)
{
// TODO do we want more than 1 vector? 1 for queues and 1 for config?
// TODO do we want to read the pending bits later?
bool msix_cap_configure(struct bdf bdf, uint8_t cap_offset, uint64_t addr, uint32_t msg) {
if (cap_offset == 0) return false;
uint32_t dword0 = pci_config_read_u32(bdf, cap_offset);
uint32_t dword1 = pci_config_read_u32(bdf, cap_offset + 4);
@ -292,8 +292,5 @@ bool msix_cap_configure(struct bdf bdf, uint8_t cap_offset, uint64_t addr, uint3
dword0 = (dword0 & 0xFFFFu) | ((uint32_t)control << 16);
pci_config_write_u32(bdf, cap_offset, dword0);
*table_out = table;
*pba_out = pba;
return true;
}

View file

@ -15,20 +15,16 @@ void virtio_blk_device_init(struct bdf bdf, struct virtio_blk_context *ctx) {
ASSERT(virtio_dev_is_of_kind(bdf, VIRTIO_DEV_KIND_BLOCK));
ASSERT(pci_config_read_u8(bdf, PCI_HEADER_HEADER_TYPE) == 0); // expect endpoint
struct virtio_cap cap_common, cap_notify, cap_device, cap_isr;
struct virtio_cap cap_common, cap_notify, cap_device;
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_COMMON_CFG, &cap_common));
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_NOTIFY_CFG, &cap_notify));
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_DEVICE_CFG, &cap_device));
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_ISR_CFG, &cap_isr));
struct pci_bar bar = pci_config_get_bar(bdf, cap_common.barno);
struct pa pa = pa_from_value(bar.base_address + cap_common.offset);
volatile struct virtio_pci_common_cfg *cfg = pa_to_pointer(pa);
bar = pci_config_get_bar(bdf, cap_device.barno);
pa = pa_from_value(bar.base_address + cap_device.offset);
volatile struct virtio_blk_config *dev_cfg = pa_to_pointer(pa);
bar = pci_config_get_bar(bdf, cap_isr.barno);
pa = pa_from_value(bar.base_address + cap_isr.offset);
volatile uint8_t *interrupt_byte = pa_to_pointer(pa);
// TODO read configuration correctly (make sure `generation` doesn't change; p.24)
uint8_t wanted_status = 0;
@ -115,6 +111,13 @@ void virtio_blk_device_init(struct bdf bdf, struct virtio_blk_context *ctx) {
cfg->queue_enable = CPU_TO_LE16(1);
ASSERT(LE16_TO_CPU(cfg->queue_enable));
// use MSIX vector 0
cfg->config_msix_vector = CPU_TO_LE16(0);
ASSERT(LE16_TO_CPU(cfg->config_msix_vector) == 0);
// TODO if we have more than 1 queue we need to set this for every queue
cfg->queue_msix_vector = CPU_TO_LE16(0);
ASSERT(LE16_TO_CPU(cfg->queue_msix_vector) == 0);
// finish device initialization
wanted_status |= VIRTIO_DEV_STATUS_DRIVER_OK;
virtio_cfg_set_status(cfg, wanted_status);
@ -128,7 +131,6 @@ void virtio_blk_device_init(struct bdf bdf, struct virtio_blk_context *ctx) {
ctx->avail = ppn_to_pointer(queue_driver_ppn);
ctx->used = ppn_to_pointer(queue_device_ppn);
ctx->queue0_notify_location = queue0_notify_location;
ctx->interrupt_byte = interrupt_byte;
ctx->request_head = NULL;
}
@ -173,23 +175,17 @@ unsigned virtio_blk_init(void) {
printlinef(" cap %d (0x%x)", id, id);
}
uint8_t msi_cap_offset = pci_find_cap_by_id(bdf, PCI_CAP_ID_MSI);
uint64_t msi_addr = MSI_MAKE_ADDRESS(lapic_id);
uint32_t msi_data = MSI_MAKE_MESSAGE(VIRTIO_INTERRUPT_VECTOR_BASE + num_devices);
bool msi_enabled = msi_cap_configure(bdf, msi_cap_offset, 1, msi_addr, msi_data);
if (msi_enabled) {
uint8_t msix_cap_offset = pci_find_cap_by_id(bdf, PCI_CAP_ID_MSIX);
bool msix_enabled = msix_cap_configure(bdf, msix_cap_offset, msi_addr, msi_data);
if (msix_enabled) {
virtio_blk_device_init(bdf, &virtio_blk_devices[num_devices]);
num_devices++;
printlinef("initialized virtio block device %x with msi-x", pci_bdf_pack(bdf));
} else {
uint8_t msix_cap_offset = pci_find_cap_by_id(bdf, PCI_CAP_ID_MSIX);
bool msix_enabled = msix_cap_configure(bdf, msix_cap_offset);
if (msix_enabled) {
virtio_blk_device_init(bdf, &virtio_blk_devices[num_devices]);
num_devices++;
} else {
printlinef("could not enable msi or msi-x for %x, skip", pci_bdf_pack(bdf));
}
printlinef("could not enable msi-x for %x, skip", pci_bdf_pack(bdf));
}
}
return num_devices;
@ -223,11 +219,7 @@ static struct queued_op *find_queued(struct virtio_blk_context *ctx, uint16_t de
void virtio_blk_on_used_notif(struct virtio_blk_context *ctx) {
ASSERT(slab_cache_initialized);
// read interrupt byte to clear it
uint8_t interrupt_status = *ctx->interrupt_byte;
if (interrupt_status & 2) TODO(); // configuration change
// we don't check if bit 1 (interrupt) is set; simply move through our queue.
// we will notice if there is work or not.
while (ctx->used_idx_to_handle < LE16_TO_CPU(ctx->used->idx)) {
// TODO overflow?
struct virtq_used_elem *used_elem = ctx->used->ring + ctx->used_idx_to_handle;