uint_t rsvd4 : 24; // Should be 0
} __attribute__((packed));
-#define SVM_INJECTION_EXTERNAL_INTR 0
-#define SVM_INJECTION_VIRTUAL_INTR 0
+#define SVM_INJECTION_IRQ 0
#define SVM_INJECTION_NMI 2
#define SVM_INJECTION_EXCEPTION 3
#define SVM_INJECTION_SOFT_INTR 4
#include <palacios/vmm_lock.h>
-typedef enum {INVALID_INTR, EXTERNAL_IRQ, NMI, SOFTWARE_INTR, VIRTUAL_INTR} intr_type_t;
+typedef enum {V3_INVALID_INTR, V3_EXTERNAL_IRQ, V3_VIRTUAL_IRQ, V3_NMI, V3_SOFTWARE_INTR} v3_intr_type_t;
struct guest_info;
struct v3_interrupt;
void * priv_data;
};
+#define MAX_IRQ 256
uint_t irq_started;
uint_t irq_vector;
+ uint8_t virq_map[MAX_IRQ / 8];
+
v3_lock_t irq_lock;
/* some way to get the [A]PIC intr */
void v3_init_interrupt_state(struct guest_info * info);
+int v3_raise_virq(struct guest_info * info, int irq);
+int v3_lower_virq(struct guest_info * info, int irq);
+
int v3_raise_irq(struct guest_info * info, int irq);
int v3_lower_irq(struct guest_info * info, int irq);
void v3_register_intr_controller(struct guest_info * info, struct intr_ctrl_ops * ops, void * state);
-int v3_intr_pending(struct guest_info * info);
-uint_t v3_get_intr_number(struct guest_info * info);
-intr_type_t v3_get_intr_type(struct guest_info * info);
+v3_intr_type_t v3_intr_pending(struct guest_info * info);
+uint32_t v3_get_intr(struct guest_info * info);
+
+//intr_type_t v3_get_intr_type(struct guest_info * info);
-int v3_injecting_intr(struct guest_info * info, uint_t intr_num, intr_type_t type);
+int v3_injecting_intr(struct guest_info * info, uint_t intr_num, v3_intr_type_t type);
/*
int start_irq(struct vm_intr * intr);
uchar_t flag = 0x1 << minor_offset;
uchar_t * svc_location = apic->int_svc_reg + major_offset;
- PrintDebug("Received APIC EOI\n");
+ PrintDebug("Received APIC EOI for IRQ %d\n", isr_irq);
*svc_location &= ~flag;
}
tmr_ticks = cpu_cycles >> shift_num;
- PrintDebug("Timer Ticks: %p\n", (void *)tmr_ticks);
+ // PrintDebug("Timer Ticks: %p\n", (void *)tmr_ticks);
if (tmr_ticks < apic->tmr_cur_cnt) {
apic->tmr_cur_cnt -= tmr_ticks;
PrintDebug("Raising APIC Timer interrupt (periodic=%d) (icnt=%d) (div=%d)\n",
apic->tmr_vec_tbl.tmr_mode, apic->tmr_init_cnt, shift_num);
+ if (apic_intr_pending(priv_data)) {
+ PrintDebug("Overriding pending IRQ %d\n", apic_get_intr_number(priv_data));
+ }
+
if (activate_internal_irq(apic, APIC_TMR_INT) == -1) {
PrintError("Could not raise Timer interrupt\n");
}
-device_register("LAPIC", &apic_init)
+device_register("LAPIC", apic_init)
}
bars[4].type = PCI_BAR_IO;
- bars[4].default_base_port = PRI_DEFAULT_DMA_PORT;
+ // bars[4].default_base_port = PRI_DEFAULT_DMA_PORT;
+ bars[4].default_base_port = -1;
bars[4].num_ports = 16;
bars[4].io_read = read_dma_port;
#include <palacios/vm_guest_mem.h>
#include <devices/pci.h>
+#include <palacios/vmm_io.h>
-
+/*
#ifndef DEBUG_VIRTIO_BLK
#undef PrintDebug
#define PrintDebug(fmt, args...)
#endif
+*/
#define BLK_CAPACITY_PORT 20
#define BLK_MAX_SIZE_PORT 28
bars[i].type = PCI_BAR_NONE;
}
+ PrintDebug("Virtio-BLK io_range_size = %d\n", virtio_state->io_range_size);
+
bars[0].type = PCI_BAR_IO;
bars[0].default_base_port = -1;
bars[0].num_ports = virtio_state->io_range_size;
#define CONFIG_ADDR_PORT 0x0cf8
#define CONFIG_DATA_PORT 0x0cfc
+#define PCI_DEV_IO_PORT_BASE 0xc000
#define PCI_BUS_COUNT 1
// Bitmap of the allocated device numbers
uint8_t dev_map[MAX_BUS_DEVICES / 8];
+
int (*raise_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev);
int (*lower_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev);
struct vm_device * irq_bridge_dev;
// Configuration address register
struct pci_addr_reg addr_reg;
+ // Base IO Port which PCI devices will register with...
+ uint16_t dev_io_base;
+
// Attached Busses
struct pci_bus bus_list[PCI_BUS_COUNT];
};
static int bar_update(struct pci_device * pci, int bar_num, uint32_t new_val) {
struct v3_pci_bar * bar = &(pci->bar[bar_num]);
- PrintDebug("Updating BAR Register (Dev=%s) (bar=%d) (old_val=%x) (new_val=%x)\n",
+ PrintError("Updating BAR Register (Dev=%s) (bar=%d) (old_val=0x%x) (new_val=0x%x)\n",
pci->name, bar_num, bar->val, new_val);
switch (bar->type) {
case PCI_BAR_IO: {
int i = 0;
- PrintDebug("\tRehooking %d IO ports from base %x to %x\n",
- bar->num_ports, PCI_IO_BASE(bar->val), PCI_IO_BASE(new_val));
+ PrintError("\tRehooking %d IO ports from base 0x%x to 0x%x for %d ports\n",
+ bar->num_ports, PCI_IO_BASE(bar->val), PCI_IO_BASE(new_val),
+ bar->num_ports);
// only do this if pci device is enabled....
+ if (!(pci->config_header.status & 0x1)) {
+ PrintError("PCI Device IO space not enabled\n");
+ }
+
for (i = 0; i < bar->num_ports; i++) {
+ PrintError("Rehooking PCI IO port (old port=%u) (new port=%u)\n",
+ PCI_IO_BASE(bar->val) + i, PCI_IO_BASE(new_val) + i);
+
v3_dev_unhook_io(pci->vm_dev, PCI_IO_BASE(bar->val) + i);
- v3_dev_hook_io(pci->vm_dev, PCI_IO_BASE(new_val) + i,
- bar->io_read, bar->io_write);
+ if (v3_dev_hook_io(pci->vm_dev, PCI_IO_BASE(new_val) + i,
+ bar->io_read, bar->io_write) == -1) {
+
+ PrintError("Could not hook PCI IO port (old port=%u) (new port=%u)\n",
+ PCI_IO_BASE(bar->val) + i, PCI_IO_BASE(new_val) + i);
+ return -1;
+ }
}
bar->val = new_val;
pci_state->addr_reg.val = 0;
+ pci_state->dev_io_base = PCI_DEV_IO_PORT_BASE;
init_pci_busses(pci_state);
int j = 0;
pci_dev->bar[i].mask = (~((pci_dev->bar[i].num_ports) - 1)) | 0x01;
- pci_dev->bar[i].val = pci_dev->bar[i].default_base_port & pci_dev->bar[i].mask;
+ if (pci_dev->bar[i].default_base_port != 0xffff) {
+ pci_dev->bar[i].val = pci_dev->bar[i].default_base_port & pci_dev->bar[i].mask;
+ } else {
+ pci_dev->bar[i].val = 0;
+ }
+
pci_dev->bar[i].val |= 0x00000001;
for (j = 0; j < pci_dev->bar[i].num_ports; j++) {
pci_dev->bar[i].mask = ~((pci_dev->bar[i].num_pages << 12) - 1);
pci_dev->bar[i].mask |= 0xf; // preserve the configuration flags
- pci_dev->bar[i].val = pci_dev->bar[i].default_base_addr & pci_dev->bar[i].mask;
+ if (pci_dev->bar[i].default_base_addr != 0xffffffff) {
+ pci_dev->bar[i].val = pci_dev->bar[i].default_base_addr & pci_dev->bar[i].mask;
+ } else {
+ pci_dev->bar[i].val = 0;
+ }
// hook memory
if (pci_dev->bar[i].mem_read) {
if (pci_dev->bar[i].type == PCI_BAR_IO) {
pci_dev->bar[i].num_ports = bars[i].num_ports;
- pci_dev->bar[i].default_base_port = bars[i].default_base_port;
+
+ // This is a horrible HACK becaues the BIOS is supposed to set the PCI base ports
+ // And if the BIOS doesn't, Linux just happily overlaps device port assignments
+ if (bars[i].default_base_port != (uint16_t)-1) {
+ pci_dev->bar[i].default_base_port = bars[i].default_base_port;
+ } else {
+ pci_dev->bar[i].default_base_port = pci_state->dev_io_base;
+ pci_state->dev_io_base += ( 0x100 * ((bars[i].num_ports / 0x100) + 1) );
+ }
+
pci_dev->bar[i].io_read = bars[i].io_read;
pci_dev->bar[i].io_write = bars[i].io_write;
} else if (pci_dev->bar[i].type == PCI_BAR_MEM32) {
*/
// disable global interrupts for vm state transition
-
v3_clgi();
// reenable global interrupts after vm exit
v3_stgi();
+
+ // Conditionally yield the CPU if the timeslice has expired
+ v3_yield_cond(info);
+
+
v3_update_time(info, tmp_tsc - info->time_state.cached_host_tsc);
num_exits++;
}
}
-
- // Conditionally yield the CPU if the timeslice has expired
- v3_yield_cond(info);
-
if (v3_handle_svm_exit(info) != 0) {
vmcb_ctrl_t * guest_ctrl = GET_VMCB_CTRL_AREA((vmcb_t*)(info->vmm_data));
addr_t host_addr;
//
-// This should trigger a #GP if cpl!=0, otherwise, yield to host
+// This should trigger a #GP if cpl != 0, otherwise, yield to host
//
int v3_handle_svm_halt(struct guest_info * info) {
v3_raise_exception(info, GPF_EXCEPTION);
} else {
- ullong_t yield_start = 0;
- ullong_t yield_stop = 0;
+ uint64_t yield_start = 0;
+ uint64_t yield_stop = 0;
uint32_t gap = 0;
PrintDebug("CPU Yield\n");
//v3_update_time(info, yield_stop - yield_start);
gap = yield_stop - yield_start;
- v3_raise_irq(info, 0);
+ /* WARNING!!! WARNING!!!
+ *
+ * DO NOT REMOVE THIS CONDITIONAL!!!
+ *
+ * It is common for an OS to issue an IO op, and then sit in a halt loop
+ * waiting for the device to complete and raise an irq.
+ * If you remove this then the timer interrupt will ALWAYS subvert the completion
+ * interrupt and stall the guest.
+ */
+ if (!v3_intr_pending(info)) {
+ v3_raise_irq(info, 0);
+ }
PrintDebug("CPU Yield Done (%d cycles)\n", gap);
info->intr_state.irq_started = 1;
info->intr_state.irq_pending = 0;
- v3_injecting_intr(info, info->intr_state.irq_vector, EXTERNAL_IRQ);
+ v3_injecting_intr(info, info->intr_state.irq_vector, V3_EXTERNAL_IRQ);
}
if ((info->intr_state.irq_started == 1) && (guest_ctrl->exit_int_info.valid == 0)) {
}
- // Disable printing io exits due to bochs debug messages
- //if (!((exit_code == VMEXIT_IOIO) && ((ushort_t)(guest_ctrl->exit_info1 >> 16) == 0x402))) {
-
- if ((0) && (exit_code <= VMEXIT_EXCP14)) {
- uchar_t instr[32];
- int ret;
- // Dump out the instr stream
-
- //PrintDebug("RIP: %x\n", guest_state->rip);
- PrintDebug("\n\n\nRIP Linear: %p\n", (void *)get_addr_linear(info, info->rip, &(info->segments.cs)));
-
- v3_print_GPRs(info);
- v3_print_ctrl_regs(info);
-
-
- // OK, now we will read the instruction
- // The only difference between PROTECTED and PROTECTED_PG is whether we read
- // from guest_pa or guest_va
- if (info->mem_mode == PHYSICAL_MEM) {
- // The real rip address is actually a combination of the rip + CS base
- ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 32, instr);
- } else {
- ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 32, instr);
- }
-
-
- if (ret != 32) {
- // I think we should inject a GPF into the guest
- PrintDebug("Could not read instruction (ret=%d)\n", ret);
- } else {
- PrintDebug("Instr Stream:\n");
- PrintTraceMemDump(instr, 32);
- }
- }
-
-
if (info->enable_profiler) {
rdtscll(info->profiler.start_time);
}
-
if (v3_excp_pending(info)) {
uint_t excp = v3_get_excp_number(info);
guest_ctrl->guest_ctrl.V_IGN_TPR = 1;
guest_ctrl->guest_ctrl.V_INTR_PRIO = 0xf;
- } else if (v3_intr_pending(info)) {
+ } else {
+ switch (v3_intr_pending(info)) {
+ case V3_EXTERNAL_IRQ: {
+ uint32_t irq = v3_get_intr(info);
- switch (v3_get_intr_type(info)) {
- case EXTERNAL_IRQ: {
- uint_t irq = v3_get_intr_number(info);
-
- // check to see if ==-1 (non exists)
-
-
guest_ctrl->guest_ctrl.V_IRQ = 1;
guest_ctrl->guest_ctrl.V_INTR_VECTOR = irq;
guest_ctrl->guest_ctrl.V_IGN_TPR = 1;
info->intr_state.irq_pending = 1;
info->intr_state.irq_vector = irq;
-
+
break;
}
- case NMI:
+ case V3_NMI:
guest_ctrl->EVENTINJ.type = SVM_INJECTION_NMI;
break;
- case SOFTWARE_INTR:
+ case V3_SOFTWARE_INTR:
guest_ctrl->EVENTINJ.type = SVM_INJECTION_SOFT_INTR;
break;
- case VIRTUAL_INTR:
- guest_ctrl->EVENTINJ.type = SVM_INJECTION_VIRTUAL_INTR;
+ case V3_VIRTUAL_IRQ:
+ guest_ctrl->EVENTINJ.type = SVM_INJECTION_IRQ;
break;
-
- case INVALID_INTR:
+
+ case V3_INVALID_INTR:
default:
- PrintError("Attempted to issue an invalid interrupt\n");
- return -1;
+ break;
}
- } else {
- //PrintDebug("Not interrupts or exceptions pending\n");
}
int read_size = 0;
if (hook == NULL) {
- PrintError("Hook Not present for in on port %x\n", io_info->port);
+ PrintError("Hook Not present for in on port 0x%x\n", io_info->port);
// error, we should not have exited on this port
return -1;
}
if (hook->read(io_info->port, &(info->vm_regs.rax), read_size, hook->priv_data) != read_size) {
// not sure how we handle errors.....
- PrintError("Read Failure for in on port %x\n", io_info->port);
+ PrintError("Read Failure for in on port 0x%x\n", io_info->port);
return -1;
}
if (hook == NULL) {
- PrintError("Hook Not present for ins on port %x\n", io_info->port);
+ PrintError("Hook Not present for ins on port 0x%x\n", io_info->port);
// error, we should not have exited on this port
return -1;
}
if (hook->read(io_info->port, (char *)host_addr, read_size, hook->priv_data) != read_size) {
// not sure how we handle errors.....
- PrintError("Read Failure for ins on port %x\n", io_info->port);
+ PrintError("Read Failure for ins on port 0x%x\n", io_info->port);
return -1;
}
int write_size = 0;
if (hook == NULL) {
- PrintError("Hook Not present for out on port %x\n", io_info->port);
+ PrintError("Hook Not present for out on port 0x%x\n", io_info->port);
// error, we should not have exited on this port
return -1;
}
if (hook->write(io_info->port, &(info->vm_regs.rax), write_size, hook->priv_data) != write_size) {
// not sure how we handle errors.....
- PrintError("Write Failure for out on port %x\n", io_info->port);
+ PrintError("Write Failure for out on port 0x%x\n", io_info->port);
return -1;
}
if (hook == NULL) {
- PrintError("Hook Not present for outs on port %x\n", io_info->port);
+ PrintError("Hook Not present for outs on port 0x%x\n", io_info->port);
// error, we should not have exited on this port
return -1;
}
if (hook->write(io_info->port, (char*)host_addr, write_size, hook->priv_data) != write_size) {
// not sure how we handle errors.....
- PrintError("Write Failure for outs on port %x\n", io_info->port);
+ PrintError("Write Failure for outs on port 0x%x\n", io_info->port);
return -1;
}
+int v3_raise_virq(struct guest_info * info, int irq) {
+ struct v3_intr_state * intr_state = &(info->intr_state);
+ int major = irq / 8;
+ int minor = irq % 8;
+
+ intr_state->virq_map[major] |= (1 << minor);
+
+ return 0;
+}
+
+int v3_lower_virq(struct guest_info * info, int irq) {
+ struct v3_intr_state * intr_state = &(info->intr_state);
+ int major = irq / 8;
+ int minor = irq % 8;
+ intr_state->virq_map[major] &= ~(1 << minor);
+ return 0;
+}
int v3_lower_irq(struct guest_info * info, int irq) {
-int v3_intr_pending(struct guest_info * info) {
+v3_intr_type_t v3_intr_pending(struct guest_info * info) {
struct v3_intr_state * intr_state = &(info->intr_state);
struct intr_controller * ctrl = NULL;
- int ret = 0;
+ int ret = V3_INVALID_INTR;
+ int i = 0;
// PrintDebug("[intr_pending]\n");
- addr_t irq_state = v3_lock_irqsave(intr_state->irq_lock);
+ addr_t irq_state = v3_lock_irqsave(intr_state->irq_lock);
- list_for_each_entry(ctrl, &(intr_state->controller_list), ctrl_node) {
- if (ctrl->ctrl_ops->intr_pending(ctrl->priv_data) == 1) {
- ret = 1;
+ // VIRQs have priority
+ for (i = 0; i < MAX_IRQ / 8; i++) {
+ if (intr_state->virq_map[i] != 0) {
+ ret = V3_VIRTUAL_IRQ;
break;
}
}
+ if (ret == V3_INVALID_INTR) {
+ list_for_each_entry(ctrl, &(intr_state->controller_list), ctrl_node) {
+ if (ctrl->ctrl_ops->intr_pending(ctrl->priv_data) == 1) {
+ ret = V3_EXTERNAL_IRQ;
+ break;
+ }
+ }
+ }
+
v3_unlock_irqrestore(intr_state->irq_lock, irq_state);
return ret;
}
-uint_t v3_get_intr_number(struct guest_info * info) {
+uint32_t v3_get_intr(struct guest_info * info) {
struct v3_intr_state * intr_state = &(info->intr_state);
struct intr_controller * ctrl = NULL;
uint_t ret = 0;
+ int i = 0;
+ int j = 0;
addr_t irq_state = v3_lock_irqsave(intr_state->irq_lock);
- list_for_each_entry(ctrl, &(intr_state->controller_list), ctrl_node) {
- if (ctrl->ctrl_ops->intr_pending(ctrl->priv_data)) {
- uint_t intr_num = ctrl->ctrl_ops->get_intr_number(ctrl->priv_data);
-
- // PrintDebug("[get_intr_number] intr_number = %d\n", intr_num);
- ret = intr_num;
+ // virqs have priority
+ for (i = 0; i < MAX_IRQ / 8; i++) {
+ if (intr_state->virq_map[i] != 0) {
+ for (j = 0; j < 8; j++) {
+ if (intr_state->virq_map[i] & (1 << j)) {
+ ret = (i * 8) + j;
+ break;
+ }
+ }
break;
}
}
- v3_unlock_irqrestore(intr_state->irq_lock, irq_state);
+ if (!ret) {
+ list_for_each_entry(ctrl, &(intr_state->controller_list), ctrl_node) {
+ if (ctrl->ctrl_ops->intr_pending(ctrl->priv_data)) {
+ uint_t intr_num = ctrl->ctrl_ops->get_intr_number(ctrl->priv_data);
+
+ // PrintDebug("[get_intr_number] intr_number = %d\n", intr_num);
+ ret = intr_num;
+ break;
+ }
+ }
+ }
+ v3_unlock_irqrestore(intr_state->irq_lock, irq_state);
return ret;
}
-
+/*
intr_type_t v3_get_intr_type(struct guest_info * info) {
struct v3_intr_state * intr_state = &(info->intr_state);
struct intr_controller * ctrl = NULL;
- intr_type_t type = INVALID_INTR;
+ intr_type_t type = V3_INVALID_INTR;
addr_t irq_state = v3_lock_irqsave(intr_state->irq_lock);
list_for_each_entry(ctrl, &(intr_state->controller_list), ctrl_node) {
if (ctrl->ctrl_ops->intr_pending(ctrl->priv_data) == 1) {
//PrintDebug("[get_intr_type] External_irq\n");
- type = EXTERNAL_IRQ;
+ type = V3_EXTERNAL_IRQ;
break;
}
}
#ifdef DEBUG_INTERRUPTS
- if (type == INVALID_INTR) {
+ if (type == V3_INVALID_INTR) {
PrintError("[get_intr_type] Invalid_Intr\n");
}
#endif
return type;
}
+*/
-
-int v3_injecting_intr(struct guest_info * info, uint_t intr_num, intr_type_t type) {
+int v3_injecting_intr(struct guest_info * info, uint_t intr_num, v3_intr_type_t type) {
struct v3_intr_state * intr_state = &(info->intr_state);
- if (type == EXTERNAL_IRQ) {
+ if (type == V3_EXTERNAL_IRQ) {
struct intr_controller * ctrl = NULL;
addr_t irq_state = v3_lock_irqsave(intr_state->irq_lock);
io_hook->priv_data = priv_data;
if (insert_io_hook(info, io_hook)) {
- V3_Free(io_hook);
- return -1;
+ PrintError("Could not insert IO hook for port %d\n", port);
+ V3_Free(io_hook);
+ return -1;
}
if (info->io_map.update_map(info, port,
((read == NULL) ? 0 : 1),
((write == NULL) ? 0 : 1)) == -1) {
- V3_Free(io_hook);
- return -1;
+ PrintError("Could not update IO map for port %d\n", port);
+ V3_Free(io_hook);
+ return -1;
}