*/
#include <jailhouse/acpi.h>
+#include <jailhouse/pci.h>
#include <jailhouse/utils.h>
#include <asm/cell.h>
const struct jailhouse_memory *mem);
int vtd_unmap_memory_region(struct cell *cell,
const struct jailhouse_memory *mem);
+int vtd_add_pci_device(struct cell *cell, struct pci_device *device);
+void vtd_remove_pci_device(struct pci_device *device);
void vtd_cell_exit(struct cell *cell);
void vtd_config_commit(struct cell *cell_added_removed);
*/
#include <jailhouse/pci.h>
+#include <jailhouse/printk.h>
#include <jailhouse/utils.h>
#include <asm/io.h>
#include <asm/pci.h>
+#include <asm/vtd.h>
/* protects the root bridge's PIO interface to the PCI config space */
static DEFINE_SPINLOCK(pci_lock);
return result;
}
+
+int arch_pci_add_device(struct cell *cell, struct pci_device *device)
+{
+ return vtd_add_pci_device(cell, device);
+}
+
+void arch_pci_remove_device(struct pci_device *device)
+{
+ vtd_remove_pci_device(device);
+}
return vtd_cell_init(&root_cell);
}
-static bool vtd_add_device_to_cell(struct cell *cell,
- const struct jailhouse_pci_device *device)
+int vtd_add_pci_device(struct cell *cell, struct pci_device *device)
{
- u64 *root_entry_lo = &root_entry_table[PCI_BUS(device->bdf)].lo_word;
+ u16 bdf = device->info->bdf;
+ u64 *root_entry_lo = &root_entry_table[PCI_BUS(bdf)].lo_word;
struct vtd_entry *context_entry_table, *context_entry;
+ // HACK for QEMU
+ if (dmar_units == 0)
+ return 0;
+
if (*root_entry_lo & VTD_ROOT_PRESENT) {
context_entry_table =
page_map_phys2hvirt(*root_entry_lo & PAGE_MASK);
} else {
context_entry_table = page_alloc(&mem_pool, 1);
if (!context_entry_table)
- return false;
+ return -ENOMEM;
*root_entry_lo = VTD_ROOT_PRESENT |
page_map_hvirt2phys(context_entry_table);
flush_cache(root_entry_lo, sizeof(u64));
}
- context_entry = &context_entry_table[PCI_DEVFN(device->bdf)];
+ context_entry = &context_entry_table[PCI_DEVFN(bdf)];
if (context_entry->lo_word & VTD_CTX_PRESENT)
return true;
- printk("Adding PCI device %02x:%02x.%x to cell \"%s\"\n",
- PCI_BDF_PARAMS(device->bdf), cell->config->name);
-
context_entry->lo_word = VTD_CTX_PRESENT | VTD_CTX_TTYPE_MLP_UNTRANS |
page_map_hvirt2phys(cell->vtd.pg_structs.root_table);
context_entry->hi_word =
(cell->id << VTD_CTX_DID_SHIFT);
flush_cache(context_entry, sizeof(*context_entry));
- return true;
+ return 0;
}
-static void
-vtd_remove_device_from_cell(struct cell *cell,
- const struct jailhouse_pci_device *device)
+void vtd_remove_pci_device(struct pci_device *device)
{
- u64 *root_entry_lo = &root_entry_table[PCI_BUS(device->bdf)].lo_word;
+ u16 bdf = device->info->bdf;
+ u64 *root_entry_lo = &root_entry_table[PCI_BUS(bdf)].lo_word;
struct vtd_entry *context_entry_table;
struct vtd_entry *context_entry;
unsigned int n;
+ // HACK for QEMU
+ if (dmar_units == 0)
+ return;
+
if (!(*root_entry_lo & VTD_ROOT_PRESENT))
return;
context_entry_table = page_map_phys2hvirt(*root_entry_lo & PAGE_MASK);
- context_entry = &context_entry_table[PCI_DEVFN(device->bdf)];
+ context_entry = &context_entry_table[PCI_DEVFN(bdf)];
if (!(context_entry->lo_word & VTD_CTX_PRESENT))
return;
- printk("Removing PCI device %02x:%02x.%x from cell \"%s\"\n",
- PCI_BDF_PARAMS(device->bdf), cell->config->name);
-
context_entry->lo_word &= ~VTD_CTX_PRESENT;
flush_cache(&context_entry->lo_word, sizeof(u64));
int vtd_cell_init(struct cell *cell)
{
- const struct jailhouse_pci_device *dev =
- jailhouse_cell_pci_devices(cell->config);
- int n;
-
// HACK for QEMU
if (dmar_units == 0)
return 0;
if (!cell->vtd.pg_structs.root_table)
return -ENOMEM;
- for (n = 0; n < cell->config->num_pci_devices; n++) {
- vtd_remove_device_from_cell(&root_cell, &dev[n]);
- if (!vtd_add_device_to_cell(cell, &dev[n])) {
- vtd_cell_exit(cell);
- return -ENOMEM;
- }
- }
-
vtd_init_fault_nmi();
return 0;
mem->size, PAGE_MAP_COHERENT);
}
-static bool
-vtd_return_device_to_root_cell(const struct jailhouse_pci_device *dev)
-{
- const struct jailhouse_pci_device *root_cell_dev =
- jailhouse_cell_pci_devices(root_cell.config);
- unsigned int n;
-
- for (n = 0; n < root_cell.config->num_pci_devices; n++)
- if (root_cell_dev[n].domain == dev->domain &&
- root_cell_dev[n].bdf == dev->bdf)
- return vtd_add_device_to_cell(&root_cell,
- &root_cell_dev[n]);
- return true;
-}
-
void vtd_cell_exit(struct cell *cell)
{
- const struct jailhouse_pci_device *dev =
- jailhouse_cell_pci_devices(cell->config);
- unsigned int n;
-
// HACK for QEMU
if (dmar_units == 0)
return;
- for (n = 0; n < cell->config->num_pci_devices; n++) {
- vtd_remove_device_from_cell(cell, &dev[n]);
- if (!vtd_return_device_to_root_cell(&dev[n]))
- printk("WARNING: Failed to re-assign PCI device to "
- "root cell\n");
- }
-
page_free(&mem_pool, cell->vtd.pg_structs.root_table, 1);
}
u32 arch_pci_read_config(u16 bdf, u16 address, unsigned int size);
void arch_pci_write_config(u16 bdf, u16 address, u32 value, unsigned int size);
+int arch_pci_add_device(struct cell *cell, struct pci_device *device);
+void arch_pci_remove_device(struct pci_device *device);
+
#endif /* !_JAILHOUSE_PCI_H */
*/
#include <jailhouse/acpi.h>
+#include <jailhouse/control.h>
#include <jailhouse/mmio.h>
#include <jailhouse/pci.h>
#include <jailhouse/printk.h>
* locality. */
for (n = 0; n < cell->config->num_pci_devices; n++)
if (dev_info[n].bdf == bdf)
- return &cell->pci_devices[n];
+ return cell->pci_devices[n].cell ?
+ &cell->pci_devices[n] : NULL;
return NULL;
}
}
+static int pci_add_device(struct cell *cell, struct pci_device *device)
+{
+ printk("Adding PCI device %02x:%02x.%x to cell \"%s\"\n",
+ PCI_BDF_PARAMS(device->info->bdf), cell->config->name);
+ return arch_pci_add_device(cell, device);
+}
+
+static void pci_remove_device(struct pci_device *device)
+{
+ printk("Removing PCI device %02x:%02x.%x from cell \"%s\"\n",
+ PCI_BDF_PARAMS(device->info->bdf), device->cell->config->name);
+ arch_pci_remove_device(device);
+}
+
int pci_cell_init(struct cell *cell)
{
unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
sizeof(struct pci_device));
const struct jailhouse_pci_device *dev_infos =
jailhouse_cell_pci_devices(cell->config);
- struct pci_device *device;
+ struct pci_device *device, *root_device;
unsigned int ndev;
+ int err;
cell->pci_devices = page_alloc(&mem_pool, array_size / PAGE_SIZE);
if (!cell->pci_devices)
* We order device states in the same way as the static information
* so that we can use the index of the latter to find the former. For
* the other way around and for obtaining the owner cell, we use more
- * handy pointers.
+ * handy pointers. The cell pointer also encodes active ownership.
*/
for (ndev = 0; ndev < cell->config->num_pci_devices; ndev++) {
device = &cell->pci_devices[ndev];
device->info = &dev_infos[ndev];
+
+ root_device = pci_get_assigned_device(&root_cell,
+ dev_infos[ndev].bdf);
+ if (root_device) {
+ pci_remove_device(root_device);
+ root_device->cell = NULL;
+ }
+
+ err = pci_add_device(cell, device);
+ if (err) {
+ pci_cell_exit(cell);
+ return err;
+ }
+
device->cell = cell;
}
return 0;
}
+static void pci_return_device_to_root_cell(struct pci_device *device)
+{
+ struct pci_device *root_device = root_cell.pci_devices;
+ unsigned int n;
+
+ for (n = 0; n < root_cell.config->num_pci_devices; n++, root_device++)
+ if (root_device->info->domain == device->info->domain &&
+ root_device->info->bdf == device->info->bdf) {
+ if (pci_add_device(&root_cell, root_device) < 0)
+ printk("WARNING: Failed to re-assign PCI "
+ "device to root cell\n");
+ else
+ root_device->cell = &root_cell;
+ break;
+ }
+}
+
void pci_cell_exit(struct cell *cell)
{
unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
sizeof(struct pci_device));
+ unsigned int n;
+
+ /*
+ * Do not destroy the root cell. We will shut down the complete
+ * hypervisor instead.
+ */
+ if (cell == &root_cell)
+ return;
+
+ for (n = 0; n < cell->config->num_pci_devices; n++) {
+ if (!cell->pci_devices[n].cell)
+ continue;
+ pci_remove_device(&cell->pci_devices[n]);
+ pci_return_device_to_root_cell(&cell->pci_devices[n]);
+ }
page_free(&mem_pool, cell->pci_devices, array_size / PAGE_SIZE);
}