return 0;
}
-static long long palacios_file_size(void * file_ptr) {
+static unsigned long long palacios_file_size(void * file_ptr) {
struct palacios_file * pfile = (struct palacios_file *)file_ptr;
struct file * filp = pfile->filp;
struct kstat s;
return s.size;
}
-static long long palacios_file_read(void * file_ptr, void * buffer, long long length, long long offset){
+static unsigned long long palacios_file_read(void * file_ptr, void * buffer, unsigned long long length, unsigned long long offset){
struct palacios_file * pfile = (struct palacios_file *)file_ptr;
struct file * filp = pfile->filp;
ssize_t ret;
set_fs(old_fs);
if (ret <= 0) {
- printk("sys_read of %p for %lld bytes failed\n", filp, length);
+ printk("sys_read of %p for %lld bytes at offset %llu failed (ret=%ld)\n", filp, length, offset, ret);
}
return ret;
}
-static long long palacios_file_write(void * file_ptr, void * buffer, long long length, long long offset) {
+static unsigned long long palacios_file_write(void * file_ptr, void * buffer, unsigned long long length, unsigned long long offset) {
struct palacios_file * pfile = (struct palacios_file *)file_ptr;
struct file * filp = pfile->filp;
mm_segment_t old_fs;
if (ret <= 0) {
- printk("sys_write failed\n");
+ printk("sys_write for %llu bytes at offset %llu failed (ret=%ld)\n", length, offset, ret);
}
return ret;
struct lnx_thread_arg {
int (*fn)(void * arg);
void * arg;
+ char * name;
};
static int lnx_thread_target(void * arg) {
kfree(thread_info);
// handle cleanup
+
+ printk("Palacios Thread (%s) EXITTING\n", thread_info->name);
+
do_exit(ret);
return 0; // should not get here.
thread_info->fn = fn;
thread_info->arg = arg;
+ thread_info->name = thread_name;
return kthread_run( lnx_thread_target, thread_info, thread_name );
}
thread_info->fn = fn;
thread_info->arg = arg;
+ thread_info->name = thread_name;
thread = kthread_create( lnx_thread_target, thread_info, thread_name );
#include <linux/file.h>
#include <linux/spinlock.h>
#include <linux/rbtree.h>
+#include <linux/module.h>
#include <palacios/vmm.h>
writeit(cons_fd,sc);
sc |= 0x80;
writeit(cons_fd,sc);
+ } else if (key == '~') { // CTRL-C
+ unsigned char sc;
+ sc = 0x1d; // left ctrl down
+ writeit(cons_fd,sc);
+ sc = 0x2e; // c down
+ writeit(cons_fd,sc);
+ sc = 0x2e | 0x80; // c up
+ writeit(cons_fd,sc);
+ sc = 0x1d | 0x80; // left ctrl up
+ writeit(cons_fd,sc);
}else {
if (send_char_to_palacios_as_scancodes(cons_fd,key)) {
fprintf(stderr, "Error sendign key to console\n");
#include <palacios/vmm_dev_mgr.h>
-
typedef enum {IPI_FIXED = 0,
IPI_LOWEST_PRIO = 1,
IPI_SMI = 2,
+ IPI_RES1 = 3,
IPI_NMI = 4,
IPI_INIT = 5,
- IPI_EXINT = 7 } ipi_mode_t;
+ IPI_SIPI = 6,
+ IPI_EXTINT = 7 } ipi_mode_t;
struct v3_gen_ipi {
uint8_t dst_shorthand : 2;
uint8_t dst;
+
+
+ int (*ack)(struct guest_info * core, uint32_t irq, void * private_data);
+ void * private_data;
} __attribute__((packed));
int v3_apic_send_ipi(struct v3_vm_info * vm, struct v3_gen_ipi * ipi, void * dev_data);
-int v3_apic_raise_intr(struct v3_vm_info * vm,
- uint32_t irq, uint32_t dst,
- void * dev_data);
#include <palacios/vmm_types.h>
#include <palacios/vmm_rbtree.h>
+#include <palacios/vmm_intr.h>
#include <devices/pci_types.h>
-
struct vm_device;
+typedef enum { PCI_CMD_DMA_DISABLE = 1,
+ PCI_CMD_DMA_ENABLE = 2,
+ PCI_CMD_INTX_DISABLE = 3,
+ PCI_CMD_INTX_ENABLE = 4,
+ PCI_CMD_MSI_DISABLE = 5,
+ PCI_CMD_MSI_ENABLE = 6,
+ PCI_CMD_MSIX_DISABLE = 7,
+ PCI_CMD_MSIX_ENABLE = 8 } pci_cmd_t;
+
typedef enum { PCI_BAR_IO,
PCI_BAR_MEM24,
PCI_BAR_MEM32,
#define PCI_MEM64_BASE_LO(bar_val) (bar_val & PCI_MEM64_MASK_LO)
#define PCI_EXP_ROM_BASE(rom_val) (rom_val & PCI_EXP_ROM_MASK)
+#define PCI_IO_BAR_VAL(addr) ((addr & PCI_IO_MASK) | 0x1)
+#define PCI_MEM24_BAR_VAL(addr, prefetch) (((addr & PCI_MEM24_MASK) | 0x2) | ((prefetch) != 0) << 3)
+#define PCI_MEM32_BAR_VAL(addr, prefetch) (((addr & PCI_MEM_MASK) | ((prefetch) != 0) << 3))
+#define PCI_MEM64_HI_BAR_VAL(addr, prefetch) (addr & PCI_MEM64_MASK_HI)
+#define PCI_MEM64_LO_BAR_VAL(addr, prefetch) ((((addr) & PCI_MEM64_MASK_LO) | 0x4) | ((prefetch) != 0) << 3)
+#define PCI_EXP_ROM_VAL(addr, enable) (((addr) & PCI_EXP_ROM_MASK) | ((enable) != 0))
+
struct pci_device {
char name[64];
- int (*config_update)(uint_t reg_num, void * src, uint_t length, void * priv_data);
-
- int (*cmd_update)(struct pci_device * pci_dev, uchar_t io_enabled, uchar_t mem_enabled);
+ int (*config_write)(struct pci_device * pci_dev, uint32_t reg_num, void * src,
+ uint_t length, void * priv_data);
+ int (*config_read)(struct pci_device * pci_dev, uint32_t reg_num, void * dst,
+ uint_t length, void * priv_data);
+ int (*cmd_update)(struct pci_device * pci_dev, pci_cmd_t cmd, uint64_t arg, void * priv_data);
int (*exp_rom_update)(struct pci_device * pci_dev, uint32_t * src, void * private_data);
- int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data);
- int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data);
+ struct v3_vm_info * vm;
+ struct list_head cfg_hooks;
+ struct list_head capabilities;
- int exp_rom_update_flag;
- int bar_update_flag;
+ struct msi_msg_ctrl * msi_cap;
+ struct msix_cap * msix_cap;
+ struct vm_device * apic_dev;
+
+ enum {IRQ_NONE, IRQ_INTX, IRQ_MSI, IRQ_MSIX} irq_type;
void * priv_data;
};
int v3_pci_set_irq_bridge(struct vm_device * pci_bus, int bus_num,
- int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data),
- int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data),
+ int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec),
+ int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec),
void * dev_data);
-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);
+/* Raising a PCI IRQ requires the specification of a vector index.
+ * If you are not sure, set vec_index to 0.
+ * For IntX IRQs, the index is the interrupt line the device is using (INTA=0, INTB=1, ...) - only used in multi-function devices
+ * For MSI and MSIX, the index is the vector index if multi-vectors are enabled
+ */
+
+int v3_pci_raise_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index);
+int v3_pci_lower_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index);
+
+int v3_pci_raise_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec);
+int v3_pci_lower_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec);
struct pci_device *
v3_pci_register_device(struct vm_device * pci,
int fn_num,
const char * name,
struct v3_pci_bar * bars,
- int (*config_update)(uint_t reg_num, void * src, uint_t length, void * private_data),
- int (*cmd_update)(struct pci_device *pci_dev, uchar_t io_enabled, uchar_t mem_enabled),
+ int (*config_write)(struct pci_device * pci_dev, uint32_t reg_num, void * src,
+ uint_t length, void * private_data),
+ int (*config_read)(struct pci_device * pci_dev, uint32_t reg_num, void * dst,
+ uint_t length, void * private_data),
+ int (*cmd_update)(struct pci_device *pci_dev, pci_cmd_t cmd, uint64_t arg, void * priv_data),
int (*exp_rom_update)(struct pci_device * pci_dev, uint32_t * src, void * private_data),
void * priv_data);
-struct pci_device *
-v3_pci_register_passthrough_device(struct vm_device * pci,
- int bus_num,
- int dev_num,
- int fn_num,
- const char * name,
- int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data),
- int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data),
- void * private_data);
+
+int v3_pci_hook_config_range(struct pci_device * pci,
+ uint32_t start, uint32_t length,
+ int (*write)(struct pci_device * pci_dev, uint32_t offset,
+ void * src, uint_t length, void * private_data),
+ int (*read)(struct pci_device * pci_dev, uint32_t offset,
+ void * src, uint_t length, void * private_data),
+ void * private_data);
+
+
+
+
+typedef enum { PCI_CAP_INVALID = 0,
+ PCI_CAP_PM = 0x1,
+ PCI_CAP_MSI = 0x5,
+ PCI_CAP_MSIX = 0x11,
+ PCI_CAP_PCIE = 0x10 } pci_cap_type_t;
+
+int v3_pci_enable_capability(struct pci_device * pci, pci_cap_type_t cap_type);
#endif
} __attribute__((packed));
+struct pci_cmd_reg {
+ union {
+ uint16_t val;
+ struct {
+ uint16_t io_enable : 1;
+ uint16_t mem_enable : 1;
+ uint16_t dma_enable : 1;
+ uint16_t special_cycles : 1;
+ uint16_t mem_wr_inv_enable : 1;
+ uint16_t vga_snoop : 1;
+ uint16_t parity_err_resp : 1;
+ uint16_t rsvd1 : 1;
+ uint16_t serr_enable : 1;
+ uint16_t fast_b2b_enable : 1;
+ uint16_t intx_disable : 1;
+ uint16_t rsvd2 : 5;
+ } __attribute__((packed));
+ } __attribute__((packed));
+
+} __attribute__((packed));
+
+
typedef enum { PCI_CLASS_PRE2 = 0x00,
PCI_CLASS_STORAGE = 0x01,
PCI_CLASS_NETWORK = 0x02,
};
*/
+
+
+struct msi_addr {
+ union {
+ uint32_t val;
+ struct {
+ uint32_t rsvd1 : 2;
+ uint32_t dst_mode : 1;
+ uint32_t redir_hint : 1;
+ uint32_t rsvd2 : 8;
+ uint32_t dst_id : 8;
+ uint32_t fixaddr : 12;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct msi_data {
+ union {
+ uint16_t val;
+ struct {
+ uint16_t vector : 8;
+ uint16_t del_mode : 3;
+ uint16_t rsvd1 : 3;
+ uint16_t level : 1;
+ uint16_t trig_mode : 1;
+ } __attribute__((packed));;
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct msi_msg_ctrl {
+ union {
+ uint16_t val;
+ struct {
+ uint16_t msi_enable : 1;
+ uint16_t mult_msg_capable : 3;
+ uint16_t mult_msg_enable : 3;
+ uint16_t cap_64bit : 1;
+ uint16_t per_vect_mask : 1;
+ uint16_t rsvd : 7;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct msi32_msg_addr {
+ struct msi_msg_ctrl msg_ctrl;
+ struct msi_addr addr;
+ struct msi_data data;
+} __attribute__((packed));
+
+
+struct msi64_msg_addr {
+ struct msi_msg_ctrl msg_ctrl;
+ struct msi_addr addr;
+ uint32_t hi_addr;
+ struct msi_data data;
+} __attribute__((packed));
+
+
+struct msi64_pervec_msg_addr {
+ struct msi_msg_ctrl msg_ctrl;
+ struct msi_addr addr;
+ uint32_t hi_addr;
+ struct msi_data data;
+ uint16_t rsvd;
+ uint32_t mask;
+ uint32_t pending;
+} __attribute__((packed));
+
+
+struct msix_msg_ctrl {
+ uint16_t table_size : 11;
+ uint16_t rsvd : 4;
+ uint16_t msix_enable : 1;
+} __attribute__((packed));
+
+struct msix_cap {
+ struct msix_msg_ctrl msg_ctrl;
+ uint32_t hi_addr;
+ uint32_t bir : 3;
+ uint32_t table_offset : 29;
+} __attribute__((packed));
+
+
+struct msix_table {
+ struct {
+ struct msi_data data;
+ struct msi_addr addr;
+ } __attribute__((packed)) entries[0];
+} __attribute__((packed));
+
+
+
+// See PCI power management specification (1.2)
+struct pmc_cap {
+ struct {
+ uint16_t version : 3;
+ uint16_t pme_clock : 1;
+ uint16_t rsvd1 : 1;
+ uint16_t dsi : 1;
+ uint16_t aux_current : 3;
+ uint16_t d1_support : 1;
+ uint16_t d2_support : 1;
+ uint16_t pme_support : 5;
+ } __attribute__((packed)) pmc;
+
+ struct {
+ uint16_t power_state : 2;
+ uint16_t rsvd1 : 1;
+ uint16_t no_soft_reset : 1;
+ uint16_t rsvd2 : 4;
+ uint16_t pme_en : 1;
+ uint16_t data_select : 4;
+ uint16_t data_scale : 2;
+ uint16_t pme_status : 1;
+ } __attribute__((packed)) pmcsr;
+
+ struct {
+ uint8_t rsvd1 : 6;
+ uint8_t b2_b3 : 1;
+ uint8_t bpcc_en : 1;
+ } __attribute__((packed)) pmcsr_bse;
+
+ uint8_t data;
+} __attribute__((packed));
+
+
+struct pcie_cap_reg {
+ uint16_t version : 4;
+ uint16_t type : 4;
+ uint16_t slot_impl : 1;
+ uint16_t irq_msg_num : 5;
+ uint16_t rsvd : 2;
+} __attribute__((packed));
+
+struct pcie_cap_v1 {
+ struct pcie_cap_reg pcie_cap;
+
+ union {
+ uint32_t val;
+ struct {
+ uint32_t max_payload : 3;
+ uint32_t phantom_fns : 2;
+ uint32_t ext_tag_support : 1;
+ uint32_t endpt_L0_latency : 3;
+ uint32_t endpt_L1_latency : 3;
+ uint32_t maybe_rsvd1 : 3;
+ uint32_t role_err_reporting : 1;
+ uint32_t rsvd2 : 2;
+ uint32_t slot_pwr_lim_val : 8;
+ uint32_t slot_pwr_lim_scale : 2;
+ uint32_t rsvd3 : 4;
+ } __attribute__((packed));
+ } __attribute__((packed)) dev_cap;
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t correctable_err_enable : 1;
+ uint16_t non_fatal_err_enable : 1;
+ uint16_t fatal_err_enable : 1;
+ uint16_t unsupp_req_enable : 1;
+ uint16_t relaxed_order_enable : 1;
+ uint16_t max_payload_size : 3;
+ uint16_t ext_tag_field_enable : 1;
+ uint16_t phantom_fn_enable : 1;
+ uint16_t aux_pwr_enable : 1;
+ uint16_t no_snoop_enable : 1;
+ uint16_t max_read_req_size : 3;
+ uint16_t bridge_cfg_retry_enable : 1;
+ } __attribute__((packed));
+ } __attribute__((packed)) dev_ctrl;
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t correctable_err : 1;
+ uint16_t non_fatal_err : 1;
+ uint16_t fatal_err : 1;
+ uint16_t unsupp_req : 1;
+ uint16_t aux_pwr : 1;
+ uint16_t transaction_pending : 1;
+ uint16_t rsvd : 10;
+ } __attribute__((packed));
+ } __attribute__((packed)) dev_status;
+
+ union {
+ uint32_t val;
+ struct {
+ uint32_t max_link_speed : 4;
+ uint32_t max_link_width : 6;
+ uint32_t aspm_support : 2; /* Active State Power Management Support */
+ uint32_t L0_exit_latency : 3;
+ uint32_t L1_exit_latency : 3;
+ uint32_t clk_pwr_mngmt : 1;
+ uint32_t surprise_pwr_down_capable : 1;
+ uint32_t data_link_active_capable : 1;
+ uint32_t rsvd1 : 3;
+ uint32_t port_number : 8;
+ } __attribute__((packed));
+ } __attribute__((packed)) link_cap;
+
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t aspm_ctrl : 2;
+ uint16_t rsvd1 : 1;
+ uint16_t rd_cmpl_bndry : 1;
+ uint16_t link_disable : 1;
+ uint16_t retrain_link : 1;
+ uint16_t common_clk_cfg : 1;
+ uint16_t ext_synch : 1;
+ uint16_t clk_pwr_mngmt_enable : 1;
+ uint16_t rsvd2 : 7;
+ } __attribute__((packed));
+ } __attribute__((packed)) link_ctrl;
+
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t link_speed : 4;
+ uint16_t negotiate_link_width : 6;
+ uint16_t undef : 1;
+ uint16_t link_training : 1;
+ uint16_t slot_clk_cfg : 1;
+ uint16_t data_link_layer_active : 1;
+ uint16_t rsvd : 2;
+ } __attribute__((packed));
+ } __attribute__((packed)) link_status;
+
+} __attribute__((packed));
+
+
+
+
+struct pcie_cap_v2 {
+ struct pcie_cap_reg pcie_cap;
+
+ union {
+ uint32_t val;
+ struct {
+ uint32_t max_payload : 3;
+ uint32_t phantom_fns : 2;
+ uint32_t ext_tag_support : 1;
+ uint32_t endpt_L0_latency : 3;
+ uint32_t endpt_L1_latency : 3;
+ uint32_t maybe_rsvd1 : 3;
+ uint32_t role_err_reporting : 1;
+ uint32_t rsvd2 : 2;
+ uint32_t slot_pwr_lim_val : 8;
+ uint32_t slot_pwr_lim_scale : 2;
+ uint32_t fn_level_reset : 1;
+ uint32_t rsvd3 : 3;
+ } __attribute__((packed));
+ } __attribute__((packed)) dev_cap;
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t correctable_err_enable : 1;
+ uint16_t non_fatal_err_enable : 1;
+ uint16_t fatal_err_enable : 1;
+ uint16_t unsupp_req_enable : 1;
+ uint16_t relaxed_order_enable : 1;
+ uint16_t max_payload_size : 3;
+ uint16_t ext_tag_field_enable : 1;
+ uint16_t phantom_fn_enable : 1;
+ uint16_t aux_pwr_enable : 1;
+ uint16_t no_snoop_enable : 1;
+ uint16_t max_read_req_size : 3;
+ uint16_t bridge_cfg_retry_enable : 1;
+ } __attribute__((packed));
+ } __attribute__((packed)) dev_ctrl;
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t correctable_err : 1;
+ uint16_t non_fatal_err : 1;
+ uint16_t fatal_err : 1;
+ uint16_t unsupp_req : 1;
+ uint16_t aux_pwr : 1;
+ uint16_t transaction_pending : 1;
+ uint16_t rsvd : 10;
+ } __attribute__((packed));
+ } __attribute__((packed)) dev_status;
+
+ union {
+ uint32_t val;
+ struct {
+ uint32_t max_link_speed : 4;
+ uint32_t max_link_width : 6;
+ uint32_t aspm_support : 2; /* Active State Power Management Support */
+ uint32_t L0_exit_latency : 3;
+ uint32_t L1_exit_latency : 3;
+ uint32_t clk_pwr_mngmt : 1;
+ uint32_t surprise_pwr_down_capable : 1;
+ uint32_t data_link_active_capable : 1;
+ uint32_t rsvd1 : 3;
+ uint32_t port_number : 8;
+ } __attribute__((packed));
+ } __attribute__((packed)) link_cap;
+
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t aspm_ctrl : 2;
+ uint16_t rsvd1 : 1;
+ uint16_t rd_cmpl_bndry : 1;
+ uint16_t link_disable : 1;
+ uint16_t retrain_link : 1;
+ uint16_t common_clk_cfg : 1;
+ uint16_t ext_synch : 1;
+ uint16_t clk_pwr_mngmt_enable : 1;
+ uint16_t rsvd2 : 7;
+ } __attribute__((packed));
+ } __attribute__((packed)) link_ctrl;
+
+
+ union {
+ uint16_t val;
+ struct {
+ uint16_t link_speed : 4;
+ uint16_t negotiate_link_width : 6;
+ uint16_t undef : 1;
+ uint16_t link_training : 1;
+ uint16_t slot_clk_cfg : 1;
+ uint16_t data_link_layer_active : 1;
+ uint16_t rsvd : 2;
+ } __attribute__((packed));
+ } __attribute__((packed)) link_status;
+
+
+ /* Some crap whose format we don't know because the PCI-SIG sucks */
+
+ uint32_t slot_cap;
+ uint16_t slot_ctrl;
+ uint16_t slot_status;
+
+ uint16_t root_ctrl;
+ uint16_t root_cap;
+ uint32_t root_status;
+
+} __attribute__((packed));
+
+
+
#endif
void * (*open)(const char * path, int mode, void * host_data);
int (*close)(void * fd);
- long long (*size)(void * fd);
+ unsigned long long (*size)(void * fd);
// blocking reads and writes
- long long (*read)(void * fd, void * buffer, long long length, long long offset);
- long long (*write)(void * fd, void * buffer, long long length, long long offset);
+ unsigned long long (*read)(void * fd, void * buffer, unsigned long long length, unsigned long long offset);
+ unsigned long long (*write)(void * fd, void * buffer, unsigned long long length, unsigned long long offset);
};
struct v3_interrupt;
+struct v3_irq {
+ uint32_t irq;
+
+ int (*ack)(struct guest_info * core, uint32_t irq, void * private_data);
+ void * private_data;
+};
+
+
struct v3_irq_hook {
int (*handler)(struct v3_vm_info * vm, struct v3_interrupt * intr, void * priv_data);
int v3_raise_irq(struct v3_vm_info * vm, int irq);
int v3_lower_irq(struct v3_vm_info * vm, int irq);
+/* The irq structure is passed by value to avoid confusion and
+ * the possibility that people will dynamically allocate memory for it
+ */
+int v3_raise_acked_irq(struct v3_vm_info * vm, struct v3_irq irq);
+int v3_lower_acked_irq(struct v3_vm_info * vm, struct v3_irq irq);
+
int v3_raise_swintr(struct guest_info * core, uint8_t vector);
};
struct intr_router_ops {
- int (*raise_intr)(struct v3_vm_info * vm, void * private_data, int irq);
- int (*lower_intr)(struct v3_vm_info * vm, void * private_data, int irq);
+ int (*raise_intr)(struct v3_vm_info * vm, void * private_data, struct v3_irq * irq);
+ int (*lower_intr)(struct v3_vm_info * vm, void * private_data, struct v3_irq * irq);
};
void v3_clear_pending_intr(struct guest_info * core);
struct guest_info * core;
+ struct {
+ int (*ack)(struct guest_info * core, uint32_t irq, void * private_data);
+ void * private_data;
+ } irq_ack_cbs[15];
+
void * router_handle;
void * controller_handle;
}
-static int pic_raise_intr(struct v3_vm_info * vm, void * private_data, int irq) {
+static int pic_raise_intr(struct v3_vm_info * vm, void * private_data, struct v3_irq * irq) {
struct pic_internal * state = (struct pic_internal*)private_data;
+ uint8_t irq_num = irq->irq;
- if (irq == 2) {
- irq = 9;
- state->master_irr |= 0x04; // PAD
+ if (irq_num == 2) {
+ irq_num = 9;
+ state->master_irr |= 0x04;
}
- PrintDebug("8259 PIC: Raising irq %d in the PIC\n", irq);
+ PrintDebug("8259 PIC: Raising irq %d in the PIC\n", irq_num);
- if (irq <= 7) {
- state->master_irr |= 0x01 << irq;
- } else if ((irq > 7) && (irq < 16)) {
- state->slave_irr |= 0x01 << (irq - 8); // PAD if -7 then irq 15=no irq
+ if (irq_num <= 7) {
+ state->master_irr |= 0x01 << irq_num;
+ } else if ((irq_num > 7) && (irq_num < 16)) {
+ state->slave_irr |= 0x01 << (irq_num - 8);
} else {
- PrintDebug("8259 PIC: Invalid IRQ raised (%d)\n", irq);
+ PrintDebug("8259 PIC: Invalid IRQ raised (%d)\n", irq_num);
return -1;
}
-#ifdef V3_CONFIG_MULTITHREAD_OS
- v3_interrupt_cpu(vm, 0, 0);
-#endif
+ state->irq_ack_cbs[irq_num].ack = irq->ack;
+ state->irq_ack_cbs[irq_num].private_data = irq->private_data;
+
+ if (V3_Get_CPU() != vm->cores[0].pcpu_id) {
+ // guest is running on another core, interrupt it to deliver irq
+ v3_interrupt_cpu(vm, 0, 0);
+ }
return 0;
}
-static int pic_lower_intr(struct v3_vm_info * vm, void * private_data, int irq) {
+static int pic_lower_intr(struct v3_vm_info * vm, void * private_data, struct v3_irq * irq) {
struct pic_internal * state = (struct pic_internal*)private_data;
+ uint8_t irq_num = irq->irq;
- PrintDebug("[pic_lower_intr] IRQ line %d now low\n", irq);
- if (irq <= 7) {
- state->master_irr &= ~(1 << irq);
+ PrintDebug("[pic_lower_intr] IRQ line %d now low\n", irq_num);
+ if (irq_num <= 7) {
+
+ state->master_irr &= ~(1 << irq_num);
if ((state->master_irr & ~(state->master_imr)) == 0) {
PrintDebug("\t\tFIXME: Master maybe should do sth\n");
}
- } else if ((irq > 7) && (irq < 16)) {
+ } else if ((irq_num > 7) && (irq_num < 16)) {
- state->slave_irr &= ~(1 << (irq - 8));
+ state->slave_irr &= ~(1 << (irq_num - 8));
if ((state->slave_irr & (~(state->slave_imr))) == 0) {
PrintDebug("\t\tFIXME: Slave maybe should do sth\n");
}
state->master_irr &= ~(0x1 << irq);
}
} else {
- PrintDebug("8259 PIC: (master) Ignoring begin_irq for %d since I don't own it\n",irq);
+ PrintDebug("8259 PIC: (master) Ignoring begin_irq for %d since I don't own it\n", irq);
}
} else {
state->slave_irr &= ~(0x1 << (irq - 8));
}
} else {
- PrintDebug("8259 PIC: (slave) Ignoring begin_irq for %d since I don't own it\n",irq);
+ PrintDebug("8259 PIC: (slave) Ignoring begin_irq for %d since I don't own it\n", irq);
}
-
}
+
+
return 0;
}
if ((cw2->EOI) && (!cw2->R) && (cw2->SL)) {
// specific EOI;
state->master_isr &= ~(0x01 << cw2->level);
+
+
+ /*
+ // ack the irq if requested
+ if (state->irq_ack_cbs[irq].ack) {
+ state->irq_ack_cbs[irq].ack(info, irq, state->irq_ack_cbs[irq].private_data);
+ }
+ */
+
} else if ((cw2->EOI) & (!cw2->R) && (!cw2->SL)) {
int i;
// Non-specific EOI
typedef enum { APIC_TMR_INT, APIC_THERM_INT, APIC_PERF_INT,
APIC_LINT0_INT, APIC_LINT1_INT, APIC_ERR_INT } apic_irq_type_t;
-#define APIC_FIXED_DELIVERY 0x0
-#define APIC_LOWEST_DELIVERY 0x1
-#define APIC_SMI_DELIVERY 0x2
-#define APIC_RES1_DELIVERY 0x3
-#define APIC_NMI_DELIVERY 0x4
-#define APIC_INIT_DELIVERY 0x5
-#define APIC_SIPI_DELIVERY 0x6
-#define APIC_EXTINT_DELIVERY 0x7
#define APIC_SHORTHAND_NONE 0x0
#define APIC_SHORTHAND_SELF 0x1
+
+struct irq_queue_entry {
+ uint32_t vector;
+ int (*ack)(struct guest_info * core, uint32_t irq, void * private_data);
+ void * private_data;
+
+ struct list_head list_node;
+};
+
+
+
+
typedef enum {INIT_ST,
SIPI,
STARTED} ipi_state_t;
uint8_t int_en_reg[32];
uint8_t trig_mode_reg[32];
+ struct {
+ int (*ack)(struct guest_info * core, uint32_t irq, void * private_data);
+ void * private_data;
+ } irq_ack_cbs[256];
+
struct guest_info * core;
void * controller_handle;
struct v3_timer * timer;
- struct v3_queue irq_queue;
+ struct {
+ v3_lock_t lock;
+
+ uint64_t num_entries;
+ struct list_head entries;
+ } irq_queue ;
uint32_t eoi;
apic->spec_eoi.val = 0x00000000;
- v3_init_queue(&(apic->irq_queue));
-
+ INIT_LIST_HEAD(&(apic->irq_queue.entries));
+ v3_lock_init(&(apic->irq_queue.lock));
+ apic->irq_queue.num_entries = 0;
}
// irq_num is the bit offset into a 256 bit buffer...
-static int activate_apic_irq(struct apic_state * apic, uint32_t irq_num) {
+static int activate_apic_irq(struct apic_state * apic, uint32_t irq_num,
+ int (*ack)(struct guest_info * core, uint32_t irq, void * private_data),
+ void * private_data) {
int major_offset = (irq_num & ~0x00000007) >> 3;
int minor_offset = irq_num & 0x00000007;
uint8_t * req_location = apic->int_req_reg + major_offset;
if (*en_location & flag) {
*req_location |= flag;
+ apic->irq_ack_cbs[irq_num].ack = ack;
+ apic->irq_ack_cbs[irq_num].private_data = private_data;
+
return 1;
} else {
PrintDebug("apic %u: core %d: Interrupt not enabled... %.2x\n",
}
-static int add_apic_irq_entry(struct apic_state * apic, uint8_t irq_num) {
+
+static int add_apic_irq_entry(struct apic_state * apic, uint32_t irq_num,
+ int (*ack)(struct guest_info * core, uint32_t irq, void * private_data),
+ void * private_data) {
+ unsigned int flags = 0;
+ struct irq_queue_entry * entry = NULL;
if (irq_num <= 15) {
PrintError("core %d: Attempting to raise an invalid interrupt: %d\n",
return -1;
}
- v3_enqueue(&(apic->irq_queue), (addr_t)irq_num);
+ entry = V3_Malloc(sizeof(struct irq_queue_entry));
+
+ if (entry == NULL) {
+ PrintError("Could not allocate irq queue entry\n");
+ return -1;
+ }
+
+ entry->vector = irq_num;
+ entry->ack = ack;
+ entry->private_data = private_data;
+
+ flags = v3_lock_irqsave(apic->irq_queue.lock);
+
+ list_add_tail(&(entry->list_node), &(apic->irq_queue.entries));
+ apic->irq_queue.num_entries++;
+
+ v3_unlock_irqrestore(apic->irq_queue.lock, flags);
+
return 0;
}
static void drain_irq_entries(struct apic_state * apic) {
- uint32_t irq = 0;
- while ((irq = (uint32_t)v3_dequeue(&(apic->irq_queue))) != 0) {
- activate_apic_irq(apic, irq);
+ while (1) {
+ unsigned int flags = 0;
+ struct irq_queue_entry * entry = NULL;
+
+ flags = v3_lock_irqsave(apic->irq_queue.lock);
+
+ if (!list_empty(&(apic->irq_queue.entries))) {
+ struct list_head * q_entry = apic->irq_queue.entries.next;
+ entry = list_entry(q_entry, struct irq_queue_entry, list_node);
+
+ apic->irq_queue.num_entries--;
+ list_del(q_entry);
+ }
+
+ v3_unlock_irqrestore(apic->irq_queue.lock, flags);
+
+ if (entry == NULL) {
+ break;
+ }
+
+ activate_apic_irq(apic, entry->vector, entry->ack, entry->private_data);
+
+ V3_Free(entry);
}
}
-static int apic_do_eoi(struct apic_state * apic) {
+static int apic_do_eoi(struct guest_info * core, struct apic_state * apic) {
int isr_irq = get_highest_isr(apic);
if (isr_irq != -1) {
*svc_location &= ~flag;
+ if (apic->irq_ack_cbs[isr_irq].ack) {
+ apic->irq_ack_cbs[isr_irq].ack(core, isr_irq, apic->irq_ack_cbs[isr_irq].private_data);
+ }
+
#ifdef V3_CONFIG_CRAY_XT
if ((isr_irq == 238) ||
switch (int_type) {
case APIC_TMR_INT:
vec_num = apic->tmr_vec_tbl.vec;
- del_mode = APIC_FIXED_DELIVERY;
+ del_mode = IPI_FIXED;
masked = apic->tmr_vec_tbl.mask;
break;
case APIC_THERM_INT:
break;
case APIC_ERR_INT:
vec_num = apic->err_vec_tbl.vec;
- del_mode = APIC_FIXED_DELIVERY;
+ del_mode = IPI_FIXED;
masked = apic->err_vec_tbl.mask;
break;
default:
return 0;
}
- if (del_mode == APIC_FIXED_DELIVERY) {
+ if (del_mode == IPI_FIXED) {
//PrintDebug("Activating internal APIC IRQ %d\n", vec_num);
- return add_apic_irq_entry(apic, vec_num);
+ return add_apic_irq_entry(apic, vec_num, NULL, NULL);
} else {
PrintError("apic %u: core ?: Unhandled Delivery Mode\n", apic->lapic_id.val);
return -1;
// Only the src_apic pointer is used
static int deliver_ipi(struct apic_state * src_apic,
struct apic_state * dst_apic,
- uint32_t vector, uint8_t del_mode) {
+ struct v3_gen_ipi * ipi) {
struct guest_info * dst_core = dst_apic->core;
- switch (del_mode) {
+ switch (ipi->mode) {
- case APIC_FIXED_DELIVERY:
- case APIC_LOWEST_DELIVERY: {
+ case IPI_FIXED:
+ case IPI_LOWEST_PRIO: {
// lowest priority -
// caller needs to have decided which apic to deliver to!
- PrintDebug("delivering IRQ %d to core %u\n", vector, dst_core->vcpu_id);
+ PrintDebug("delivering IRQ %d to core %u\n", ipi->vector, dst_core->vcpu_id);
- add_apic_irq_entry(dst_apic, vector);
+ add_apic_irq_entry(dst_apic, ipi->vector, ipi->ack, ipi->private_data);
-#ifdef V3_CONFIG_MULTITHREAD_OS
if (dst_apic != src_apic) {
PrintDebug(" non-local core with new interrupt, forcing it to exit now\n");
v3_interrupt_cpu(dst_core->vm_info, dst_core->pcpu_id, 0);
}
-#endif
-
break;
}
- case APIC_INIT_DELIVERY: {
+ case IPI_INIT: {
PrintDebug(" INIT delivery to core %u\n", dst_core->vcpu_id);
break;
}
- case APIC_SIPI_DELIVERY: {
+ case IPI_SIPI: {
// Sanity check
if (dst_apic->ipi_state != SIPI) {
break;
}
- v3_reset_vm_core(dst_core, vector);
+ v3_reset_vm_core(dst_core, ipi->vector);
PrintDebug(" SIPI delivery (0x%x -> 0x%x:0x0) to core %u\n",
- vector, dst_core->segments.cs.selector, dst_core->vcpu_id);
+ ipi->vector, dst_core->segments.cs.selector, dst_core->vcpu_id);
// Maybe need to adjust the APIC?
// We transition the target core to SIPI state
break;
}
- case APIC_EXTINT_DELIVERY: // EXTINT
+ case IPI_EXTINT: // EXTINT
/* Two possible things to do here:
* 1. Ignore the IPI and assume the 8259a (PIC) will handle it
* 2. Add 32 to the vector and inject it...
*/
return 0;
- case APIC_SMI_DELIVERY:
- case APIC_RES1_DELIVERY: // reserved
- case APIC_NMI_DELIVERY:
+ case IPI_SMI:
+ case IPI_RES1: // reserved
+ case IPI_NMI:
default:
- PrintError("IPI %d delivery is unsupported\n", del_mode);
+ PrintError("IPI %d delivery is unsupported\n", ipi->mode);
return -1;
}
static int route_ipi(struct apic_dev_state * apic_dev,
struct apic_state * src_apic,
- struct int_cmd_reg * icr) {
+ struct v3_gen_ipi * ipi) {
struct apic_state * dest_apic = NULL;
- PrintDebug("apic: IPI %s %u from apic %p to %s %s %u (icr=0x%llx)\n",
- deliverymode_str[icr->del_mode],
- icr->vec,
+ PrintDebug("apic: IPI %s %u from apic %p to %s %s %u\n",
+ deliverymode_str[ipi->mode],
+ ipi->vector,
src_apic,
- (icr->dst_mode == 0) ? "(physical)" : "(logical)",
- shorthand_str[icr->dst_shorthand],
- icr->dst,
- icr->val);
+ (ipi->logical == 0) ? "(physical)" : "(logical)",
+ shorthand_str[ipi->dst_shorthand],
+ ipi->dst);
- switch (icr->dst_shorthand) {
+ switch (ipi->dst_shorthand) {
case APIC_SHORTHAND_NONE: // no shorthand
- if (icr->dst_mode == APIC_DEST_PHYSICAL) {
+ if (ipi->logical == APIC_DEST_PHYSICAL) {
- dest_apic = find_physical_apic(apic_dev, icr->dst);
+ dest_apic = find_physical_apic(apic_dev, ipi->dst);
if (dest_apic == NULL) {
- PrintError("apic: Attempted send to unregistered apic id=%u\n", icr->dst);
+ PrintError("apic: Attempted send to unregistered apic id=%u\n", ipi->dst);
return -1;
}
- if (deliver_ipi(src_apic, dest_apic,
- icr->vec, icr->del_mode) == -1) {
+ if (deliver_ipi(src_apic, dest_apic, ipi) == -1) {
PrintError("apic: Could not deliver IPI\n");
return -1;
}
PrintDebug("apic: done\n");
- } else if (icr->dst_mode == APIC_DEST_LOGICAL) {
+ } else if (ipi->logical == APIC_DEST_LOGICAL) {
- if (icr->del_mode != APIC_LOWEST_DELIVERY) {
+ if (ipi->mode != IPI_LOWEST_PRIO) {
int i;
- uint8_t mda = icr->dst;
+ uint8_t mda = ipi->dst;
// logical, but not lowest priority
// we immediately trigger
return -1;
} else if (del_flag == 1) {
- if (deliver_ipi(src_apic, dest_apic,
- icr->vec, icr->del_mode) == -1) {
+ if (deliver_ipi(src_apic, dest_apic, ipi) == -1) {
PrintError("apic: Error: Could not deliver IPI\n");
return -1;
}
}
} else { // APIC_LOWEST_DELIVERY
struct apic_state * cur_best_apic = NULL;
- uint8_t mda = icr->dst;
+ uint8_t mda = ipi->dst;
int i;
// logical, lowest priority
v3_unlock_irqrestore(apic_dev->state_lock, flags);
- }
+ }
}
// now we will deliver to the best one if it exists
if (!cur_best_apic) {
PrintDebug("apic: lowest priority deliver, but no destinations!\n");
} else {
- if (deliver_ipi(src_apic, cur_best_apic,
- icr->vec, icr->del_mode) == -1) {
+ if (deliver_ipi(src_apic, cur_best_apic, ipi) == -1) {
PrintError("apic: Error: Could not deliver IPI\n");
return -1;
}
- if (icr->dst_mode == APIC_DEST_PHYSICAL) { /* physical delivery */
- if (deliver_ipi(src_apic, src_apic, icr->vec, icr->del_mode) == -1) {
+ if (ipi->logical == APIC_DEST_PHYSICAL) { /* physical delivery */
+ if (deliver_ipi(src_apic, src_apic, ipi) == -1) {
PrintError("apic: Could not deliver IPI to self (physical)\n");
return -1;
}
- } else if (icr->dst_mode == APIC_DEST_LOGICAL) { /* logical delivery */
+ } else if (ipi->logical == APIC_DEST_LOGICAL) { /* logical delivery */
PrintError("apic: use of logical delivery in self (untested)\n");
- if (deliver_ipi(src_apic, src_apic, icr->vec, icr->del_mode) == -1) {
+ if (deliver_ipi(src_apic, src_apic, ipi) == -1) {
PrintError("apic: Could not deliver IPI to self (logical)\n");
return -1;
}
for (i = 0; i < apic_dev->num_apics; i++) {
dest_apic = &(apic_dev->apics[i]);
- if ((dest_apic != src_apic) || (icr->dst_shorthand == APIC_SHORTHAND_ALL)) {
- if (deliver_ipi(src_apic, dest_apic, icr->vec, icr->del_mode) == -1) {
+ if ((dest_apic != src_apic) || (ipi->dst_shorthand == APIC_SHORTHAND_ALL)) {
+ if (deliver_ipi(src_apic, dest_apic, ipi) == -1) {
PrintError("apic: Error: Could not deliver IPI\n");
return -1;
}
break;
}
default:
- PrintError("apic: Error routing IPI, invalid Mode (%d)\n", icr->dst_shorthand);
+ PrintError("apic: Error routing IPI, invalid Mode (%d)\n", ipi->dst_shorthand);
return -1;
}
// Action Registers
case EOI_OFFSET:
// do eoi
- apic_do_eoi(apic);
+ apic_do_eoi(core, apic);
break;
case INT_CMD_LO_OFFSET: {
// execute command
- struct int_cmd_reg tmp_icr;
+ struct v3_gen_ipi tmp_ipi;
apic->int_cmd.lo = op_val;
- tmp_icr = apic->int_cmd;
+ tmp_ipi.vector = apic->int_cmd.vec;
+ tmp_ipi.mode = apic->int_cmd.del_mode;
+ tmp_ipi.logical = apic->int_cmd.dst_mode;
+ tmp_ipi.trigger_mode = apic->int_cmd.trig_mode;
+ tmp_ipi.dst_shorthand = apic->int_cmd.dst_shorthand;
+ tmp_ipi.dst = apic->int_cmd.dst;
+
+ tmp_ipi.ack = NULL;
+ tmp_ipi.private_data = NULL;
+
// V3_Print("apic %u: core %u: sending cmd 0x%llx to apic %u\n",
// apic->lapic_id.val, core->vcpu_id,
// apic->int_cmd.val, apic->int_cmd.dst);
- if (route_ipi(apic_dev, apic, &tmp_icr) == -1) {
+ if (route_ipi(apic_dev, apic, &tmp_ipi) == -1) {
PrintError("IPI Routing failure\n");
return -1;
}
int v3_apic_send_ipi(struct v3_vm_info * vm, struct v3_gen_ipi * ipi, void * dev_data) {
struct apic_dev_state * apic_dev = (struct apic_dev_state *)
(((struct vm_device *)dev_data)->private_data);
- struct int_cmd_reg tmp_icr;
-
- // zero out all the fields
- tmp_icr.val = 0;
- tmp_icr.vec = ipi->vector;
- tmp_icr.del_mode = ipi->mode;
- tmp_icr.dst_mode = ipi->logical;
- tmp_icr.trig_mode = ipi->trigger_mode;
- tmp_icr.dst_shorthand = ipi->dst_shorthand;
- tmp_icr.dst = ipi->dst;
-
-
- return route_ipi(apic_dev, NULL, &tmp_icr);
+ return route_ipi(apic_dev, NULL, ipi);
}
-int v3_apic_raise_intr(struct v3_vm_info * vm, uint32_t irq, uint32_t dst, void * dev_data) {
- struct apic_dev_state * apic_dev = (struct apic_dev_state *)
- (((struct vm_device*)dev_data)->private_data);
- struct apic_state * apic = &(apic_dev->apics[dst]);
-
- PrintDebug("apic %u core ?: raising interrupt IRQ %u (dst = %u).\n", apic->lapic_id.val, irq, dst);
-
- add_apic_irq_entry(apic, irq);
-
-#ifdef V3_CONFIG_MULTITHREAD_OS
- if ((V3_Get_CPU() != dst)) {
- v3_interrupt_cpu(vm, dst, 0);
- }
-#endif
-
- return 0;
-}
-
static int apic_begin_irq(struct guest_info * core, void * private_data, int irq) {
// Not sure if STD
pci_dev = v3_pci_register_device(video_state->pci_bus, PCI_STD_DEVICE, 0,
//or0 1st null could be pci_config_update
- -1, 0, "CIRRUS_GFX_CARD", bars, NULL, NULL,
- NULL, dev);
+ -1, 0, "CIRRUS_GFX_CARD", bars, NULL, NULL,
+ NULL, NULL, dev);
if (pci_dev == NULL) {
PrintError("Failed to register VIDEO %d with PCI\n", i);
-static int write_all(v3_file_t fd, char * buf, int offset, int length) {
- int bytes_written = 0;
+static int write_all(v3_file_t fd, char * buf, uint64_t offset, uint64_t length) {
+ uint64_t bytes_written = 0;
- PrintDebug("Writing %d bytes\n", length - bytes_written);
+ PrintDebug("Writing %llu bytes\n", length - bytes_written);
while (bytes_written < length) {
int tmp_bytes = v3_file_write(fd, buf + bytes_written, length - bytes_written, offset + bytes_written);
PrintDebug("Wrote %d bytes\n", tmp_bytes);
}
-static int read_all(v3_file_t fd, char * buf, int offset, int length) {
- int bytes_read = 0;
+static int read_all(v3_file_t fd, char * buf, uint64_t offset, uint64_t length) {
+ uint64_t bytes_read = 0;
- PrintDebug("Reading %d bytes\n", length - bytes_read);
+ PrintDebug("Reading %llu bytes\n", length - bytes_read);
while (bytes_read < length) {
int tmp_bytes = v3_file_read(fd, buf + bytes_read, length - bytes_read, offset + bytes_read);
PrintDebug("Read %d bytes\n", tmp_bytes);
static int read(uint8_t * buf, uint64_t lba, uint64_t num_bytes, void * private_data) {
struct disk_state * disk = (struct disk_state *)private_data;
- PrintDebug("Reading %d bytes from %p to %p\n", (uint32_t)num_bytes, (uint8_t *)(disk->disk_image + lba), buf);
+ PrintDebug("Reading %llu bytes from %p to %p\n", num_bytes, (uint8_t *)(disk->disk_image + lba), buf);
if (lba + num_bytes > disk->capacity) {
PrintError("Out of bounds read: lba=%llu, num_bytes=%llu, capacity=%llu\n",
static int write(uint8_t * buf, uint64_t lba, uint64_t num_bytes, void * private_data) {
struct disk_state * disk = (struct disk_state *)private_data;
- PrintDebug("Writing %d bytes from %p to %p\n", (uint32_t)num_bytes, buf, (uint8_t *)(disk->disk_image + lba));
+ PrintDebug("Writing %llu bytes from %p to %p\n", num_bytes, buf, (uint8_t *)(disk->disk_image + lba));
if (lba + num_bytes > disk->capacity) {
PrintError("Out of bounds read: lba=%llu, num_bytes=%llu, capacity=%llu\n",
static uint64_t get_capacity(void * private_data) {
struct disk_state * disk = (struct disk_state *)private_data;
- PrintDebug("Querying FILEDISK capacity %d\n",
- (uint32_t)(disk->capacity));
+ PrintDebug("Querying FILEDISK capacity %llu\n", disk->capacity);
return disk->capacity;
}
disk->capacity = v3_file_size(disk->fd);
- PrintDebug("Registering FILEDISK %s (path=%s, fd=%lu, size=%lu)\n",
- dev_id, path, file->fd, file->capacity);
+ V3_Print("Registering FILEDISK %s (path=%s, fd=%lu, size=%llu)\n",
+ dev_id, path, (addr_t)disk->fd, disk->capacity);
if (v3_dev_connect_blk(vm, v3_cfg_val(frontend_cfg, "tag"),
pci_dev = v3_pci_register_device(state->pci, PCI_STD_DEVICE,
0, 0, 0, "i440FX", bars,
- NULL, NULL, NULL, state);
+ NULL, NULL, NULL, NULL, state);
if (!pci_dev) {
v3_remove_device(dev);
// Control Registers
struct ide_ctrl_reg ctrl_reg; // [write] 0x3f6,0x376
- struct ide_dma_cmd_reg dma_cmd;
- struct ide_dma_status_reg dma_status;
- uint32_t dma_prd_addr;
+ union {
+ uint8_t dma_ports[8];
+ struct {
+ struct ide_dma_cmd_reg dma_cmd;
+ uint8_t rsvd1;
+ struct ide_dma_status_reg dma_status;
+ uint8_t rsvd2;
+ uint32_t dma_prd_addr;
+ } __attribute__((packed));
+ } __attribute__((packed));
+
uint32_t dma_tbl_index;
};
/* Drive Commands */
static void ide_raise_irq(struct ide_internal * ide, struct ide_channel * channel) {
if (channel->ctrl_reg.irq_disable == 0) {
- // PrintError("Raising IDE Interrupt %d\n", channel->irq);
+
+ //PrintError("Raising IDE Interrupt %d\n", channel->irq);
+
channel->dma_status.int_gen = 1;
v3_raise_irq(ide->vm, channel->irq);
}
#include "ata.h"
-#ifdef V3_CONFIG_DEBUG_IDE
+
static void print_prd_table(struct ide_internal * ide, struct ide_channel * channel) {
struct ide_dma_prd prd_entry;
int index = 0;
- PrintDebug("Dumping PRD table\n");
+ V3_Print("Dumping PRD table\n");
while (1) {
uint32_t prd_entry_addr = channel->dma_prd_addr + (sizeof(struct ide_dma_prd) * index);
return;
}
- PrintDebug("\tPRD Addr: %x, PRD Len: %d, EOT: %d\n",
+ V3_Print("\tPRD Addr: %x, PRD Len: %d, EOT: %d\n",
prd_entry.base_addr,
(prd_entry.size == 0) ? 0x10000 : prd_entry.size,
prd_entry.end_of_table);
return;
}
-#endif
+
/* IO Operations */
static int dma_read(struct guest_info * core, struct ide_internal * ide, struct ide_channel * channel) {
}
} else {
/*
- PrintError("DMA of command packet\n");
PrintError("How does this work (ATAPI CMD=%x)???\n", drive->cd_state.atapi_cmd);
return -1;
*/
int cmd_ret = 0;
+ //V3_Print("DMA of command packet\n");
+
bytes_to_write = (prd_bytes_left > bytes_left) ? bytes_left : prd_bytes_left;
prd_bytes_left = bytes_to_write;
+
+ // V3_Print("Writing ATAPI cmd OP DMA (cmd=%x) (len=%d)\n", drive->cd_state.atapi_cmd, prd_bytes_left);
cmd_ret = v3_write_gpa_memory(core, prd_entry.base_addr + prd_offset,
bytes_to_write, drive->data_buf);
PrintDebug("PRD Addr: %x, PRD Len: %d, EOT: %d\n",
prd_entry.base_addr, prd_entry.size, prd_entry.end_of_table);
- prd_bytes_left = prd_entry.size;
+
+ if (prd_entry.size == 0) {
+ // a size of 0 means 64k
+ prd_bytes_left = 0x10000;
+ } else {
+ prd_bytes_left = prd_entry.size;
+ }
while (prd_bytes_left > 0) {
uint_t bytes_to_write = 0;
if ((prd_entry.end_of_table == 1) && (bytes_left > 0)) {
PrintError("DMA table not large enough for data transfer...\n");
+ PrintError("\t(bytes_left=%u) (transfer_length=%u)...\n",
+ bytes_left, drive->transfer_length);
+ PrintError("PRD Addr: %x, PRD Len: %d, EOT: %d\n",
+ prd_entry.base_addr, prd_entry.size, prd_entry.end_of_table);
+
+ print_prd_table(ide, channel);
return -1;
}
}
}
-static int read_dma_port(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * private_data) {
+static int read_dma_port(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * private_data) {
struct ide_internal * ide = (struct ide_internal *)private_data;
uint16_t port_offset = port & (DMA_CHANNEL_FLAG - 1);
uint_t channel_flag = (port & DMA_CHANNEL_FLAG) >> 3;
PrintDebug("Reading DMA port %d (%x) (channel=%d)\n", port, port, channel_flag);
- switch (port_offset) {
- case DMA_CMD_PORT:
- *(uint8_t *)dst = channel->dma_cmd.val;
- break;
-
- case DMA_STATUS_PORT:
- if (length != 1) {
- PrintError("Invalid read length for DMA status port\n");
- return -1;
- }
-
- *(uint8_t *)dst = channel->dma_status.val;
- break;
-
- case DMA_PRD_PORT0:
- case DMA_PRD_PORT1:
- case DMA_PRD_PORT2:
- case DMA_PRD_PORT3: {
- uint_t addr_index = port_offset & 0x3;
- uint8_t * addr_buf = (uint8_t *)&(channel->dma_prd_addr);
- int i = 0;
-
- if (addr_index + length > 4) {
- PrintError("DMA Port space overrun port=%x len=%d\n", port_offset, length);
- return -1;
- }
-
- for (i = 0; i < length; i++) {
- *((uint8_t *)dst + i) = addr_buf[addr_index + i];
- }
-
- break;
- }
- default:
- PrintError("IDE: Invalid DMA Port (%d) (%s)\n", port, dma_port_to_str(port_offset));
- break;
+ if (port_offset + length > 16) {
+ PrintError("DMA Port Read: Port overrun (port_offset=%d, length=%d)\n", port_offset, length);
+ return -1;
}
+ memcpy(dst, channel->dma_ports + port_offset, length);
+
PrintDebug("\tval=%x (len=%d)\n", *(uint32_t *)dst, length);
return length;
channel->cmd_reg = 0x00;
channel->ctrl_reg.val = 0x08;
-
channel->dma_cmd.val = 0;
channel->dma_status.val = 0;
channel->dma_prd_addr = 0;
}
-static int pci_config_update(uint_t reg_num, void * src, uint_t length, void * private_data) {
+static int pci_config_update(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) {
PrintDebug("PCI Config Update\n");
/*
struct ide_internal * ide = (struct ide_internal *)(private_data);
pci_dev = v3_pci_register_device(ide->pci_bus, PCI_STD_DEVICE, 0, sb_pci->dev_num, 1,
"PIIX3_IDE", bars,
- pci_config_update, NULL, NULL, ide);
+ pci_config_update, NULL, NULL, NULL, ide);
if (pci_dev == NULL) {
PrintError("Failed to register IDE BUS %d with PCI\n", i);
}
-static int ioapic_raise_irq(struct v3_vm_info * vm, void * private_data, int irq) {
+static int ioapic_raise_irq(struct v3_vm_info * vm, void * private_data, struct v3_irq * irq) {
struct io_apic_state * ioapic = (struct io_apic_state *)(private_data);
struct redir_tbl_entry * irq_entry = NULL;
+ uint8_t irq_num = irq->irq;
- if (irq==0) {
+ if (irq_num == 0) {
// IRQ 0 being raised, in the Palacios context, means the PIT
// However, the convention is that it is the PIC that is connected
// to PIN 0 of the IOAPIC and the PIT is connected to pin 2
// the PIC may signal to the IOAPIC in a different path.
// Yes, this is kind of hideous, but it is needed to have the
// PIT correctly show up via the IOAPIC
- irq=2;
+ irq_num = 2;
}
- if (irq > 24) {
+ if (irq_num > 24) {
PrintDebug("ioapic %u: IRQ out of range of IO APIC\n", ioapic->ioapic_id.id);
return -1;
}
- irq_entry = &(ioapic->redir_tbl[irq]);
+ irq_entry = &(ioapic->redir_tbl[irq_num]);
if (irq_entry->mask == 0) {
struct v3_gen_ipi ipi;
ipi.dst = irq_entry->dst_field;
ipi.dst_shorthand = 0;
+ ipi.ack = irq->ack;
+ ipi.private_data = irq->private_data;
PrintDebug("ioapic %u: IPI: vector 0x%x, mode 0x%x, logical 0x%x, trigger 0x%x, dst 0x%x, shorthand 0x%x\n",
ioapic->ioapic_id.id, ipi.vector, ipi.mode, ipi.logical, ipi.trigger_mode, ipi.dst, ipi.dst_shorthand);
}
/* I don't know if we can do anything here.... */
-static int ioapic_lower_irq(struct v3_vm_info * vm, void * private_data, int irq) {
+static int ioapic_lower_irq(struct v3_vm_info * vm, void * private_data, struct v3_irq * irq) {
return 0;
}
// after having a f0 sent to 60
// we wait for a new output byte on 60
GETSET_SCANCODES,
+ // first send ACK (0xfa)
+ // then wait for reception, and reset kb state
+ SET_DEFAULTS,
} state;
};
+static int keyboard_reset_device(struct keyboard_internal * kbd);
+
+
static int update_kb_irq(struct keyboard_internal * state) {
int irq_num = 0;
kbd->state = NORMAL;
break;
+ case SET_DEFAULTS:
+ keyboard_reset_device(kbd);
+ kbd->state = NORMAL;
+ break;
+
default:
case NORMAL: {
// command is being sent to keyboard controller
kbd->state = GETSET_SCANCODES;
break;
+
+ case 0xf6: // set defaults
+ // ACK command
+ // clear output buffer
+ // reset to init state
+ push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
+ kbd->state = SET_DEFAULTS;
+ break;
+
case 0xfe: // resend
case 0xfd: // set key type make
case 0xfc: // set key typ make/break
case 0xf9: // set all make
case 0xf8: // set all make/break
case 0xf7: // set all typemaktic
- case 0xf6: // set defaults
+
PrintError("keyboard: unhandled known command 0x%x on output buffer (60h)\n", data);
ret = -1;
break;
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("Raising IRQ %d\n", virtio->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
}
case VIRTIO_ISR_PORT:
*(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
virtio->virtio_cfg.pci_isr = 0;
- v3_pci_lower_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ v3_pci_lower_irq(virtio->pci_bus, virtio->pci_dev, 0);
break;
default:
PrintDebug("Requesting %d pages\n", virtio->balloon_cfg.requested_pages);
- v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE | VIRTIO_ISR_CFG_CHANGED;
return 0;
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_BALLOON", bars,
- NULL, NULL, NULL, virtio_state);
+ NULL, NULL, NULL, NULL, virtio_state);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("Raising IRQ %d\n", blk_state->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(blk_state->virtio_dev->pci_bus, 0, blk_state->pci_dev);
+ v3_pci_raise_irq(blk_state->virtio_dev->pci_bus, blk_state->pci_dev, 0);
blk_state->virtio_cfg.pci_isr = 1;
}
case VIRTIO_ISR_PORT:
*(uint8_t *)dst = blk_state->virtio_cfg.pci_isr;
blk_state->virtio_cfg.pci_isr = 0;
- v3_pci_lower_irq(blk_state->virtio_dev->pci_bus, 0, blk_state->pci_dev);
+ v3_pci_lower_irq(blk_state->virtio_dev->pci_bus, blk_state->pci_dev, 0);
break;
default:
pci_dev = v3_pci_register_device(virtio->pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_BLK", bars,
- NULL, NULL, NULL, blk_state);
+ NULL, NULL, NULL, NULL, blk_state);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("Raising IRQ %d\n", virtio->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
}
// say hello
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
- v3_pci_raise_irq(cons_state->pci_bus, 0, cons_state->pci_dev);
+ v3_pci_raise_irq(cons_state->pci_bus, cons_state->pci_dev, 0);
cons_state->virtio_cfg.pci_isr = 0x1;
}
case VIRTIO_ISR_PORT:
*(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
virtio->virtio_cfg.pci_isr = 0;
- v3_pci_lower_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ v3_pci_lower_irq(virtio->pci_bus, virtio->pci_dev, 0);
break;
default:
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_CONSOLE", bars,
- NULL, NULL, NULL, virtio_state);
+ NULL, NULL, NULL, NULL, virtio_state);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
struct virtio_net_state * virtio_state,
int quote)
{
- struct virtio_queue *q = &(virtio_state->tx_vq);
+ struct virtio_queue * q;
int txed = 0, left = 0;
unsigned long flags;
+ q = &(virtio_state->tx_vq);
if (!q->ring_avail_addr) {
return -1;
}
- flags = v3_lock_irqsave(virtio_state->tx_lock);
- while (q->cur_avail_idx != q->avail->index) {
- struct virtio_net_hdr_mrg_rxbuf * hdr = NULL;
+ while (1) {
struct vring_desc * hdr_desc = NULL;
addr_t hdr_addr = 0;
- uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
- int desc_cnt = get_desc_count(q, desc_idx);
+ uint16_t desc_idx, tmp_idx;
+ int desc_cnt;
+
+ flags = v3_lock_irqsave(virtio_state->tx_lock);
+
+ if(q->cur_avail_idx == q->avail->index ||
+ (quote > 0 && txed >= quote)) {
+ left = (q->cur_avail_idx != q->avail->index);
+ break;
+ }
+
+ desc_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
+ tmp_idx = q->cur_avail_idx ++;
+
+ v3_unlock_irqrestore(virtio_state->tx_lock, flags);
+ desc_cnt = get_desc_count(q, desc_idx);
if(desc_cnt != 2){
PrintError("VNIC: merged rx buffer not supported, desc_cnt %d\n", desc_cnt);
- goto exit_error;
}
hdr_desc = &(q->desc[desc_idx]);
- if (v3_gpa_to_hva(core, hdr_desc->addr_gpa, &(hdr_addr)) == -1) {
- PrintError("Could not translate block header address\n");
- goto exit_error;
- }
+ if (v3_gpa_to_hva(core, hdr_desc->addr_gpa, &(hdr_addr)) != -1) {
+ struct virtio_net_hdr_mrg_rxbuf * hdr;
+ struct vring_desc * buf_desc;
- hdr = (struct virtio_net_hdr_mrg_rxbuf *)hdr_addr;
- desc_idx = hdr_desc->next;
+ hdr = (struct virtio_net_hdr_mrg_rxbuf *)hdr_addr;
+ desc_idx = hdr_desc->next;
- V3_Net_Print(2, "Virtio NIC: TX hdr count : %d\n", hdr->num_buffers);
-
- /* here we assumed that one ethernet pkt is not splitted into multiple buffer */
- struct vring_desc * buf_desc = &(q->desc[desc_idx]);
- if (tx_one_pkt(core, virtio_state, buf_desc) == -1) {
- PrintError("Virtio NIC: Fails to send packet\n");
- }
- if(buf_desc->next & VIRTIO_NEXT_FLAG){
- PrintError("Virtio NIC: TX more buffer need to read\n");
+ /* here we assumed that one ethernet pkt is not splitted into multiple buffer */
+ buf_desc = &(q->desc[desc_idx]);
+ if (tx_one_pkt(core, virtio_state, buf_desc) == -1) {
+ PrintError("Virtio NIC: Fails to send packet\n");
+ }
+ } else {
+ PrintError("Could not translate block header address\n");
}
+
+ flags = v3_lock_irqsave(virtio_state->tx_lock);
q->used->ring[q->used->index % q->queue_size].id =
- q->avail->ring[q->cur_avail_idx % q->queue_size];
+ q->avail->ring[tmp_idx % q->queue_size];
- q->used->ring[q->used->index % q->queue_size].length =
- buf_desc->length; /* What do we set this to???? */
+ //q->used->ring[q->used->index % q->queue_size].length = buf_desc->length; /* What do we set this to???? */
q->used->index ++;
- q->cur_avail_idx ++;
- if(++txed >= quote && quote > 0){
- left = (q->cur_avail_idx != q->avail->index);
- break;
- }
+ v3_unlock_irqrestore(virtio_state->tx_lock, flags);
+
+ txed ++;
}
-
- v3_unlock_irqrestore(virtio_state->tx_lock, flags);
-
+
if (txed && !(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
v3_pci_raise_irq(virtio_state->virtio_dev->pci_bus,
- 0, virtio_state->pci_dev);
+ virtio_state->pci_dev, 0);
virtio_state->virtio_cfg.pci_isr = 0x1;
virtio_state->stats.rx_interrupts ++;
}
- if(txed > 0) {
- V3_Net_Print(2, "Virtio Handle TX: txed pkts: %d, left %d\n", txed, left);
- }
-
return left;
-
- exit_error:
-
- v3_unlock_irqrestore(virtio_state->tx_lock, flags);
- return -1;
}
*(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
virtio->virtio_cfg.pci_isr = 0;
v3_pci_lower_irq(virtio->virtio_dev->pci_bus,
- 0, virtio->pci_dev);
+ virtio->pci_dev, 0);
break;
case VIRTIO_NET_CONFIG ... VIRTIO_NET_CONFIG + ETH_ALEN:
virtio->pci_dev->config_header.intr_line);
virtio->virtio_cfg.pci_isr = 0x1;
- v3_pci_raise_irq(virtio->virtio_dev->pci_bus, 0, virtio->pci_dev);
+ v3_pci_raise_irq(virtio->virtio_dev->pci_bus, virtio->pci_dev, 0);
virtio->stats.rx_interrupts ++;
}
pci_dev = v3_pci_register_device(virtio->pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_NIC", bars,
- NULL, NULL, NULL, net_state);
+ NULL, NULL, NULL, NULL, net_state);
if (!pci_dev) {
PrintError("Virtio NIC: Could not register PCI Device\n");
return 0;
}
+#if 0
#define RATE_UPPER_THRESHOLD 10 /* 10000 pkts per second, around 100Mbits */
#define RATE_LOWER_THRESHOLD 1
#define PROFILE_PERIOD 10000 /*us*/
static struct v3_timer_ops timer_ops = {
.update_timer = virtio_nic_timer,
};
-
+#endif
static int connect_fn(struct v3_vm_info * info,
void * frontend_data,
net_state->tx_notify = 1;
net_state->rx_notify = 1;
- net_state->timer = v3_add_timer(&(info->cores[0]),
- &timer_ops,net_state);
+ //net_state->timer = v3_add_timer(&(info->cores[0]), &timer_ops,net_state);
ops->recv = virtio_rx;
ops->poll = virtio_poll;
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("Raising IRQ %d\n", sym_state->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+ v3_pci_raise_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
sym_state->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
}
case VIRTIO_ISR_PORT:
*(uint8_t *)dst = sym_state->virtio_cfg.pci_isr;
sym_state->virtio_cfg.pci_isr = 0;
- v3_pci_lower_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+ v3_pci_lower_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
break;
default:
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_SYM", bars,
- NULL, NULL, NULL, virtio_state);
+ NULL, NULL, NULL, NULL, virtio_state);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("Raising IRQ %d\n", sym_state->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+ v3_pci_raise_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
sym_state->virtio_cfg.pci_isr = 1;
}
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("Raising IRQ %d\n", sym_state->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+ v3_pci_raise_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
sym_state->virtio_cfg.pci_isr = 1;
}
case VIRTIO_ISR_PORT:
*(uint8_t *)dst = sym_state->virtio_cfg.pci_isr;
sym_state->virtio_cfg.pci_isr = 0;
- v3_pci_lower_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+ v3_pci_lower_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
break;
default:
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("SYMMOD: Raising IRQ %d\n", virtio->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
virtio->virtio_cfg.pci_isr = 0x1;
}
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, PCI_AUTO_DEV_NUM, 0,
"LNX_VIRTIO_SYMMOD", bars,
- NULL, NULL, NULL, virtio_state);
+ NULL, NULL, NULL, NULL, virtio_state);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
PrintDebug("Raising IRQ %d\n", vnet_state->pci_dev->config_header.intr_line);
- v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
+ v3_pci_raise_irq(vnet_state->pci_bus, vnet_state->pci_dev, 0);
vnet_state->virtio_cfg.pci_isr = 1;
}
}
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
- v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
+ v3_pci_raise_irq(vnet_state->pci_bus, vnet_state->pci_dev, 0);
vnet_state->virtio_cfg.pci_isr = 0x1;
PrintDebug("Raising IRQ %d\n", vnet_state->pci_dev->config_header.intr_line);
}
}
if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
- v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
- vnet_state->virtio_cfg.pci_isr = 0x1;
+ v3_pci_raise_irq(vnet_state->pci_bus, vnet_state->pci_dev, 0);
+ vnet_state->virtio_cfg.pci_isr = 0x1;
}
return 0;
case VIRTIO_ISR_PORT:
*(uint8_t *)dst = vnet_state->virtio_cfg.pci_isr;
vnet_state->virtio_cfg.pci_isr = 0;
- v3_pci_lower_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
+ v3_pci_lower_irq(vnet_state->pci_bus, vnet_state->pci_dev, 0);
break;
default:
pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE,
0, 5 /*PCI_AUTO_DEV_NUM*/, 0,
"LNX_VIRTIO_VNET", bars,
- NULL, NULL, NULL, vnet_state);
+ NULL, NULL, NULL, NULL, vnet_state);
if (!pci_dev) {
PrintError("Could not register PCI Device\n");
if (pci_dev == NULL){
v3_raise_virq(&(nic_state->vm->cores[0]), NE2K_DEFAULT_IRQ);
} else {
- v3_pci_raise_irq(nic_state->pci_bus, 0, nic_state->pci_dev);
+ v3_pci_raise_irq(nic_state->pci_bus, nic_state->pci_dev, 0);
}
nic_state->statistics.rx_interrupts ++;
pci_dev = v3_pci_register_device(nic_state->pci_bus, PCI_STD_DEVICE, 0, -1, 0,
"NE2000", bars,
- pci_config_update, NULL, NULL, nic_state);
+ pci_config_update, NULL, NULL, NULL, nic_state);
if (pci_dev == NULL) {
#include <devices/pci_types.h>
#include <palacios/vm_guest.h>
+#include <palacios/vm_guest_mem.h>
+#include <devices/apic.h>
+
#ifndef V3_CONFIG_DEBUG_PCI
#undef PrintDebug
// This must always be a multiple of 8
#define MAX_BUS_DEVICES 32
+#define PCI_CAP_ID_MSI 0x05
+#define PCI_CAP_ID_MSIX 0x11
+
+
struct pci_addr_reg {
union {
uint32_t val;
uint_t fn_num : 3;
uint_t dev_num : 5;
uint_t bus_num : 8;
- uint_t rsvd2 : 7;
+ uint_t hi_reg_num : 4;
+ uint_t rsvd2 : 3;
uint_t enable : 1;
} __attribute__((packed));
} __attribute__((packed));
uint8_t dev_map[MAX_BUS_DEVICES / 8];
- int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data);
- int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data);
+ int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec);
+ int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec);
void * irq_dev_data;
};
+struct cfg_range_hook {
+ uint32_t start;
+ uint32_t length;
+
+ int (*write)(struct pci_device * pci_dev, uint32_t offset,
+ void * src, uint_t length, void * private_data);
+
+ int (*read)(struct pci_device * pci_dev, uint32_t offset,
+ void * dst, uint_t length, void * private_data);
+
+ void * private_data;
+
+ struct list_head list_node;
+};
+
+
+
+struct pci_cap {
+ uint8_t id;
+ uint32_t offset;
+ uint8_t enabled;
+
+ struct list_head cap_node;
+};
+
+
+// These mark read only fields in the pci config header.
+// If a bit is 1, then the field is writable in the header
+/* Notes:
+ * BIST is disabled by default (All writes to it will be dropped
+ * Cardbus CIS is disabled (All writes are dropped)
+ * Writes to capability pointer are disabled
+ */
+static uint8_t pci_hdr_write_mask_00[64] = { 0x00, 0x00, 0x00, 0x00, /* Device ID, Vendor ID */
+ 0xbf, 0xff, 0x00, 0xf9, /* Command, status */
+ 0x00, 0x00, 0x00, 0x00, /* Revision ID, Class code */
+ 0x00, 0xff, 0x00, 0x00, /* CacheLine Size, Latency Timer, Header Type, BIST */
+ 0xff, 0xff, 0xff, 0xff, /* BAR 0 */
+ 0xff, 0xff, 0xff, 0xff, /* BAR 1 */
+ 0xff, 0xff, 0xff, 0xff, /* BAR 2 */
+ 0xff, 0xff, 0xff, 0xff, /* BAR 3 */
+ 0xff, 0xff, 0xff, 0xff, /* BAR 4 */
+ 0xff, 0xff, 0xff, 0xff, /* BAR 5 */
+ 0x00, 0x00, 0x00, 0x00, /* CardBus CIS Ptr */
+ 0xff, 0xff, 0xff, 0xff, /* SubSystem Vendor ID, SubSystem ID */
+ 0xff, 0xff, 0xff, 0xff, /* ExpRom BAR */
+ 0x00, 0x00, 0x00, 0x00, /* CAP ptr (0xfc to enable), RSVD */
+ 0x00, 0x00, 0x00, 0x00, /* Reserved */
+ 0xff, 0x00, 0x00, 0x00 /* INTR Line, INTR Pin, MIN_GNT, MAX_LAT */
+};
+
+
#ifdef V3_CONFIG_DEBUG_PCI
+// There won't be many hooks at all, so unordered lists are acceptible for now
+static struct cfg_range_hook * find_cfg_range_hook(struct pci_device * pci, uint32_t start, uint32_t length) {
+ uint32_t end = start + length - 1; // end is inclusive
+ struct cfg_range_hook * hook = NULL;
+ list_for_each_entry(hook, &(pci->cfg_hooks), list_node) {
+ uint32_t hook_end = hook->start + hook->length - 1;
+ if (!((hook->start > end) || (hook_end < start))) {
+ return hook;
+ }
+ }
+
+ return NULL;
+}
-static int addr_port_read(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
- struct pci_internal * pci_state = priv_data;
- int reg_offset = port & 0x3;
- uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+int v3_pci_hook_config_range(struct pci_device * pci,
+ uint32_t start, uint32_t length,
+ int (*write)(struct pci_device * pci_dev, uint32_t offset,
+ void * src, uint_t length, void * private_data),
+ int (*read)(struct pci_device * pci_dev, uint32_t offset,
+ void * dst, uint_t length, void * private_data),
+ void * private_data) {
+ struct cfg_range_hook * hook = NULL;
+
- PrintDebug("Reading PCI Address Port (%x): %x len=%d\n", port, pci_state->addr_reg.val, length);
+ if (find_cfg_range_hook(pci, start, length)) {
+ PrintError("Tried to hook an already hooked config region\n");
+ return -1;
+ }
+
+ hook = V3_Malloc(sizeof(struct cfg_range_hook));
- if (length == 4) {
- if (reg_offset != 0) {
- PrintError("Invalid Address Port Read\n");
- return -1;
+ if (!hook) {
+ PrintError("Could not allocate range hook\n");
+ return -1;
+ }
+
+ memset(hook, 0, sizeof(struct cfg_range_hook));
+
+ hook->start = start;
+ hook->length = length;
+ hook->private_data = private_data;
+ hook->write = write;
+ hook->read = read;
+
+ list_add(&(hook->list_node), &(pci->cfg_hooks));
+
+ return 0;
+
+}
+
+
+
+
+// Note byte ordering: LSB -> MSB
+static uint8_t msi_32_rw_bitmask[10] = { 0x00, 0x00, /* ID, next ptr */
+ 0x71, 0x00, /* MSG CTRL */
+ 0xfc, 0xff, 0xff, 0xff, /* MSG ADDR */
+ 0xff, 0xff}; /* MSG DATA */
+
+static uint8_t msi_64_rw_bitmask[14] = { 0x00, 0x00, /* ID, next ptr */
+ 0x71, 0x00, /* MSG CTRL */
+ 0xfc, 0xff, 0xff, 0xff, /* MSG LO ADDR */
+ 0xff, 0xff, 0xff, 0xff, /* MSG HI ADDR */
+ 0xff, 0xff}; /* MSG DATA */
+
+static uint8_t msi_64pervect_rw_bitmask[24] = { 0x00, 0x00, /* ID, next ptr */
+ 0x71, 0x00, /* MSG CTRL */
+ 0xfc, 0xff, 0xff, 0xff, /* MSG LO CTRL */
+ 0xff, 0xff, 0xff, 0xff, /* MSG HI ADDR */
+ 0xff, 0xff, /* MSG DATA */
+ 0x00, 0x00, /* RSVD */
+ 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00};
+
+static uint8_t msix_rw_bitmask[12] = { 0x00, 0x00, /* ID, next ptr */
+ 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0xff, 0xff, 0xff};
+
+
+/* I am completely guessing what the format is here.
+ I only have version 1 of the PCIe spec and cannot download version 2 or 3
+ without paying the PCI-SIG $3000 a year for membership.
+ So this is just cobbled together from the version 1 spec and KVM.
+*/
+
+
+static uint8_t pciev1_rw_bitmask[20] = { 0x00, 0x00, /* ID, next ptr */
+ 0x00, 0x00, /* PCIE CAP register */
+ 0x00, 0x00, 0x00, 0x00, /* DEV CAP */
+ 0xff, 0xff, /* DEV CTRL */
+ 0x0f, 0x00, /* DEV STATUS */
+ 0x00, 0x00, 0x00, 0x00, /* LINK CAP */
+ 0xfb, 0x01, /* LINK CTRL */
+ 0x00, 0x00 /* LINK STATUS */
+};
+
+
+static uint8_t pciev2_rw_bitmask[60] = { 0x00, 0x00, /* ID, next ptr */
+ 0x00, 0x00, /* PCIE CAP register */
+ 0x00, 0x00, 0x00, 0x00, /* DEV CAP */
+ 0xff, 0xff, /* DEV CTRL */
+ 0x0f, 0x00, /* DEV STATUS */
+ 0x00, 0x00, 0x00, 0x00, /* LINK CAP */
+ 0xfb, 0x01, /* LINK CTRL */
+ 0x00, 0x00, /* LINK STATUS */
+ 0x00, 0x00, 0x00, 0x00, /* SLOT CAP ?? */
+ 0x00, 0x00, /* SLOT CTRL ?? */
+ 0x00, 0x00, /* SLOT STATUS */
+ 0x00, 0x00, /* ROOT CTRL */
+ 0x00, 0x00, /* ROOT CAP */
+ 0x00, 0x00, 0x00, 0x00, /* ROOT STATUS */
+ 0x00, 0x00, 0x00, 0x00, /* WHO THE FUCK KNOWS */
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static uint8_t pm_rw_bitmask[] = { 0x00, 0x00, /* ID, next ptr */
+ 0x00, 0x00, /* PWR MGMT CAPS */
+ 0x03, 0x9f, /* PWR MGMT CTRL */
+ 0x00, 0x00 /* PMCSR_BSE, Data */
+};
+
+
+
+int cap_write(struct pci_device * pci, uint32_t offset, void * src, uint_t length, void * private_data) {
+ struct pci_cap * cap = private_data;
+ uint32_t cap_offset = cap->offset;
+ pci_cap_type_t cap_type = cap->id;
+
+ uint32_t write_offset = offset - cap_offset;
+ void * cap_ptr = &(pci->config_space[cap_offset + 2]);
+ int i = 0;
+
+ int msi_was_enabled = 0;
+ int msix_was_enabled = 0;
+
+
+ V3_Print("CAP write trapped (val=%x, cfg_offset=%d, write_offset=%d)\n", *(uint32_t *)src, offset, write_offset);
+
+ if (cap_type == PCI_CAP_MSI) {
+ struct msi_msg_ctrl * msg_ctrl = cap_ptr;
+
+ if (msg_ctrl->msi_enable == 1) {
+ msi_was_enabled = 1;
}
- *(uint32_t *)dst = *(uint32_t *)reg_addr;
- } else if (length == 2) {
- if (reg_offset > 2) {
- PrintError("Invalid Address Port Read\n");
+ } else if (cap_type == PCI_CAP_MSIX) {
+ struct msix_cap * msix_cap = cap_ptr;
+
+ if (msix_cap->msg_ctrl.msix_enable == 1) {
+ msix_was_enabled = 1;
+ }
+ }
+
+ for (i = 0; i < length; i++) {
+ uint8_t mask = 0;
+
+ if (cap_type == PCI_CAP_MSI) {
+ struct msi_msg_ctrl * msg_ctrl = cap_ptr;
+
+ V3_Print("MSI Cap Ctrl=%x\n", *(uint16_t *)pci->msi_cap);
+ V3_Print("MSI ADDR=%x\n", *(uint32_t *)(cap_ptr + 2));
+ V3_Print("MSI HI ADDR=%x\n", *(uint32_t *)(cap_ptr + 6));
+ V3_Print("MSI Data=%x\n", *(uint16_t *)(cap_ptr + 10));
+
+ if (msg_ctrl->cap_64bit) {
+ if (msg_ctrl->per_vect_mask) {
+ mask = msi_64pervect_rw_bitmask[write_offset];
+ } else {
+ mask = msi_64_rw_bitmask[write_offset];
+ }
+ } else {
+ mask = msi_32_rw_bitmask[write_offset];
+ }
+ } else if (cap_type == PCI_CAP_MSIX) {
+ mask = msix_rw_bitmask[write_offset];
+ } else if (cap_type == PCI_CAP_PCIE) {
+ struct pcie_cap_reg * pcie_cap = cap_ptr;
+
+ if (pcie_cap->version == 1) {
+ mask = pciev1_rw_bitmask[write_offset];
+ } else if (pcie_cap->version == 2) {
+ mask = pciev2_rw_bitmask[write_offset];
+ } else {
+ return 0;
+ }
+ } else if (cap_type == PCI_CAP_PM) {
+ mask = pm_rw_bitmask[write_offset];
+ }
+
+ pci->config_space[offset + i] &= ~mask;
+ pci->config_space[offset + i] |= ((*(uint8_t *)(src + i)) & mask);
+
+ write_offset++;
+ }
+
+
+ if (pci->cmd_update) {
+
+ /* Detect changes to interrupt types for cmd updates */
+ if (cap_type == PCI_CAP_MSI) {
+ struct msi_msg_ctrl * msg_ctrl = cap_ptr;
+
+ V3_Print("msi_was_enabled=%d, msi_is_enabled=%d\n", msi_was_enabled, msg_ctrl->msi_enable);
+
+ if ((msg_ctrl->msi_enable == 1) && (msi_was_enabled == 0)) {
+ pci->irq_type = IRQ_MSI;
+ pci->cmd_update(pci, PCI_CMD_MSI_ENABLE, msg_ctrl->mult_msg_enable, pci->priv_data);
+ } else if ((msg_ctrl->msi_enable == 0) && (msi_was_enabled == 1)) {
+ pci->irq_type = IRQ_NONE;
+ pci->cmd_update(pci, PCI_CMD_MSI_DISABLE, 0, pci->priv_data);
+ }
+ } else if (cap_type == PCI_CAP_MSIX) {
+ struct msix_cap * msix_cap = cap_ptr;
+
+ if ((msix_cap->msg_ctrl.msix_enable == 1) && (msix_was_enabled == 0)) {
+ pci->irq_type = IRQ_MSIX;
+ pci->cmd_update(pci, PCI_CMD_MSIX_ENABLE, msix_cap->msg_ctrl.table_size, pci->priv_data);
+ } else if ((msix_cap->msg_ctrl.msix_enable == 0) && (msix_was_enabled == 1)) {
+ pci->irq_type = IRQ_NONE;
+ pci->cmd_update(pci, PCI_CMD_MSIX_DISABLE, msix_cap->msg_ctrl.table_size, pci->priv_data);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int init_pci_cap(struct pci_device * pci, pci_cap_type_t cap_type, uint_t cap_offset) {
+ void * cap_ptr = &(pci->config_space[cap_offset + 2]);
+
+ if (cap_type == PCI_CAP_MSI) {
+ struct msi32_msg_addr * msi = cap_ptr;
+
+ // We only expose a basic 32 bit MSI interface
+ msi->msg_ctrl.msi_enable = 0;
+ msi->msg_ctrl.mult_msg_enable = 0;
+ msi->msg_ctrl.cap_64bit = 0;
+ msi->msg_ctrl.per_vect_mask = 0;
+
+ msi->addr.val = 0;
+ msi->data.val = 0;
+
+ } else if (cap_type == PCI_CAP_MSIX) {
+
+
+
+ } else if (cap_type == PCI_CAP_PCIE) {
+ struct pcie_cap_v2 * pcie = cap_ptr;
+
+ // The v1 and v2 formats are identical for the first X bytes
+ // So we use the v2 struct, and only modify extended fields if v2 is detected
+
+ pcie->dev_cap.fn_level_reset = 0;
+
+ pcie->dev_ctrl.val &= 0x70e0; // only preserve max_payload_size and max_read_req_size untouched
+ pcie->dev_ctrl.relaxed_order_enable = 1;
+ pcie->dev_ctrl.no_snoop_enable = 1;
+
+ pcie->dev_status.val = 0;
+
+ pcie->link_cap.val &= 0x0003ffff;
+
+ pcie->link_status.val &= 0x03ff;
+
+ if (pcie->pcie_cap.version >= 2) {
+ pcie->slot_cap = 0;
+ pcie->slot_ctrl = 0;
+ pcie->slot_status = 0;
+
+ pcie->root_ctrl = 0;
+ pcie->root_cap = 0;
+ pcie->root_status = 0;
+ }
+ } else if (cap_type == PCI_CAP_PM) {
+
+ }
+
+
+ return 0;
+}
+
+
+// enumerate all capabilities and disable them.
+static int scan_pci_caps(struct pci_device * pci) {
+ uint_t cap_offset = pci->config_header.cap_ptr;
+
+ V3_Print("Scanning for Capabilities (cap_offset=%d)\n", cap_offset);
+
+ while (cap_offset != 0) {
+ uint8_t id = pci->config_space[cap_offset];
+ uint8_t next = pci->config_space[cap_offset + 1];
+
+ V3_Print("Found Capability 0x%x at offset %d (0x%x)\n",
+ id, cap_offset, cap_offset);
+
+ struct pci_cap * cap = V3_Malloc(sizeof(struct pci_cap));
+
+ if (!cap) {
+ PrintError("Error allocating PCI CAP info\n");
return -1;
}
- *(uint16_t *)dst = *(uint16_t *)reg_addr;
- } else if (length == 1) {
- *(uint8_t *)dst = *(uint8_t *)reg_addr;
- } else {
- PrintError("Invalid read length (%d) for PCI address register\n", length);
+ memset(cap, 0, sizeof(struct pci_cap));
+
+ cap->id = id;
+ cap->offset = cap_offset;
+
+ list_add(&(cap->cap_node), &(pci->capabilities));
+
+ // set correct init values
+ init_pci_cap(pci, id, cap_offset);
+
+
+ // set to the next pointer
+ cap_offset = next;
+ }
+
+ // Disable Capabilities
+ pci->config_header.cap_ptr = 0;
+
+ // Hook Cap pointer to return cached config space value
+ if (v3_pci_hook_config_range(pci, 0x34, 1,
+ NULL, NULL, NULL) == -1) {
+ PrintError("Could not hook cap pointer\n");
return -1;
}
- return length;
+
+/*
+ // Disable all PCIE extended capabilities for now
+ pci->config_space[0x100] = 0;
+ pci->config_space[0x101] = 0;
+ pci->config_space[0x102] = 0;
+ pci->config_space[0x103] = 0;
+*/
+
+
+ return 0;
+
}
+int v3_pci_enable_capability(struct pci_device * pci, pci_cap_type_t cap_type) {
+ uint32_t size = 0;
+ struct pci_cap * tmp_cap = NULL;
+ struct pci_cap * cap = NULL;
+ void * cap_ptr = NULL;
-static int addr_port_write(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
- struct pci_internal * pci_state = priv_data;
- int reg_offset = port & 0x3;
- uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+
+ list_for_each_entry(tmp_cap, &(pci->capabilities), cap_node) {
+ if (tmp_cap->id == cap_type) {
+ cap = tmp_cap;
+ break;
+ }
+ }
+
+ if ((cap == NULL) || (cap->enabled)) {
+ return -1;
+ }
- if (length == 4) {
- if (reg_offset != 0) {
- PrintError("Invalid Address Port Write\n");
- return -1;
+ V3_Print("Found Capability %x at %x (%d)\n", cap_type, cap->offset, cap->offset);
+
+ // found the capability
+
+ // mark it as enabled
+ cap->enabled = 1;
+
+ cap_ptr = &(pci->config_space[cap->offset + 2]);
+
+ if (cap_type == PCI_CAP_MSI) {
+ pci->msi_cap = cap_ptr;
+
+ if (pci->msi_cap->cap_64bit) {
+ if (pci->msi_cap->per_vect_mask) {
+ // 64 bit MSI w/ per vector masking
+ size = 22;
+ } else {
+ // 64 bit MSI
+ size = 12;
+ }
+ } else {
+ // 32 bit MSI
+ size = 8;
}
+ } else if (cap_type == PCI_CAP_MSIX) {
+ pci->msix_cap = cap_ptr;
+
+ // disable passthrough for MSIX BAR
+
+ pci->bar[pci->msix_cap->bir].type = PCI_BAR_MEM32;
- PrintDebug("Writing PCI 4 bytes Val=%x\n", *(uint32_t *)src);
+ size = 10;
+ } else if (cap_type == PCI_CAP_PCIE) {
+ struct pcie_cap_reg * pcie_cap = (struct pcie_cap_reg *)&(pci->config_space[cap->offset + 2]);
- *(uint32_t *)reg_addr = *(uint32_t *)src;
- } else if (length == 2) {
- if (reg_offset > 2) {
- PrintError("Invalid Address Port Write\n");
+ if (pcie_cap->version == 1) {
+ size = 20;
+ } else if (pcie_cap->version == 2) {
+ size = 60;
+ } else {
return -1;
}
+ } else if (cap_type == PCI_CAP_PM) {
+ size = 8;
+ }
- PrintDebug("Writing PCI 2 byte Val=%x\n", *(uint16_t *)src);
- *(uint16_t *)reg_addr = *(uint16_t *)src;
- } else if (length == 1) {
- PrintDebug("Writing PCI 1 byte Val=%x\n", *(uint8_t *)src);
- *(uint8_t *)reg_addr = *(uint8_t *)src;
- } else {
- PrintError("Invalid write length (%d) for PCI address register\n", length);
+ V3_Print("Hooking capability range (offset=%d, size=%d)\n", cap->offset, size);
+
+ if (v3_pci_hook_config_range(pci, cap->offset, size + 2,
+ cap_write, NULL, cap) == -1) {
+ PrintError("Could not hook config range (start=%d, size=%d)\n",
+ cap->offset + 2, size);
return -1;
}
- PrintDebug("Writing PCI Address Port(%x): %x\n", port, pci_state->addr_reg.val);
+
+
+ // link it to the active capabilities list
+ pci->config_space[cap->offset + 1] = pci->config_header.cap_ptr;
+ pci->config_header.cap_ptr = cap->offset; // add to the head of the list
+
+ return 0;
+}
+
+
+
+
+static int addr_port_read(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
+ struct pci_internal * pci_state = priv_data;
+ int reg_offset = port & 0x3;
+ uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+
+ PrintDebug("Reading PCI Address Port (%x): %x len=%d\n", port, pci_state->addr_reg.val, length);
+
+ if (reg_offset + length > 4) {
+ PrintError("Invalid Address port write\n");
+ return -1;
+ }
+
+ memcpy(dst, reg_addr, length);
return length;
}
-static int data_port_read(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
+static int addr_port_write(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
+ struct pci_internal * pci_state = priv_data;
+ int reg_offset = port & 0x3;
+ uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+
+ if (reg_offset + length > 4) {
+ PrintError("Invalid Address port write\n");
+ return -1;
+ }
+
+ // Set address register
+ memcpy(reg_addr, src, length);
+
+ PrintDebug("Writing PCI Address Port(%x): AddrReg=%x (op_val = %x, len=%d) \n", port, pci_state->addr_reg.val, *(uint32_t *)src, length);
+
+ return length;
+}
+
+
+static int data_port_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
struct pci_internal * pci_state = priv_data;
struct pci_device * pci_dev = NULL;
- uint_t reg_num = (pci_state->addr_reg.reg_num << 2) + (port & 0x3);
- int i;
+ uint_t reg_num = (pci_state->addr_reg.hi_reg_num << 16) +(pci_state->addr_reg.reg_num << 2) + (port & 0x3);
+ int i = 0;
+ int bytes_left = length;
if (pci_state->addr_reg.bus_num != 0) {
- int i = 0;
- for (i = 0; i < length; i++) {
- *((uint8_t *)dst + i) = 0xff;
- }
-
+ memset(dst, 0xff, length);
return length;
}
- PrintDebug("Reading PCI Data register. bus = %d, dev = %d, reg = %d (%x), cfg_reg = %x\n",
+
+ pci_dev = get_device(&(pci_state->bus_list[0]), pci_state->addr_reg.dev_num, pci_state->addr_reg.fn_num);
+
+ if (pci_dev == NULL) {
+ memset(dst, 0xff, length);
+ return length;
+ }
+
+ PrintDebug("Reading PCI Data register. bus = %d, dev = %d, fn = %d, reg = %d (%x), cfg_reg = %x\n",
pci_state->addr_reg.bus_num,
pci_state->addr_reg.dev_num,
+ pci_state->addr_reg.fn_num,
reg_num, reg_num,
pci_state->addr_reg.val);
- pci_dev = get_device(&(pci_state->bus_list[0]), pci_state->addr_reg.dev_num, pci_state->addr_reg.fn_num);
-
- if (pci_dev == NULL) {
- for (i = 0; i < length; i++) {
- *(uint8_t *)((uint8_t *)dst + i) = 0xff;
- }
- return length;
- }
+ while (bytes_left > 0) {
+ struct cfg_range_hook * cfg_hook = find_cfg_range_hook(pci_dev, reg_num + i, 1);
+ void * cfg_dst = &(pci_dev->config_space[reg_num + i]);
- if (pci_dev->type == PCI_PASSTHROUGH) {
- if (pci_dev->config_read(reg_num, dst, length, pci_dev->priv_data) == -1) {
- PrintError("Failed to handle configuration update for passthrough pci_device\n");
- return -1;
- }
-
- return 0;
- }
+ if (cfg_hook) {
+ uint_t range_len = cfg_hook->length - ((reg_num + i) - cfg_hook->start);
+ range_len = (range_len > bytes_left) ? bytes_left : range_len;
- for (i = 0; i < length; i++) {
- *(uint8_t *)((uint8_t *)dst + i) = pci_dev->config_space[reg_num + i];
- }
+ if (cfg_hook->read) {
+ cfg_hook->read(pci_dev, reg_num + i, cfg_dst, range_len, cfg_hook->private_data);
+ }
+
+ bytes_left -= range_len;
+ i += range_len;
+ } else {
+ if (pci_dev->config_read) {
+ if (pci_dev->config_read(pci_dev, reg_num + i, cfg_dst, 1, pci_dev->priv_data) != 0) {
+ PrintError("Error in config_read from PCI device (%s)\n", pci_dev->name);
+ }
+ }
+ bytes_left--;
+ i++;
+ }
+ }
+
+ memcpy(dst, &(pci_dev->config_space[reg_num]), length);
+
PrintDebug("\tVal=%x, len=%d\n", *(uint32_t *)dst, length);
return length;
}
-static inline int is_cfg_reg_writable(uchar_t header_type, int reg_num) {
- if (header_type == 0x00) {
- switch (reg_num) {
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0e:
- case 0x3d:
- return 0;
-
- default:
- return 1;
-
- }
- } else if (header_type == 0x80) {
- switch (reg_num) {
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0e:
- case 0x3d:
- return 0;
-
- default:
- return 1;
-
- }
- } else {
- // PCI to PCI Bridge = 0x01
- // CardBus Bridge = 0x02
- // huh?
- PrintError("Invalid PCI Header type (0x%.2x)\n", header_type);
+static int bar_update(struct pci_device * pci_dev, uint32_t offset,
+ void * src, uint_t length, void * private_data) {
+ struct v3_pci_bar * bar = (struct v3_pci_bar *)private_data;
+ int bar_offset = offset & ~0x03;
+ int bar_num = (bar_offset - 0x10) / 4;
+ uint32_t new_val = *(uint32_t *)src;
+
+ PrintDebug("Updating BAR Register (Dev=%s) (bar=%d) (old_val=0x%x) (new_val=0x%x)\n",
+ pci_dev->name, bar_num, bar->val, new_val);
- return -1;
- }
-}
+ // Cache the changes locally
+ memcpy(&(pci_dev->config_space[offset]), src, length);
-static int bar_update(struct guest_info * info, struct pci_device * pci, int bar_num, uint32_t new_val) {
- struct v3_pci_bar * bar = &(pci->bar[bar_num]);
+ if (bar->type == PCI_BAR_PASSTHROUGH) {
+ if (bar->bar_write(bar_num, (void *)(pci_dev->config_space + bar_offset), bar->private_data) == -1) {
+ PrintError("Error in Passthrough bar write operation\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ // Else we are a virtualized BAR
- PrintDebug("Updating BAR Register (Dev=%s) (bar=%d) (old_val=0x%x) (new_val=0x%x)\n",
- pci->name, bar_num, bar->val, new_val);
+ *(uint32_t *)(pci_dev->config_space + offset) &= bar->mask;
switch (bar->type) {
case PCI_BAR_IO: {
bar->num_ports);
// only do this if pci device is enabled....
- if (!(pci->config_header.status & 0x1)) {
+ if (!(pci_dev->config_header.status & 0x1)) {
PrintError("PCI Device IO space not enabled\n");
}
PrintDebug("Rehooking PCI IO port (old port=%u) (new port=%u)\n",
PCI_IO_BASE(bar->val) + i, PCI_IO_BASE(new_val) + i);
- v3_unhook_io_port(info->vm_info, PCI_IO_BASE(bar->val) + i);
+ v3_unhook_io_port(pci_dev->vm, PCI_IO_BASE(bar->val) + i);
- if (v3_hook_io_port(info->vm_info, PCI_IO_BASE(new_val) + i,
+ if (v3_hook_io_port(pci_dev->vm, PCI_IO_BASE(new_val) + i,
bar->io_read, bar->io_write,
bar->private_data) == -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);
- v3_print_io_map(info->vm_info);
+ v3_print_io_map(pci_dev->vm);
return -1;
}
}
break;
}
case PCI_BAR_MEM32: {
- v3_unhook_mem(info->vm_info, V3_MEM_CORE_ANY, (addr_t)(bar->val));
+ v3_unhook_mem(pci_dev->vm, V3_MEM_CORE_ANY, (addr_t)(bar->val));
if (bar->mem_read) {
- v3_hook_full_mem(info->vm_info, V3_MEM_CORE_ANY, PCI_MEM32_BASE(new_val),
+ v3_hook_full_mem(pci_dev->vm, V3_MEM_CORE_ANY, PCI_MEM32_BASE(new_val),
PCI_MEM32_BASE(new_val) + (bar->num_pages * PAGE_SIZE_4KB),
- bar->mem_read, bar->mem_write, pci->priv_data);
+ bar->mem_read, bar->mem_write, pci_dev->priv_data);
} else {
PrintError("Write hooks not supported for PCI\n");
return -1;
}
case PCI_BAR_NONE: {
PrintDebug("Reprogramming an unsupported BAR register (Dev=%s) (bar=%d) (val=%x)\n",
- pci->name, bar_num, new_val);
+ pci_dev->name, bar_num, new_val);
break;
}
default:
}
-static int data_port_write(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
+static int data_port_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
struct pci_internal * pci_state = priv_data;
struct pci_device * pci_dev = NULL;
- uint_t reg_num = (pci_state->addr_reg.reg_num << 2) + (port & 0x3);
- int i;
-
+ uint_t reg_num = (pci_state->addr_reg.hi_reg_num << 16) +(pci_state->addr_reg.reg_num << 2) + (port & 0x3);
+ int i = 0;
+ int ret = length;
if (pci_state->addr_reg.bus_num != 0) {
return length;
pci_state->addr_reg.dev_num);
return -1;
}
-
- if (pci_dev->type == PCI_PASSTHROUGH) {
- if (pci_dev->config_write(reg_num, src, length, pci_dev->priv_data) == -1) {
- PrintError("Failed to handle configuration update for passthrough pci_device\n");
- return -1;
- }
-
- return 0;
- }
+ /* update the config space
+ If a hook has been registered for a given region, call the hook with the max write length
+ */
+ while (length > 0) {
+ struct cfg_range_hook * cfg_hook = find_cfg_range_hook(pci_dev, reg_num + i, 1);
- for (i = 0; i < length; i++) {
- uint_t cur_reg = reg_num + i;
- int writable = is_cfg_reg_writable(pci_dev->config_header.header_type, cur_reg);
-
- if (writable == -1) {
- PrintError("Invalid PCI configuration space\n");
- return -1;
- }
-
- if (writable) {
- pci_dev->config_space[cur_reg] = *(uint8_t *)((uint8_t *)src + i);
+ if (cfg_hook) {
+ uint_t range_len = cfg_hook->length - ((reg_num + i) - cfg_hook->start);
+ range_len = (range_len > length) ? length : range_len;
+
+ if (cfg_hook->write) {
+ cfg_hook->write(pci_dev, reg_num + i, (void *)(src + i), range_len, cfg_hook->private_data);
+ }
- if ((cur_reg >= 0x10) && (cur_reg < 0x28)) {
- // BAR Register Update
- int bar_reg = ((cur_reg & ~0x3) - 0x10) / 4;
-
- pci_dev->bar_update_flag = 1;
- pci_dev->bar[bar_reg].updated = 1;
-
- // PrintDebug("Updating BAR register %d\n", bar_reg);
+ length -= range_len;
+ i += range_len;
+ } else {
+ // send the writes to the cached config space, and to the generic callback if present
+ uint8_t mask = 0xff;
- } else if ((cur_reg >= 0x30) && (cur_reg < 0x34)) {
- // Extension ROM update
+ if (reg_num < 64) {
+ mask = pci_hdr_write_mask_00[reg_num + i];
+ }
+
+ if (mask != 0) {
+ uint8_t new_val = *(uint8_t *)(src + i);
+ uint8_t old_val = pci_dev->config_space[reg_num + i];
- pci_dev->exp_rom_update_flag = 1;
- } else if (cur_reg == 0x04) {
- // COMMAND update
- uint8_t command = *((uint8_t *)src + i);
+ pci_dev->config_space[reg_num + i] = ((new_val & mask) | (old_val & ~mask));
- PrintError("command update for %s old=%x new=%x\n",
- pci_dev->name,
- pci_dev->config_space[cur_reg],command);
-
- pci_dev->config_space[cur_reg] = command;
-
- if (pci_dev->cmd_update) {
- pci_dev->cmd_update(pci_dev, (command & 0x01), (command & 0x02));
+ if (pci_dev->config_write) {
+ pci_dev->config_write(pci_dev, reg_num + i, &(pci_dev->config_space[reg_num + i]), 1, pci_dev->priv_data);
}
-
- } else if (cur_reg == 0x0f) {
- // BIST update
- pci_dev->config_header.BIST = 0x00;
- }
- } else {
- PrintError("PCI Write to read only register %d\n", cur_reg);
+ }
+
+ length--;
+ i++;
}
}
- if (pci_dev->config_update) {
- pci_dev->config_update(reg_num, src, length, pci_dev->priv_data);
+ return ret;
+}
+
+
+
+static int exp_rom_write(struct pci_device * pci_dev, uint32_t offset,
+ void * src, uint_t length, void * private_data) {
+ int bar_offset = offset & ~0x03;
+
+ if (pci_dev->exp_rom_update) {
+ pci_dev->exp_rom_update(pci_dev, (void *)(pci_dev->config_space + bar_offset), pci_dev->priv_data);
+
+ return 0;
}
- // Scan for BAR updated
- if (pci_dev->bar_update_flag) {
- for (i = 0; i < 6; i++) {
- if (pci_dev->bar[i].updated) {
- int bar_offset = 0x10 + 4 * i;
+ PrintError("Expansion ROM update not handled. Will appear to not Exist\n");
+ return 0;
+}
- if (pci_dev->bar[i].type == PCI_BAR_PASSTHROUGH) {
- if (pci_dev->bar[i].bar_write(i, (uint32_t *)(pci_dev->config_space + bar_offset), pci_dev->bar[i].private_data) == -1) {
- PrintError("Error in passthrough bar write operation\n");
- return -1;
- }
- } else {
- *(uint32_t *)(pci_dev->config_space + bar_offset) &= pci_dev->bar[i].mask;
- // check special flags....
+static int cmd_write(struct pci_device * pci_dev, uint32_t offset,
+ void * src, uint_t length, void * private_data) {
- // bar_update
- if (bar_update(core, pci_dev, i, *(uint32_t *)(pci_dev->config_space + bar_offset)) == -1) {
- PrintError("PCI Device %s: Bar update Error Bar=%d\n", pci_dev->name, i);
- return -1;
- }
- }
+ int i = 0;
- pci_dev->bar[i].updated = 0;
- }
- }
- pci_dev->bar_update_flag = 0;
- }
+ struct pci_cmd_reg old_cmd;
+ struct pci_cmd_reg new_cmd;
+ old_cmd.val = pci_dev->config_header.command;
+
+ for (i = 0; i < length; i++) {
+ uint8_t mask = pci_hdr_write_mask_00[offset + i];
+ uint8_t new_val = *(uint8_t *)(src + i);
+ uint8_t old_val = pci_dev->config_space[offset + i];
- if ((pci_dev->exp_rom_update_flag) && (pci_dev->exp_rom_update)) {
- pci_dev->exp_rom_update(pci_dev, &(pci_dev->config_header.expansion_rom_address), pci_dev->priv_data);
- pci_dev->exp_rom_update_flag = 0;
+ pci_dev->config_space[offset + i] = ((new_val & mask) | (old_val & ~mask));
}
+ new_cmd.val = pci_dev->config_header.command;
- return length;
-}
+
+ if (pci_dev->cmd_update) {
+ if ((new_cmd.intx_disable == 1) && (old_cmd.intx_disable == 0)) {
+ pci_dev->irq_type = IRQ_NONE;
+ pci_dev->cmd_update(pci_dev, PCI_CMD_INTX_DISABLE, 0, pci_dev->priv_data);
+ } else if ((new_cmd.intx_disable == 0) && (old_cmd.intx_disable == 1)) {
+ pci_dev->irq_type = IRQ_INTX;
+ pci_dev->cmd_update(pci_dev, PCI_CMD_INTX_ENABLE, 0, pci_dev->priv_data);
+ }
+ if ((new_cmd.dma_enable == 1) && (old_cmd.dma_enable == 0)) {
+ pci_dev->cmd_update(pci_dev, PCI_CMD_DMA_ENABLE, 0, pci_dev->priv_data);
+ } else if ((new_cmd.dma_enable == 0) && (old_cmd.dma_enable == 1)) {
+ pci_dev->cmd_update(pci_dev, PCI_CMD_DMA_DISABLE, 0, pci_dev->priv_data);
+ }
+ }
+ return 0;
+}
static void init_pci_busses(struct pci_internal * pci_state) {
node = v3_rb_next(node);
v3_rb_erase(&(dev->dev_tree_node), &(bus->devices));
+
+ // Free config range hooks
+ {
+ struct cfg_range_hook * hook = NULL;
+ struct cfg_range_hook * tmp = NULL;
+ list_for_each_entry_safe(hook, tmp, &(dev->cfg_hooks), list_node) {
+ list_del(&(hook->list_node));
+ V3_Free(hook);
+ }
+ }
+
+ // Free caps
+ {
+ struct pci_cap * cap = NULL;
+ struct pci_cap * tmp = NULL;
+ list_for_each_entry_safe(cap, tmp, &(dev->cfg_hooks), cap_node) {
+ list_del(&(cap->cap_node));
+ V3_Free(cap);
+ }
+ }
+
V3_Free(dev);
}
for (i = 0; i < 6; i++) {
int bar_offset = 0x10 + (4 * i);
+ struct v3_pci_bar * bar = &(pci_dev->bar[i]);
- if (pci_dev->bar[i].type == PCI_BAR_IO) {
+ if (bar->type == PCI_BAR_IO) {
int j = 0;
- pci_dev->bar[i].mask = (~((pci_dev->bar[i].num_ports) - 1)) | 0x01;
+ bar->mask = (~((bar->num_ports) - 1)) | 0x01;
- 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;
+ if (bar->default_base_port != 0xffff) {
+ bar->val = bar->default_base_port & bar->mask;
} else {
- pci_dev->bar[i].val = 0;
+ bar->val = 0;
}
- pci_dev->bar[i].val |= 0x00000001;
+ bar->val |= 0x00000001;
- for (j = 0; j < pci_dev->bar[i].num_ports; j++) {
+ for (j = 0; j < bar->num_ports; j++) {
// hook IO
- if (pci_dev->bar[i].default_base_port != 0xffff) {
- if (v3_hook_io_port(vm, pci_dev->bar[i].default_base_port + j,
- pci_dev->bar[i].io_read, pci_dev->bar[i].io_write,
- pci_dev->bar[i].private_data) == -1) {
- PrintError("Could not hook default io port %x\n", pci_dev->bar[i].default_base_port + j);
+ if (bar->default_base_port != 0xffff) {
+ if (v3_hook_io_port(vm, bar->default_base_port + j,
+ bar->io_read, bar->io_write,
+ bar->private_data) == -1) {
+ PrintError("Could not hook default io port %x\n", bar->default_base_port + j);
return -1;
}
}
}
- *(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
+ *(uint32_t *)(pci_dev->config_space + bar_offset) = bar->val;
- } else if (pci_dev->bar[i].type == PCI_BAR_MEM32) {
- pci_dev->bar[i].mask = ~((pci_dev->bar[i].num_pages << 12) - 1);
- pci_dev->bar[i].mask |= 0xf; // preserve the configuration flags
+ } else if (bar->type == PCI_BAR_MEM32) {
+ bar->mask = ~((bar->num_pages << 12) - 1);
+ bar->mask |= 0xf; // preserve the configuration flags
- 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;
+ if (bar->default_base_addr != 0xffffffff) {
+ bar->val = bar->default_base_addr & bar->mask;
} else {
- pci_dev->bar[i].val = 0;
+ bar->val = 0;
}
// hook memory
- if (pci_dev->bar[i].mem_read) {
+ if (bar->mem_read) {
// full hook
- v3_hook_full_mem(vm, V3_MEM_CORE_ANY, pci_dev->bar[i].default_base_addr,
- pci_dev->bar[i].default_base_addr + (pci_dev->bar[i].num_pages * PAGE_SIZE_4KB),
- pci_dev->bar[i].mem_read, pci_dev->bar[i].mem_write, pci_dev->priv_data);
- } else if (pci_dev->bar[i].mem_write) {
+ v3_hook_full_mem(vm, V3_MEM_CORE_ANY, bar->default_base_addr,
+ bar->default_base_addr + (bar->num_pages * PAGE_SIZE_4KB),
+ bar->mem_read, bar->mem_write, pci_dev->priv_data);
+ } else if (bar->mem_write) {
// write hook
PrintError("Write hooks not supported for PCI devices\n");
return -1;
/*
- v3_hook_write_mem(pci_dev->vm_dev->vm, pci_dev->bar[i].default_base_addr,
- pci_dev->bar[i].default_base_addr + (pci_dev->bar[i].num_pages * PAGE_SIZE_4KB),
- pci_dev->bar[i].mem_write, pci_dev->vm_dev);
+ v3_hook_write_mem(pci_dev->vm_dev->vm, bar->default_base_addr,
+ bar->default_base_addr + (bar->num_pages * PAGE_SIZE_4KB),
+ bar->mem_write, pci_dev->vm_dev);
*/
} else {
// set the prefetchable flag...
- pci_dev->bar[i].val |= 0x00000008;
+ bar->val |= 0x00000008;
}
- *(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
+ *(uint32_t *)(pci_dev->config_space + bar_offset) = bar->val;
- } else if (pci_dev->bar[i].type == PCI_BAR_MEM24) {
+ } else if (bar->type == PCI_BAR_MEM24) {
PrintError("16 Bit memory ranges not supported (reg: %d)\n", i);
return -1;
- } else if (pci_dev->bar[i].type == PCI_BAR_NONE) {
- pci_dev->bar[i].val = 0x00000000;
- pci_dev->bar[i].mask = 0x00000000; // This ensures that all updates will be dropped
- *(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
- } else if (pci_dev->bar[i].type == PCI_BAR_PASSTHROUGH) {
+ } else if (bar->type == PCI_BAR_NONE) {
+ bar->val = 0x00000000;
+ bar->mask = 0x00000000; // This ensures that all updates will be dropped
+ *(uint32_t *)(pci_dev->config_space + bar_offset) = bar->val;
+ } else if (bar->type == PCI_BAR_PASSTHROUGH) {
// Call the bar init function to get the local cached value
- pci_dev->bar[i].bar_init(i, &(pci_dev->bar[i].val), pci_dev->bar[i].private_data);
+ bar->bar_init(i, &(bar->val), bar->private_data);
} else {
PrintError("Invalid BAR type for bar #%d\n", i);
return -1;
}
+
+ v3_pci_hook_config_range(pci_dev, bar_offset, 4, bar_update, NULL, bar);
}
return 0;
int v3_pci_set_irq_bridge(struct vm_device * pci_bus, int bus_num,
- int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data),
- int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data),
+ int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec),
+ int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec),
void * priv_data) {
struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
return 0;
}
-int v3_pci_raise_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev) {
- struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
- struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
+int v3_pci_raise_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index) {
+ struct v3_irq vec;
- return bus->raise_pci_irq(dev, bus->irq_dev_data);
+ vec.ack = NULL;
+ vec.private_data = NULL;
+ vec.irq = vec_index;
+
+ return v3_pci_raise_acked_irq(pci_bus, dev, vec);
}
-int v3_pci_lower_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev) {
+int v3_pci_lower_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index) {
+ struct v3_irq vec;
+
+ vec.irq = vec_index;
+ vec.ack = NULL;
+ vec.private_data = NULL;
+
+ return v3_pci_lower_acked_irq(pci_bus, dev, vec);
+}
+
+int v3_pci_raise_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec) {
struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
- struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
+ struct pci_bus * bus = &(pci_state->bus_list[dev->bus_num]);
+
+
+ if (dev->irq_type == IRQ_INTX) {
+ return bus->raise_pci_irq(dev, bus->irq_dev_data, &vec);
+ } else if (dev->irq_type == IRQ_MSI) {
+ struct v3_gen_ipi ipi;
+ struct msi_addr * addr = NULL;
+ struct msi_data * data = NULL;
+
+ if (dev->msi_cap->cap_64bit) {
+ if (dev->msi_cap->per_vect_mask) {
+ struct msi64_pervec_msg_addr * msi = (void *)dev->msi_cap;
+ addr = &(msi->addr);
+ data = &(msi->data);
+ } else {
+ struct msi64_msg_addr * msi = (void *)dev->msi_cap;
+ addr = &(msi->addr);
+ data = &(msi->data);
+ }
+ } else {
+ struct msi32_msg_addr * msi = (void *)dev->msi_cap;
+ addr = &(msi->addr);
+ data = &(msi->data);
+ }
+
+ memset(&ipi, 0, sizeof(struct v3_gen_ipi));
+
+ // decode MSI fields into IPI
+
+ ipi.vector = data->vector + vec.irq;
+ ipi.mode = data->del_mode;
+ ipi.logical = addr->dst_mode;
+ ipi.trigger_mode = data->trig_mode;
+ ipi.dst_shorthand = 0;
+ ipi.dst = addr->dst_id;
+
+
+ v3_apic_send_ipi(dev->vm, &ipi, dev->apic_dev);
+
+ return 0;
+ } else if (dev->irq_type == IRQ_MSIX) {
+ addr_t msix_table_gpa = 0;
+ struct msix_table * msix_table = NULL;
+ uint_t bar_idx = dev->msix_cap->bir;
+ struct v3_gen_ipi ipi;
+ struct msi_addr * addr = NULL;
+ struct msi_data * data = NULL;
+
+ if (dev->bar[bar_idx].type != PCI_BAR_MEM32) {
+ PrintError("Non 32bit MSIX BAR registers are not supported\n");
+ return -1;
+ }
+
+ msix_table_gpa = dev->bar[bar_idx].val;
+ msix_table_gpa += dev->msix_cap->table_offset;
+
+ if (v3_gpa_to_hva(&(dev->vm->cores[0]), msix_table_gpa, (void *)&(msix_table)) != 0) {
+ PrintError("Could not translate MSIX Table GPA (%p)\n", (void *)msix_table_gpa);
+ return -1;
+ }
+
+ memset(&ipi, 0, sizeof(struct v3_gen_ipi));
+
+ data = &(msix_table->entries[vec.irq].data);
+ addr = &(msix_table->entries[vec.irq].addr);;
+
+ // decode MSIX fields into IPI
+ ipi.vector = data->vector + vec.irq;
+ ipi.mode = data->del_mode;
+ ipi.logical = addr->dst_mode;
+ ipi.trigger_mode = data->trig_mode;
+ ipi.dst_shorthand = 0;
+ ipi.dst = addr->dst_id;
+
+
+
+ V3_Print("Decode MSIX\n");
+
+ v3_apic_send_ipi(dev->vm, &ipi, dev->apic_dev);
+
+ return 0;
+ }
+
+ // Should never get here
+ return -1;
- return bus->lower_pci_irq(dev, bus->irq_dev_data);
}
+int v3_pci_lower_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec) {
+ if (dev->irq_type == IRQ_INTX) {
+ struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
+ struct pci_bus * bus = &(pci_state->bus_list[dev->bus_num]);
+
+ return bus->lower_pci_irq(dev, bus->irq_dev_data, &vec);
+ } else {
+ return -1;
+ }
+}
+
+
// if dev_num == -1, auto assign
struct pci_device * v3_pci_register_device(struct vm_device * pci,
pci_device_type_t dev_type,
int fn_num,
const char * name,
struct v3_pci_bar * bars,
- int (*config_update)(uint_t reg_num, void * src, uint_t length, void * priv_data),
- int (*cmd_update)(struct pci_device * pci_dev, uchar_t io_enabled, uchar_t mem_enabled),
+ int (*config_write)(struct pci_device * pci_dev, uint32_t reg_num, void * src,
+ uint_t length, void * priv_data),
+ int (*config_read)(struct pci_device * pci_dev, uint32_t reg_num, void * dst,
+ uint_t length, void * priv_data),
+ int (*cmd_update)(struct pci_device * pci_dev, pci_cmd_t cmd, uint64_t arg, void * priv_data),
int (*exp_rom_update)(struct pci_device * pci_dev, uint32_t * src, void * priv_data),
void * priv_data) {
pci_dev->fn_num = fn_num;
strncpy(pci_dev->name, name, sizeof(pci_dev->name));
+ pci_dev->vm = pci->vm;
pci_dev->priv_data = priv_data;
+ INIT_LIST_HEAD(&(pci_dev->cfg_hooks));
+ INIT_LIST_HEAD(&(pci_dev->capabilities));
+
+
+ {
+ // locate APIC for MSI/MSI-X
+ pci_dev->apic_dev = v3_find_dev(pci->vm, "apic");
+ }
+
// register update callbacks
- pci_dev->config_update = config_update;
+ pci_dev->config_write = config_write;
+ pci_dev->config_read = config_read;
pci_dev->cmd_update = cmd_update;
pci_dev->exp_rom_update = exp_rom_update;
+
+ if (config_read) {
+ int i = 0;
+
+ // Only 256 bytes for now, should expand it in the future
+ for (i = 0; i < 256; i++) {
+ config_read(pci_dev, i, &(pci_dev->config_space[i]), 1, pci_dev->priv_data);
+ }
+ }
+
+ V3_Print("Scanning for Capabilities\n");
+
+ // scan for caps
+ scan_pci_caps(pci_dev);
+
+ pci_dev->irq_type = IRQ_INTX;
+
+ V3_Print("Caps scanned\n");
+
+ // hook important regions
+ v3_pci_hook_config_range(pci_dev, 0x30, 4, exp_rom_write, NULL, NULL); // ExpRom
+ v3_pci_hook_config_range(pci_dev, 0x04, 2, cmd_write, NULL, NULL); // CMD Reg
+ // * Status resets
+ // * Drop BIST
+ //
+
+
+
//copy bars
for (i = 0; i < 6; i ++) {
pci_dev->bar[i].type = bars[i].type;
return pci_dev;
}
-
-
-// if dev_num == -1, auto assign
-struct pci_device * v3_pci_register_passthrough_device(struct vm_device * pci,
- int bus_num,
- int dev_num,
- int fn_num,
- const char * name,
- int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data),
- int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data),
- void * private_data) {
-
- struct pci_internal * pci_state = (struct pci_internal *)pci->private_data;
- struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
- struct pci_device * pci_dev = NULL;
-
- if (dev_num > MAX_BUS_DEVICES) {
- PrintError("Requested Invalid device number (%d)\n", dev_num);
- return NULL;
- }
-
- if (dev_num == PCI_AUTO_DEV_NUM) {
- PrintDebug("Searching for free device number\n");
- if ((dev_num = get_free_dev_num(bus)) == -1) {
- PrintError("No more available PCI slots on bus %d\n", bus->bus_num);
- return NULL;
- }
- }
-
- PrintDebug("Checking for PCI Device\n");
-
- if (get_device(bus, dev_num, fn_num) != NULL) {
- PrintError("PCI Device already registered at slot %d on bus %d\n",
- dev_num, bus->bus_num);
- return NULL;
- }
-
-
- pci_dev = (struct pci_device *)V3_Malloc(sizeof(struct pci_device));
-
- if (pci_dev == NULL) {
- PrintError("Could not allocate pci device\n");
- return NULL;
- }
-
- memset(pci_dev, 0, sizeof(struct pci_device));
-
- pci_dev->bus_num = bus_num;
- pci_dev->dev_num = dev_num;
- pci_dev->fn_num = fn_num;
-
- strncpy(pci_dev->name, name, sizeof(pci_dev->name));
- pci_dev->priv_data = private_data;
-
- // register update callbacks
- pci_dev->config_write = config_write;
- pci_dev->config_read = config_read;
-
- // add the device
- add_device_to_bus(bus, pci_dev);
-
-#ifdef V3_CONFIG_DEBUG_PCI
- pci_dump_state(pci_state);
-#endif
-
- return pci_dev;
-}
}
-static int pci_front_config_update(uint_t reg_num, void * src, uint_t length, void * private_data)
+static int pci_front_config_update(struct pci_device *pci_dev, uint_t reg_num, void * src, uint_t length, void * private_data)
{
int i;
struct vm_device * dev = (struct vm_device *)private_data;
bus_num, -1, 0,
state->name, bars,
pci_front_config_update,
+ NULL, // no suport for config reads
NULL, // no support for command updates
NULL, // no support for expansion roms
dev);
}
-static int pt_config_update(uint_t reg_num, void * src, uint_t length, void * private_data) {
+static int pt_config_update(struct pci_device * pci_dev, uint_t reg_num, void * src, uint_t length, void * private_data) {
struct vm_device * dev = (struct vm_device *)private_data;
struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
union pci_addr_reg pci_addr = {state->phys_pci_addr.value};
bus_num, -1, 0,
state->name, bars,
pt_config_update,
+ NULL,
NULL,
pt_exp_rom_write,
dev);
struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
- v3_pci_raise_irq(state->pci_bus, 0, state->pci_dev);
+ v3_pci_raise_irq(state->pci_bus, state->pci_dev, 0);
V3_ACK_IRQ(intr->irq);
*/
-static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) {
+static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec) {
struct v3_southbridge * piix3 = dev_data;
struct pci_device * piix3_pci = piix3->southbridge_pci;
struct piix3_config_space * piix3_cfg = (struct piix3_config_space *)(piix3_pci->config_data);
int intr_pin = pci_dev->config_header.intr_pin - 1;
int irq_index = (intr_pin + pci_dev->dev_num - 1) & 0x3;
+ struct v3_irq irq; // Make a copy of the irq state because we will switch the irq number
+
+ irq.ack = vec->ack;
+ irq.private_data = vec->private_data;
/*
PrintError("Raising PCI dev %d intr %d via IOAPIC as IRQ %d and via PIRQ as IRQ %d on VM %p\n",
// deliver first by PIRQ, if it exists
//
if (piix3_cfg->pirq_rc[irq_index] < 16) {
- v3_raise_irq(piix3->vm, piix3_cfg->pirq_rc[irq_index] & 0xf);
+ irq.irq = piix3_cfg->pirq_rc[irq_index] & 0xf;
+
+ // V3_Print("Raising PIIX IRQ %d\n", irq.irq);
+ v3_raise_acked_irq(piix3->vm, irq);
} else {
// not an error
}
// deliver next via the PCI0 to ioapic mapping defined in the
// mptable (ioapic, pins 16->19 are used for PCI0)
// ideally this would check to verify that an ioapic is actually available
- v3_raise_irq(piix3->vm, 16+irq_index);
+ irq.irq = (irq_index + 1) + 16;
+ // V3_Print("Raising PIIX IRQ (#2) %d\n", irq.irq);
+ v3_raise_acked_irq(piix3->vm, irq);
return 0;
-static int lower_pci_irq(struct pci_device * pci_dev, void * dev_data) {
+static int lower_pci_irq(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec) {
struct v3_southbridge * piix3 = dev_data;
struct pci_device * piix3_pci = piix3->southbridge_pci;
struct piix3_config_space * piix3_cfg = (struct piix3_config_space *)(piix3_pci->config_data);
int intr_pin = pci_dev->config_header.intr_pin - 1;
int irq_index = (intr_pin + pci_dev->dev_num - 1) & 0x3;
-
+ struct v3_irq irq; // Make a copy of the irq state because we will switch the irq number
+
+ irq.ack = vec->ack;
+ irq.private_data = vec->private_data;
// PrintError("Lowering PCI IRQ %d\n", piix3_cfg->pirq_rc[irq_index]);
// First, lower the pin on the ioapic
- v3_lower_irq(piix3->vm, irq_index+16);
+ irq.irq = (irq_index + 1) + 16;
+ v3_lower_acked_irq(piix3->vm, irq);
// Next, lower whatever we asserted by the PIRQs
if (piix3_cfg->pirq_rc[irq_index] < 16) {
- v3_lower_irq(piix3->vm, piix3_cfg->pirq_rc[irq_index] & 0xf);
+ irq.irq = piix3_cfg->pirq_rc[irq_index] & 0xf;
+ v3_lower_acked_irq(piix3->vm, irq);
} else {
// not an error
}
pci_dev = v3_pci_register_device(piix3->pci_bus, PCI_MULTIFUNCTION,
bus_num, -1, 0,
"PIIX3", bars,
- NULL, NULL, NULL, piix3);
+ NULL, NULL, NULL, NULL, piix3);
if (pci_dev == NULL) {
PrintError("Could not register PCI Device for PIIX3\n");
return -1;
int isr = ((nic_state->regs.isr & nic_state->regs.imr) & 0xffff);
if(isr & 0xffff){
- v3_pci_raise_irq(nic_state->pci_bus, 0, nic_state->pci_dev);
+ v3_pci_raise_irq(nic_state->pci_bus, nic_state->pci_dev, 0);
nic_state->statistic.tx_interrupts ++;
}
}
pci_dev = v3_pci_register_device(nic_state->pci_bus, PCI_STD_DEVICE, 0, -1, 0,
"RTL8139", bars,
- NULL, NULL, NULL, nic_state);
+ NULL, NULL, NULL, NULL, nic_state);
if (pci_dev == NULL) {
void V3_Init_File(struct v3_file_hooks * hooks) {
file_hooks = hooks;
- PrintDebug("V3 file access inited\n");
-
+ V3_Print("V3 file interface intialized\n");
return;
}
ctrl_area->instrs.NMI = 1;
ctrl_area->instrs.SMI = 0; // allow SMIs to run in guest
ctrl_area->instrs.INIT = 1;
- ctrl_area->instrs.PAUSE = 1;
+ // ctrl_area->instrs.PAUSE = 1;
ctrl_area->instrs.shutdown_evts = 1;
#endif
- //PrintDebug("SVM Returned: Exit Code: %x\n",exit_code);
+
+ // PrintDebug("SVM Returned: Exit Code: %p\n", (void *)exit_code);
switch (exit_code) {
case VMEXIT_IOIO: {
PrintError("SVM Returned: Exit Code: %p\n", (void *)(addr_t)exit_code);
PrintError("io_info1 low = 0x%.8x\n", *(uint_t*)&(exit_info1));
- PrintError("io_info1 high = 0x%.8x\n", *(uint_t *)(((uchar_t *)&(exit_info1)) + 4));
+ PrintError("io_info1 high = 0x%.8x\n", *(uint_t *)(((uint8_t *)&(exit_info1)) + 4));
PrintError("io_info2 low = 0x%.8x\n", *(uint_t*)&(exit_info2));
- PrintError("io_info2 high = 0x%.8x\n", *(uint_t *)(((uchar_t *)&(exit_info2)) + 4));
+ PrintError("io_info2 high = 0x%.8x\n", *(uint_t *)(((uint8_t *)&(exit_info2)) + 4));
if (info->shdw_pg_mode == SHADOW_PAGING) {
while (tmp_dev != __stop__v3_devices) {
- PrintDebug("Device: %s\n", tmp_dev->name);
+ V3_Print("Registering Device: %s\n", tmp_dev->name);
if (v3_htable_search(master_dev_table, (addr_t)(tmp_dev->name))) {
PrintError("Multiple instance of device (%s)\n", tmp_dev->name);
} else {
PrintDebug("CPU Yield\n");
- while (!v3_intr_pending(info)) {
+ while (!v3_intr_pending(info) && (info->vm_info->run_state == VM_RUNNING)) {
uint64_t t, cycles;
/* Yield, allowing time to pass while yielded */
t = v3_get_host_time(&info->time_state);
int v3_lower_irq(struct v3_vm_info * vm, int irq) {
+ struct v3_irq irq_state;
+
+ irq_state.irq = irq;
+ irq_state.ack = NULL;
+ irq_state.private_data = NULL;
+
+ return v3_lower_acked_irq(vm, irq_state);
+}
+
+int v3_raise_irq(struct v3_vm_info * vm, int irq) {
+ struct v3_irq irq_state;
+
+ irq_state.irq = irq;
+ irq_state.ack = NULL;
+ irq_state.private_data = NULL;
+
+ return v3_raise_acked_irq(vm, irq_state);
+}
+
+
+int v3_raise_acked_irq(struct v3_vm_info * vm, struct v3_irq irq) {
struct intr_router * router = NULL;
struct v3_intr_routers * routers = &(vm->intr_routers);
- // PrintDebug("[v3_lower_irq]\n");
+ // PrintDebug("[v3_raise_irq (%d)]\n", irq);
addr_t irq_state = v3_lock_irqsave(routers->irq_lock);
list_for_each_entry(router, &(routers->router_list), router_node) {
- router->router_ops->lower_intr(vm, router->priv_data, irq);
+ router->router_ops->raise_intr(vm, router->priv_data, &irq);
}
-
+
v3_unlock_irqrestore(routers->irq_lock, irq_state);
return 0;
}
-int v3_raise_irq(struct v3_vm_info * vm, int irq) {
+
+int v3_lower_acked_irq(struct v3_vm_info * vm, struct v3_irq irq) {
struct intr_router * router = NULL;
struct v3_intr_routers * routers = &(vm->intr_routers);
- // PrintDebug("[v3_raise_irq (%d)]\n", irq);
+ // PrintDebug("[v3_lower_irq]\n");
addr_t irq_state = v3_lock_irqsave(routers->irq_lock);
list_for_each_entry(router, &(routers->router_list), router_node) {
- router->router_ops->raise_intr(vm, router->priv_data, irq);
+ router->router_ops->lower_intr(vm, router->priv_data, &irq);
}
-
+
v3_unlock_irqrestore(routers->irq_lock, irq_state);
return 0;
-}
+}
// PrintDebug("[intr_pending]\n");
addr_t irq_state = v3_lock_irqsave(intr_state->irq_lock);
- // VIRQs have priority
+ // External IRQs have lowest priority
+ list_for_each_entry(ctrl, &(intr_state->controller_list), ctrl_node) {
+ if (ctrl->ctrl_ops->intr_pending(info, ctrl->priv_data) == 1) {
+ ret = V3_EXTERNAL_IRQ;
+ break;
+ }
+ }
+
+ // VIRQs have 2nd priority
for (i = 0; i < MAX_IRQ / 8; i++) {
if (intr_state->virq_map[i] != 0) {
ret = V3_VIRTUAL_IRQ;
}
}
- if (ret == V3_INVALID_INTR) {
- list_for_each_entry(ctrl, &(intr_state->controller_list), ctrl_node) {
- if (ctrl->ctrl_ops->intr_pending(info, ctrl->priv_data) == 1) {
- ret = V3_EXTERNAL_IRQ;
- break;
- }
- }
- }
-
- /* for swintr injection */
+ /* SWINTRs have highest */
if (intr_state->swintr_posted == 1) {
ret = V3_SOFTWARE_INTR;
}
int v3_deinit_shdw_impl(struct v3_vm_info * vm) {
struct v3_shdw_pg_impl * impl = vm->shdw_impl.current_impl;
+ if (impl == NULL) {
+ // Shadow paging not implemented
+ return 0;
+ }
+
if (impl->deinit(vm) == -1) {
PrintError("Error deinitializing shadow paging implementation\n");
return -1;