From f060f0407fe752f5c73d96afbb15290e718d4c31 Mon Sep 17 00:00:00 2001 From: Alexander Kudryavtsev Date: Fri, 23 Sep 2011 23:11:29 +0400 Subject: [PATCH 04/32] PCI MSI router support --- palacios/include/devices/pci.h | 5 +- palacios/include/devices/pci_msi_router.h | 59 ++++ palacios/src/devices/Kconfig | 6 + palacios/src/devices/Makefile | 2 +- palacios/src/devices/pci.c | 19 ++ palacios/src/devices/pci_msi_router.c | 499 +++++++++++++++++++++++++++++ palacios/src/devices/pci_msi_types.h | 72 ++++ palacios/src/devices/piix3.c | 26 +- 8 files changed, 676 insertions(+), 12 deletions(-) create mode 100644 palacios/include/devices/pci_msi_router.h create mode 100644 palacios/src/devices/pci_msi_router.c create mode 100644 palacios/src/devices/pci_msi_types.h diff --git a/palacios/include/devices/pci.h b/palacios/include/devices/pci.h index dc0c665..edc3e36 100644 --- a/palacios/include/devices/pci.h +++ b/palacios/include/devices/pci.h @@ -146,9 +146,10 @@ struct pci_device { int exp_rom_update_flag; int bar_update_flag; - int is_passthrough; // true if this device is real device passed through into guest + int msi_handle; // controlled by MSI router. 0 - MSI not available for device; > 0 - MSI available, index used by MSI router + void * priv_data; }; @@ -162,6 +163,8 @@ int v3_pci_set_irq_bridge(struct vm_device * pci_bus, int bus_num, int v3_pci_raise_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev); int v3_pci_lower_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev); +void *v3_pci_get_msi_router(struct vm_device *pci_bus); + struct pci_device * v3_pci_register_device(struct vm_device * pci, pci_device_type_t dev_type, diff --git a/palacios/include/devices/pci_msi_router.h b/palacios/include/devices/pci_msi_router.h new file mode 100644 index 0000000..53a2833 --- /dev/null +++ b/palacios/include/devices/pci_msi_router.h @@ -0,0 +1,59 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2009, Jack Lange + * Copyright (c) 2009, The V3VEE Project + * All rights reserved. + * + * Author: Alexander Kudryavtsev + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#ifndef __DEVICES_PCI_MSI_ROUTER_H__ +#define __DEVICES_PCI_MSI_ROUTER_H__ + +#ifdef __V3VEE__ + +#include +#include + + +enum msi_dest_mode {MODE_PHYS_DEST = 0, MODE_LOGIC_DEST = 1}; +enum msi_del_mode {DEL_MODE_FIXED = 0, DEL_MODE_LOWPRI = 1}; + +struct msi_info { + int enabled; + int vector; + int vec_count; + enum msi_dest_mode dest_mode; + int dest_cpu; + enum msi_del_mode del_mode; // 0 -fixed, 1 - lowpri +}; + +#define MSI_DEV_COUNT 256 + +int v3_msi_register_device(struct pci_device *dev, int msi_cap, void *private_data); +int v3_msi_capability_write(struct pci_device *dev, uint_t reg_num, uint_t length, void *private_data); + +// return: -1 - error, 0 - no MSI for this device, 1 - MSI is available, *vector contains vector. +int v3_msi_get_vector(struct pci_device *dev, int *vector, void *private_data); + +// when multiple vectors are assigned, this is the way for device to tell which vector to raise +int v3_msi_set_vector_hint(struct pci_device *dev, int raise_vector_hint, void *private_data); + +int v3_msi_get_info(struct pci_device *dev, struct msi_info *info, void *private_data); + + + +#endif + + +#endif diff --git a/palacios/src/devices/Kconfig b/palacios/src/devices/Kconfig index 23a88ff..39334ea 100644 --- a/palacios/src/devices/Kconfig +++ b/palacios/src/devices/Kconfig @@ -299,6 +299,12 @@ config DEBUG_PCI help Enable debugging for the PCI +config DEBUG_PCI_MSI + bool "PCI MSI debugging" + depends on PCI && DEBUG_ON + help + Enable debugging for the PCI MSI + config PCI_FRONT bool "PCI front-end device" diff --git a/palacios/src/devices/Makefile b/palacios/src/devices/Makefile index 1154059..8db8c3f 100644 --- a/palacios/src/devices/Makefile +++ b/palacios/src/devices/Makefile @@ -19,7 +19,7 @@ obj-$(V3_CONFIG_LINUX_VIRTIO_CONSOLE) += lnx_virtio_console.o obj-$(V3_CONFIG_VNET_NIC) += vnet_nic.o obj-$(V3_CONFIG_NVRAM) += nvram.o obj-$(V3_CONFIG_OS_DEBUG) += os_debug.o -obj-$(V3_CONFIG_PCI) += pci.o +obj-$(V3_CONFIG_PCI) += pci.o pci_msi_router.o obj-$(V3_CONFIG_PIIX3) += piix3.o obj-$(V3_CONFIG_SWAPBYPASS_DISK_CACHE) += swapbypass_cache.o obj-$(V3_CONFIG_SWAPBYPASS_DISK_CACHE2) += swapbypass_cache2.o diff --git a/palacios/src/devices/pci.c b/palacios/src/devices/pci.c index 8ddaba4..9ddbe5d 100644 --- a/palacios/src/devices/pci.c +++ b/palacios/src/devices/pci.c @@ -98,6 +98,8 @@ struct pci_internal { // Attached Busses struct pci_bus bus_list[PCI_BUS_COUNT]; + + void *msi_router_private; }; @@ -693,6 +695,17 @@ static int pci_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { return -1; } + pci_state->msi_router_private = NULL; + if(v3_cfg_val(cfg, "msi_router")) { + struct vm_device * msi_router = v3_find_dev(vm, v3_cfg_val(cfg, "msi_router")); + if(!msi_router) { + PrintError("Could not find MSI router device %s\n", v3_cfg_val(cfg, "msi_router")); + v3_remove_device(dev); + return -1; + } + pci_state->msi_router_private = msi_router->private_data; + } + return 0; } @@ -815,6 +828,12 @@ int v3_pci_lower_irq(struct vm_device * pci_bus, int bus_num, struct pci_device return bus->lower_pci_irq(dev, bus->irq_dev_data); } +void *v3_pci_get_msi_router(struct vm_device *pci_bus) { + struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data; + return pci_state->msi_router_private; +} + + // if dev_num == -1, auto assign struct pci_device * v3_pci_register_device(struct vm_device * pci, pci_device_type_t dev_type, diff --git a/palacios/src/devices/pci_msi_router.c b/palacios/src/devices/pci_msi_router.c new file mode 100644 index 0000000..cd049f7 --- /dev/null +++ b/palacios/src/devices/pci_msi_router.c @@ -0,0 +1,499 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2008, Jack Lange + * Copyright (c) 2008, The V3VEE Project + * All rights reserved. + * + * Author: Alexander Kudryavtsev + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + + + +#include +#include +#include +#include +#include + +#include +#include + +#include "pci_msi_types.h" + +#ifndef V3_CONFIG_DEBUG_PCI_MSI +#undef PrintDebug +#define PrintDebug(fmt, args...) +#endif + +struct msi_desc { + int cap; + int cap_len; + + enum msi_dest_mode dest_mode; + int vector; + int vec_count; + int raise_vector_hint; + + union msi_control *control; // points to guest config_space + union msi_msg_address *msg_address_lo; // points to guest config_space + union msi_msg_data *msg_data; // points to guest config_space + uint32_t *mask_bits; // points to guest config_space + uint32_t *pending_bits; // points to guest config_space + + union msi_msg_data msg_data_cached; // contains previous value of MSI data, to check for changes + + + struct pci_device *dev; +}; + +static int msi_raise_intr(struct v3_vm_info *vm, void *private_data, int irq); + +struct msi_router_state { + char *name; + + struct msi_desc msi[MSI_DEV_COUNT]; + int last_index; + + int vector_to_index[MAX_IRQ]; // maps vectors to msi array elements for fast access + + void *router_handle; + struct v3_vm_info *vm; + void *apic_dev_data; +}; + + + +int v3_msi_register_device(struct pci_device *dev, int msi_cap, void *private_data) { + V3_ASSERT(private_data); + struct msi_router_state *state = (struct msi_router_state*)private_data; + if(dev->msi_handle) { + PrintError("MSI already registered for device %s!\n", dev->name); + return -1; + } + if(state->last_index == MSI_DEV_COUNT) { + PrintError("Out of MSI device slots.\n"); + return -1; + } + + + struct msi_desc *md = &state->msi[state->last_index]; + + md->cap = msi_cap; + md->control = (union msi_control*)&dev->config_space[msi_cap + 2]; + + if(md->control->enable) { + PrintError("MSI already enabled! Smth is wrong?\n"); + return -1; + } + + int msi_cap_len = 10; // Minimal MSI capability size + if(md->control->is_64bit_cap) // 64-bit message address + msi_cap_len += 4; + if(md->control->mask_cap) { // per-vector masking support + msi_cap_len += 10; + md->mask_bits = (uint32_t*)&dev->config_space[MSI_CAP_MASK_BITS(msi_cap, md->control->is_64bit_cap)]; + md->pending_bits = (uint32_t*)&dev->config_space[MSI_CAP_PEND_BITS(msi_cap, md->control->is_64bit_cap)]; + } + md->cap_len = msi_cap_len; + + md->msg_address_lo = (union msi_msg_address*)&dev->config_space[msi_cap + 4]; + md->msg_data = (union msi_msg_data*)&dev->config_space[MSI_CAP_DATA(msi_cap, md->control->is_64bit_cap)]; + + md->dev = dev; + + dev->msi_handle = state->last_index + 1; + ++state->last_index; + + PrintDebug("Device %s registered with MSI router: cap %x:%x, device MSI handle %d\n", dev->name, md->cap, md->cap + md->cap_len, dev->msi_handle); + + return 0; +} + +#define MSI_VECTOR_MIN 0x10 +#define MSI_VECTOR_MAX 0xfe +#define MSI_ADDR_HDR 0xfee + +static int msi_update_mode(struct msi_router_state *state, int msi_dev_index) { + struct msi_desc *md = &state->msi[msi_dev_index]; + union msi_control *c = (union msi_control*)md->control; + union msi_msg_address *ad = (union msi_msg_address *)md->msg_address_lo; + union msi_msg_data *dt = (union msi_msg_data *)md->msg_data; + + int i; + if(c->enable) { + if(c->mask_cap) { + PrintError("Unsupported MSI per-vector masking feature: control %x!\n", c->val); + return -1; + } + if(c->mult_msg_en > c->mult_msg_cap) { + PrintError("Software enables more interrupt vectors (%x) than requested by device (%x)\n", + c->mult_msg_en, c->mult_msg_cap); + return -1; + } + if(c->is_64bit_cap) { + if(*(uint32_t*)&md->dev->config_space[MSI_CAP_ADDR_HI(md->cap, 1)] != 0) { + PrintError("MSI upper address is non-zero!\n"); + return -1; + } + } + if(ad->addr_hdr != MSI_ADDR_HDR) { + PrintError("MSI: Strange Address header %x.\n", ad->addr_hdr); + return -1; + } + if(ad->mbz0 || ad->mbz1) { + PrintError("MSI: MBZ fields are not zero.\n"); + return -1; + } + if(!ad->redir_cpu || !ad->dest_mode) { + md->dest_mode = MODE_PHYS_DEST; + } else { + md->dest_mode = MODE_LOGIC_DEST; + } + if(dt->vector < MSI_VECTOR_MIN || dt->vector > MSI_VECTOR_MAX) { + PrintError("MSI vector out of limits!\n"); + return -1; + } + if(dt->del_mode > 1) { + PrintError("Unsupported delivery mode specified!\n"); + return -1; + } + if(dt->trigger) { + PrintError("MSI level interrupt is not supported!\n"); + return -1; + } + md->vector = dt->vector; + md->vec_count = 1 << c->mult_msg_en; + + for(i = md->vector; i < md->vector + md->vec_count; ++i) { + state->vector_to_index[i] = msi_dev_index; + } + + PrintDebug(" enabled %d vectors starting at %x, device index %d\n", md->vec_count, md->vector, msi_dev_index); + } else { + if (md->vector) for(i = md->vector; i < md->vector + md->vec_count; ++i) { + state->vector_to_index[i] = -1; + } + PrintDebug(" disabled %d vectors starting at %x, device index %d\n", md->vec_count, md->vector, msi_dev_index); + } + + return 0; +} + +int v3_msi_get_vector(struct pci_device *dev, int *vector, void *private_data) { + if(!private_data || !dev->msi_handle) return 0; + + struct msi_router_state *state = (struct msi_router_state*)private_data; + + V3_ASSERT(dev->msi_handle > 0 && dev->msi_handle <= MSI_DEV_COUNT && state->msi[dev->msi_handle - 1].dev == dev); + + int index = dev->msi_handle - 1; + struct msi_desc *md = &state->msi[index]; + + if(!md->control->enable) { + return 0; + } + + *vector = md->raise_vector_hint; + return 1; +} + +int v3_msi_set_vector_hint(struct pci_device *dev, int raise_vector_hint, void *private_data) { + if(!private_data || !dev->msi_handle) return 0; + + struct msi_router_state *state = (struct msi_router_state*)private_data; + + V3_ASSERT(dev->msi_handle > 0 && dev->msi_handle <= MSI_DEV_COUNT && state->msi[dev->msi_handle - 1].dev == dev); + + int index = dev->msi_handle - 1; + struct msi_desc *md = &state->msi[index]; + + if(!md->control->enable) { + return 0; + } + + md->raise_vector_hint = raise_vector_hint; + V3_ASSERT(md->raise_vector_hint >= md->vector && md->raise_vector_hint <= md->vector + md->vec_count); + + //PrintDebug("MSI router set vector hint (0x%x) for device %s\n", raise_vector_hint, dev->name); + return 0; +} + +int v3_msi_get_info(struct pci_device *dev, struct msi_info *info, void *private_data) { + V3_ASSERT(private_data && info); + struct msi_router_state *state = (struct msi_router_state*)private_data; + + V3_ASSERT(dev->msi_handle > 0 && dev->msi_handle <= MSI_DEV_COUNT && state->msi[dev->msi_handle - 1].dev == dev); + + int index = dev->msi_handle - 1; + struct msi_desc *md = &state->msi[index]; + + info->enabled = md->control->enable; + info->vector = md->vector; + info->vec_count = md->vec_count; + info->dest_mode = md->dest_mode; + info->del_mode = md->msg_data->del_mode; + info->dest_cpu = md->msg_address_lo->dest_cpu; + + return 0; +} + +int v3_msi_capability_write(struct pci_device *dev, uint_t reg_num, uint_t length, void *private_data) { + V3_ASSERT(private_data); + struct msi_router_state *state = (struct msi_router_state*)private_data; + + V3_ASSERT(dev->msi_handle > 0 && dev->msi_handle <= MSI_DEV_COUNT && state->msi[dev->msi_handle - 1].dev == dev); + + int index = dev->msi_handle - 1; + struct msi_desc *md = &state->msi[index]; + + if(reg_num < MSI_CAP_CTL(md->cap)) { + PrintError("MSI: Write to read-only capability area!\n"); + return -1; + } + +#ifdef V3_CONFIG_DEBUG_PCI_MSI + union msi_msg_address *address = md->msg_address_lo; +#endif + union msi_msg_data *data = md->msg_data; + + // the idea is not to check some things until enable bit is set up + if(reg_num == MSI_CAP_CTL(md->cap)) { + if(length != 2) { + PrintError("MSI: strange control access length %d\n", length); + return -1; + } + union msi_control *new_ctl = md->control; + if(new_ctl->enable) { + PrintDebug("Guest enables MSI interrupt for device %s (%x:%x:%x):\n", dev->name, + dev->bus_num, dev->dev_num, dev->fn_num); + PrintDebug(" Address is %08x (dest_mode: %s, redir_cpu: %s, dest_cpu: %x)\n" + " Data is %04x (vector: %02x, delivery: %s, level: %s, trigger: %s)\n", + address->val, address->dest_mode ? "logic" : "phys", + address->redir_cpu ? "low_pri" : "no", address->dest_cpu, + data->val, data->vector, data->del_mode ? "low_pri" : "fixed", + data->level ? "assert" : "deassert", data->trigger ? "level" : "edge"); + + + } else { + PrintDebug("Guest disables MSI interrupt for device %s (%x:%x:%x):\n", dev->name, + dev->bus_num, dev->dev_num, dev->fn_num); + } + + if(msi_update_mode(state, index)) return -1; + md->msg_data_cached.val = md->msg_data->val; + + } else if(reg_num == MSI_CAP_ADDR_LO(md->cap)) { + if(md->control->enable) { + if(length != 4) { + PrintError("Attempt to change MSI address with bad access length while MSI enabled!\n"); + return -1; + } + PrintDebug("Guest changes MSI address for device %s (%x:%x:%x):\n", dev->name, + dev->bus_num, dev->dev_num, dev->fn_num); + PrintDebug(" New address is %08x (dest_mode: %s, redir_cpu: %s, dest_cpu: %x)\n", + address->val, address->dest_mode ? "logic" : "phys", + address->redir_cpu ? "low_pri" : "no", address->dest_cpu); + + if(msi_update_mode(state, index)) return -1; + } + } else if(reg_num == MSI_CAP_ADDR_HI(md->cap, md->control->is_64bit_cap)) { + if(md->control->enable) { + if(length != 4) { + PrintError("Attempt to change MSI upper address with bad access length while MSI enabled!\n"); + return -1; + } + if(*(uint32_t*)&dev->config_space[MSI_CAP_ADDR_HI(md->cap, 1)] != 0) { + PrintError("New MSI upper address is non-zero!\n"); + return -1; + } + } + } else if(reg_num == MSI_CAP_DATA(md->cap, md->control->is_64bit_cap)) { + if(md->control->enable) { + if(length < 2) { + PrintError("Attempt to change MSI message data with bad access length while MSI enabled!\n"); + return -1; + } + PrintDebug("Guest changes MSI message data for device %s (%x:%x:%x):\n", dev->name, + dev->bus_num, dev->dev_num, dev->fn_num); + PrintDebug(" New data is %04x (vector: %02x, delivery: %s, level: %s, trigger: %s)\n", + data->val, data->vector, data->del_mode ? "low_pri" : "fixed", + data->level ? "assert" : "deassert", data->trigger ? "level" : "edge"); + + if(md->msg_data_cached.vector != data->vector) { + // this should not happen + PrintError("Vector has changed!\n"); + return -1; + } + + if(msi_update_mode(state, index)) return -1; + + md->msg_data_cached.val = data->val; + } + } else if(md->control->mask_cap && reg_num == MSI_CAP_MASK_BITS(md->cap, md->control->is_64bit_cap)){ + if(length != 4) { + PrintError("Attempt to change MSI mask bits with bad access length!\n"); + return -1; + } + // if vector is unmasked now and pending, raise interrupt. + if(md->control->enable && *md->pending_bits) { + uint32_t i, m; + for (i = 0; i < md->vec_count; ++i) { + m = 1 << i; + if(!(*md->mask_bits & m) && (*md->pending_bits & m)) { + PrintDebug("Vector %x is unmasked and pending; raising it.\n", i + md->vector); + return msi_raise_intr(state->vm, state, i + md->vector); + } + } + } + } else { + PrintError("Unhandled MSI access at %x (MSI CAP %x)!\n", reg_num, md->cap); + return -1; + } + + return 0; +} + + +static int msi_raise_intr(struct v3_vm_info *vm, void *private_data, int irq) { + struct msi_router_state *state = (struct msi_router_state*)private_data; + + V3_ASSERT(irq < MAX_IRQ && irq >= 0); + + if(state->vector_to_index[irq] == -1) { + // we dont know anything about this IRQ + return -1; + } + + struct msi_desc *md = &state->msi[state->vector_to_index[irq]]; + + if(!md->control->enable) { + PrintDebug("MSI vector 0x%x is known, but disabled.\n", irq); + return -1; + } + + V3_ASSERT(irq >= md->vector && irq <= md->vector + md->vec_count); + + if(md->control->mask_cap) { + // if masked, setup pending bit. + uint32_t irq_bit = (1 << (uint32_t)irq); + if(*md->mask_bits & irq_bit) { + PrintDebug("MSI vector 0x%x is masked, setting pending bit.\n", irq); + *md->pending_bits |= irq_bit; + return 0; + } + } + + struct v3_gen_ipi ipi; + + + + ipi.vector = irq; + ipi.mode = md->msg_data->del_mode; + ipi.logical = md->dest_mode; + ipi.trigger_mode = md->msg_data->trigger; + ipi.dst = md->msg_address_lo->dest_cpu; + ipi.dst_shorthand = 0; + + //PrintDebug("MSI router signalling APIC to raise vector %x. Current pcpu_id %d, " + // "target vcpu_id %d, pcpu_id %d\n", irq, V3_Get_CPU(), ipi.dst, + // ipi.dst < vm->num_cores ? vm->cores[ipi.dst].pcpu_id : -1); + + if (v3_apic_send_ipi(vm, &ipi, state->apic_dev_data) == -1) { + PrintError("Error sending IPI to destination %d\n", ipi.dst); + return -1; + } + return 0; +} + +static int msi_lower_intr(struct v3_vm_info *vm, void *private_data, int irq) { + struct msi_router_state *state = (struct msi_router_state*)private_data; + + V3_ASSERT(irq < MAX_IRQ && irq >= 0); + + if(state->vector_to_index[irq] == -1) { + // we dont know anything about this IRQ + return -1; + } + + struct msi_desc *md = &state->msi[state->vector_to_index[irq]]; + + if(!md->control->enable) { + PrintDebug("MSI vector 0x%x is known, but disabled.\n", irq); + return -1; + } + + V3_ASSERT(irq >= md->vector && irq <= md->vector + md->vec_count); + + if(md->control->mask_cap) { + // clear pending bit. + uint32_t irq_bit = (1 << (uint32_t)irq); + *md->pending_bits &= ~irq_bit; + } + + return 0; +} + +static struct intr_router_ops router_ops = { + .raise_intr = msi_raise_intr, + .lower_intr = msi_lower_intr +}; + + +static int msi_router_free(struct msi_router_state *state) { + + v3_remove_intr_router(state->vm, state->router_handle); + V3_Free(state); + return 0; +} + +static struct v3_device_ops dev_ops = { + .free = (int (*)(void *))msi_router_free, + +}; + +static int msi_router_init(struct v3_vm_info *vm, v3_cfg_tree_t *cfg) { + + struct vm_device *apic_dev = v3_find_dev(vm, v3_cfg_val(cfg, "apic")); + struct msi_router_state *state = NULL; + char * dev_id = v3_cfg_val(cfg, "ID"); + int i; + + if(!apic_dev) { + PrintError("MSI router cannot work without APIC present!\n"); + return -1; + } + + state = (struct msi_router_state *)V3_Malloc(sizeof(struct msi_router_state)); + V3_ASSERT(state != NULL); + memset(state, 0x00, sizeof(*state)); + + struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state); + + if (dev == NULL) { + PrintError("Could not add device %s\n", dev_id); + V3_Free(state); + return -1; + } + + state->name = dev->name; + state->vm = vm; + state->router_handle = v3_register_intr_router(vm, &router_ops, state); + for(i = 0; i < MAX_IRQ; ++i) { + state->vector_to_index[i] = -1; + } + state->apic_dev_data = apic_dev; + + return 0; +} + +device_register("MSI_ROUTER", msi_router_init); diff --git a/palacios/src/devices/pci_msi_types.h b/palacios/src/devices/pci_msi_types.h new file mode 100644 index 0000000..648fef6 --- /dev/null +++ b/palacios/src/devices/pci_msi_types.h @@ -0,0 +1,72 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2009, Jack Lange + * Copyright (c) 2009, The V3VEE Project + * All rights reserved. + * + * Author: Alexander Kudryavtsev + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#ifndef __DEVICES_PCI_MSI_TYPES_H__ +#define __DEVICES_PCI_MSI_TYPES_H__ + +#ifdef __V3VEE__ + +#include + + +union msi_control { + uint16_t val; + struct { + uint16_t enable: 1; + uint16_t mult_msg_cap: 3; + uint16_t mult_msg_en: 3; + uint16_t is_64bit_cap: 1; + uint16_t mask_cap: 1; + }; +} __attribute__((packed)); +// NOTE: see Intel manual 3A, 10.11 on APIC MSI generation (address and data format) +union msi_msg_address { + uint32_t val; + struct { + uint32_t mbz0: 2; + uint32_t dest_mode: 1; // 0 - dest_cpu phys, 1 - logic + uint32_t redir_cpu: 1; // 0 - dest_cpu used (no redirection), 1 - lowest priority + uint32_t mbz1: 8; + uint32_t dest_cpu: 8; + uint32_t addr_hdr: 12; // should be 0xfee (APIC address header) + }; +} __attribute__((packed)); +union msi_msg_data { + uint16_t val; + struct { + uint16_t vector: 8; + uint16_t del_mode: 3; // delivery mode. 0 - fixed, 1 - lowpri. others not used (smi, nmi, init, extint). + uint16_t rsv0: 3; + uint16_t level: 1; // 0 - deassert, 1 - assert (meaningful for level triggered only) + uint16_t trigger: 1; // 0 - edge, 1 - level (level not used) + }; +} __attribute__((packed)); + + + +#define MSI_CAP_CTL(cap) ((cap) + 2) +#define MSI_CAP_ADDR_LO(cap) ((cap) + 4) +#define MSI_CAP_ADDR_HI(cap, is_64) ((is_64) ? (cap) + 8 : 0) +#define MSI_CAP_DATA(cap, is_64) ((is_64) ? (cap) + 0xc : (cap) + 0x8) +#define MSI_CAP_MASK_BITS(cap, is_64) ((is_64) ? (cap) + 0x10 : (cap) + 0xc) +#define MSI_CAP_PEND_BITS(cap, is_64) ((is_64) ? (cap) + 0x14 : (cap) + 0x10) + +#endif + +#endif diff --git a/palacios/src/devices/piix3.c b/palacios/src/devices/piix3.c index 720800f..40085dc 100644 --- a/palacios/src/devices/piix3.c +++ b/palacios/src/devices/piix3.c @@ -24,6 +24,7 @@ #include #include +#include struct iort_reg { @@ -378,16 +379,18 @@ static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) { int intr_pin = pci_dev->config_header.intr_pin - 1; int irq_index = (intr_pin + pci_dev->dev_num - 1) & 0x3; - //PrintError("Raising PCI IRQ %d, %p\n", piix3_cfg->pirq_rc[irq_index], piix3->vm); - - if(pci_dev->is_passthrough) { - int guest_irq = pci_dev->config_header.intr_line; - - v3_raise_irq(piix3->vm, guest_irq); - - } else if (piix3_cfg->pirq_rc[irq_index] < 16) { - v3_raise_irq(piix3->vm, piix3_cfg->pirq_rc[irq_index] & 0xf); - } else { + int guest_irq = -1, msi_vector; + + int is_msi = v3_msi_get_vector(pci_dev, &msi_vector, v3_pci_get_msi_router(piix3->pci_bus)); + if(is_msi < 0) return -1; + + if(is_msi == 1) + guest_irq = msi_vector; + else if(pci_dev->is_passthrough) + guest_irq = pci_dev->config_header.intr_line; + else if (piix3_cfg->pirq_rc[irq_index] < 16) + guest_irq = piix3_cfg->pirq_rc[irq_index] & 0xf; + else { PrintError("Tried to raise interrupt on disabled PIRQ entry (%d)\n", irq_index); PrintError("\tpirq_rc values: 0=0x%x, 1=0x%x, 2=0x%x, 3=0x%x\n", piix3_cfg->pirq_rc[0], piix3_cfg->pirq_rc[1], @@ -395,6 +398,9 @@ static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) { return -1; } + //PrintError("Raising PCI %sIRQ %d, %p\n", is_msi ? "MSI ":"", guest_irq, piix3->vm); + v3_raise_irq(piix3->vm, guest_irq); + return 0; } -- 1.7.5.4