menu "Time Management"
+config TIME_HIDE_VM_COST
+ bool "Hide VMM Run Cost"
+ default n
+ help
+ Offset guest time from host time sufficiently to hide the cost of
+ running in the virtual machine. This can aid the consistency of
+ time between multiple timers, but can cause the guest to run
+ a good bit slower than the host in VM-intensive parts of the code.
+
config TIME_VIRTUALIZE_TSC
- bool "Virtualize guest TSC"
+ bool "Fully virtualize guest TSC"
default n
help
Virtualize the processor time stamp counter in the guest,
generally increasing consistency between various time sources
but also potentially making guest time run slower than real time.
-
+
endmenu
menu "Symbiotic Functions"
config BUILT_IN_STRDUP
bool "strdup()"
- default n
depends on BUILT_IN_STDLIB
help
This enables Palacios' internal implementation of strdup
/* the logical cpu on which this core runs */
uint32_t cpu_id;
+ /* the physical cpu on which this core runs */
+ uint32_t host_cpu_id;
+
};
#include <palacios/vmm.h>
+struct nic_statistics {
+ uint64_t tx_pkts;
+ uint64_t tx_bytes;
+ uint64_t tx_dropped;
+
+ uint64_t rx_pkts;
+ uint64_t rx_bytes;
+ uint64_t rx_dropped;
+
+ uint32_t interrupts;
+};
+
static inline int is_multicast_ethaddr(const uint8_t * addr)
{
V3_ASSERT(ETH_ALEN == 6);
addr [0] |= 0x02; /* set local assignment bit (IEEE802) */
}
+/*-
+ * CRC32 code derived from work by Gary S. Brown.
+ *
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ */
+static uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+static inline uint32_t v3_crc32(uint32_t crc, uint8_t *buf, int size)
+{
+ const uint8_t *p;
+
+ p = buf;
+ crc = crc ^ ~0U;
+
+ while (size--){
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ }
+
+ return crc ^ ~0U;
+}
#endif
* and the University of New Mexico. You can find out more at
* http://www.v3vee.org
*
+ * Copyright (c) 2010, Patrick Bridges <bridges@cs.unm.edu>
* Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
* Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
* All rights reserved.
*
* Author: Jack Lange <jarusl@cs.northwestern.edu>
+ * Patrick Bridges <bridges@cs.unm.edu>
*
* This is free software. You are permitted to use,
* redistribute, and modify it as specified in the file "V3VEE_LICENSE".
struct vm_time {
uint32_t host_cpu_freq; // in kHZ
uint32_t guest_cpu_freq; // can be lower than host CPU freq!
-
+
sint64_t guest_host_offset;// Offset of monotonic guest time from host time
sint64_t tsc_guest_offset; // Offset of guest TSC from monotonic guest time
-
+
uint64_t last_update; // Last time (in monotonic guest time) the
// timers were updated
uint64_t initial_time; // Time when VMM started.
-
+ uint64_t enter_time; // Host time the guest was last entered
+ uint64_t exit_time; // Host time the the VM was exited to
struct v3_msr tsc_aux; // Auxilliary MSR for RDTSCP
// Installed Timers slaved off of the guest monotonic TSC
void v3_deinit_time_core(struct guest_info * core);
void v3_deinit_time_vm(struct v3_vm_info * vm);
-
int v3_start_time(struct guest_info * core);
+
+int v3_time_enter_vm(struct guest_info * core);
+int v3_time_exit_vm(struct guest_info * core);
+
int v3_adjust_time(struct guest_info * core);
+int v3_offset_time(struct guest_info * core, sint64_t offset);
// Basic functions for attaching timers to the passage of time
struct v3_timer * v3_add_timer(struct guest_info * info, struct v3_timer_ops * ops, void * private_data);
// Returns *monotonic* guest time.
static inline uint64_t v3_get_guest_time(struct vm_time *t) {
+#ifdef CONFIG_TIME_HIDE_VM_COST
+ V3_ASSERT(t->exit_time);
+ return t->exit_time + t->guest_host_offset;
+#else
return v3_get_host_time(t) + t->guest_host_offset;
+#endif
}
// Returns the TSC value seen by the guest
#include <palacios/vmm.h>
#include <palacios/vmm_ethernet.h>
-#define MAC_ANY 0
-#define MAC_NOT 1
-#define MAC_NONE 2
-#define MAC_ADDR 3
+#define MAC_NOSET 0
+#define MAC_ANY 11
+#define MAC_NOT 12
+#define MAC_NONE 13
+#define MAC_ADDR 14
-#define LINK_INTERFACE 0
-#define LINK_EDGE 1
-#define LINK_ANY 2
+#define LINK_NOSET 0
+#define LINK_INTERFACE 11
+#define LINK_EDGE 12
+#define LINK_ANY 13
#define VNET_HASH_SIZE 17
uint8_t src_mac_qual;
uint8_t dst_mac_qual;
- uint32_t dst_id;
+ int dst_id;
uint8_t dst_type;
- uint32_t src_id;
+ int src_id;
uint8_t src_type;
} __attribute__((packed));
void * priv_data);
int v3_vnet_add_route(struct v3_vnet_route route);
int v3_vnet_send_pkt(struct v3_vnet_pkt * pkt, void * private_data);
+int v3_vnet_find_dev(uint8_t * mac);
#ifdef __V3VEE__
struct v3_vnet_pkt * pkt,
void * dev_data);
void (*poll) (struct v3_vm_info * vm, void * dev_data);
-
- void (*start_tx)(void * dev_data);
- void (*stop_tx)(void * dev_data);
};
int v3_init_vnet(void);
const char *v3_xml_attr(struct v3_xml * xml, const char * attr);
+
+
// Traverses the v3_xml sturcture to retrieve a specific subtag. Takes a
// variable length list of tag names and indexes. The argument list must be
// terminated by either an index of -1 or an empty string tag name. Example:
// frees the memory allocated for an v3_xml structure
void v3_xml_free(struct v3_xml * xml);
+
+
+
+
+
+char * v3_xml_tostr(struct v3_xml * xml);
+
+struct v3_xml * v3_xml_insert(struct v3_xml * xml, struct v3_xml * dest, size_t off);
+struct v3_xml * v3_xml_set_txt(struct v3_xml * xml, const char *txt);
+
+
+struct v3_xml * v3_xml_set_attr(struct v3_xml * xml, const char * name, const char * value);
+
+
#endif // __VMM_XML_H
// update counter with remainder (mod reload)
state->pit_counter = state->pit_reload - cpu_cycles;
- //PrintDebug("8254 PIT: Handling %d crystal tics\n", oscillations);
+ if (oscillations) {
+ PrintDebug("8254 PIT: Handling %d crystal tics\n", oscillations);
+ }
+
if (handle_crystal_tics(state, &(state->ch_0), oscillations) == 1) {
// raise interrupt
- PrintDebug("8254 PIT: Injecting Timer interrupt to guest\n");
+ // PrintDebug("8254 PIT: Injecting Timer interrupt to guest\n");
v3_raise_irq(info->vm_info, 0);
}
// just hardcode the core context
struct guest_info * info = &(vm->cores[0]);
- uint_t cpu_khz = V3_CPU_KHZ();
+ uint_t cpu_khz = info->time_state.guest_cpu_freq;
ullong_t reload_val = (ullong_t)cpu_khz * 1000;
pit_state = (struct pit *)V3_Malloc(sizeof(struct pit));
config APIC
bool "APIC"
default y
- depends on EXPERIMENTAL
help
Includes the Virtual APIC device
config IO_APIC
bool "IOAPIC"
- depends on EXPERIMENTAL
default y
help
Includes the Virtual IO APIC
help
Enable debugging for the NE2K
+config RTL8139
+ bool "RTL8139"
+ depends on EXPERIMENTAL
+ default n
+ help
+ Includes the Virtual RTL8139 network card
+
+config DEBUG_RTL8139
+ bool "RTL8139 debugging"
+ default n
+ depends on RTL8139 && DEBUG_ON
+ help
+ Enable debugging for the RTL8139
config NIC_BRIDGE
bool "Enable Direct Bridge to Host network"
obj-$(CONFIG_NIC_BRIDGE) += nic_bridge.o
obj-$(CONFIG_NE2K) += ne2k.o
+obj-$(CONFIG_RTL8139) += rtl8139.o
obj-$(CONFIG_TMPDISK) += tmpdisk.o
obj-$(CONFIG_RAMDISK) += ramdisk.o
#define PrintDebug(fmt, args...)
#endif
-#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10))
struct virtio_net_hdr {
uint8_t flags;
-
-#define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */
+
uint8_t gso_type;
uint16_t hdr_len; /* Ethernet + IP + tcp/udp hdrs */
uint16_t gso_size; /* Bytes to append to hdr_len per frame */
}__attribute__((packed));
-/* This is the version of the header to use when the MRG_RXBUF
- * feature has been negotiated. */
struct virtio_net_hdr_mrg_rxbuf {
struct virtio_net_hdr hdr;
uint16_t num_buffers; /* Number of merged rx buffers */
#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */
#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */
-/* this is not how virtio supposed to be,
- * we may need a separately implemented virtio_pci
- * In order to make guest to get virtio MAC from host
- * I added it here -- Lei
- */
- #define VIRTIO_NET_CONFIG 20
-
+/* Port to get virtio config */
+#define VIRTIO_NET_CONFIG 20
struct virtio_net_config
{
struct pci_device * pci_dev;
int io_range_size;
- struct virtio_queue rx_vq; /* idx 0, pkts to guest */
- struct virtio_queue tx_vq; /* idx 1, pkts from guest */
- struct virtio_queue ctrl_vq; /* idx 2 */
+ struct virtio_queue rx_vq; /* idx 0*/
+ struct virtio_queue tx_vq; /* idx 1*/
+ struct virtio_queue ctrl_vq; /* idx 2*/
- int buffed_rx;
- int tx_disabled; /* stop TX pkts from guest */
-
- uint64_t pkt_sent, pkt_recv, pkt_drop;
+ struct nic_statistics statistics;
struct v3_dev_net_ops * net_ops;
v3_lock_t rx_lock, tx_lock;
struct list_head dev_link;
};
-/* virtio nic error type */
-#define ERR_VIRTIO_OTHER 1
-#define ERR_VIRTIO_RXQ_FULL 2
-#define ERR_VIRTIO_RXQ_NOSET 3
-#define ERR_VIRTIO_TXQ_NOSET 4
-#define ERR_VIRTIO_TXQ_FULL 5
-#define ERR_VIRTIO_TXQ_DISABLED 6
-
-
-
static int virtio_init_state(struct virtio_net_state * virtio)
{
PrintError("Virtio NIC: Failure to init locks for net_state\n");
}
- virtio->pkt_sent = virtio->pkt_recv = virtio->pkt_drop = 0;
- virtio->buffed_rx = 0;
-
return 0;
}
-static int
-pkt_tx(struct guest_info * core,
+static int tx_one_pkt(struct guest_info * core,
struct virtio_net_state * virtio,
struct vring_desc * buf_desc)
{
uint32_t len = buf_desc->length;
if (v3_gpa_to_hva(core, buf_desc->addr_gpa, (addr_t *)&(buf)) == -1) {
- PrintError("Could not translate buffer address\n");
- return -ERR_VIRTIO_OTHER;
+ PrintDebug("Could not translate buffer address\n");
+ return -1;
+ }
+
+ if(virtio->net_ops->send(buf, len, virtio->backend_data) >= 0){
+ virtio->statistics.tx_pkts ++;
+ virtio->statistics.tx_bytes += len;
+
+ return 0;
}
- return virtio->net_ops->send(buf, len, virtio->backend_data);
+ virtio->statistics.tx_dropped ++;
+
+ return -1;
}
}
-/* interrupt the guest, so the guest core get EXIT to Palacios
- * this happens when there are either incoming pkts for the guest
- * or the guest can start TX pkts again */
+/* interrupt the guest, so the guest core get EXIT to Palacios */
static inline void notify_guest(struct virtio_net_state * virtio){
v3_interrupt_cpu(virtio->virtio_dev->vm, virtio->virtio_dev->vm->cores[0].cpu_id, 0);
}
-/* guest free some pkts from rx queue */
-static int handle_rx_kick(struct guest_info *core,
+/* guest free some pkts for rx queue */
+static int handle_rx_queue_kick(struct guest_info * core,
struct virtio_net_state * virtio)
{
- unsigned long flags;
-
- flags = v3_lock_irqsave(virtio->rx_lock);
-
- if(virtio->net_ops->start_rx != NULL){
- virtio->net_ops->start_rx(virtio->backend_data);
- }
- //disable_cb(&virtio->rx_vq);
-
- v3_unlock_irqrestore(virtio->rx_lock, flags);
-
return 0;
}
-static int handle_ctrl(struct guest_info *core,
+static int handle_ctrl(struct guest_info * core,
struct virtio_net_state * virtio) {
return 0;
}
-static int handle_pkt_tx(struct guest_info *core,
+static int handle_pkt_tx(struct guest_info * core,
struct virtio_net_state * virtio_state)
{
- struct virtio_queue * q = &(virtio_state->tx_vq);
- struct virtio_net_hdr * hdr = NULL;
- int recved = 0;
+ struct virtio_queue *q = &(virtio_state->tx_vq);
+ struct virtio_net_hdr *hdr = NULL;
+ int txed = 0;
unsigned long flags;
- if (!q->ring_avail_addr)
- return -ERR_VIRTIO_TXQ_NOSET;
-
- if(virtio_state->tx_disabled)
- return -ERR_VIRTIO_TXQ_DISABLED;
+ if (!q->ring_avail_addr) {
+ return -1;
+ }
flags = v3_lock_irqsave(virtio_state->tx_lock);
while (q->cur_avail_idx != q->avail->index) {
goto exit_error;
}
- hdr = (struct virtio_net_hdr*)hdr_addr;
+ hdr = (struct virtio_net_hdr *)hdr_addr;
desc_idx = hdr_desc->next;
if(desc_cnt > 2){
/* here we assumed that one ethernet pkt is not splitted into multiple virtio buffer */
for (i = 0; i < desc_cnt - 1; i++) {
struct vring_desc * buf_desc = &(q->desc[desc_idx]);
- if (pkt_tx(core, virtio_state, buf_desc) == -1) {
+ if (tx_one_pkt(core, virtio_state, buf_desc) == -1) {
PrintError("Error handling nic operation\n");
goto exit_error;
}
req_len += buf_desc->length;
desc_idx = buf_desc->next;
}
- virtio_state->pkt_sent ++;
- recved ++;
q->used->ring[q->used->index % q->queue_size].id = q->avail->ring[q->cur_avail_idx % q->queue_size];
- q->used->ring[q->used->index % q->queue_size].length = req_len; // What do we set this to????
+ q->used->ring[q->used->index % q->queue_size].length = req_len; /* What do we set this to???? */
q->used->index ++;
q->cur_avail_idx ++;
+
+ txed ++;
}
v3_unlock_irqrestore(virtio_state->tx_lock, flags);
-
- if(!recved)
- return 0;
- if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
+ 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->virtio_cfg.pci_isr = 0x1;
+
+ virtio_state->statistics.interrupts ++;
}
return 0;
exit_error:
v3_unlock_irqrestore(virtio_state->tx_lock, flags);
- return -ERR_VIRTIO_OTHER;
+ return -1;
}
switch (queue_idx) {
case 0:
virtio_setup_queue(core, virtio, &virtio->rx_vq, pfn, page_addr);
- //disable_cb(&virtio->rx_vq);
break;
case 1:
virtio_setup_queue(core, virtio, &virtio->tx_vq, pfn, page_addr);
- //disable_cb(&virtio->tx_vq);
break;
case 2:
virtio_setup_queue(core, virtio, &virtio->ctrl_vq, pfn, page_addr);
{
uint16_t queue_idx = *(uint16_t *)src;
if (queue_idx == 0){
- handle_rx_kick(core, virtio);
+ if(handle_rx_queue_kick(core, virtio) == -1){
+ PrintError("Could not handle Virtio NIC rx kick\n");
+ return -1;
+ }
} else if (queue_idx == 1){
if (handle_pkt_tx(core, virtio) == -1) {
- PrintError("Could not handle NIC Notification\n");
+ PrintError("Could not handle Virtio NIC tx kick\n");
return -1;
}
} else if (queue_idx == 2){
if (handle_ctrl(core, virtio) == -1) {
- PrintError("Could not handle NIC Notification\n");
+ PrintError("Could not handle Virtio NIC ctrl kick\n");
return -1;
}
} else {
case VIRTIO_STATUS_PORT:
virtio->virtio_cfg.status = *(uint8_t *)src;
if (virtio->virtio_cfg.status == 0) {
- PrintDebug("Resetting device\n");
virtio_init_state(virtio);
}
break;
}
+/* receiving raw ethernet pkt from backend */
static int virtio_rx(uint8_t * buf, uint32_t size, void * private_data) {
struct virtio_net_state * virtio = (struct virtio_net_state *)private_data;
struct virtio_queue * q = &(virtio->rx_vq);
struct virtio_net_hdr_mrg_rxbuf hdr;
uint32_t hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
- uint32_t data_len = size;
+ uint32_t data_len;
uint32_t offset = 0;
unsigned long flags;
- int ret_val = -ERR_VIRTIO_OTHER;
- int raw = 1;
#ifndef CONFIG_DEBUG_VIRTIO_NET
- {
- PrintDebug("Virtio-NIC: virtio_rx: size: %d\n", size);
- //v3_hexdump(buf, size, NULL, 0);
- }
+ PrintDebug("Virtio-NIC: virtio_rx: size: %d\n", size);
+ v3_hexdump(buf, size, NULL, 0);
#endif
flags = v3_lock_irqsave(virtio->rx_lock);
- virtio->pkt_recv ++;
- if (!raw)
- data_len -= hdr_len;
-
- if (!raw)
- memcpy(&hdr, buf, sizeof(struct virtio_net_hdr_mrg_rxbuf));
- else
- memset(&hdr, 0, sizeof(struct virtio_net_hdr_mrg_rxbuf));
+ data_len = size;
+ memset(&hdr, 0, sizeof(struct virtio_net_hdr_mrg_rxbuf));
if (q->ring_avail_addr == 0) {
- PrintError("Queue is not set\n");
- ret_val = -ERR_VIRTIO_RXQ_NOSET;
- goto exit;
+ PrintDebug("Queue is not set\n");
+ goto err_exit;
}
if (q->cur_avail_idx != q->avail->index){
hdr_desc = &(q->desc[hdr_idx]);
if (v3_gpa_to_hva(&(virtio->virtio_dev->vm->cores[0]), hdr_desc->addr_gpa, &(hdr_addr)) == -1) {
- PrintError("Could not translate receive buffer address\n");
- goto exit;
+ PrintDebug("Could not translate receive buffer address\n");
+ goto err_exit;
}
hdr.num_buffers = 1;
memcpy((void *)hdr_addr, &hdr, sizeof(struct virtio_net_hdr_mrg_rxbuf));
q->used->index++;
q->cur_avail_idx++;
- /* if there are certain num of pkts in the RX queue, notify guest
- * so guest will exit to palacios
- * when it returns, guest gets the virtio rx interrupt */
- if((++virtio->buffed_rx > q->queue_size/5) &&
- (q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
- if(virtio->virtio_dev->vm->cores[0].cpu_id != V3_Get_CPU()){
- notify_guest(virtio);
- }
- virtio->buffed_rx = 0;
- }
+ virtio->statistics.rx_pkts ++;
+ virtio->statistics.rx_bytes += size;
} else {
- virtio->pkt_drop++;
- /* RX queue is full, tell backend to stop RX on this device */
- virtio->net_ops->stop_rx(virtio->backend_data);
- enable_cb(&virtio->rx_vq);
+ virtio->statistics.rx_dropped ++;
- ret_val = -ERR_VIRTIO_RXQ_FULL;
- goto exit;
+ goto err_exit;
}
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->virtio_dev->pci_bus, 0, virtio->pci_dev);
virtio->virtio_cfg.pci_isr = 0x1;
+ virtio->statistics.interrupts ++;
}
- ret_val = offset;
+ v3_unlock_irqrestore(virtio->rx_lock, flags);
+
+ return 0;
-exit:
+err_exit:
v3_unlock_irqrestore(virtio->rx_lock, flags);
- return ret_val;
+ return -1;
}
static int virtio_free(struct virtio_dev_state * virtio) {
};
-/* TODO: Issue here: which vm info it needs? calling VM or the device's own VM? */
static void virtio_nic_poll(struct v3_vm_info * vm, void * data){
struct virtio_net_state * virtio = (struct virtio_net_state *)data;
handle_pkt_tx(&(vm->cores[0]), virtio);
}
-static void virtio_start_tx(void * data){
- struct virtio_net_state * virtio = (struct virtio_net_state *)data;
- unsigned long flags;
-
- flags = v3_lock_irqsave(virtio->tx_lock);
- virtio->tx_disabled = 0;
-
- /* notify the device's guest to start sending pkt */
- if(virtio->virtio_dev->vm->cores[0].cpu_id != V3_Get_CPU()){
- notify_guest(virtio);
- }
- v3_unlock_irqrestore(virtio->tx_lock, flags);
-}
-
-static void virtio_stop_tx(void * data){
- struct virtio_net_state * virtio = (struct virtio_net_state *)data;
- unsigned long flags;
-
- flags = v3_lock_irqsave(virtio->tx_lock);
- virtio->tx_disabled = 1;
-
- /* stop the guest to exit to palacios for sending pkt? */
- if(virtio->virtio_dev->vm->cores[0].cpu_id != V3_Get_CPU()){
- disable_cb(&virtio->tx_vq);
- }
-
- v3_unlock_irqrestore(virtio->tx_lock, flags);
-}
-
-
-
-
static int register_dev(struct virtio_dev_state * virtio,
struct virtio_net_state * net_state)
{
net_state->io_range_size <<= 1;
}
- // this is to account for any low order bits being set in num_ports
- // if there are none, then num_ports was already a power of 2 so we shift right to reset it
+ /* this is to account for any low order bits being set in num_ports
+ * if there are none, then num_ports was already a power of 2 so we shift right to reset it
+ */
if ((num_ports & ((net_state->io_range_size >> 1) - 1)) == 0) {
net_state->io_range_size >>= 1;
}
ops->recv = virtio_rx;
ops->poll = virtio_nic_poll;
- ops->start_tx = virtio_start_tx;
- ops->stop_tx = virtio_stop_tx;
ops->frontend_data = net_state;
memcpy(ops->fnt_mac, virtio->mac, ETH_ALEN);
#define VNET_ADD_LINK 21
#define VNET_DEL_LINK 22
-// structure of the vnet command header
struct vnet_ctrl_hdr {
uint8_t cmd_type;
uint32_t num_cmds;
}
-
-
static int handle_cmd_kick(struct guest_info * core,
struct virtio_vnet_state * vnet_state) {
struct virtio_queue * q = &(vnet_state->queue[0]);
uint8_t * status_ptr = NULL;
uint8_t status = 0;
-
PrintDebug("VNET Bridge: CMD: Descriptor Count=%d, index=%d, desc_idx=%d\n", desc_cnt, q->cur_avail_idx % QUEUE_SIZE, desc_idx);
if (desc_cnt < 3) {
desc_idx = hdr_desc->next;
- if (hdr->cmd_type == VNET_ADD_ROUTE) {
-
+ if (hdr->cmd_type == VNET_ADD_ROUTE) {
for (i = 0; i < hdr->num_cmds; i++) {
uint8_t tmp_status = 0;
struct v3_vnet_route * route = NULL;
return -1;
}
- // add route
- PrintDebug("VNET Bridge: Adding VNET Route\n");
-
tmp_status = v3_vnet_add_route(*route);
-
- PrintDebug("VNET Route Added\n");
-
if (tmp_status != 0) {
PrintError("Error adding VNET ROUTE\n");
+
status = tmp_status;
}
+ PrintDebug("VNET Route Added\n");
+
xfer_len += buf_desc->length;
desc_idx = buf_desc->next;
}
}
-
-
status_desc = &(q->desc[desc_idx]);
if (v3_gpa_to_hva(core, status_desc->addr_gpa, (addr_t *)&status_ptr) == -1) {
if (q->ring_avail_addr == 0) {
PrintError("Queue is not set\n");
+
goto exit;
}
struct vnet_bridge_pkt * virtio_pkt = NULL;
pkt_desc = &(q->desc[pkt_idx]);
- PrintDebug("VNET Bridge RX: buffer desc len: %d\n", pkt_desc->length);
if (v3_gpa_to_hva(&(vm->cores[0]), pkt_desc->addr_gpa, (addr_t *)&(virtio_pkt)) == -1) {
PrintError("Could not translate buffer address\n");
return ret_val;
}
-static int handle_pkt_kick(struct guest_info * core,
+static int do_tx_pkts(struct guest_info * core,
struct virtio_vnet_state * vnet_state)
{
struct virtio_queue * q = &(vnet_state->queue[XMIT_QUEUE]);
struct vnet_bridge_pkt * virtio_pkt = NULL;
pkt_desc = &(q->desc[desc_idx]);
-
- PrintDebug("VNET Bridge: Handle TX desc buf_len: %d\n", pkt_desc->length);
if (v3_gpa_to_hva(core, pkt_desc->addr_gpa, (addr_t *)&(virtio_pkt)) == -1) {
PrintError("Could not translate buffer address\n");
struct virtio_vnet_state * vnet_state = (struct virtio_vnet_state *)private_data;
if(vm == vnet_state->vm){
- handle_pkt_kick(&(vm->cores[0]), vnet_state);
+ do_tx_pkts(&(vm->cores[0]), vnet_state);
}
}
-static int handle_rx_kick(struct guest_info *core,
+static int handle_rx_queue_kick(struct guest_info *core,
struct virtio_vnet_state * vnet_state)
-{
- //v3_vnet_enable_bridge();
-
+{
return 0;
}
return -1;
}
} else if (queue_idx == 1) {
- if (handle_pkt_kick(core, vnet_state) == -1){
+ if (do_tx_pkts(core, vnet_state) == -1){
PrintError("Could not handle Virtio VNET TX\n");
return -1;
}
PrintError("Notify on TX\n");
} else if (queue_idx == 2) {
- if (handle_rx_kick(core, vnet_state) == -1){
+ if (handle_rx_queue_kick(core, vnet_state) == -1){
PrintError("Could not handle Virtio RX buffer refills Kick\n");
return -1;
}
#define NE2K_MEM_SIZE NE2K_PMEM_END
#define NIC_REG_BASE_PORT 0xc100 /* Command register (for all pages) */
-#define NIC_DATA_PORT 0xc110 /* Data read/write port */
-#define NIC_RESET_PORT 0xc11f /* Reset port */
+
+#define NE2K_CMD_OFFSET 0x00
+#define NE2K_DATA_OFFSET 0x10
+#define NE2K_RESET_OFFSET 0x1f
/* Page 0 registers */
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
uint8_t mcast_addr[8];
uint8_t mac[ETH_ALEN];
+ struct nic_statistics statistics;
+
struct v3_dev_net_ops *net_ops;
void * backend_data;
};
v3_pci_raise_irq(nic_state->pci_bus, 0, nic_state->pci_dev);
}
+ nic_state->statistics.interrupts ++;
+
PrintDebug("NE2000: Raise IRQ\n");
}
return 0;
}
-static int ne2k_send_packet(struct ne2k_state * nic_state, uchar_t *pkt, uint32_t length) {
+static int tx_one_pkt(struct ne2k_state * nic_state, uchar_t *pkt, uint32_t length) {
#ifdef CONFIG_DEBUG_NE2K
PrintDebug("NE2000: Send Packet:\n");
v3_hexdump(pkt, length, NULL, 0);
#endif
- return nic_state->net_ops->send(pkt, length, nic_state->backend_data);
+ if(nic_state->net_ops->send(pkt, length, nic_state->backend_data) >= 0){
+ nic_state->statistics.tx_pkts ++;
+ nic_state->statistics.tx_bytes += length;
+
+ return 0;
+ }
+
+ nic_state->statistics.tx_dropped ++;
+
+ return -1;
}
static int ne2k_rxbuf_full(struct ne2k_registers * regs) {
#endif
if(!rx_one_pkt(nic_state, buf, size)){
+ nic_state->statistics.rx_pkts ++;
+ nic_state->statistics.rx_bytes += size;
+
return 0;
}
-
+
+ nic_state->statistics.rx_dropped ++;
+
return -1;
}
}
if (offset + regs->tbcr <= NE2K_PMEM_END) {
- ne2k_send_packet(nic_state, nic_state->mem + offset, regs->tbcr);
+ tx_one_pkt(nic_state, nic_state->mem + offset, regs->tbcr);
}
regs->tsr.val = 0; /* clear the tx status reg */
int ret;
switch (idx) {
- case NIC_REG_BASE_PORT:
+ case NE2K_CMD_OFFSET:
ret = ne2k_cmd_write(core, port, src, length, private_data);
break;
- case NIC_REG_BASE_PORT+1 ... NIC_REG_BASE_PORT+15:
+ case NE2K_CMD_OFFSET+1 ... NE2K_CMD_OFFSET+15:
ret = ne2k_std_write(core, port, src, length, private_data);
break;
- case NIC_DATA_PORT:
+ case NE2K_DATA_OFFSET:
ret = ne2k_data_write(core, port, src, length, private_data);
break;
- case NIC_RESET_PORT:
+ case NE2K_RESET_OFFSET:
ret = ne2k_reset_port_write(core, port, src, length, private_data);
break;
int ret;
switch (idx) {
- case NIC_REG_BASE_PORT:
+ case NE2K_CMD_OFFSET:
ret = ne2k_cmd_read(core, port, dst, length, private_data);
break;
- case NIC_REG_BASE_PORT+1 ... NIC_REG_BASE_PORT+15:
+ case NE2K_CMD_OFFSET+1 ... NE2K_CMD_OFFSET+15:
ret = ne2k_std_read(core, port, dst, length, private_data);
break;
- case NIC_DATA_PORT:
+ case NE2K_DATA_OFFSET:
ret = ne2k_data_read(core, port, dst, length, private_data);
break;
- case NIC_RESET_PORT:
+ case NE2K_RESET_OFFSET:
ret = ne2k_reset_port_read(core, port, dst, length, private_data);
break;
v3_dev_hook_io(nic_state->dev, NIC_REG_BASE_PORT + i, &ne2k_std_read, &ne2k_std_write);
}
- v3_dev_hook_io(nic_state->dev, NIC_DATA_PORT, &ne2k_data_read, &ne2k_data_write);
- v3_dev_hook_io(nic_state->dev, NIC_RESET_PORT, &ne2k_reset_port_read, &ne2k_reset_port_write);
+ v3_dev_hook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_DATA_OFFSET, &ne2k_data_read, &ne2k_data_write);
+ v3_dev_hook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_RESET_OFFSET, &ne2k_reset_port_read, &ne2k_reset_port_write);
}
v3_dev_unhook_io(nic_state->dev, NIC_REG_BASE_PORT + i);
}
- v3_dev_unhook_io(nic_state->dev, NIC_DATA_PORT);
- v3_dev_unhook_io(nic_state->dev, NIC_RESET_PORT);
+ v3_dev_unhook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_DATA_OFFSET);
+ v3_dev_unhook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_RESET_OFFSET);
}else {
/* unregistered from PCI? */
}
-
- return 0;
V3_Free(nic_state);
--- /dev/null
+/*
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National
+ * Science Foundation and the Department of Energy.
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico. You can find out more at
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2011, Lei Xia <lxia@northwestern.edu>
+ * Copyright (c) 2011, The V3VEE Project <http://www.v3vee.org>
+ * All rights reserved.
+ *
+ * Author: Lei Xia <lxia@northwestern.edu>
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+#include <devices/pci.h>
+#include <palacios/vmm.h>
+#include <palacios/vmm_types.h>
+#include <palacios/vmm_io.h>
+#include <palacios/vmm_debug.h>
+#include <palacios/vm_guest_mem.h>
+#include <palacios/vmm_ethernet.h>
+#include <palacios/vmm_sprintf.h>
+
+
+
+#ifndef CONFIG_DEBUG_RTL8139
+#undef PrintDebug
+#define PrintDebug(fmts, args...)
+#endif
+
+#define RTL8139_IDR0 (0x00) /* ID Registers start, len 6*1bytes */
+#define RTL8139_MAR0 (0x08) /* Mulicast Registers start, len 8*1bytes */
+
+#define RTL8139_TSD0 (0x10) /* Tx Status of Descriptors */
+#define RTL8139_TSD1 (0x14)
+#define RTL8139_TSD2 (0x18)
+#define RTL8139_TSD3 (0x1c)
+
+#define RTL8139_TSAD0 (0x20) /* Tx Start Address of Descriptors */
+#define RTL8139_TSAD1 (0x24)
+#define RTL8139_TSAD2 (0x28)
+#define RTL8139_TSAD3 (0x2c)
+
+#define RTL8139_RBSTART (0x30) /* Rx Buffer Start Address */
+#define RTL8139_ERBCR (0x34) /* Early Rx Byte Count Register */
+#define RTL8139_ERSR (0x36) /* Early Rx Status Register */
+#define RTL8139_CR (0x37) /* Command Register */
+#define RTL8139_CAPR (0x38) /* Current Address of Pkt Read */
+#define RTL8139_CBR (0x3a) /* Current Buffer Address */
+#define RTL8139_IMR (0x3c) /* Intrpt Mask Reg */
+#define RTL8139_ISR (0x3e) /* Intrpt Status Reg */
+#define RTL8139_TCR (0x40) /* Tx Config Reg */
+#define RTL8139_RCR (0x44) /* Rx Config Reg */
+#define RTL8139_TCTR (0x48) /* Timer Count Reg */
+#define RTL8139_MPC (0x4c) /* Missed Pkt Counter */
+#define RTL8139_9346CR (0x50) /* 9346 Command Reg */
+#define RTL8139_CONFIG0 (0x51) /* Config Reg */
+#define RTL8139_CONFIG1 (0x52)
+#define RTL8139_TimerInt (0x54) /* Timer Intrpt Reg */
+#define RTL8139_MSR (0x58) /* Media Status Reg */
+#define RTL8139_CONFIG3 (0x59)
+#define RTL8139_CONFIG4 (0x5a)
+#define RTL8139_MULINT (0x5c) /* Multiple Intrpt Select */
+#define RTL8139_RERID (0x5e)
+#define RTL8139_TXSAD (0x60) /* Tx Status of All Descriptors */
+#define RTL8139_BMCR (0x62) /* Basic Mode Control Register */
+#define RTL8139_BMSR (0x64) /* Basic Mode Status Register */
+#define RTL8139_ANAR (0x66) /* Auto-Negotiation Advertisement Register */
+#define RTL8139_ANLPAR (0x68) /* Auto-Negotiation Link Partner Register */
+#define RTL8139_ANER (0x6a) /* Auto-Negotiation Expansion Register */
+#define RTL8139_DIS (0x6c) /* Disconnect Counter */
+#define RTL8139_FCSC (0x6e) /* False Carrier Sense Counter */
+#define RTL8139_NWAYTR (0x70) /* N-way Test Register */
+#define RTL8139_REC (0x72) /* RX ER Counter */
+#define RTL8139_CSCR (0x74) /* CS Config Register */
+#define RTL8139_PHY1_PARM (0x78) /* PHY parameter */
+#define RTL8139_TW_PARM (0x7c) /* Twister parameter */
+#define RTL8139_PHY2_PARM (0x80)
+
+#define RTL8139_CRC0 (0x84) /* Power Management CRC Reg for wakeup frame 8*1bytes */
+
+#define RTL8139_Wakeup0 (0x8c) /* Power Management wakeup frame */
+#define RTL8139_Wakeup1 (0x94)
+#define RTL8139_Wakeup2 (0x9c)
+#define RTL8139_Wakeup3 (0xa4)
+#define RTL8139_Wakeup4 (0xac)
+#define RTL8139_Wakeup5 (0xb4)
+#define RTL8139_Wakeup6 (0xbc)
+#define RTL8139_Wakeup7 (0xc4)
+
+#define RTL8139_LSBCRO0 (0xcc) /* LSB of the mask byte of wakeup frame */
+#define RTL8139_LSBCRO1 (0xcd)
+#define RTL8139_LSBCRO2 (0xce)
+#define RTL8139_LSBCRO3 (0xcf)
+#define RTL8139_LSBCRO4 (0xd0)
+#define RTL8139_LSBCRO5 (0xd1)
+#define RTL8139_LSBCRO6 (0xd2)
+#define RTL8139_LSBCRO7 (0xd3)
+
+#define RTL8139_Config5 (0xd8)
+
+/* Interrupts */
+#define PKT_RX 0x0001
+#define RX_ERR 0x0002
+#define TX_OK 0x0004
+#define TX_ERR 0x0008
+#define RX_BUFF_OF 0x0010
+#define RX_UNDERRUN 0x0020
+#define RX_FIFO_OF 0x0040
+#define CABLE_LEN_CHNG 0x2000
+#define TIME_OUT 0x4000
+#define SERR 0x8000
+
+#define DESC_SIZE 2048
+#define TX_FIFO_SIZE (DESC_SIZE * 4)
+#define RX_FIFO_SIZE (DESC_SIZE * 4)
+
+typedef enum {NIC_READY, NIC_REG_POSTED} nic_state_t;
+
+enum TxStatusBits {
+ TSD_Own = 1<<13,
+ TSD_Tun = 1<<14,
+ TSD_Tok = 1<<15,
+ TSD_Cdh = 1<<28,
+ TSD_Owc = 1<<29,
+ TSD_Tabt = 1<<30,
+ TSD_Crs = 1<<31,
+};
+
+/* Transmit Status Register (TSD0-3) Offset: 0x10-0x1F */
+struct tx_status_reg {
+ union {
+ uint16_t val;
+ struct {
+ uint16_t size : 13;
+ uint8_t own : 1;
+ uint8_t tun : 1;
+ uint8_t tok : 1;
+ uint8_t er_tx_th : 6;
+ uint8_t reserved : 2;
+ uint8_t ncc : 4;
+ uint8_t cdh : 1;
+ uint8_t owc : 1;
+ uint8_t tabt : 1;
+ uint8_t crs : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+enum RxStatusBits {
+ Rx_Multicast = 1<<15,
+ Rx_Physical = 1<<14,
+ Rx_Broadcast = 1<<13,
+ Rx_BadSymbol = 1<<5,
+ Rx_Runt = 1<<4,
+ Rx_TooLong = 1<<3,
+ Rx_CRCErr = 1<<2,
+ Rx_BadAlign = 1<<1,
+ Rx_OK = 1<<0,
+};
+
+
+/* Receive Status Register in RX Packet Header */
+struct rx_status_reg {
+ union {
+ uint16_t val;
+ struct {
+ uint8_t rx_ok : 1;
+ uint8_t rx_bad_align : 1;
+ uint8_t rx_crc_err : 1;
+ uint8_t rx_too_long : 1;
+ uint8_t rx_runt : 1;
+ uint8_t rx_bad_sym : 1;
+ uint8_t reserved : 7;
+ uint8_t rx_brdcast : 1;
+ uint8_t rx_phys : 1;
+ uint8_t rx_multi : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+/* ERSR - Early Rx Status Register Offset: 0x36*/
+struct errx_status_reg{
+ union {
+ uint8_t val;
+ struct {
+ uint8_t er_rx_ok : 1;
+ uint8_t er_rx_ovw : 1;
+ uint8_t er_rx_bad_pkt : 1;
+ uint8_t er_rx_good : 1;
+ uint8_t reserved : 4;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+/* Transmit Status of All Descriptors (TSAD) Register */
+enum TSAD_bits {
+ TSAD_TOK3 = 1<<15, /* TOK bits of Descriptors*/
+ TSAD_TOK2 = 1<<14,
+ TSAD_TOK1 = 1<<13,
+ TSAD_TOK0 = 1<<12,
+ TSAD_TUN3 = 1<<11, /* TUN bits of Descriptors */
+ TSAD_TUN2 = 1<<10,
+ TSAD_TUN1 = 1<<9,
+ TSAD_TUN0 = 1<<8,
+ TSAD_TABT3 = 1<<7, /* TABT bits of Descriptors */
+ TSAD_TABT2 = 1<<6,
+ TSAD_TABT1 = 1<<5,
+ TSAD_TABT0 = 1<<4,
+ TSAD_OWN3 = 1<<3, /* OWN bits of Descriptors */
+ TSAD_OWN2 = 1<<2,
+ TSAD_OWN1 = 1<<1,
+ TSAD_OWN0 = 1<<0,
+};
+
+
+/* Transmit Status of All Descriptors (TSAD) Register Offset: 0x60-0x61*/
+struct txsad_reg {
+ union {
+ uint16_t val;
+ struct {
+ uint8_t own0 : 1;
+ uint8_t own1 : 1;
+ uint8_t own2 : 1;
+ uint8_t own3 : 1;
+ uint8_t tabt0 : 1;
+ uint8_t tabt1 : 1;
+ uint8_t tabt2 : 1;
+ uint8_t tabt3 : 1;
+ uint8_t tun0 : 1;
+ uint8_t tun1 : 1;
+ uint8_t tun2 : 1;
+ uint8_t tun3 : 1;
+ uint8_t tok0 : 1;
+ uint8_t tok1 : 1;
+ uint8_t tok2 : 1;
+ uint8_t tok3 : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+
+enum ISRBits {
+ ISR_Rok = 1<<0,
+ ISR_Rer = 1<<1,
+ ISR_Tok = 1<<2,
+ ISR_Ter = 1<<3,
+ ISR_Rxovw = 1<<4,
+ ISR_Pun = 1<<5,
+ ISR_Fovw = 1<<6,
+ ISR_Lenchg = 1<<13,
+ ISR_Timeout = 1<<14,
+ ISR_Serr = 1<<15,
+};
+
+/*
+ * Interrupt Status Register (ISR) Offset: ox3e-0x3f
+ * Interrupt Mask Register (IMR 0x3c-0x3d) shares the same structure
+ */
+struct isr_imr_reg {
+ union {
+ uint16_t val;
+ struct {
+ uint8_t rx_ok :1;
+ uint8_t rx_err : 1;
+ uint8_t tx_ok : 1;
+ uint8_t tx_err : 1;
+ uint8_t rx_ovw : 1;
+ uint8_t pun_linkchg : 1;
+ uint8_t rx_fifo_ovw : 1;
+ uint8_t reservd: 6;
+ uint8_t lenchg :1;
+ uint8_t timeout :1;
+ uint8_t syserr :1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+enum CMDBits {
+ CMD_Bufe = 1<<0,
+ CMD_Te = 1<<2,
+ CMD_Re = 1<<3,
+ CMD_Rst = 1<<4,
+};
+
+
+/* Commmand Register Offset: 0x37 */
+struct cmd_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t cmd_bufe : 1;
+ uint8_t reservd_1 : 1;
+ uint8_t cmd_te : 1;
+ uint8_t cmd_re : 1;
+ uint8_t cmd_rst : 1;
+ uint8_t reservd_2 : 3;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+
+
+enum CMD9346Bits {
+ CMD9346_Lock = 0x00,
+ CMD9346_Unlock = 0xC0,
+};
+
+
+
+/* 93C46 Commmand Register Offset: 0x50 */
+struct cmd9346_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t eedo : 1;
+ uint8_t eedi : 1;
+ uint8_t eesk : 1;
+ uint8_t eecs : 1;
+ uint8_t reserved : 2;
+ uint8_t eem : 2;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+
+// Bits in TxConfig.
+enum TXConfig_bits{
+
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+
+/* Transmit Configuration Register (TCR) Offset: 0x40-0x43 */
+struct tx_config_reg {
+ union {
+ uint32_t val;
+ struct {
+ uint8_t clr_abort :1;
+ uint8_t reserved_1 : 3;
+ uint8_t tx_retry_cnt : 4;
+ uint8_t max_dma : 3;
+ uint8_t reserved_2 : 5;
+ uint8_t tx_crc : 1;
+ uint8_t loop_test : 2;
+ uint8_t reservd_3: 3;
+ uint8_t hw_verid_b :2;
+ uint8_t ifg :2;
+ uint8_t hw_verid_a :5;
+ uint8_t reservd_4 :1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+
+
+enum CSCRBits {
+ CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */
+ CSCR_LD = 1<<9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/
+ CSCR_HEART_BIT = 1<<8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/
+ CSCR_JBEN = 1<<7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/
+ CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/
+ CSCR_F_Connect = 1<<5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/
+ CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/
+ CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/
+ CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/
+};
+
+
+/* CS Configuration Register (CSCR) Offset: 0x74-0x75 */
+struct cscr_reg {
+ union {
+ uint16_t val;
+ struct {
+ uint8_t pass_scr :1;
+ uint8_t reserved_1 : 1;
+ uint8_t con_status_en : 1;
+ uint8_t con_status : 1;
+ uint8_t reserved_2 : 1;
+ uint8_t f_connect : 1;
+ uint8_t f_link_100 : 1;
+ uint8_t jben: 1;
+ uint8_t heart_beat :1;
+ uint8_t ld :1;
+ uint8_t reservd_3 :5;
+ uint8_t test_fun :1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+};
+
+
+/* Receive Configuration Register (RCR) Offset: 0x44-0x47 */
+struct rx_config_reg {
+ union {
+ uint32_t val;
+ struct {
+ uint8_t all_phy : 1;
+ uint8_t my_phy : 1;
+ uint8_t all_multi : 1;
+ uint8_t all_brdcast : 1;
+ uint8_t acpt_runt : 1;
+ uint8_t acpt_err : 1;
+ uint8_t reserved_1 : 1;
+ uint8_t wrap : 1;
+ uint8_t max_dma: 3;
+ uint8_t rx_buf_len :2;
+ uint8_t rx_fifo_thresd :3;
+ uint8_t rer8 :1;
+ uint8_t mul_er_intr :1;
+ uint8_t reserved_2 : 6;
+ uint8_t eraly_rx_thresd :4;
+ uint8_t reserved_3 : 4;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+
+
+#define RTL8139_PCI_REVID_8139 0x10
+
+#define SET_MASKED(input, mask, curr) \
+ (((input) & ~(mask)) | ((curr) & (mask)))
+
+/* arg % size for size which is a power of 2 */
+#define MOD2(input, size) \
+ ((input) & (size - 1))
+
+
+/* Size is 64 * 16bit words */
+#define EEPROM_9346_ADDR_BITS 6
+#define EEPROM_9346_SIZE (1 << EEPROM_9346_ADDR_BITS)
+#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1)
+
+enum Chip9346Operation
+{
+ Chip9346_op_mask = 0xc0, /* 10 zzzzzz */
+ Chip9346_op_read = 0x80, /* 10 AAAAAA */
+ Chip9346_op_write = 0x40, /* 01 AAAAAA D(15)..D(0) */
+ Chip9346_op_ext_mask = 0xf0, /* 11 zzzzzz */
+ Chip9346_op_write_enable = 0x30, /* 00 11zzzz */
+ Chip9346_op_write_all = 0x10, /* 00 01zzzz */
+ Chip9346_op_write_disable = 0x00, /* 00 00zzzz */
+};
+
+enum Chip9346Mode {
+ Chip9346_none = 0,
+ Chip9346_enter_command_mode,
+ Chip9346_read_command,
+ Chip9346_data_read, /* from output register */
+ Chip9346_data_write, /* to input register, then to contents at specified address */
+ Chip9346_data_write_all, /* to input register, then filling contents */
+};
+
+struct EEprom9346 {
+ uint16_t contents[EEPROM_9346_SIZE];
+ int mode;
+ uint32_t tick;
+ uint8_t address;
+ uint16_t input;
+ uint16_t output;
+
+ uint8_t eecs;
+ uint8_t eesk;
+ uint8_t eedi;
+ uint8_t eedo;
+};
+
+struct rtl8139_regs {
+ union{
+ uint8_t mem[256];
+
+ struct {
+ uint8_t id[6];
+ uint8_t reserved;
+ uint8_t mult[8];
+ uint32_t tsd[4];
+ uint32_t tsad[4];
+ uint32_t rbstart;
+ uint16_t erbcr;
+ uint8_t ersr;
+ uint8_t cmd;
+ uint16_t capr;
+ uint16_t cbr;
+ uint16_t imr;
+ uint16_t isr;
+ uint32_t tcr;
+ uint32_t rcr;
+ uint32_t tctr;
+ uint16_t mpc;
+ uint8_t cmd9346;
+ uint8_t config[2];
+ uint32_t timer_int;
+ uint8_t msr;
+ uint8_t config3[2];
+ uint16_t mulint;
+ uint16_t rerid;
+ uint16_t txsad;
+ uint16_t bmcr;
+ uint16_t bmsr;
+ uint16_t anar;
+ uint16_t anlpar;
+ uint16_t aner;
+ uint16_t dis;
+ uint16_t fcsc;
+ uint16_t nwaytr;
+ uint16_t rec;
+ uint32_t cscr;
+ uint32_t phy1_parm;
+ uint16_t tw_parm;
+ uint32_t phy2_parm;
+ uint8_t crc[8];
+ uint32_t wakeup[16];
+ uint8_t isbcr[8];
+ uint8_t config5;
+ }__attribute__((packed));
+ }__attribute__((packed));
+};
+
+
+
+struct rtl8139_state {
+ nic_state_t dev_state;
+
+ struct v3_vm_info * vm;
+ struct pci_device * pci_dev;
+ struct vm_device * pci_bus;
+ struct vm_device * dev;
+
+ struct nic_statistics statistic;
+
+ struct rtl8139_regs regs;
+ struct EEprom9346 eeprom;
+
+ uint8_t tx_fifo[TX_FIFO_SIZE];
+ uint8_t rx_fifo[RX_FIFO_SIZE];
+ uint32_t rx_bufsize;
+
+ uint8_t mac[ETH_ALEN];
+
+ struct v3_dev_net_ops *net_ops;
+ void * backend_data;
+};
+
+static inline void rtl8139_update_irq(struct rtl8139_state * nic_state) {
+ 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);
+ nic_state->statistic.interrupts ++;
+ }
+}
+
+static void prom9346_decode_command(struct EEprom9346 * eeprom, uint8_t command) {
+ PrintDebug("RTL8139: eeprom command 0x%02x\n", command);
+
+ switch (command & Chip9346_op_mask) {
+ case Chip9346_op_read:
+ {
+ eeprom->address = command & EEPROM_9346_ADDR_MASK;
+ eeprom->output = eeprom->contents[eeprom->address];
+ eeprom->eedo = 0;
+ eeprom->tick = 0;
+ eeprom->mode = Chip9346_data_read;
+ PrintDebug("RTL8139: eeprom read from address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->output);
+ }
+ break;
+
+ case Chip9346_op_write:
+ {
+ eeprom->address = command & EEPROM_9346_ADDR_MASK;
+ eeprom->input = 0;
+ eeprom->tick = 0;
+ eeprom->mode = Chip9346_none; /* Chip9346_data_write */
+ PrintDebug("RTL8139: eeprom begin write to address 0x%02x\n",
+ eeprom->address);
+ }
+ break;
+ default:
+ eeprom->mode = Chip9346_none;
+ switch (command & Chip9346_op_ext_mask) {
+ case Chip9346_op_write_enable:
+ PrintDebug("RTL8139: eeprom write enabled\n");
+ break;
+ case Chip9346_op_write_all:
+ PrintDebug("RTL8139: eeprom begin write all\n");
+ break;
+ case Chip9346_op_write_disable:
+ PrintDebug("RTL8139: eeprom write disabled\n");
+ break;
+ }
+ break;
+ }
+}
+
+static void prom9346_shift_clock(struct EEprom9346 * eeprom) {
+ int bit = eeprom->eedi?1:0;
+
+ ++ eeprom->tick;
+
+ PrintDebug("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, eeprom->eedo);
+
+ switch (eeprom->mode) {
+ case Chip9346_enter_command_mode:
+ if (bit) {
+ eeprom->mode = Chip9346_read_command;
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ PrintDebug("eeprom: +++ synchronized, begin command read\n");
+ }
+ break;
+
+ case Chip9346_read_command:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 8) {
+ prom9346_decode_command(eeprom, eeprom->input & 0xff);
+ }
+ break;
+
+ case Chip9346_data_read:
+ eeprom->eedo = (eeprom->output & 0x8000)?1:0;
+ eeprom->output <<= 1;
+ if (eeprom->tick == 16){
+#if 1
+ // the FreeBSD drivers (rl and re) don't explicitly toggle
+ // CS between reads (or does setting Cfg9346 to 0 count too?),
+ // so we need to enter wait-for-command state here
+ eeprom->mode = Chip9346_enter_command_mode;
+ eeprom->input = 0;
+ eeprom->tick = 0;
+
+ PrintDebug("eeprom: +++ end of read, awaiting next command\n");
+#else
+ // original behaviour
+ ++eeprom->address;
+ eeprom->address &= EEPROM_9346_ADDR_MASK;
+ eeprom->output = eeprom->contents[eeprom->address];
+ eeprom->tick = 0;
+
+ DEBUG_PRINT(("eeprom: +++ read next address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->output));
+#endif
+ }
+ break;
+
+ case Chip9346_data_write:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 16) {
+ PrintDebug("RTL8139: eeprom write to address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->input);
+
+ eeprom->contents[eeprom->address] = eeprom->input;
+ eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ }
+ break;
+
+ case Chip9346_data_write_all:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 16) {
+ int i;
+ for (i = 0; i < EEPROM_9346_SIZE; i++)
+ {
+ eeprom->contents[i] = eeprom->input;
+ }
+ PrintDebug("RTL8139: eeprom filled with data=0x%04x\n",
+ eeprom->input);
+
+ eeprom->mode = Chip9346_enter_command_mode;
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int prom9346_get_wire(struct rtl8139_state * nic_state) {
+ struct EEprom9346 *eeprom = &(nic_state->eeprom);
+
+ if (eeprom->eecs == 0)
+ return 0;
+
+ return eeprom->eedo;
+}
+
+static void prom9346_set_wire(struct rtl8139_state * nic_state,
+ int eecs,
+ int eesk,
+ int eedi) {
+ struct EEprom9346 *eeprom = &(nic_state->eeprom);
+ uint8_t old_eecs = eeprom->eecs;
+ uint8_t old_eesk = eeprom->eesk;
+
+ eeprom->eecs = eecs;
+ eeprom->eesk = eesk;
+ eeprom->eedi = eedi;
+
+ PrintDebug("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n",
+ eeprom->eecs, eeprom->eesk, eeprom->eedi, eeprom->eedo);
+
+ if (old_eecs == 0 && eecs) {
+ /* Synchronize start */
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ eeprom->output = 0;
+ eeprom->mode = Chip9346_enter_command_mode;
+
+ PrintDebug("=== eeprom: begin access, enter command mode\n");
+ }
+
+ if (eecs == 0) {
+ PrintDebug("=== eeprom: end access\n");
+ return;
+ }
+
+ if (!old_eesk && eesk) {
+ /* SK front rules */
+ prom9346_shift_clock(eeprom);
+ }
+}
+
+
+static inline void rtl8139_reset_rxbuf(struct rtl8139_state * nic_state, uint32_t bufsize) {
+ nic_state->rx_bufsize = bufsize;
+ nic_state->regs.capr = 0;
+ nic_state->regs.cbr = 0;
+}
+
+
+static void rtl8139_reset(struct rtl8139_state *nic_state) {
+ struct rtl8139_regs *regs = &(nic_state->regs);
+ int i;
+
+ PrintDebug("Rtl8139: Reset\n");
+
+ /* restore MAC address */
+ memcpy(regs->id, nic_state->mac, ETH_ALEN);
+ memset(regs->mult, 0xff, 8);
+
+ rtl8139_update_irq(nic_state);
+
+ // prepare eeprom
+ nic_state->eeprom.contents[0] = 0x8129;
+
+ // PCI vendor and device ID
+ nic_state->eeprom.contents[1] = 0x10ec;
+ nic_state->eeprom.contents[2] = 0x8139;
+ //Mac address
+ nic_state->eeprom.contents[7] = nic_state->mac[0] | nic_state->mac[1] << 8;
+ nic_state->eeprom.contents[8] = nic_state->mac[2] | nic_state->mac[3] << 8;
+ nic_state->eeprom.contents[9] = nic_state->mac[4] | nic_state->mac[5] << 8;
+
+ for (i = 0; i < 4; ++i) {
+ regs->tsd[i] = TSD_Own;
+ }
+
+ rtl8139_reset_rxbuf(nic_state, 1024*8);
+
+ /* ACK the reset */
+ regs->tcr = 0;
+
+ regs->tcr |= ((0x1d << 26) | (0x1 << 22)); // RTL-8139D
+ regs->rerid = RTL8139_PCI_REVID_8139;
+
+ regs->cmd = CMD_Rst; //RxBufEmpty bit is calculated on read from ChipCmd
+
+ regs->config[0] = 0x0 | (1 << 4); // No boot ROM
+ regs->config[1] = 0xC; //IO mapped and MEM mapped registers available
+ //regs->config[1] = 0x4; //Only IO mapped registers available
+ regs->config3[0] = 0x1; // fast back-to-back compatible
+ regs->config3[1] = 0x0;
+ regs->config5 = 0x0;
+
+ regs->cscr = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
+
+ //0x3100 : 100Mbps, full duplex, autonegotiation. 0x2100 : 100Mbps, full duplex
+ regs->bmcr = 0x1000; // autonegotiation
+
+ regs->bmsr = 0x7809;
+ regs->bmsr |= 0x0020; // autonegotiation completed
+ regs->bmsr |= 0x0004; // link is up
+
+ regs->anar = 0x05e1; // all modes, full duplex
+ regs->anlpar = 0x05e1; // all modes, full duplex
+ regs->aner = 0x0001; // autonegotiation supported
+
+ // reset timer and disable timer interrupt
+ regs->tctr = 0;
+ regs->timer_int = 0;
+}
+
+
+
+static void rtl8139_9346cr_write(struct rtl8139_state * nic_state, uint32_t val) {
+ val &= 0xff;
+
+ PrintDebug("RTL8139: 9346CR write val=0x%02x\n", val);
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x31, nic_state->regs.cmd9346);
+
+ uint32_t opmode = val & 0xc0;
+ uint32_t eeprom_val = val & 0xf;
+
+ if (opmode == 0x80) {
+ /* eeprom access */
+ int eecs = (eeprom_val & 0x08)?1:0;
+ int eesk = (eeprom_val & 0x04)?1:0;
+ int eedi = (eeprom_val & 0x02)?1:0;
+ prom9346_set_wire(nic_state, eecs, eesk, eedi);
+ } else if (opmode == 0x40) {
+ /* Reset. */
+ val = 0;
+ rtl8139_reset(nic_state);
+ }
+
+ nic_state->regs.cmd9346 = val;
+}
+
+static uint32_t rtl8139_9346cr_read(struct rtl8139_state * nic_state) {
+ uint32_t ret = nic_state->regs.cmd9346;
+ uint32_t opmode = ret & 0xc0;
+
+ if (opmode == 0x80) {
+ /* eeprom access */
+ int eedo = prom9346_get_wire(nic_state);
+ if (eedo){
+ ret |= 0x01;
+ } else {
+ ret &= ~0x01;
+ }
+ }
+
+ PrintDebug("RTL8139: 9346CR read val=0x%02x\n", ret);
+
+ return ret;
+}
+
+
+static inline int rtl8139_receiver_enabled(struct rtl8139_state * nic_state) {
+ return nic_state->regs.cmd & CMD_Re;
+}
+
+static inline int rtl8139_rxwrap(struct rtl8139_state * nic_state) {
+ // wrapping enabled; assume 1.5k more buffer space if size < 64K
+ return (nic_state->regs.rcr & (1 << 7));
+}
+
+static void rtl8139_rxbuf_write(struct rtl8139_state * nic_state,
+ const void * buf,
+ int size) {
+ struct rtl8139_regs *regs = &(nic_state->regs);
+ int wrap;
+ addr_t guestpa, host_rxbuf;
+
+ guestpa = (addr_t)regs->rbstart;
+ v3_gpa_to_hva(&(nic_state->vm->cores[0]), guestpa, &host_rxbuf);
+
+ //wrap to the front of rx buffer
+ if (regs->cbr + size > nic_state->rx_bufsize){
+ wrap = MOD2(regs->cbr + size, nic_state->rx_bufsize);
+
+ if (wrap && !(nic_state->rx_bufsize < 64*1024 && rtl8139_rxwrap(nic_state))){
+ PrintDebug("RTL8139: rx packet wrapped in buffer at %d\n", size-wrap);
+
+ if (size > wrap){
+ memcpy((void *)(host_rxbuf + regs->cbr), buf, size-wrap);
+ }
+
+ // reset buffer pointer
+ regs->cbr = 0;
+
+ memcpy((void *)(host_rxbuf + regs->cbr), buf + (size-wrap), wrap);
+
+ regs->cbr = wrap;
+
+ return;
+ }
+ }
+
+ memcpy((void *)(host_rxbuf + regs->cbr), buf, size);
+
+ regs->cbr += size;
+}
+
+#define POLYNOMIAL 0x04c11db6
+
+/* From FreeBSD */
+static inline int compute_mcast_idx(const uint8_t *ep) {
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ b = *ep++;
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry){
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ }
+ return (crc >> 26);
+}
+
+
+static int rx_one_pkt(struct rtl8139_state * nic_state,
+ uint8_t * pkt,
+ uint32_t len){
+ struct rtl8139_regs *regs = &(nic_state->regs);
+ uint_t rxbufsize = nic_state->rx_bufsize;
+ uint32_t header, val;
+ uint8_t bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (regs->rcr & AcceptAllPhys) {
+ PrintDebug("RTL8139: packet received in promiscuous mode\n");
+ } else {
+ if (!memcmp(pkt, bcast_addr, 6)) {
+ if (!(regs->rcr & AcceptBroadcast)){
+ PrintDebug("RTL8139: broadcast packet rejected\n");
+ return -1;
+ }
+ header |= Rx_Broadcast;
+ PrintDebug("RTL8139: broadcast packet received\n");
+ } else if (pkt[0] & 0x01) {
+ // multicast
+ if (!(regs->rcr & AcceptMulticast)){
+ PrintDebug("RTL8139: multicast packet rejected\n");
+ return -1;
+ }
+
+ int mcast_idx = compute_mcast_idx(pkt);
+
+ if (!(regs->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))){
+ PrintDebug("RTL8139: multicast address mismatch\n");
+ return -1;
+ }
+ header |= Rx_Multicast;
+ PrintDebug("RTL8139: multicast packet received\n");
+ } else if (!compare_ethaddr(regs->id, pkt)){
+ if (!(regs->rcr & AcceptMyPhys)){
+ PrintDebug("RTL8139: rejecting physical address matching packet\n");
+ return -1;
+ }
+
+ header |= Rx_Physical;
+ PrintDebug("RTL8139: physical address matching packet received\n");
+ } else {
+ PrintDebug("RTL8139: unknown packet\n");
+ return -1;
+ }
+ }
+
+ if(1){
+ PrintDebug("RTL8139: in ring Rx mode\n");
+
+ int avail = MOD2(rxbufsize + regs->capr - regs->cbr, rxbufsize);
+
+ if (avail != 0 && len + 8 >= avail){
+ PrintError("rx overflow: rx buffer length %d head 0x%04x read 0x%04x === available 0x%04x need 0x%04x\n",
+ rxbufsize, regs->cbr, regs->capr, avail, len + 8);
+
+ regs->isr |= ISR_Rxovw;
+ ++ regs->mpc;
+ rtl8139_update_irq(nic_state);
+ return -1;
+ }
+
+ header |= Rx_OK;
+ header |= ((len << 16) & 0xffff0000);
+
+ rtl8139_rxbuf_write(nic_state, (uint8_t *)&header, 4);
+
+ rtl8139_rxbuf_write(nic_state, pkt, len);
+
+ /* CRC checksum */
+ val = v3_crc32(0, pkt, len);
+
+ rtl8139_rxbuf_write(nic_state, (uint8_t *)&val, 4);
+
+ // correct buffer write pointer
+ regs->cbr = MOD2((regs->cbr + 3) & ~0x3, rxbufsize);
+
+ PrintDebug("RTL8139: received: rx buffer length %d CBR: 0x%04x CAPR: 0x%04x\n",
+ rxbufsize, regs->cbr, regs->capr);
+ }
+
+ regs->isr |= ISR_Rok;
+
+ nic_state->statistic.rx_pkts ++;
+ nic_state->statistic.rx_bytes += len;
+
+ rtl8139_update_irq(nic_state);
+
+ return 0;
+}
+
+static int rtl8139_rx(uint8_t * pkt, uint32_t len, void * private_data) {
+ struct rtl8139_state *nic_state = (struct rtl8139_state *)private_data;
+
+ if (!rtl8139_receiver_enabled(nic_state)){
+ PrintDebug("RTL8139: receiver disabled\n");
+ nic_state->statistic.rx_dropped ++;
+
+ return 0;
+ }
+
+ if(rx_one_pkt(nic_state, pkt, len) >= 0){
+ nic_state->statistic.rx_pkts ++;
+ nic_state->statistic.rx_bytes += len;
+ }else {
+ nic_state->statistic.rx_dropped ++;
+ }
+
+ return 0;
+}
+
+static void rtl8139_rcr_write(struct rtl8139_state * nic_state, uint32_t val) {
+ PrintDebug("RTL8139: RCR write val=0x%08x\n", val);
+
+ val = SET_MASKED(val, 0xf0fc0040, nic_state->regs.rcr);
+ nic_state->regs.rcr = val;
+
+#if 0
+ uchar_t rblen = (regs->rcr >> 11) & 0x3;
+ switch(rblen) {
+ case 0x0:
+ rxbufsize = 1024 * 8 + 16;
+ break;
+ case 0x1:
+ rxbufsize = 1024 * 16 + 16;
+ break;
+ case 0x2:
+ rxbufsize = 1024 * 32 + 16;
+ break;
+ default:
+ rxbufsize = 1024 * 64 + 16;
+ break;
+ }
+#endif
+
+ // reset buffer size and read/write pointers
+ rtl8139_reset_rxbuf(nic_state, 8192 << ((nic_state->regs.rcr >> 11) & 0x3));
+
+ PrintDebug("RTL8139: RCR write reset buffer size to %d\n", nic_state->rx_bufsize);
+}
+
+
+#if 0
+
+static int rtl8139_config_writeable(struct vm_device *dev)
+{
+ struct nic_context *nic_state = (struct nic_context *)(dev->private_data);
+
+ if (nic_state->regs.cmd9346 & CMD9346_Unlock)
+ {
+ return 1;
+ }
+
+ PrintDebug("RTL8139: Configuration registers are unwriteable\n");
+
+ return 0;
+}
+
+#endif
+
+static inline int transmitter_enabled(struct rtl8139_state * nic_state){
+ return nic_state->regs.cmd & CMD_Te;
+}
+
+static int rxbufempty(struct rtl8139_state * nic_state){
+ struct rtl8139_regs *regs = &(nic_state->regs);
+ int unread;
+
+ unread = MOD2(regs->cbr + nic_state->rx_bufsize - regs->capr, nic_state->rx_bufsize);
+
+ if (unread != 0){
+ PrintDebug("RTL8139: receiver buffer data available 0x%04x\n", unread);
+ return 0;
+ }
+
+ PrintDebug("RTL8139: receiver buffer is empty\n");
+
+ return 1;
+}
+
+static void rtl8139_cmd_write(struct rtl8139_state * nic_state, uint32_t val){
+ val &= 0xff;
+
+ PrintDebug("RTL8139: Cmd write val=0x%08x\n", val);
+
+ if (val & CMD_Rst){
+ PrintDebug("RTL8139: Cmd reset\n");
+ rtl8139_reset(nic_state);
+ }
+ if (val & CMD_Re){
+ PrintDebug("RTL8139: Cmd enable receiver\n");
+ }
+ if (val & CMD_Te){
+ PrintDebug("RTL8139: Cmd enable transmitter\n");
+ }
+
+ val = SET_MASKED(val, 0xe3, nic_state->regs.cmd);
+ val &= ~CMD_Rst;
+
+ nic_state->regs.cmd = val;
+}
+
+static int tx_one_packet(struct rtl8139_state * nic_state, int descriptor){
+ struct rtl8139_regs *regs = &(nic_state->regs);
+ int txsize;
+ uint8_t *pkt;
+ addr_t pkt_gpa = 0, hostva = 0;
+
+ if (!transmitter_enabled(nic_state)){
+ PrintError("RTL8139: fail to send from descriptor %d: transmitter disabled\n", descriptor);
+ return 0;
+ }
+
+ if (regs->tsd[descriptor] & TSD_Own){
+ PrintError("RTL8139: fail to send from descriptor %d: owned by host\n", descriptor);
+ return 0;
+ }
+
+ txsize = regs->tsd[descriptor] & 0x1fff;
+ pkt_gpa = (addr_t) regs->tsad[descriptor];
+
+ PrintDebug("RTL8139: sending %d bytes from guest memory at 0x%08x\n", txsize, regs->tsad[descriptor]);
+
+ v3_gpa_to_hva(&(nic_state->vm->cores[0]), (addr_t)pkt_gpa, &hostva);
+ pkt = (uchar_t *)hostva;
+
+#ifdef CONFIG_DEBUG_RTL8139
+ v3_hexdump(pkt, txsize, NULL, 0);
+#endif
+
+ if (TxLoopBack == (regs->tcr & TxLoopBack)){ /* loopback test */
+ PrintDebug(("RTL8139: transmit loopback mode\n"));
+ rx_one_pkt(nic_state, pkt, txsize);
+ } else{
+ if (nic_state->net_ops->send(pkt, txsize, nic_state->backend_data) == 0){
+ PrintDebug("RTL8139: Sent %d bytes from descriptor %d\n", txsize, descriptor);
+ nic_state->statistic.tx_pkts ++;
+ nic_state->statistic.tx_bytes += txsize;
+ } else {
+ PrintError("Rtl8139: Sending packet error: 0x%p\n", pkt);
+ nic_state->statistic.tx_dropped ++;
+ }
+ }
+
+ regs->tsd[descriptor] |= TSD_Tok;
+ regs->tsd[descriptor] |= TSD_Own;
+
+ nic_state->regs.isr |= ISR_Tok;
+ rtl8139_update_irq(nic_state);
+
+ return 0;
+}
+
+/*transmit status registers*/
+static void rtl8139_tsd_write(struct rtl8139_state * nic_state,
+ uint8_t descriptor,
+ uint32_t val){
+ if (!transmitter_enabled(nic_state)){
+ PrintDebug("RTL8139: TxStatus write val=0x%08x descriptor=%d, Transmitter not enabled\n", val, descriptor);
+
+ return;
+ }
+
+ PrintDebug("RTL8139: TSD write val=0x%08x descriptor=%d\n", val, descriptor);
+
+ // mask read-only bits
+ val &= ~0xff00c000;
+ val = SET_MASKED(val, 0x00c00000, nic_state->regs.tsd[descriptor]);
+
+ nic_state->regs.tsd[descriptor] = val;
+
+ tx_one_packet(nic_state, descriptor);
+}
+
+/* transmit status of all descriptors */
+static uint16_t rtl8139_txsad_read(struct rtl8139_state * nic_state){
+ uint16_t ret = 0;
+ struct rtl8139_regs *regs = &(nic_state->regs);
+
+ ret = ((regs->tsd[3] & TSD_Tok)?TSAD_TOK3:0)
+ |((regs->tsd[2] & TSD_Tok)?TSAD_TOK2:0)
+ |((regs->tsd[1] & TSD_Tok)?TSAD_TOK1:0)
+ |((regs->tsd[0] & TSD_Tok)?TSAD_TOK0:0)
+
+ |((regs->tsd[3] & TSD_Tun)?TSAD_TUN3:0)
+ |((regs->tsd[2] & TSD_Tun)?TSAD_TUN2:0)
+ |((regs->tsd[1] & TSD_Tun)?TSAD_TUN1:0)
+ |((regs->tsd[0] & TSD_Tun)?TSAD_TUN0:0)
+
+ |((regs->tsd[3] & TSD_Tabt)?TSAD_TABT3:0)
+ |((regs->tsd[2] & TSD_Tabt)?TSAD_TABT2:0)
+ |((regs->tsd[1] & TSD_Tabt)?TSAD_TABT1:0)
+ |((regs->tsd[0] & TSD_Tabt)?TSAD_TABT0:0)
+
+ |((regs->tsd[3] & TSD_Own)?TSAD_OWN3:0)
+ |((regs->tsd[2] & TSD_Own)?TSAD_OWN2:0)
+ |((regs->tsd[1] & TSD_Own)?TSAD_OWN1:0)
+ |((regs->tsd[0] & TSD_Own)?TSAD_OWN0:0) ;
+
+ PrintDebug("RTL8139: txsad read val=0x%04x\n", (int)ret);
+
+ return ret;
+}
+
+static inline void rtl8139_isr_write(struct rtl8139_state * nic_state, uint32_t val) {
+ struct rtl8139_regs *regs = &(nic_state->regs);
+
+ PrintDebug("RTL8139: ISR write val=0x%04x\n", val);
+
+ uint16_t newisr = regs->isr & ~val;
+
+ /* mask unwriteable bits */
+ newisr = SET_MASKED(newisr, 0x1e00, regs->isr);
+
+ /* writing 1 to interrupt status register bit clears it */
+ regs->isr = 0;
+ rtl8139_update_irq(nic_state);
+
+ regs->isr = newisr;
+ rtl8139_update_irq(nic_state);
+}
+
+static int rtl8139_mmio_write(struct guest_info * core,
+ addr_t guest_addr,
+ void * src,
+ uint_t length,
+ void * priv_data) {
+ int idx;
+ uint32_t val;
+ struct rtl8139_state *nic_state = (struct rtl8139_state *)(priv_data);
+
+ idx = guest_addr & 0xff;
+
+ memcpy(&val, src, length);
+
+ PrintDebug("rtl8139 mmio write: addr:0x%x (%u bytes): 0x%x\n", (int)guest_addr, length, val);
+
+ switch(idx) {
+ case RTL8139_IDR0 ... RTL8139_IDR0 + 5:
+ nic_state->regs.id[idx - RTL8139_IDR0] = val & 0xff;
+ break;
+
+ case RTL8139_MAR0 ... RTL8139_MAR0 + 7:
+ nic_state->regs.mult[idx - RTL8139_MAR0] = val & 0xff;
+ break;
+
+ case RTL8139_TSD0:
+ case RTL8139_TSD1:
+ case RTL8139_TSD2:
+ case RTL8139_TSD3:
+ rtl8139_tsd_write(nic_state, (idx - RTL8139_TSD0)/4, val);
+ break;
+
+ case RTL8139_TSAD0:
+ case RTL8139_TSAD1:
+ case RTL8139_TSAD2:
+ case RTL8139_TSAD3:
+ nic_state->regs.tsad[(idx - RTL8139_TSAD0)/4] = val;
+ break;
+
+ case RTL8139_RBSTART:
+ nic_state->regs.rbstart = val;
+ break;
+ case RTL8139_ERBCR:
+ nic_state->regs.erbcr = val & 0xffff;
+ break;
+ case RTL8139_ERSR:
+ //nic_state->regs.ersr = val & 0xff;
+ nic_state->regs.ersr &= (~val) & 0x0c;
+ break;
+ case RTL8139_CR:
+ rtl8139_cmd_write(nic_state, val);
+ break;
+ case RTL8139_CAPR:
+ {
+ val &= 0xffff;
+ /* this value is off by 16 */
+ nic_state->regs.capr = MOD2(val + 0x10, nic_state->rx_bufsize);
+
+ PrintDebug("RTL 8139: CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n",
+ nic_state->rx_bufsize, nic_state->regs.cbr, nic_state->regs.capr);
+ }
+ break;
+ case RTL8139_CBR: /* read only */
+ //nic_state->regs.cbr = val & 0xffff;
+ break;
+ case RTL8139_IMR:
+ {
+ PrintDebug("RTL8139: IMR write val=0x%04x\n", val);
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x1e00, nic_state->regs.imr);
+ nic_state->regs.imr = val;
+ rtl8139_update_irq(nic_state);
+ }
+ break;
+ case RTL8139_ISR:
+ rtl8139_isr_write(nic_state, val);
+ break;
+ case RTL8139_TCR:
+ nic_state->regs.tcr = val;
+ break;
+ case RTL8139_RCR:
+ rtl8139_rcr_write(nic_state, val);
+ break;
+ case RTL8139_TCTR:
+ nic_state->regs.tctr = 0; /* write clear current tick */
+ break;
+ case RTL8139_MPC:
+ nic_state->regs.mpc = 0; /* clear on write */
+ break;
+ case RTL8139_9346CR:
+ rtl8139_9346cr_write(nic_state, val);
+ break;
+ case RTL8139_CONFIG0:
+ nic_state->regs.config[0] = val & 0xff;
+ break;
+ case RTL8139_CONFIG1:
+ nic_state->regs.config[1] = val & 0xff;
+ break;
+ case RTL8139_TimerInt:
+ nic_state->regs.timer_int = val;
+ break;
+ case RTL8139_MSR:
+ nic_state->regs.msr = val & 0xff;
+ break;
+ case RTL8139_CONFIG3:
+ nic_state->regs.config3[0] = val & 0xff;
+ break;
+ case RTL8139_CONFIG4:
+ nic_state->regs.config3[1] = val & 0xff;
+ break;
+
+ case RTL8139_MULINT:
+ nic_state->regs.mulint = val & 0xffff;
+ break;
+ case RTL8139_RERID:
+ nic_state->regs.rerid = val & 0xffff;
+ break;
+ case RTL8139_TXSAD:
+ nic_state->regs.txsad = val & 0xffff;
+ break;
+ case RTL8139_BMCR:
+ nic_state->regs.bmcr = val & 0xffff;
+ break;
+ case RTL8139_BMSR:
+ nic_state->regs.bmsr = val & 0xffff;
+ break;
+ case RTL8139_ANAR:
+ nic_state->regs.anar = val & 0xffff;
+ break;
+ case RTL8139_ANLPAR:
+ nic_state->regs.anlpar = val & 0xffff;
+ break;
+ case RTL8139_ANER:
+ nic_state->regs.aner = val & 0xffff;
+ break;
+ case RTL8139_DIS:
+ nic_state->regs.dis = val & 0xffff;
+ break;
+ case RTL8139_FCSC:
+ nic_state->regs.fcsc = val & 0xffff;
+ break;
+ case RTL8139_NWAYTR:
+ nic_state->regs.nwaytr = val & 0xffff;
+ break;
+ case RTL8139_REC:
+ nic_state->regs.rec = val & 0xffff;
+ break;
+
+ case RTL8139_CSCR:
+ nic_state->regs.cscr = val;
+ break;
+ case RTL8139_PHY1_PARM:
+ nic_state->regs.phy1_parm = val;
+ break;
+ case RTL8139_TW_PARM:
+ nic_state->regs.tw_parm = val & 0xffff;
+ break;
+ case RTL8139_PHY2_PARM:
+ nic_state->regs.phy2_parm = val;
+ break;
+ case RTL8139_CRC0 ... RTL8139_CRC0 + 7:
+ nic_state->regs.crc[idx - RTL8139_CRC0] = val & 0xff;
+ break;
+
+ case RTL8139_Config5:
+ nic_state->regs.config5 = val & 0xff;
+ break;
+ default:
+ PrintDebug("rtl8139 write error: invalid port: 0x%x\n", idx);
+ }
+
+ return length;
+}
+
+static int rtl8139_mmio_read(struct guest_info * core,
+ addr_t guest_addr,
+ void * dst,
+ uint_t length,
+ void * priv_data) {
+ uint16_t idx;
+ uint32_t val;
+ struct rtl8139_state *nic_state = (struct rtl8139_state *)priv_data;
+
+ idx = guest_addr & 0xff;
+
+ switch(idx) {
+ case RTL8139_IDR0 ... RTL8139_IDR0 + 5:
+ val = nic_state->regs.id[idx - RTL8139_IDR0];
+ break;
+
+ case RTL8139_MAR0 ... RTL8139_MAR0 + 7:
+ val = nic_state->regs.mult[idx - RTL8139_MAR0];
+ break;
+
+ case RTL8139_TSD0:
+ case RTL8139_TSD1:
+ case RTL8139_TSD2:
+ case RTL8139_TSD3:
+ val = nic_state->regs.tsd[(idx - RTL8139_TSD0)/4];
+ break;
+
+ case RTL8139_TSAD0:
+ case RTL8139_TSAD1:
+ case RTL8139_TSAD2:
+ case RTL8139_TSAD3:
+ val = nic_state->regs.tsad[(idx - RTL8139_TSAD0)/4];
+ break;
+
+ case RTL8139_RBSTART:
+ val = nic_state->regs.rbstart;
+ break;
+ case RTL8139_ERBCR:
+ val = nic_state->regs.erbcr;
+ break;
+ case RTL8139_ERSR:
+ val = nic_state->regs.ersr;
+ break;
+ case RTL8139_CR:
+ {
+ val = nic_state->regs.cmd;
+ if (rxbufempty(nic_state)){
+ val |= CMD_Bufe;
+ }
+ }
+ break;
+ case RTL8139_CAPR:
+ /* this value is off by 16 -- don't know why - Lei*/
+ val = nic_state->regs.capr - 0x10;
+ break;
+ case RTL8139_CBR:
+ val = nic_state->regs.cbr;
+ break;
+ case RTL8139_IMR:
+ val = nic_state->regs.imr;
+ break;
+ case RTL8139_ISR:
+ val = (uint32_t)nic_state->regs.isr;
+ break;
+ case RTL8139_TCR:
+ val = nic_state->regs.tcr;
+ break;
+ case RTL8139_RCR:
+ val = nic_state->regs.rcr;
+ break;
+ case RTL8139_TCTR:
+ val = nic_state->regs.tctr;
+ break;
+ case RTL8139_MPC:
+ val = nic_state->regs.mpc;
+ break;
+ case RTL8139_9346CR:
+ val = rtl8139_9346cr_read(nic_state);
+ break;
+ case RTL8139_CONFIG0:
+ val = nic_state->regs.config[0];
+ break;
+ case RTL8139_CONFIG1:
+ val = nic_state->regs.config[1];
+ break;
+ case RTL8139_TimerInt:
+ val = nic_state->regs.timer_int;
+ break;
+ case RTL8139_MSR:
+ val = nic_state->regs.msr;
+ break;
+ case RTL8139_CONFIG3:
+ val = nic_state->regs.config3[0];
+ break;
+ case RTL8139_CONFIG4:
+ val = nic_state->regs.config3[1];
+ break;
+ case RTL8139_MULINT:
+ val = nic_state->regs.mulint;
+ break;
+ case RTL8139_RERID:
+ val = nic_state->regs.rerid;
+ break;
+ case RTL8139_TXSAD:
+ val = rtl8139_txsad_read(nic_state);
+ break;
+ case RTL8139_BMCR:
+ val = nic_state->regs.bmcr;
+ break;
+ case RTL8139_BMSR:
+ val = nic_state->regs.bmsr;
+ break;
+ case RTL8139_ANAR:
+ val = nic_state->regs.anar;
+ break;
+ case RTL8139_ANLPAR:
+ val = nic_state->regs.anlpar;
+ break;
+ case RTL8139_ANER:
+ val = nic_state->regs.aner;
+ break;
+ case RTL8139_DIS:
+ val = nic_state->regs.dis;
+ break;
+ case RTL8139_FCSC:
+ val = nic_state->regs.fcsc;
+ break;
+ case RTL8139_NWAYTR:
+ val = nic_state->regs.nwaytr;
+ break;
+ case RTL8139_REC:
+ val = nic_state->regs.rec;
+ break;
+ case RTL8139_CSCR:
+ val = nic_state->regs.cscr;
+ break;
+ case RTL8139_PHY1_PARM:
+ val = nic_state->regs.phy1_parm;
+ break;
+ case RTL8139_TW_PARM:
+ val = nic_state->regs.tw_parm;
+ break;
+ case RTL8139_PHY2_PARM:
+ val = nic_state->regs.phy2_parm;
+ break;
+ case RTL8139_CRC0 ... RTL8139_CRC0 + 7:
+ val = nic_state->regs.crc[idx - RTL8139_CRC0];
+ break;
+ case RTL8139_Config5:
+ val = nic_state->regs.config5;
+ break;
+ default:
+ val = 0x0;
+ break;
+ }
+
+ memcpy(dst, &val, length);
+
+ PrintDebug("rtl8139 mmio read: port:0x%x (%u bytes): 0x%x\n", (int)guest_addr, length, val);
+
+ return length;
+}
+
+
+static int rtl8139_ioport_write(struct guest_info * core,
+ uint16_t port,
+ void *src,
+ uint_t length,
+ void * private_data) {
+ return rtl8139_mmio_write(core, (addr_t)port,
+ src, length, private_data);
+}
+
+static int rtl8139_ioport_read(struct guest_info * core,
+ uint16_t port,
+ void *dst,
+ uint_t length,
+ void * private_data) {
+ return rtl8139_mmio_read(core, (addr_t)port,
+ dst, length, private_data);
+}
+
+
+static int rtl8139_init_state(struct rtl8139_state *nic_state) {
+ PrintDebug("rtl8139: init_state\n");
+
+ nic_state->regs.tsd[0] = nic_state->regs.tsd[1] = nic_state->regs.tsd[2] = nic_state->regs.tsd[3] = TSD_Own;
+
+ nic_state->regs.rerid = RTL8139_PCI_REVID_8139;
+ nic_state->regs.tcr |= ((0x1d << 26) | (0x1 << 22));
+
+ rtl8139_reset(nic_state);
+
+ return 0;
+}
+
+
+#if 0
+static inline int rtl8139_reset_device(struct rtl8139_state * nic_state) {
+ nic_state->regs.cmd |= CMD_Rst;
+ rtl8139_init_state(nic_state);
+ nic_state->regs.cmd &= ~CMD_Rst;
+
+ return 0;
+}
+
+static inline int rtl8139_start_device(struct rtl8139_state * nic_state) {
+ nic_state->regs.cmd |= CMD_Re | CMD_Te;
+
+ return 0;
+}
+
+static int rtl8139_stop_device(struct rtl8139_state * nic_state) {
+ PrintDebug("rtl8139: stop device\n");
+
+ nic_state->regs.cmd &= ~(CMD_Re | CMD_Te);
+
+ return 0;
+}
+
+static int rtl8139_hook_iospace(struct rtl8139_state * nic_state,
+ addr_t base_addr,
+ int size,
+ int type,
+ void *data) {
+ int i;
+
+ if (base_addr <= 0){
+ PrintError("In RTL8139: Fail to Hook IO Space, base address 0x%x\n", (int) base_addr);
+ return -1;
+ }
+
+ if (type == PCI_BAR_IO){
+ PrintDebug("In RTL8139: Hook IO ports starting from %x, size %d\n", (int) base_addr, size);
+
+ for (i = 0; i < 0xff; i++){
+ v3_dev_hook_io(nic_state->dev, base_addr + i, &rtl8139_ioport_read, &rtl8139_ioport_write);
+ }
+ } else if (type == PCI_BAR_MEM32) {
+ PrintDebug("In RTL8139: Hook memory space starting from %x, size %d\n", (int) base_addr, size);
+
+ //hook memory mapped I/O
+ v3_hook_full_mem(nic_state->vm, nic_state->vm->cores[0].cpu_id, base_addr, base_addr + 0xff,
+ &rtl8139_mmio_read, &rtl8139_mmio_write, nic_state);
+ } else {
+ PrintError("In RTL8139: unknown memory type: start %x, size %d\n", (int) base_addr, size);
+ }
+
+ return 0;
+}
+#endif
+
+static int register_dev(struct rtl8139_state * nic_state) {
+ int i;
+
+ if (nic_state->pci_bus == NULL) {
+ PrintError("RTL8139: Not attached to any PCI bus\n");
+
+ return -1;
+ }
+
+ struct v3_pci_bar bars[6];
+ struct pci_device * pci_dev = NULL;
+
+ for (i = 0; i < 6; i++) {
+ bars[i].type = PCI_BAR_NONE;
+ }
+
+ bars[0].type = PCI_BAR_IO;
+ bars[0].default_base_port = 0xc100;
+ bars[0].num_ports = 0x100;
+
+ bars[0].io_read = rtl8139_ioport_read;
+ bars[0].io_write = rtl8139_ioport_write;
+ bars[0].private_data = nic_state;
+
+/*
+ bars[1].type = PCI_BAR_MEM32;
+ bars[1].default_base_addr = -1;
+ bars[1].num_pages = 1;
+
+ bars[1].mem_read = rtl8139_mmio_read;
+ bars[1].mem_write = rtl8139_mmio_write;
+ bars[1].private_data = nic_state;
+*/
+
+ pci_dev = v3_pci_register_device(nic_state->pci_bus, PCI_STD_DEVICE, 0, -1, 0,
+ "RTL8139", bars,
+ NULL, NULL, NULL, nic_state);
+
+
+ if (pci_dev == NULL) {
+ PrintError("RTL8139: Could not register PCI Device\n");
+ return -1;
+ }
+
+ pci_dev->config_header.vendor_id = 0x10ec;
+ pci_dev->config_header.device_id = 0x8139;
+ pci_dev->config_header.command = 0x05;
+
+ pci_dev->config_header.revision = RTL8139_PCI_REVID_8139;
+
+ pci_dev->config_header.subclass = 0x00;
+ pci_dev->config_header.class = 0x02;
+ pci_dev->config_header.header_type = 0x00;
+
+ pci_dev->config_header.intr_line = 12;
+ pci_dev->config_header.intr_pin = 1;
+ pci_dev->config_space[0x34] = 0xdc;
+
+ nic_state->pci_dev = pci_dev;
+
+ return 0;
+}
+
+static int connect_fn(struct v3_vm_info * info,
+ void * frontend_data,
+ struct v3_dev_net_ops * ops,
+ v3_cfg_tree_t * cfg,
+ void * private_data) {
+ struct rtl8139_state * nic_state = (struct rtl8139_state *)frontend_data;
+
+ rtl8139_init_state(nic_state);
+ register_dev(nic_state);
+
+ nic_state->net_ops = ops;
+ nic_state->backend_data = private_data;
+
+ ops->recv = rtl8139_rx;
+ ops->poll = NULL;
+ ops->start_tx = NULL;
+ ops->stop_tx = NULL;
+ ops->frontend_data = nic_state;
+ memcpy(ops->fnt_mac, nic_state->mac, ETH_ALEN);
+
+ return 0;
+}
+
+
+static int rtl8139_free(void * private_data) {
+ struct rtl8139_state * nic_state = (struct rtl8139_state *)private_data;
+
+ /* dettached from backend */
+
+ /* unregistered from PCI? */
+
+ V3_Free(nic_state);
+
+ return 0;
+}
+
+
+static struct v3_device_ops dev_ops = {
+ .free = rtl8139_free,
+};
+
+
+static int rtl8139_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
+ struct vm_device * pci_bus = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
+ struct rtl8139_state * nic_state = NULL;
+ char * dev_id = v3_cfg_val(cfg, "ID");
+ char * macstr = v3_cfg_val(cfg, "mac");
+
+ nic_state = (struct rtl8139_state *)V3_Malloc(sizeof(struct rtl8139_state));
+ memset(nic_state, 0, sizeof(struct rtl8139_state));
+
+ nic_state->pci_bus = pci_bus;
+ nic_state->vm = vm;
+
+ if (macstr != NULL && !str2mac(macstr, nic_state->mac)) {
+ PrintDebug("RTL8139: Mac specified %s\n", macstr);
+ }else {
+ PrintDebug("RTL8139: MAC not specified\n");
+ random_ethaddr(nic_state->mac);
+ }
+
+ struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, nic_state);
+
+ if (dev == NULL) {
+ PrintError("RTL8139: Could not attach device %s\n", dev_id);
+ V3_Free(nic_state);
+ return -1;
+ }
+
+ nic_state->dev = dev;
+
+ if (v3_dev_add_net_frontend(vm, dev_id, connect_fn, (void *)nic_state) == -1) {
+ PrintError("RTL8139: Could not register %s as net frontend\n", dev_id);
+ v3_remove_device(dev);
+ V3_Free(nic_state);
+ return -1;
+ }
+
+ return 0;
+}
+
+device_register("RTL8139", rtl8139_init)
#define PrintDebug(fmt, args...)
#endif
-
struct vnet_nic_state {
struct v3_vm_info * vm;
struct v3_dev_net_ops net_ops;
int vnet_dev_id;
};
-/* called by frontend device,
- * tell the VNET can start sending pkt to it */
-static void start_rx(void * private_data){
- //struct vnet_nic_state *vnetnic = (struct vnet_nic_state *)private_data;
-
- //v3_vnet_enable_device(vnetnic->vnet_dev_id);
-}
-
-/* called by frontend device,
- * tell the VNET stop sending pkt to it */
-static void stop_rx(void * private_data){
- //struct vnet_nic_state *vnetnic = (struct vnet_nic_state *)private_data;
-
- //v3_vnet_disable_device(vnetnic->vnet_dev_id);
-}
/* called by frontend, send pkt to VNET */
static int vnet_nic_send(uint8_t * buf, uint32_t len,
vnetnic->net_ops.frontend_data);
}
-/* tell frontend device to poll data from guest */
+/* poll data from front-end */
static void virtio_poll(struct v3_vm_info * info,
void * private_data){
struct vnet_nic_state *vnetnic = (struct vnet_nic_state *)private_data;
}
-/* notify the frontend to start sending pkt to VNET*/
-static void start_tx(void * private_data){
- struct vnet_nic_state *vnetnic = (struct vnet_nic_state *)private_data;
-
- vnetnic->net_ops.start_tx(vnetnic->net_ops.frontend_data);
-}
-
-/* notify the frontend device to stop sending pkt to VNET*/
-static void stop_tx(void * private_data){
- struct vnet_nic_state *vnetnic = (struct vnet_nic_state *)private_data;
-
- vnetnic->net_ops.stop_tx(vnetnic->net_ops.frontend_data);
-}
-
-
static int vnet_nic_free(struct vnet_nic_state * vnetnic) {
v3_vnet_del_dev(vnetnic->vnet_dev_id);
static struct v3_vnet_dev_ops vnet_dev_ops = {
.input = virtio_input,
.poll = virtio_poll,
- .start_tx = start_tx,
- .stop_tx = stop_tx,
};
}
vnetnic->net_ops.send = vnet_nic_send;
- vnetnic->net_ops.start_rx = start_rx;
- vnetnic->net_ops.stop_rx = stop_rx;
vnetnic->vm = vm;
if (v3_dev_connect_net(vm, v3_cfg_val(frontend_cfg, "tag"),
if ((vnet_dev_id = v3_vnet_add_dev(vm, vnetnic->net_ops.fnt_mac, &vnet_dev_ops, (void *)vnetnic)) == -1) {
PrintError("Vnet-nic device %s fails to registered to VNET\n", dev_id);
+
v3_remove_device(dev);
return 0;
}
vnetnic->vnet_dev_id = vnet_dev_id;
- PrintDebug("Vnet-nic device %s registered to VNET\n", dev_id);
-
return 0;
}
ctrl_area->instrs.INTR = 1;
+ v3_hook_msr(core->vm_info, EFER_MSR,
+ &v3_handle_efer_read,
+ &v3_handle_efer_write,
+ core);
+
if (core->shdw_pg_mode == SHADOW_PAGING) {
PrintDebug("Creating initial shadow page table\n");
ctrl_area->cr_reads.cr3 = 1;
ctrl_area->cr_writes.cr3 = 1;
- v3_hook_msr(core->vm_info, EFER_MSR,
- &v3_handle_efer_read,
- &v3_handle_efer_write,
- core);
+
ctrl_area->instrs.INVLPG = 1;
vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data));
addr_t exit_code = 0, exit_info1 = 0, exit_info2 = 0;
- v3_adjust_time(info);
-
// Conditionally yield the CPU if the timeslice has expired
v3_yield_cond(info);
+ // Perform any additional yielding needed for time adjustment
+ v3_adjust_time(info);
+
+ // Update timer devices prior to entering VM.
+ v3_update_timers(info);
+
// disable global interrupts for vm state transition
v3_clgi();
}
#endif
- v3_update_timers(info);
+ v3_time_enter_vm(info);
guest_ctrl->TSC_OFFSET = v3_tsc_host_offset(&info->time_state);
//V3_Print("Calling v3_svm_launch\n");
- v3_svm_launch((vmcb_t *)V3_PAddr(info->vmm_data), &(info->vm_regs), (vmcb_t *)host_vmcbs[info->cpu_id]);
+ v3_svm_launch((vmcb_t *)V3_PAddr(info->vmm_data), &(info->vm_regs), (vmcb_t *)host_vmcbs[info->host_cpu_id]);
//V3_Print("SVM Returned: Exit Code: %x, guest_rip=%lx\n", (uint32_t)(guest_ctrl->exit_code), (unsigned long)guest_state->rip);
v3_last_exit = (uint32_t)(guest_ctrl->exit_code);
- //PrintDebug("SVM Returned\n");
-
+ // Immediate exit from VM time bookkeeping
+ v3_time_exit_vm(info);
+
info->num_exits++;
// Save Guest state from VMCB
struct guest_info * core = (struct guest_info *)p;
- PrintDebug("core %u: in start_core (RIP=%p)\n",
- core->cpu_id, (void *)(addr_t)core->rip);
+ PrintDebug("virtual core %u/physical core %u: in start_core (RIP=%p)\n",
+ core->cpu_id, core->host_cpu_id, (void *)(addr_t)core->rip);
// JRL: Whoa WTF? cpu_types are tied to the vcoreID????
- switch (v3_cpu_types[core->cpu_id]) {
+ switch (v3_cpu_types[core->host_cpu_id]) {
#ifdef CONFIG_SVM
case V3_SVM_CPU:
case V3_SVM_REV3_CPU:
int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
uint32_t i;
-#ifdef CONFIG_MULTITHREAD_OS
- int vcore_id = 0;
-#endif
uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
uint32_t avail_cores = 0;
-
-
/// CHECK IF WE ARE MULTICORE ENABLED....
V3_Print("V3 -- Starting VM (%u cores)\n", vm->num_cores);
V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
- // Check that enough cores are present in the mask to handle vcores
- for (i = 0; i < MAX_CORES; i++) {
- int major = i / 8;
- int minor = i % 8;
-
- if (core_mask[major] & (0x1 << minor)) {
- avail_cores++;
- }
-
- }
-
- if (vm->num_cores > avail_cores) {
+ if (vm->num_cores > MAX_CORES ) {
PrintError("Attempted to start a VM with too many cores (vm->num_cores = %d, avail_cores = %d, MAX=%d)\n", vm->num_cores, avail_cores, MAX_CORES);
return -1;
}
+ if (vm->cores[0].host_cpu_id != 0) {
+ PrintError("First virtual core must run on host core 0.\n");
+ return -1;
+ }
#ifdef CONFIG_MULTITHREAD_OS
// spawn off new threads, for other cores
- for (i = 0, vcore_id = 1; (i < MAX_CORES) && (vcore_id < vm->num_cores); i++) {
- int major = i / 8;
- int minor = i % 8;
+ for (i = 1; i < vm->num_cores; i++) {
+ struct guest_info *core = &(vm->cores[i]);
+ int major = core->host_cpu_id / 8;
+ int minor = core->host_cpu_id % 8;
void * core_thread = NULL;
- struct guest_info * core = &(vm->cores[vcore_id]);
-
- /* This assumes that the core 0 thread has been mapped to physical core 0 */
- if (i == V3_Get_CPU()) {
- // We skip the local CPU, because it is reserved for vcore 0
- continue;
- }
-
if ((core_mask[major] & (0x1 << minor)) == 0) {
- // cpuid not set in cpu_mask
+ PrintError("Host CPU %d not available for virtual core %d; not started\n",
+ core->host_cpu_id, i);
continue;
}
PrintDebug("Starting virtual core %u on logical core %u\n",
- vcore_id, i);
+ i, core->host_cpu_id);
- sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
+ sprintf(core->exec_name, "%s-%u", vm->name, i);
PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
- i, start_core, core, core->exec_name);
+ core->host_cpu_id, start_core, core, core->exec_name);
// TODO: actually manage these threads instead of just launching them
- core_thread = V3_CREATE_THREAD_ON_CPU(i, start_core, core, core->exec_name);
+ core_thread = V3_CREATE_THREAD_ON_CPU(core->host_cpu_id, start_core,
+ core, core->exec_name);
if (core_thread == NULL) {
PrintError("Thread launch failed\n");
return -1;
}
-
- vcore_id++;
}
#endif
int v3_vm_enter(struct guest_info * info) {
- switch (v3_cpu_types[info->cpu_id]) {
+ switch (v3_cpu_types[info->host_cpu_id]) {
#ifdef CONFIG_SVM
case V3_SVM_CPU:
case V3_SVM_REV3_CPU:
vm->mem_size = (addr_t)atoi(memory_str) * 1024 * 1024;
vm->mem_align = get_alignment(align_str);
+
PrintDebug("Alignment for %lu bytes of memory computed as 0x%x\n", vm->mem_size, vm->mem_align);
if (strcasecmp(vm_class, "PC") == 0) {
if (pg_mode) {
if ((strcasecmp(pg_mode, "nested") == 0)) {
- if (v3_cpu_types[info->cpu_id] == V3_SVM_REV3_CPU) {
+ if (v3_cpu_types[info->host_cpu_id] == V3_SVM_REV3_CPU) {
info->shdw_pg_mode = NESTED_PAGING;
} else {
PrintError("Nested paging not supported on this hardware. Defaulting to shadow paging\n");
}
static int pre_config_core(struct guest_info * info, v3_cfg_tree_t * core_cfg) {
-
+ char *hcpu;
if (determine_paging_mode(info, core_cfg))
return -1;
+ hcpu = v3_cfg_val(core_cfg, "hostcpu");
+ if (hcpu) {
+ int req_id = atoi(hcpu);
+ if (req_id < 0) {
+ PrintError("Invalid host core %d requested by"
+ " virtual cpu %d - ignored.\n", req_id, info->cpu_id);
+ } else {
+ PrintDebug("Assigned host core %d to virtual core %d.\n", info->cpu_id, req_id);
+ info->host_cpu_id = req_id;
+ }
+ }
+
v3_init_core(info);
if (info->vm_info->vm_class == V3_PC_VM) {
}
num_cores = atoi(v3_cfg_val(cores_cfg, "count"));
-
if (num_cores == 0) {
PrintError("No cores specified in configuration\n");
return NULL;
return NULL;
}
-
V3_Print("Per core configuration\n");
per_core_cfg = v3_cfg_subtree(cores_cfg, "core");
info->cpu_id = i;
info->vm_info = vm;
info->core_cfg_data = per_core_cfg;
+ info->host_cpu_id = i; // may be overriden by core config
if (pre_config_core(info, per_core_cfg) == -1) {
PrintError("Error in core %d preconfiguration\n", i);
return NULL;
}
+
per_core_cfg = v3_cfg_next_branch(per_core_cfg);
}
PrintDebug("EFER Write\n");
PrintDebug("EFER Write Values: HI=%x LO=%x\n", src.hi, src.lo);
+
//PrintDebug("Old EFER=%p\n", (void *)*(addr_t*)(shadow_efer));
// We virtualize the guests efer to hide the SVME and LMA bits
guest_efer->value = src.value;
-
- // Enable/Disable Syscall
- shadow_efer->sce = src.value & 0x1;
-
+ if (core->shdw_pg_mode == SHADOW_PAGING) {
+ // Enable/Disable Syscall
+ shadow_efer->sce = src.value & 0x1;
+ } else if (core->shdw_pg_mode == NESTED_PAGING) {
+ *(uint64_t *)shadow_efer = src.value;
+ shadow_efer->svme = 1;
+ }
return 0;
}
static struct v3_packet_hooks * packet_hooks = 0;
int V3_send_raw(const char * pkt, uint32_t len) {
- V3_ASSERT(packet_hooks);
- V3_ASSERT(packet_hooks->send);
+ if(packet_hooks != NULL && packet_hooks->send != NULL){
+ return packet_hooks->send(pkt, len, NULL);
+ }
- return packet_hooks->send(pkt, len, NULL);
+ return -1;
}
int V3_packet_add_recver(const char * mac, struct v3_vm_info * vm){
+ if(packet_hooks != NULL && packet_hooks->add_recver != NULL){
+ return packet_hooks->add_recver(mac, vm);
+ }
- return packet_hooks->add_recver(mac, vm);
+ return -1;
}
int V3_packet_del_recver(const char * mac, struct v3_vm_info * vm){
+ if(packet_hooks != NULL && packet_hooks->del_recver != NULL){
+ return packet_hooks->del_recver(mac, vm);
+ }
- return packet_hooks->del_recver(mac, vm);
+ return -1;
}
void V3_Init_Packet(struct v3_packet_hooks * hooks) {
*eax = *(uint32_t *)"V3V";
- if ((v3_cpu_types[core->cpu_id] == V3_SVM_CPU) ||
- (v3_cpu_types[core->cpu_id] == V3_SVM_REV3_CPU)) {
+ if ((v3_cpu_types[core->host_cpu_id] == V3_SVM_CPU) ||
+ (v3_cpu_types[core->host_cpu_id] == V3_SVM_REV3_CPU)) {
*ebx = *(uint32_t *)"SVM";
- } else if ((v3_cpu_types[core->cpu_id] == V3_VMX_CPU) ||
- (v3_cpu_types[core->cpu_id] == V3_VMX_EPT_CPU)) {
+ } else if ((v3_cpu_types[core->host_cpu_id] == V3_VMX_CPU) ||
+ (v3_cpu_types[core->host_cpu_id] == V3_VMX_EPT_CPU)) {
*ebx = *(uint32_t *)"VMX";
}
* redistribute, and modify it as specified in the file "V3VEE_LICENSE".
*/
-#include <palacios/vmm_time.h>
#include <palacios/vmm.h>
+#include <palacios/vmm_time.h>
#include <palacios/vm_guest.h>
#ifndef CONFIG_DEBUG_TIME
uint64_t t = v3_get_host_time(&info->time_state);
PrintDebug("Starting initial guest time as %llu\n", t);
+ info->time_state.enter_time = 0;
+ info->time_state.exit_time = t;
info->time_state.last_update = t;
info->time_state.initial_time = t;
info->yield_start_cycle = t;
return 0;
}
-// If the guest is supposed to run slower than the host, yield out until
-// the host time is appropriately far along;
+// Control guest time in relation to host time so that the two stay
+// appropriately synchronized to the extent possible.
int v3_adjust_time(struct guest_info * info) {
struct vm_time * time_state = &(info->time_state);
+ uint64_t host_time, target_host_time;
+ uint64_t guest_time, target_guest_time, old_guest_time;
+ uint64_t guest_elapsed, host_elapsed, desired_elapsed;
- if (time_state->host_cpu_freq == time_state->guest_cpu_freq) {
- time_state->guest_host_offset = 0;
- } else {
- uint64_t guest_time, guest_elapsed, desired_elapsed;
- uint64_t host_time, target_host_time;
+ /* Compute the target host time given how much time has *already*
+ * passed in the guest */
+ guest_time = v3_get_guest_time(time_state);
+ guest_elapsed = (guest_time - time_state->initial_time);
+ desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
+ target_host_time = time_state->initial_time + desired_elapsed;
+
+ /* Now, let the host run while the guest is stopped to make the two
+ * sync up. */
+ host_time = v3_get_host_time(time_state);
+ old_guest_time = v3_get_guest_time(time_state);
+ while (target_host_time > host_time) {
+ v3_yield(info);
+ host_time = v3_get_host_time(time_state);
+ }
+ guest_time = v3_get_guest_time(time_state);
+ // We do *not* assume the guest timer was paused in the VM. If it was
+ // this offseting is 0. If it wasn't we need this.
+ v3_offset_time(info, (sint64_t)old_guest_time - (sint64_t)guest_time);
+
+ /* Now the host may have gotten ahead of the guest because
+ * yielding is a coarse grained thing. Figure out what guest time
+ * we want to be at, and use the use the offsetting mechanism in
+ * the VMM to make the guest run forward. We limit *how* much we skew
+ * it forward to prevent the guest time making large jumps,
+ * however. */
+ host_elapsed = host_time - time_state->initial_time;
+ desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
+ target_guest_time = time_state->initial_time + desired_elapsed;
+
+ if (guest_time < target_guest_time) {
+ uint64_t max_skew, desired_skew, skew;
+
+ if (time_state->enter_time) {
+ max_skew = (time_state->exit_time - time_state->enter_time)/10;
+ } else {
+ max_skew = 0;
+ }
+ desired_skew = target_guest_time - guest_time;
+ skew = desired_skew > max_skew ? max_skew : desired_skew;
+/* PrintDebug("Guest %llu cycles behind where it should be.\n",
+ desired_skew);
+ PrintDebug("Limit on forward skew is %llu. Skewing forward %llu.\n",
+ max_skew, skew); */
+
+ v3_offset_time(info, skew);
+ }
+
+ return 0;
+}
- guest_time = v3_get_guest_time(time_state);
+/* Called immediately upon entry in the the VMM */
+int
+v3_time_exit_vm( struct guest_info * info )
+{
+ struct vm_time * time_state = &(info->time_state);
+
+ time_state->exit_time = v3_get_host_time(time_state);
- /* Compute what host time this guest time should correspond to. */
- guest_elapsed = (guest_time - time_state->initial_time);
- desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
- target_host_time = time_state->initial_time + desired_elapsed;
+ return 0;
+}
- /* Yield until that host time is reached */
- host_time = v3_get_host_time(time_state);
+/* Called immediately prior to entry to the VM */
+int
+v3_time_enter_vm( struct guest_info * info )
+{
+ struct vm_time * time_state = &(info->time_state);
+ uint64_t guest_time, host_time;
- while (host_time < target_host_time) {
- v3_yield(info);
- host_time = v3_get_host_time(time_state);
- }
+ guest_time = v3_get_guest_time(time_state);
+ host_time = v3_get_host_time(time_state);
+ time_state->enter_time = host_time;
+ time_state->guest_host_offset = guest_time - host_time;
- time_state->guest_host_offset = (sint64_t)guest_time - (sint64_t)host_time;
- }
+ // Because we just modified the offset - shouldn't matter as this should be
+ // the last time-related call prior to entering the VMM, but worth it
+ // just in case.
+ time_state->exit_time = host_time;
return 0;
}
-
+
+int v3_offset_time( struct guest_info * info, sint64_t offset )
+{
+ struct vm_time * time_state = &(info->time_state);
+// PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
+ time_state->guest_host_offset += offset;
+ return 0;
+}
+
struct v3_timer * v3_add_timer(struct guest_info * info,
struct v3_timer_ops * ops,
void * private_data) {
}
void v3_update_timers(struct guest_info * info) {
+ struct vm_time *time_state = &info->time_state;
struct v3_timer * tmp_timer;
uint64_t old_time = info->time_state.last_update;
- uint64_t cycles;
+ sint64_t cycles;
- info->time_state.last_update = v3_get_guest_time(&info->time_state);
- cycles = info->time_state.last_update - old_time;
+ time_state->last_update = v3_get_guest_time(time_state);
+ cycles = time_state->last_update - old_time;
- list_for_each_entry(tmp_timer, &(info->time_state.timers), timer_link) {
- tmp_timer->ops->update_timer(info, cycles, info->time_state.guest_cpu_freq, tmp_timer->private_data);
+ list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
+ tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
}
}
int v3_handle_rdtscp(struct guest_info * info) {
+ PrintDebug("Handling virtual RDTSCP call.\n");
v3_rdtscp(info);
-
+
info->vm_regs.rax &= 0x00000000ffffffffLL;
info->vm_regs.rcx &= 0x00000000ffffffffLL;
info->vm_regs.rdx &= 0x00000000ffffffffLL;
time_state->guest_cpu_freq = atoi(khz);
PrintDebug("Core %d CPU frequency requested at %d khz.\n",
info->cpu_id, time_state->guest_cpu_freq);
- }
+ }
- if ((khz == NULL) || (time_state->guest_cpu_freq > time_state->host_cpu_freq)) {
+ if ((khz == NULL) || (time_state->guest_cpu_freq <= 0)
+ || (time_state->guest_cpu_freq > time_state->host_cpu_freq)) {
time_state->guest_cpu_freq = time_state->host_cpu_freq;
}
#ifdef CONFIG_DEBUG_VNET
static inline void mac_to_string(uint8_t * mac, char * buf) {
- snprintf(buf, 100, "%d:%d:%d:%d:%d:%d",
+ snprintf(buf, 100, "%2x:%2x:%2x:%2x:%2x:%2x",
mac[0], mac[1], mac[2],
mac[3], mac[4], mac[5]);
}
-static void print_route(struct vnet_route_info * route){
+static void print_route(struct v3_vnet_route * route){
char str[50];
- mac_to_string(route->route_def.src_mac, str);
+ mac_to_string(route->src_mac, str);
PrintDebug("Src Mac (%s), src_qual (%d)\n",
- str, route->route_def.src_mac_qual);
- mac_to_string(route->route_def.dst_mac, str);
+ str, route->src_mac_qual);
+ mac_to_string(route->dst_mac, str);
PrintDebug("Dst Mac (%s), dst_qual (%d)\n",
- str, route->route_def.dst_mac_qual);
+ str, route->dst_mac_qual);
PrintDebug("Src dev id (%d), src type (%d)",
- route->route_def.src_id,
- route->route_def.src_type);
+ route->src_id,
+ route->src_type);
PrintDebug("Dst dev id (%d), dst type (%d)\n",
- route->route_def.dst_id,
- route->route_def.dst_type);
- if (route->route_def.dst_type == LINK_INTERFACE) {
- PrintDebug("dst_dev (%p), dst_dev_id (%d), dst_dev_ops(%p), dst_dev_data (%p)\n",
- route->dst_dev,
- route->dst_dev->dev_id,
- (void *)&(route->dst_dev->dev_ops),
- route->dst_dev->private_data);
- }
+ route->dst_id,
+ route->dst_type);
}
static void dump_routes(){
- struct vnet_route_info *route;
+ struct vnet_route_info *route;
- int i = 0;
- PrintDebug("\n========Dump routes starts ============\n");
- list_for_each_entry(route, &(vnet_state.routes), node) {
- PrintDebug("\nroute %d:\n", i++);
+ int i = 0;
+ PrintDebug("\n========Dump routes starts ============\n");
+ list_for_each_entry(route, &(vnet_state.routes), node) {
+ PrintDebug("\nroute %d:\n", i++);
- print_route(route);
+ print_route(&(route->route_def));
+ if (route->route_def.dst_type == LINK_INTERFACE) {
+ PrintDebug("dst_dev (%p), dst_dev_id (%d), dst_dev_ops(%p), dst_dev_data (%p)\n",
+ route->dst_dev,
+ route->dst_dev->dev_id,
+ (void *)&(route->dst_dev->dev_ops),
+ route->dst_dev->private_data);
}
- PrintDebug("\n========Dump routes end ============\n");
+ }
+
+ PrintDebug("\n========Dump routes end ============\n");
}
#endif
struct vnet_dev * dev = NULL;
list_for_each_entry(dev, &(vnet_state.devs), node) {
- if (!memcmp(dev->mac_addr, mac, ETH_ALEN))
+ if (!compare_ethaddr(dev->mac_addr, mac)){
return dev;
+ }
+
+ char *dmac = dev->mac_addr;
+ PrintDebug("device %d: %2x:%2x:%2x:%2x:%2x:%2x\n", dev->dev_id, dmac[0], dmac[1], dmac[2], dmac[3], dmac[4], dmac[5]);
}
return NULL;
}
+int v3_vnet_find_dev(uint8_t * mac) {
+ struct vnet_dev * dev = NULL;
+
+ PrintDebug("find_dev: %2x:%2x:%2x:%2x:%2x:%2x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ dev = dev_by_mac(mac);
+
+ if(dev != NULL) {
+ return dev->dev_id;
+ }
+
+ return -1;
+}
+
+
int v3_vnet_add_route(struct v3_vnet_route route) {
struct vnet_route_info * new_route = NULL;
unsigned long flags;
new_route = (struct vnet_route_info *)V3_Malloc(sizeof(struct vnet_route_info));
memset(new_route, 0, sizeof(struct vnet_route_info));
- PrintDebug("VNET/P Core: add_route_entry: dst_id: %d, dst_type: %d\n",
- route.dst_id, route.dst_type);
+ PrintDebug("VNET/P Core: add_route_entry:\n");
+ print_route(&route);
memcpy(new_route->route_def.src_mac, route.src_mac, ETH_ALEN);
memcpy(new_route->route_def.dst_mac, route.dst_mac, ETH_ALEN);
new_route->route_def.dst_mac_qual = route.dst_mac_qual;
new_route->route_def.dst_type = route.dst_type;
new_route->route_def.src_type = route.src_type;
-
- if(route.dst_id == -1){
- if (new_route->route_def.dst_type == LINK_INTERFACE) {
- new_route->dst_dev = dev_by_mac(route.dst_mac);
- }
- new_route->route_def.dst_id = new_route->dst_dev->dev_id;
- } else {
- new_route->route_def.dst_id = route.dst_id;
- if (new_route->route_def.dst_type == LINK_INTERFACE) {
- new_route->dst_dev = dev_by_id(new_route->route_def.dst_id);
- }
+ new_route->route_def.src_id = route.src_id;
+ new_route->route_def.dst_id = route.dst_id;
+
+ if (new_route->route_def.dst_type == LINK_INTERFACE) {
+ new_route->dst_dev = dev_by_id(new_route->route_def.dst_id);
}
- if(route.src_id == -1){
- if (new_route->route_def.src_type == LINK_INTERFACE) {
- new_route->src_dev = dev_by_mac(route.src_mac);
- }
- new_route->route_def.src_id = new_route->src_dev->dev_id;
- } else {
- new_route->route_def.src_id = route.src_id;
- if (new_route->route_def.src_type == LINK_INTERFACE) {
- new_route->src_dev = dev_by_id(new_route->route_def.src_id);
- }
+ if (new_route->route_def.src_type == LINK_INTERFACE) {
+ new_route->src_dev = dev_by_id(new_route->route_def.src_id);
}
+
flags = v3_lock_irqsave(vnet_state.lock);
list_add(&(new_route->node), &(vnet_state.routes));
int max_rank = 0;
struct list_head match_list;
struct eth_hdr * hdr = (struct eth_hdr *)(pkt->data);
- uint8_t src_type = pkt->src_type;
- uint32_t src_link = pkt->src_id;
+ // uint8_t src_type = pkt->src_type;
+ // uint32_t src_link = pkt->src_id;
#ifdef CONFIG_DEBUG_VNET
{
list_for_each_entry(route, &(vnet_state.routes), node) {
struct v3_vnet_route * route_def = &(route->route_def);
+/*
// CHECK SOURCE TYPE HERE
if ( (route_def->src_type != LINK_ANY) &&
( (route_def->src_type != src_type) ||
( (route_def->src_id != src_link) &&
- (route_def->src_id != (uint32_t)-1)))) {
+ (route_def->src_id != -1)))) {
continue;
}
-
+*/
if ((route_def->dst_mac_qual == MAC_ANY) &&
(route_def->src_mac_qual == MAC_ANY)) {
}
}
-/* TODO: Round-bin or ?? */
-void v3_vnet_poll(struct v3_vm_info * vm){
- struct vnet_dev * dev = NULL;
- struct vnet_brg_dev *bridge = vnet_state.bridge;
-
- list_for_each_entry(dev, &(vnet_state.devs), node) {
- if(dev->mode == VMM_DRIVERN &&
- dev->active &&
- dev->vm == vm){
-
- dev->dev_ops.poll(vm, dev->private_data);
- }
- }
-
- if (bridge != NULL &&
- bridge->active &&
- bridge->mode == VMM_DRIVERN) {
-
- bridge->brg_ops.poll(bridge->vm, bridge->private_data);
- }
-
-}
-
int v3_vnet_add_bridge(struct v3_vm_info * vm,
struct v3_vnet_bridge_ops * ops,
uint8_t type,
#define V3_XML_DUP 0x20 // attribute name and value are strduped
//
+static char * V3_XML_NIL[] = { NULL }; // empty, null terminated array of strings
+
#define V3_XML_WS "\t\r\n " // whitespace
#define V3_XML_ERRL 128 // maximum error string length
// sets a flag for the given tag and returns the tag
static struct v3_xml * v3_xml_set_flag(struct v3_xml * xml, short flag)
{
- if (xml) xml->flags |= flag;
+ if (xml) {
+ xml->flags |= flag;
+ }
return xml;
}
}
// inserts an existing tag into an v3_xml structure
-static struct v3_xml * v3_xml_insert(struct v3_xml * xml, struct v3_xml * dest, size_t off) {
+struct v3_xml * v3_xml_insert(struct v3_xml * xml, struct v3_xml * dest, size_t off) {
struct v3_xml * cur, * prev, * head;
xml->next = NULL;
V3_Free(xml);
}
+
+
+
+
+
+
+// sets the character content for the given tag and returns the tag
+struct v3_xml * v3_xml_set_txt(struct v3_xml * xml, const char *txt) {
+ if (! xml) {
+ return NULL;
+ }
+
+ if (xml->flags & V3_XML_TXTM) {
+ // existing txt was malloced
+ V3_Free(xml->txt);
+ }
+
+ xml->flags &= ~V3_XML_TXTM;
+ xml->txt = (char *)txt;
+ return xml;
+}
+
+// Sets the given tag attribute or adds a new attribute if not found. A value
+// of NULL will remove the specified attribute. Returns the tag given.
+struct v3_xml * v3_xml_set_attr(struct v3_xml * xml, const char * name, const char * value) {
+ int l = 0;
+ int c;
+
+ if (! xml) {
+ return NULL;
+ }
+
+ while (xml->attr[l] && strcmp(xml->attr[l], name)) {
+ l += 2;
+ }
+
+ if (! xml->attr[l]) {
+ // not found, add as new attribute
+
+ if (! value) {
+ // nothing to do
+ return xml;
+ }
+
+ if (xml->attr == V3_XML_NIL) {
+ // first attribute
+ xml->attr = V3_Malloc(4 * sizeof(char *));
+
+ // empty list of malloced names/vals
+ xml->attr[1] = strdup("");
+ } else {
+ xml->attr = tmp_realloc(xml->attr, l * sizeof(char *), (l + 4) * sizeof(char *));
+ }
+
+ // set attribute name
+ xml->attr[l] = (char *)name;
+
+ // null terminate attribute list
+ xml->attr[l + 2] = NULL;
+
+ xml->attr[l + 3] = tmp_realloc(xml->attr[l + 1],
+ strlen(xml->attr[l + 1]),
+ (c = strlen(xml->attr[l + 1])) + 2);
+
+ // set name/value as not malloced
+ strcpy(xml->attr[l + 3] + c, " ");
+
+ if (xml->flags & V3_XML_DUP) {
+ xml->attr[l + 3][c] = V3_XML_NAMEM;
+ }
+ } else if (xml->flags & V3_XML_DUP) {
+ // name was strduped
+ V3_Free((char *)name);
+ }
+
+
+ // find end of attribute list
+ for (c = l; xml->attr[c]; c += 2);
+
+ if (xml->attr[c + 1][l / 2] & V3_XML_TXTM) {
+ //old val
+ V3_Free(xml->attr[l + 1]);
+ }
+
+ if (xml->flags & V3_XML_DUP) {
+ xml->attr[c + 1][l / 2] |= V3_XML_TXTM;
+ } else {
+ xml->attr[c + 1][l / 2] &= ~V3_XML_TXTM;
+ }
+
+
+ if (value) {
+ // set attribute value
+ xml->attr[l + 1] = (char *)value;
+ } else {
+ // remove attribute
+
+ if (xml->attr[c + 1][l / 2] & V3_XML_NAMEM) {
+ V3_Free(xml->attr[l]);
+ }
+
+ memmove(xml->attr + l, xml->attr + l + 2, (c - l + 2) * sizeof(char*));
+
+ xml->attr = tmp_realloc(xml->attr, c * sizeof(char *), (c + 2) * sizeof(char *));
+
+ // fix list of which name/vals are malloced
+ memmove(xml->attr[c + 1] + (l / 2), xml->attr[c + 1] + (l / 2) + 1,
+ (c / 2) - (l / 2));
+ }
+
+ // clear strdup() flag
+ xml->flags &= ~V3_XML_DUP;
+
+ return xml;
+}
+
+// removes a tag along with its subtags without freeing its memory
+struct v3_xml * v3_xml_cut(struct v3_xml * xml) {
+ struct v3_xml * cur;
+
+ if (! xml) {
+ // nothing to do
+ return NULL;
+ }
+
+ if (xml->next) {
+ // patch sibling list
+ xml->next->sibling = xml->sibling;
+ }
+
+
+ if (xml->parent) {
+ // not root tag
+
+ // find head of subtag list
+ cur = xml->parent->child;
+
+ if (cur == xml) {
+ // first subtag
+ xml->parent->child = xml->ordered;
+ } else {
+ // not first subtag
+
+ while (cur->ordered != xml) {
+ cur = cur->ordered;
+ }
+
+ // patch ordered list
+ cur->ordered = cur->ordered->ordered;
+
+ // go back to head of subtag list
+ cur = xml->parent->child;
+
+ if (strcmp(cur->name, xml->name)) {
+ // not in first sibling list
+
+ while (strcmp(cur->sibling->name, xml->name)) {
+ cur = cur->sibling;
+ }
+
+ if (cur->sibling == xml) {
+ // first of a sibling list
+ cur->sibling = (xml->next) ? xml->next
+ : cur->sibling->sibling;
+ } else {
+ // not first of a sibling list
+ cur = cur->sibling;
+ }
+ }
+
+ while (cur->next && cur->next != xml) {
+ cur = cur->next;
+ }
+
+ if (cur->next) {
+ // patch next list
+ cur->next = cur->next->next;
+ }
+ }
+ }
+ xml->ordered = xml->sibling = xml->next = NULL;
+ return xml;
+}
+
+
+
+
+/* ************************** */
+/* *** XML ENCODING *** */
+/* ************************** */
+
+// Encodes ampersand sequences appending the results to *dst, reallocating *dst
+// if length excedes max. a is non-zero for attribute encoding. Returns *dst
+static char *ampencode(const char *s, size_t len, char **dst, size_t *dlen,
+ size_t * max, short a)
+{
+ const char * e;
+
+ for (e = s + len; s != e; s++) {
+ while (*dlen + 10 > *max) *dst = tmp_realloc(*dst, *max, *max += V3_XML_BUFSIZE);
+
+ switch (*s) {
+ case '\0': return *dst;
+ case '&': *dlen += sprintf(*dst + *dlen, "&"); break;
+ case '<': *dlen += sprintf(*dst + *dlen, "<"); break;
+ case '>': *dlen += sprintf(*dst + *dlen, ">"); break;
+ case '"': *dlen += sprintf(*dst + *dlen, (a) ? """ : "\""); break;
+ case '\n': *dlen += sprintf(*dst + *dlen, (a) ? "
" : "\n"); break;
+ case '\t': *dlen += sprintf(*dst + *dlen, (a) ? "	" : "\t"); break;
+ case '\r': *dlen += sprintf(*dst + *dlen, "
"); break;
+ default: (*dst)[(*dlen)++] = *s;
+ }
+ }
+ return *dst;
+}
+
+
+
+// Recursively converts each tag to xml appending it to *s. Reallocates *s if
+// its length excedes max. start is the location of the previous tag in the
+// parent tag's character content. Returns *s.
+static char *toxml_r(struct v3_xml * xml, char **s, size_t *len, size_t *max,
+ size_t start, char ***attr) {
+ int i, j;
+ char *txt = (xml->parent) ? xml->parent->txt : "";
+ size_t off = 0;
+
+ // parent character content up to this tag
+ *s = ampencode(txt + start, xml->off - start, s, len, max, 0);
+
+ while (*len + strlen(xml->name) + 4 > *max) // reallocate s
+ *s = tmp_realloc(*s, *max, *max += V3_XML_BUFSIZE);
+
+ *len += sprintf(*s + *len, "<%s", xml->name); // open tag
+ for (i = 0; xml->attr[i]; i += 2) { // tag attributes
+ if (v3_xml_attr(xml, xml->attr[i]) != xml->attr[i + 1]) continue;
+ while (*len + strlen(xml->attr[i]) + 7 > *max) // reallocate s
+ *s = tmp_realloc(*s, *max, *max += V3_XML_BUFSIZE);
+
+ *len += sprintf(*s + *len, " %s=\"", xml->attr[i]);
+ ampencode(xml->attr[i + 1], -1, s, len, max, 1);
+ *len += sprintf(*s + *len, "\"");
+ }
+
+ for (i = 0; attr[i] && strcmp(attr[i][0], xml->name); i++);
+ for (j = 1; attr[i] && attr[i][j]; j += 3) { // default attributes
+ if (! attr[i][j + 1] || v3_xml_attr(xml, attr[i][j]) != attr[i][j + 1])
+ continue; // skip duplicates and non-values
+ while (*len + strlen(attr[i][j]) + 7 > *max) // reallocate s
+ *s = tmp_realloc(*s, *max, *max += V3_XML_BUFSIZE);
+
+ *len += sprintf(*s + *len, " %s=\"", attr[i][j]);
+ ampencode(attr[i][j + 1], -1, s, len, max, 1);
+ *len += sprintf(*s + *len, "\"");
+ }
+ *len += sprintf(*s + *len, ">");
+
+ *s = (xml->child) ? toxml_r(xml->child, s, len, max, 0, attr) //child
+ : ampencode(xml->txt, -1, s, len, max, 0); //data
+
+ while (*len + strlen(xml->name) + 4 > *max) // reallocate s
+ *s = tmp_realloc(*s, *max, *max += V3_XML_BUFSIZE);
+
+ *len += sprintf(*s + *len, "</%s>", xml->name); // close tag
+
+ while (txt[off] && off < xml->off) off++; // make sure off is within bounds
+ return (xml->ordered) ? toxml_r(xml->ordered, s, len, max, off, attr)
+ : ampencode(txt + off, -1, s, len, max, 0);
+}
+
+// Converts an xml structure back to xml. Returns a string of xml data that
+// must be freed.
+char * v3_xml_tostr(struct v3_xml * xml) {
+ struct v3_xml * p = (xml) ? xml->parent : NULL;
+ struct v3_xml * o = (xml) ? xml->ordered : NULL;
+ struct v3_xml_root * root = (struct v3_xml_root *)xml;
+ size_t len = 0, max = V3_XML_BUFSIZE;
+ char *s = strcpy(V3_Malloc(max), "");
+
+ if (! xml || ! xml->name) return tmp_realloc(s, max, len + 1);
+ while (root->xml.parent) root = (struct v3_xml_root *)root->xml.parent; // root tag
+
+
+ xml->parent = xml->ordered = NULL;
+ s = toxml_r(xml, &s, &len, &max, 0, root->attr);
+ xml->parent = p;
+ xml->ordered = o;
+
+
+ return tmp_realloc(s, max, len + 1);
+}
#include <palacios/vmm_lowlevel.h>
#include <palacios/vmm_ctrl_regs.h>
#include <palacios/vmm_config.h>
+#include <palacios/vmm_time.h>
#include <palacios/vm_guest_mem.h>
#include <palacios/vmm_direct_paging.h>
#include <palacios/vmx_io.h>
// Conditionally yield the CPU if the timeslice has expired
v3_yield_cond(info);
- /* If this guest is frequency-lagged behind host time, wait
- * for the appropriate host time before resuming the guest. */
+ // Perform any additional yielding needed for time adjustment
v3_adjust_time(info);
- // v3_print_guest_state(info);
+ // Update timer devices prior to entering VM.
+ v3_update_timers(info);
// disable global interrupts for vm state transition
v3_disable_ints();
vmcs_write(VMCS_GUEST_CR3, guest_cr3);
}
- v3_update_timers(info);
+ // Perform last-minute time bookkeeping prior to entering the VM
+ v3_time_enter_vm(info);
tsc_offset_high = (uint32_t)((v3_tsc_host_offset(&info->time_state) >> 32) & 0xffffffff);
tsc_offset_low = (uint32_t)(v3_tsc_host_offset(&info->time_state) & 0xffffffff);
return -1;
}
+ // Immediate exit from VM time bookkeeping
+ v3_time_exit_vm(info);
+
info->num_exits++;
/* Update guest state */