/* This means don't interrupt guest when buffer consumed. */
#define VIRTIO_NO_IRQ_FLAG 0x1
+
+/* ISR Flags */
+#define VIRTIO_ISR_ACTIVE 0x1
+#define VIRTIO_ISR_CFG_CHANGED 0x2
+
+
+
/* The virtio configuration space is a hybrid io/memory mapped model
* All IO is done via IO port accesses
* The IO ports access fields in a virtio data structure, and the base io port
int v3_pci_set_irq_bridge(struct vm_device * pci_bus, int bus_num,
- int (*raise_pci_irq)(struct vm_device * dev, uint_t intr_line),
- int (*lower_pci_irq)(struct vm_device * dev, uint_t intr_line),
+ int (*raise_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev),
+ int (*lower_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev),
struct vm_device * bridge_dev);
#define PAGE_SIZE 4096
-#define BALLOON_HCALL 0xba00
+#define BALLOON_START_HCALL 0xba00 // size in rax
+#define BALLOON_QUERY_HCALL 0xba01 // req_pgs in rcx, alloc_pgs in rdx
struct balloon_config {
uint32_t requested_pages;
/* How this works:
* A ballooning request is made by specifying the new memory size of the guest. The guest
* will then shrink the amount of of memory it uses to target. The target size is stored in the
- * Virtio PCI configuration space in the requested pages field. The device raises its irq, to notify the guest
+ * Virtio PCI configuration space in the requested pages field.
+ * The device raises its irq, to notify the guest
*
* The guest might not be able to shrink to target, so it stores the size it was able to shrink to
* into the allocate_pages field of the pci configuration space.
*
- * When the guest frees pages it writes the addresses to the deflation queue (the 2nd one), and does a kick.
- * When pages are given back to the host they are fed in via the inflation queue (the 1st one), and raises an irq.
+ * When the guest frees pages it writes the addresses to the deflation queue (the 2nd one),
+ * and does a kick.
+ * When pages are given back to the host they are fed in via the inflation queue (the 1st one),
+ * and raises an irq.
*/
#define VIRTIO_NOTIFY_HOST 0x01
-
-
-
struct virtio_balloon_state {
struct balloon_config balloon_cfg;
struct virtio_config virtio_cfg;
return 0;
}
+static int get_desc_count(struct virtio_queue * q, int index) {
+ struct vring_desc * tmp_desc = &(q->desc[index]);
+ int cnt = 1;
+
+ while (tmp_desc->flags & VIRTIO_NEXT_FLAG) {
+ tmp_desc = &(q->desc[tmp_desc->next]);
+ cnt++;
+ }
+ return cnt;
+}
static int handle_kick(struct vm_device * dev) {
struct virtio_balloon_state * virtio = (struct virtio_balloon_state *)dev->private_data;
struct virtio_queue * q = virtio->cur_queue;
- PrintDebug("VIRTIO KICK: cur_index=%d, avail_index=%d\n", q->cur_avail_idx, q->avail->index);
+ PrintDebug("VIRTIO BALLOON KICK: cur_index=%d (mod=%d), avail_index=%d\n",
+ q->cur_avail_idx, q->cur_avail_idx % QUEUE_SIZE, q->avail->index);
while (q->cur_avail_idx < q->avail->index) {
- struct vring_desc * hdr_desc = NULL;
- struct vring_desc * buf_desc = NULL;
- struct vring_desc * status_desc = NULL;
- uint16_t chain_idx = q->avail->ring[q->cur_avail_idx];
+ struct vring_desc * tmp_desc = NULL;
+ uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
+ int desc_cnt = get_desc_count(q, desc_idx);
+ int i = 0;
uint32_t req_len = 0;
- int chained = 1;
- PrintDebug("chained=%d, Chain Index=%d\n", chained, chain_idx);
- while (chained) {
- hdr_desc = &(q->desc[chain_idx]);
+ PrintDebug("Descriptor Count=%d, index=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE);
+
+ for (i = 0; i < desc_cnt; i++) {
+ addr_t page_addr;
+ tmp_desc = &(q->desc[desc_idx]);
- PrintDebug("Header Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", hdr_desc,
- (void *)(hdr_desc->addr_gpa), hdr_desc->length, hdr_desc->flags, hdr_desc->next);
+ PrintDebug("Header Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", tmp_desc,
+ (void *)(tmp_desc->addr_gpa), tmp_desc->length,
+ tmp_desc->flags, tmp_desc->next);
- if (!(hdr_desc->flags & VIRTIO_NEXT_FLAG)) {
- PrintError("Balloon operations must chain a buffer descriptor\n");
- return -1;
- }
- buf_desc = &(q->desc[hdr_desc->next]);
-
- PrintDebug("Buffer Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", buf_desc,
- (void *)(buf_desc->addr_gpa), buf_desc->length, buf_desc->flags, buf_desc->next);
-
- if (!(buf_desc->flags & VIRTIO_NEXT_FLAG)) {
- PrintError("Balloon operatoins must chain a status descriptor\n");
+ if (guest_pa_to_host_va(dev->vm, tmp_desc->addr_gpa, (addr_t *)&(page_addr)) == -1) {
+ PrintError("Could not translate block header address\n");
return -1;
}
-
- status_desc = &(q->desc[buf_desc->next]);
-
- // We detect whether we are chained here...
- if (status_desc->flags & VIRTIO_NEXT_FLAG) {
- chained = 1;
- chain_idx = status_desc->next;
- } else {
- chained = 0;
- }
-
- PrintDebug("Status Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", status_desc,
- (void *)(status_desc->addr_gpa), status_desc->length, status_desc->flags, status_desc->next);
-
/*
- if (handle_balloon_op(dev, hdr_desc, buf_desc, status_desc) == -1) {
+ if (handle_balloon_op(dev, tmp_desc, buf_desc, status_desc) == -1) {
PrintError("Error handling balloon operation\n");
return -1;
}
*/
PrintDebug("Guest Balloon Currently Ignored\n");
- PrintDebug("\t Requested=%d, Allocated=%d\n", virtio->balloon_cfg.requested_pages, virtio->balloon_cfg.allocated_pages);
+ PrintDebug("\t Requested=%d, Allocated=%d\n",
+ virtio->balloon_cfg.requested_pages,
+ virtio->balloon_cfg.allocated_pages);
-
- req_len += (buf_desc->length + status_desc->length);
+ req_len += tmp_desc->length;
+ desc_idx = tmp_desc->next;
}
- q->used->ring[q->used->index].id = q->avail->ring[q->cur_avail_idx];
- q->used->ring[q->used->index].length = req_len; // What do we set this to????
-
- q->used->index = (q->used->index + 1) % (QUEUE_SIZE * sizeof(struct vring_desc));;
-
+ q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
+ q->used->ring[q->used->index % QUEUE_SIZE].length = req_len; // What do we set this to????
- q->cur_avail_idx = (q->cur_avail_idx + 1) % (QUEUE_SIZE * sizeof(struct vring_desc));
+ q->used->index++;
+ q->cur_avail_idx++;
}
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);
- virtio->virtio_cfg.pci_isr = 1;
+ virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
}
return 0;
default:
if ( (port_idx >= sizeof(struct virtio_config)) &&
(port_idx < (sizeof(struct virtio_config) + sizeof(struct balloon_config))) ) {
-
+ int cfg_offset = port_idx - sizeof(struct virtio_config);
uint8_t * cfg_ptr = (uint8_t *)&(virtio->balloon_cfg);
- memcpy(dst, cfg_ptr, length);
+
+ memcpy(dst, cfg_ptr + cfg_offset, length);
} else {
PrintError("Read of Unhandled Virtio Read\n");
struct virtio_balloon_state * virtio = (struct virtio_balloon_state *)dev->private_data;
virtio->balloon_cfg.requested_pages = size / PAGE_SIZE; // number of pages
+
+ PrintDebug("Requesting %d pages\n", virtio->balloon_cfg.requested_pages);
+
v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+ virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE | VIRTIO_ISR_CFG_CHANGED;
return 0;
}
+static int handle_query_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) {
+ struct vm_device * dev = (struct vm_device *)priv_data;
+ struct virtio_balloon_state * virtio = (struct virtio_balloon_state *)dev->private_data;
+
+ info->vm_regs.rcx = virtio->balloon_cfg.requested_pages;
+ info->vm_regs.rdx = virtio->balloon_cfg.allocated_pages;
+
+
+ return 0;
+}
+
+
+
static int virtio_init(struct guest_info * vm, void * cfg_data) {
virtio_reset(dev);
- v3_register_hypercall(vm, BALLOON_HCALL, handle_hcall, dev);
+ v3_register_hypercall(vm, BALLOON_START_HCALL, handle_hcall, dev);
+ v3_register_hypercall(vm, BALLOON_QUERY_HCALL, handle_query_hcall, dev);
return 0;
}
#include <devices/pci.h>
+#ifndef DEBUG_VIRTIO_BLK
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
+#endif
+
#define BLK_CAPACITY_PORT 20
#define BLK_MAX_SIZE_PORT 28
#define BLK_MAX_SEG_PORT 32
return 0;
}
-static int handle_read_op(struct vm_device * dev, uint8_t * buf, uint64_t sector, uint32_t len) {
+static int handle_read_op(struct vm_device * dev, uint8_t * buf, uint64_t * sector, uint32_t len) {
struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
+ int ret = -1;
if (virtio->block_type == BLOCK_DISK) {
if (len % HD_SECTOR_SIZE) {
PrintDebug("Reading Disk\n");
+
+ ret = virtio->hd_ops->read(buf, len / HD_SECTOR_SIZE, *sector, virtio->backend_data);
+
+ *sector += len / HD_SECTOR_SIZE;
- return virtio->hd_ops->read(buf, len / HD_SECTOR_SIZE, sector / HD_SECTOR_SIZE, virtio->backend_data);
} else if (virtio->block_type == BLOCK_CDROM) {
if (len % ATAPI_BLOCK_SIZE) {
PrintError("Write of something that is not an ATAPI block len %d, mod=%d\n", len, len % ATAPI_BLOCK_SIZE);
return -1;
}
- return virtio->cd_ops->read(buf, len / ATAPI_BLOCK_SIZE, sector / ATAPI_BLOCK_SIZE, virtio->backend_data);
+ ret = virtio->cd_ops->read(buf, len / ATAPI_BLOCK_SIZE, *sector , virtio->backend_data);
+ *sector += len / ATAPI_BLOCK_SIZE;
}
- return -1;
+ return ret;
}
-static int handle_write_op(struct vm_device * dev, uint8_t * buf, uint64_t sector, uint32_t len) {
+static int handle_write_op(struct vm_device * dev, uint8_t * buf, uint64_t * sector, uint32_t len) {
struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
-
+ int ret = -1;
if (virtio->block_type == BLOCK_DISK) {
if (len % HD_SECTOR_SIZE) {
PrintDebug("Writing Disk\n");
- return virtio->hd_ops->write(buf, len / HD_SECTOR_SIZE, sector / HD_SECTOR_SIZE, virtio->backend_data);
+ ret = virtio->hd_ops->write(buf, len / HD_SECTOR_SIZE, *sector, virtio->backend_data);
+
+ *sector += len / HD_SECTOR_SIZE;
}
- return -1;
+ return ret;
}
+
+// multiple block operations need to increment the sector
+
static int handle_block_op(struct vm_device * dev, struct blk_op_hdr * hdr,
struct vring_desc * buf_desc, uint8_t * status) {
struct virtio_blk_state * virtio = (struct virtio_blk_state *)dev->private_data;
if (hdr->type == BLK_IN_REQ) {
if (virtio->block_type != BLOCK_NONE) {
- if (handle_read_op(dev, buf, hdr->sector, buf_desc->length) == -1) {
+ if (handle_read_op(dev, buf, &(hdr->sector), buf_desc->length) == -1) {
*status = BLK_STATUS_ERR;
+ return -1;
} else {
*status = BLK_STATUS_OK;
}
} else if (hdr->type == BLK_OUT_REQ) {
if (virtio->block_type == BLOCK_DISK) {
- if (handle_write_op(dev, buf, hdr->sector, buf_desc->length) == -1) {
+ if (handle_write_op(dev, buf, &(hdr->sector), buf_desc->length) == -1) {
*status = BLK_STATUS_ERR;
+ return -1;
} else {
*status = BLK_STATUS_OK;
}
+
} else {
*status = BLK_STATUS_NOT_SUPPORTED;
}
+
} else if (hdr->type == BLK_SCSI_CMD) {
- PrintDebug("VIRTIO: SCSI Command Not supported!!!\n");
+ PrintError("VIRTIO: SCSI Command Not supported!!!\n");
*status = BLK_STATUS_NOT_SUPPORTED;
+ return -1;
}
+
+
PrintDebug("Returning Status: %d\n", *status);
return 0;
struct vring_desc * hdr_desc = NULL;
struct vring_desc * buf_desc = NULL;
struct vring_desc * status_desc = NULL;
- struct blk_op_hdr * hdr = NULL;
+ struct blk_op_hdr hdr;
+ addr_t hdr_addr = 0;
uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
int desc_cnt = get_desc_count(q, desc_idx);
int i = 0;
PrintDebug("Header Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", hdr_desc,
(void *)(hdr_desc->addr_gpa), hdr_desc->length, hdr_desc->flags, hdr_desc->next);
- if (guest_pa_to_host_va(dev->vm, hdr_desc->addr_gpa, (addr_t *)&(hdr)) == -1) {
+ if (guest_pa_to_host_va(dev->vm, hdr_desc->addr_gpa, &(hdr_addr)) == -1) {
PrintError("Could not translate block header address\n");
return -1;
}
+ // We copy the block op header out because we are going to modify its contents
+ memcpy(&hdr, (void *)hdr_addr, sizeof(struct blk_op_hdr));
+
+ PrintDebug("Blk Op Hdr (ptr=%p) type=%d, sector=%p\n", (void *)hdr_addr, hdr.type, (void *)hdr.sector);
+
desc_idx = hdr_desc->next;
for (i = 0; i < desc_cnt - 2; i++) {
PrintDebug("Buffer Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", buf_desc,
(void *)(buf_desc->addr_gpa), buf_desc->length, buf_desc->flags, buf_desc->next);
- if (handle_block_op(dev, hdr, buf_desc, &tmp_status) == -1) {
+ if (handle_block_op(dev, &hdr, buf_desc, &tmp_status) == -1) {
PrintError("Error handling block operation\n");
return -1;
}
req_len += status_desc->length;
*status_ptr = status;
- q->used->ring[q->used->index].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
- q->used->ring[q->used->index].length = req_len; // What do we set this to????
+ q->used->ring[q->used->index % QUEUE_SIZE].id = q->avail->ring[q->cur_avail_idx % QUEUE_SIZE];
+ q->used->ring[q->used->index % QUEUE_SIZE].length = req_len; // What do we set this to????
q->used->index++;
q->cur_avail_idx++;
int msg_len = info->vm_regs.rcx;
addr_t msg_gpa = info->vm_regs.rbx;
-
+ int buf_is_va = info->vm_regs.rdx;
+
if (msg_len >= BUF_SIZE) {
PrintError("Console message too large for buffer (len=%d)\n", msg_len);
return -1;
}
-
- if (read_guest_pa_memory(info, msg_gpa, msg_len, (uchar_t *)state->debug_buf) != msg_len) {
- PrintError("Could not read debug message\n");
- return -1;
- }
+
+ if (buf_is_va == 1) {
+ if (read_guest_va_memory(info, msg_gpa, msg_len, (uchar_t *)state->debug_buf) != msg_len) {
+ PrintError("Could not read debug message\n");
+ return -1;
+ }
+ } else {
+ if (read_guest_pa_memory(info, msg_gpa, msg_len, (uchar_t *)state->debug_buf) != msg_len) {
+ PrintError("Could not read debug message\n");
+ return -1;
+ }
+ }
state->debug_buf[msg_len] = 0;
// Bitmap of the allocated device numbers
uint8_t dev_map[MAX_BUS_DEVICES / 8];
- int (*raise_pci_irq)(struct vm_device * dev, uint_t intr_pin);
- int (*lower_pci_irq)(struct vm_device * dev, uint_t intr_pin);
+ int (*raise_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev);
+ int (*lower_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev);
struct vm_device * irq_bridge_dev;
};
int v3_pci_set_irq_bridge(struct vm_device * pci_bus, int bus_num,
- int (*raise_pci_irq)(struct vm_device * dev, uint_t intr_line),
- int (*lower_pci_irq)(struct vm_device * dev, uint_t intr_line),
+ int (*raise_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev),
+ int (*lower_pci_irq)(struct vm_device * dev, struct pci_device * pci_dev),
struct vm_device * bridge_dev) {
struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
- return bus->raise_pci_irq(bus->irq_bridge_dev, dev->config_header.intr_pin);
+ return bus->raise_pci_irq(bus->irq_bridge_dev, dev);
}
int v3_pci_lower_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]);
- return bus->lower_pci_irq(bus->irq_bridge_dev, dev->config_header.intr_pin);
+ return bus->lower_pci_irq(bus->irq_bridge_dev, dev);
}
// if dev_num == -1, auto assign
return 0;
}
-static int raise_pci_irq(struct vm_device * dev, uint_t intr_pin) {
- struct v3_southbridge * piix3 = (struct v3_southbridge *)(dev->private_data);
- struct pci_device * pci_dev = piix3->southbridge_pci;
- struct piix3_config_space * piix3_cfg = (struct piix3_config_space *)(pci_dev->config_data);
- PrintError("Raising PCI IRQ %d\n", piix3_cfg->pirq_rc[intr_pin]);
+//irq is pirq_rc[intr_pin + pci_dev_num - 1] & 0x3
+
+static int raise_pci_irq(struct vm_device * dev, struct pci_device * pci_dev) {
+ struct v3_southbridge * piix3 = (struct v3_southbridge *)(dev->private_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;
- v3_raise_irq(dev->vm, piix3_cfg->pirq_rc[intr_pin]);
+ PrintError("Raising PCI IRQ %d\n", piix3_cfg->pirq_rc[irq_index]);
+
+ v3_raise_irq(dev->vm, piix3_cfg->pirq_rc[irq_index]);
return 0;
}
-static int lower_pci_irq(struct vm_device * dev, uint_t intr_pin) {
+static int lower_pci_irq(struct vm_device * dev, struct pci_device * pci_dev) {
struct v3_southbridge * piix3 = (struct v3_southbridge *)(dev->private_data);
- struct pci_device * pci_dev = piix3->southbridge_pci;
- struct piix3_config_space * piix3_cfg = (struct piix3_config_space *)(pci_dev->config_data);
-
- PrintError("Lowering PCI IRQ %d\n", piix3_cfg->pirq_rc[intr_pin]);
+ 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;
+
+ PrintError("Lowering PCI IRQ %d\n", piix3_cfg->pirq_rc[irq_index]);
- v3_lower_irq(dev->vm, piix3_cfg->pirq_rc[intr_pin]);
+ v3_lower_irq(dev->vm, piix3_cfg->pirq_rc[irq_index]);
return 0;
}
struct vm_device * blk_dev;
+ uint_t swapped_pages;
+ uint_t unswapped_pages;
+
uint64_t capacity;
uint8_t * swap_space;
addr_t swap_base_addr;
-
};
int offset = lba * HD_SECTOR_SIZE;
int length = sector_count * HD_SECTOR_SIZE;
+
PrintDebug("SymSwap: Reading %d bytes to %p from %p\n", length,
buf, (void *)(swap->swap_space + offset));
+
+ if (length % 4096) {
+ PrintError("Swapping in length that is not a page multiple\n");
+ }
memcpy(buf, swap->swap_space + offset, length);
+ swap->unswapped_pages += (length / 4096);
+
+ PrintDebug("Swapped in %d pages\n", length / 4096);
+
return 0;
}
struct swap_state * swap = (struct swap_state *)(dev->private_data);
int offset = lba * HD_SECTOR_SIZE;
int length = sector_count * HD_SECTOR_SIZE;
-
- PrintDebug("SymSwap: Writing %d bytes to %p from %p\n", length,
- (void *)(swap->swap_space + offset), buf);
+ /*
+ PrintDebug("SymSwap: Writing %d bytes to %p from %p\n", length,
+ (void *)(swap->swap_space + offset), buf);
+ */
+ if (length % 4096) {
+ PrintError("Swapping out length that is not a page multiple\n");
+ }
memcpy(swap->swap_space + offset, buf, length);
+ swap->swapped_pages += (length / 4096);
+
+ PrintDebug("Swapped out %d pages\n", length / 4096);
+
return 0;
}
swap->blk_dev = virtio_blk;
swap->capacity = SWAP_CAPACITY;
+ swap->swapped_pages = 0;
+ swap->unswapped_pages = 0;
+
swap->swap_base_addr = (addr_t)V3_AllocPages(swap->capacity / 4096);
swap->swap_space = (uint8_t *)V3_VAddr((void *)(swap->swap_base_addr));
memset(swap->swap_space, 0, SWAP_CAPACITY);