From: Peter Dinda Date: Wed, 2 Mar 2011 21:46:24 +0000 (-0600) Subject: Merge branch 'devel' of palacios@newskysaw.cs.northwestern.edu:/home/palacios/palacio... X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=commitdiff_plain;h=f2dd1e1c84245765e5fb67c19ba21697d694d5e6;hp=a866797999bb9910cf3c235d5fb21f97d92f886d;p=palacios.git Merge branch 'devel' of palacios@newskysaw.cs.northwestern.edu:/home/palacios/palacios into devel --- diff --git a/Kconfig b/Kconfig index dcee752..45a0568 100644 --- a/Kconfig +++ b/Kconfig @@ -226,14 +226,23 @@ endmenu 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" diff --git a/Kconfig.stdlibs b/Kconfig.stdlibs index 09e082a..4fa3315 100644 --- a/Kconfig.stdlibs +++ b/Kconfig.stdlibs @@ -109,7 +109,6 @@ config BUILT_IN_STRNCPY config BUILT_IN_STRDUP bool "strdup()" - default n depends on BUILT_IN_STDLIB help This enables Palacios' internal implementation of strdup diff --git a/bios/vgabios/vgabios-qemu-0.14.bin b/bios/vgabios/vgabios-qemu-0.14.bin new file mode 100644 index 0000000..892a2b5 Binary files /dev/null and b/bios/vgabios/vgabios-qemu-0.14.bin differ diff --git a/palacios/include/palacios/vm_guest.h b/palacios/include/palacios/vm_guest.h index b435390..8e91634 100644 --- a/palacios/include/palacios/vm_guest.h +++ b/palacios/include/palacios/vm_guest.h @@ -125,6 +125,9 @@ struct guest_info { /* 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; + }; diff --git a/palacios/include/palacios/vmm_ethernet.h b/palacios/include/palacios/vmm_ethernet.h index ddd1435..06dde7b 100644 --- a/palacios/include/palacios/vmm_ethernet.h +++ b/palacios/include/palacios/vmm_ethernet.h @@ -30,6 +30,18 @@ #include +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); @@ -96,6 +108,105 @@ static inline void random_ethaddr(uint8_t * addr) 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 diff --git a/palacios/include/palacios/vmm_time.h b/palacios/include/palacios/vmm_time.h index 9f1c432..c9248b8 100644 --- a/palacios/include/palacios/vmm_time.h +++ b/palacios/include/palacios/vmm_time.h @@ -7,11 +7,13 @@ * and the University of New Mexico. You can find out more at * http://www.v3vee.org * + * Copyright (c) 2010, Patrick Bridges * Copyright (c) 2008, Jack Lange * Copyright (c) 2008, The V3VEE Project * All rights reserved. * * Author: Jack Lange + * Patrick Bridges * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "V3VEE_LICENSE". @@ -32,15 +34,16 @@ struct guest_info; 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 @@ -67,9 +70,13 @@ int v3_init_time_vm(struct v3_vm_info * vm); 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); @@ -85,7 +92,12 @@ static inline uint64_t v3_get_host_time(struct vm_time *t) { // 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 diff --git a/palacios/include/palacios/vmm_vnet.h b/palacios/include/palacios/vmm_vnet.h index 03d2298..9748674 100644 --- a/palacios/include/palacios/vmm_vnet.h +++ b/palacios/include/palacios/vmm_vnet.h @@ -25,14 +25,16 @@ #include #include -#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 @@ -44,10 +46,10 @@ struct v3_vnet_route { 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)); @@ -90,6 +92,7 @@ int v3_vnet_add_bridge(struct v3_vm_info * vm, 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__ @@ -101,9 +104,6 @@ struct v3_vnet_dev_ops { 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); diff --git a/palacios/include/palacios/vmm_xml.h b/palacios/include/palacios/vmm_xml.h index f45f804..16a93cb 100644 --- a/palacios/include/palacios/vmm_xml.h +++ b/palacios/include/palacios/vmm_xml.h @@ -76,6 +76,8 @@ struct v3_xml * v3_xml_idx(struct v3_xml * xml, int idx); 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: @@ -88,4 +90,18 @@ struct v3_xml * v3_xml_get(struct v3_xml * xml, ...); // 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 diff --git a/palacios/src/devices/8254.c b/palacios/src/devices/8254.c index 7bf6a8c..4c3a8af 100644 --- a/palacios/src/devices/8254.c +++ b/palacios/src/devices/8254.c @@ -299,10 +299,13 @@ static void pit_update_timer(struct guest_info * info, ullong_t cpu_cycles, ullo // 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); } @@ -676,7 +679,7 @@ static int pit_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { // 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)); diff --git a/palacios/src/devices/Kconfig b/palacios/src/devices/Kconfig index 671625f..873792d 100644 --- a/palacios/src/devices/Kconfig +++ b/palacios/src/devices/Kconfig @@ -3,7 +3,6 @@ menu "Virtual Devices" config APIC bool "APIC" default y - depends on EXPERIMENTAL help Includes the Virtual APIC device @@ -19,7 +18,6 @@ config DEBUG_APIC config IO_APIC bool "IOAPIC" - depends on EXPERIMENTAL default y help Includes the Virtual IO APIC @@ -181,6 +179,19 @@ config DEBUG_NE2K 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" diff --git a/palacios/src/devices/Makefile b/palacios/src/devices/Makefile index cdce9ea..2e4a6d6 100644 --- a/palacios/src/devices/Makefile +++ b/palacios/src/devices/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_DISK_MODEL) += disk_model.o 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 diff --git a/palacios/src/devices/lnx_virtio_nic.c b/palacios/src/devices/lnx_virtio_nic.c index 654698f..da6644e 100644 --- a/palacios/src/devices/lnx_virtio_nic.c +++ b/palacios/src/devices/lnx_virtio_nic.c @@ -37,14 +37,12 @@ #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 */ @@ -53,8 +51,6 @@ struct virtio_net_hdr { }__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 */ @@ -70,13 +66,8 @@ struct virtio_net_hdr_mrg_rxbuf { #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 { @@ -100,14 +91,11 @@ struct virtio_net_state { 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; @@ -117,16 +105,6 @@ struct virtio_net_state { 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) { @@ -161,14 +139,10 @@ 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) { @@ -176,11 +150,20 @@ pkt_tx(struct guest_info * core, 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; } @@ -227,52 +210,37 @@ static inline void disable_cb(struct virtio_queue *queue) { } -/* 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) { @@ -289,7 +257,7 @@ static int handle_pkt_tx(struct guest_info *core, 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){ @@ -300,7 +268,7 @@ static int handle_pkt_tx(struct guest_info *core, /* 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; } @@ -308,24 +276,23 @@ static int handle_pkt_tx(struct guest_info *core, 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; @@ -333,7 +300,7 @@ static int handle_pkt_tx(struct guest_info *core, exit_error: v3_unlock_irqrestore(virtio_state->tx_lock, flags); - return -ERR_VIRTIO_OTHER; + return -1; } @@ -407,11 +374,9 @@ static int virtio_io_write(struct guest_info *core, 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); @@ -434,15 +399,18 @@ static int virtio_io_write(struct guest_info *core, { 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 { @@ -454,7 +422,6 @@ static int virtio_io_write(struct guest_info *core, 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; @@ -558,39 +525,29 @@ static int virtio_io_read(struct guest_info *core, } +/* 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){ @@ -601,8 +558,8 @@ static int virtio_rx(uint8_t * buf, uint32_t size, void * private_data) { 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)); @@ -629,39 +586,31 @@ static int virtio_rx(uint8_t * buf, uint32_t size, void * private_data) { 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) { @@ -687,45 +636,12 @@ static struct v3_device_ops dev_ops = { }; -/* 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) { @@ -742,8 +658,9 @@ static int register_dev(struct virtio_dev_state * virtio, 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; } @@ -815,8 +732,6 @@ static int connect_fn(struct v3_vm_info * info, 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); diff --git a/palacios/src/devices/lnx_virtio_vnet.c b/palacios/src/devices/lnx_virtio_vnet.c index 9e10784..b4b7342 100644 --- a/palacios/src/devices/lnx_virtio_vnet.c +++ b/palacios/src/devices/lnx_virtio_vnet.c @@ -75,7 +75,6 @@ struct virtio_vnet_state { #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; @@ -123,8 +122,6 @@ static int get_desc_count(struct virtio_queue * q, int index) { } - - static int handle_cmd_kick(struct guest_info * core, struct virtio_vnet_state * vnet_state) { struct virtio_queue * q = &(vnet_state->queue[0]); @@ -143,7 +140,6 @@ static int handle_cmd_kick(struct guest_info * core, 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) { @@ -160,8 +156,7 @@ static int handle_cmd_kick(struct guest_info * core, 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; @@ -173,26 +168,21 @@ static int handle_cmd_kick(struct guest_info * core, 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) { @@ -235,6 +225,7 @@ static int vnet_pkt_input_cb(struct v3_vm_info * vm, if (q->ring_avail_addr == 0) { PrintError("Queue is not set\n"); + goto exit; } @@ -244,7 +235,6 @@ static int vnet_pkt_input_cb(struct v3_vm_info * vm, 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"); @@ -282,7 +272,7 @@ exit: 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]); @@ -298,8 +288,6 @@ static int handle_pkt_kick(struct guest_info * core, 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"); @@ -343,15 +331,13 @@ static void vnet_virtio_poll(struct v3_vm_info * vm, void * private_data){ 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; } @@ -451,13 +437,13 @@ static int vnet_virtio_io_write(struct guest_info * core, 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; } diff --git a/palacios/src/devices/ne2k.c b/palacios/src/devices/ne2k.c index 46594c3..939bd28 100644 --- a/palacios/src/devices/ne2k.c +++ b/palacios/src/devices/ne2k.c @@ -43,8 +43,10 @@ #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 */ @@ -319,6 +321,8 @@ struct ne2k_state { uint8_t mcast_addr[8]; uint8_t mac[ETH_ALEN]; + struct nic_statistics statistics; + struct v3_dev_net_ops *net_ops; void * backend_data; }; @@ -333,20 +337,31 @@ static int ne2k_update_irq(struct ne2k_state * nic_state) { 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) { @@ -475,9 +490,14 @@ static int ne2k_rx(uint8_t * buf, uint32_t size, void * private_data){ #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; } @@ -717,7 +737,7 @@ static int ne2k_cmd_write(struct guest_info * core, } 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 */ @@ -1015,16 +1035,16 @@ static int ne2k_pci_write(struct guest_info * core, 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; @@ -1045,16 +1065,16 @@ static int ne2k_pci_read(struct guest_info * core, 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; @@ -1133,8 +1153,8 @@ static int register_dev(struct ne2k_state * nic_state) 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); } @@ -1175,13 +1195,11 @@ static int ne2k_free(struct ne2k_state * nic_state) { 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); diff --git a/palacios/src/devices/rtl8139.c b/palacios/src/devices/rtl8139.c new file mode 100644 index 0000000..199d0d7 --- /dev/null +++ b/palacios/src/devices/rtl8139.c @@ -0,0 +1,1843 @@ +/* + * 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 + * Copyright (c) 2011, The V3VEE Project + * All rights reserved. + * + * Author: Lei Xia + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + + +#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) diff --git a/palacios/src/devices/vnet_nic.c b/palacios/src/devices/vnet_nic.c index 6a6406c..1831270 100644 --- a/palacios/src/devices/vnet_nic.c +++ b/palacios/src/devices/vnet_nic.c @@ -33,28 +33,12 @@ #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, @@ -93,7 +77,7 @@ static int virtio_input(struct v3_vm_info * info, 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; @@ -102,21 +86,6 @@ static void virtio_poll(struct v3_vm_info * info, } -/* 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); @@ -133,8 +102,6 @@ static struct v3_device_ops dev_ops = { static struct v3_vnet_dev_ops vnet_dev_ops = { .input = virtio_input, .poll = virtio_poll, - .start_tx = start_tx, - .stop_tx = stop_tx, }; @@ -156,8 +123,6 @@ static int vnet_nic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { } 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"), @@ -173,13 +138,12 @@ static int vnet_nic_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { 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; } diff --git a/palacios/src/palacios/svm.c b/palacios/src/palacios/svm.c index fe2727e..72a1b58 100644 --- a/palacios/src/palacios/svm.c +++ b/palacios/src/palacios/svm.c @@ -218,6 +218,11 @@ static void Init_VMCB_BIOS(vmcb_t * vmcb, struct guest_info * core) { 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"); @@ -246,10 +251,7 @@ static void Init_VMCB_BIOS(vmcb_t * vmcb, struct guest_info * core) { 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; @@ -451,11 +453,15 @@ int v3_svm_enter(struct guest_info * info) { 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(); @@ -503,19 +509,20 @@ int v3_svm_enter(struct guest_info * info) { } #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 diff --git a/palacios/src/palacios/vmm.c b/palacios/src/palacios/vmm.c index dca1018..9acfbd0 100644 --- a/palacios/src/palacios/vmm.c +++ b/palacios/src/palacios/vmm.c @@ -204,12 +204,12 @@ static int start_core(void * p) 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: @@ -241,73 +241,54 @@ static int start_core(void * p) 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 @@ -479,7 +460,7 @@ void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) { 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: diff --git a/palacios/src/palacios/vmm_config.c b/palacios/src/palacios/vmm_config.c index 50e3b1c..8844dee 100644 --- a/palacios/src/palacios/vmm_config.c +++ b/palacios/src/palacios/vmm_config.c @@ -237,6 +237,7 @@ static int pre_config_vm(struct v3_vm_info * vm, v3_cfg_tree_t * vm_cfg) { 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) { @@ -291,7 +292,7 @@ static int determine_paging_mode(struct guest_info * info, v3_cfg_tree_t * core_ 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"); @@ -337,10 +338,22 @@ static int determine_paging_mode(struct guest_info * info, v3_cfg_tree_t * core_ } 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) { @@ -457,7 +470,6 @@ struct v3_vm_info * v3_config_guest(void * cfg_blob, void * priv_data) { } num_cores = atoi(v3_cfg_val(cores_cfg, "count")); - if (num_cores == 0) { PrintError("No cores specified in configuration\n"); return NULL; @@ -483,7 +495,6 @@ struct v3_vm_info * v3_config_guest(void * cfg_blob, void * priv_data) { return NULL; } - V3_Print("Per core configuration\n"); per_core_cfg = v3_cfg_subtree(cores_cfg, "core"); @@ -494,12 +505,14 @@ struct v3_vm_info * v3_config_guest(void * cfg_blob, void * priv_data) { 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); } diff --git a/palacios/src/palacios/vmm_ctrl_regs.c b/palacios/src/palacios/vmm_ctrl_regs.c index 7d6e381..b27df4b 100644 --- a/palacios/src/palacios/vmm_ctrl_regs.c +++ b/palacios/src/palacios/vmm_ctrl_regs.c @@ -566,15 +566,19 @@ int v3_handle_efer_write(struct guest_info * core, uint_t msr, struct v3_msr src 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; } diff --git a/palacios/src/palacios/vmm_packet.c b/palacios/src/palacios/vmm_packet.c index bda3b1f..7d90d3e 100644 --- a/palacios/src/palacios/vmm_packet.c +++ b/palacios/src/palacios/vmm_packet.c @@ -25,22 +25,29 @@ 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) { diff --git a/palacios/src/palacios/vmm_symbiotic.c b/palacios/src/palacios/vmm_symbiotic.c index 7d9ea4c..3b41305 100644 --- a/palacios/src/palacios/vmm_symbiotic.c +++ b/palacios/src/palacios/vmm_symbiotic.c @@ -32,11 +32,11 @@ static int cpuid_fn(struct guest_info * core, uint32_t cpuid, *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"; } diff --git a/palacios/src/palacios/vmm_time.c b/palacios/src/palacios/vmm_time.c index 51fbba7..dea7cdb 100644 --- a/palacios/src/palacios/vmm_time.c +++ b/palacios/src/palacios/vmm_time.c @@ -18,8 +18,8 @@ * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ -#include #include +#include #include #ifndef CONFIG_DEBUG_TIME @@ -86,44 +86,112 @@ int v3_start_time(struct guest_info * info) { 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) { @@ -149,15 +217,16 @@ int v3_remove_timer(struct guest_info * info, struct v3_timer * timer) { } 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); } } @@ -213,9 +282,10 @@ int v3_rdtscp(struct guest_info * info) { 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; @@ -321,9 +391,10 @@ void v3_init_time_core(struct guest_info * info) { 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; } diff --git a/palacios/src/palacios/vmm_vnet_core.c b/palacios/src/palacios/vmm_vnet_core.c index 6baef5a..4ce9b04 100644 --- a/palacios/src/palacios/vmm_vnet_core.c +++ b/palacios/src/palacios/vmm_vnet_core.c @@ -101,46 +101,47 @@ static struct { #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 @@ -202,14 +203,33 @@ static struct vnet_dev * dev_by_mac(uint8_t * mac) { 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; @@ -217,8 +237,8 @@ int v3_vnet_add_route(struct v3_vnet_route route) { 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); @@ -226,31 +246,18 @@ int v3_vnet_add_route(struct v3_vnet_route route) { 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)); @@ -299,8 +306,8 @@ static struct route_list * match_route(const struct v3_vnet_pkt * pkt) { 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 { @@ -332,14 +339,15 @@ static struct route_list * match_route(const struct v3_vnet_pkt * pkt) { 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)) { @@ -573,29 +581,6 @@ static void free_routes(){ } } -/* 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, diff --git a/palacios/src/palacios/vmm_xml.c b/palacios/src/palacios/vmm_xml.c index f3641be..4b4a8f6 100644 --- a/palacios/src/palacios/vmm_xml.c +++ b/palacios/src/palacios/vmm_xml.c @@ -41,6 +41,8 @@ #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 @@ -201,7 +203,9 @@ struct v3_xml * v3_xml_get(struct v3_xml * xml, ...) { // 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; } @@ -454,7 +458,7 @@ static struct v3_xml * v3_xml_new(const char * name) { } // 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; @@ -900,3 +904,294 @@ void v3_xml_free(struct v3_xml * xml) { 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, "", 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); +} diff --git a/palacios/src/palacios/vmx.c b/palacios/src/palacios/vmx.c index f71df0a..ef0f614 100644 --- a/palacios/src/palacios/vmx.c +++ b/palacios/src/palacios/vmx.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -662,11 +663,11 @@ int v3_vmx_enter(struct guest_info * info) { // 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(); @@ -688,7 +689,8 @@ int v3_vmx_enter(struct guest_info * info) { 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); @@ -713,6 +715,9 @@ int v3_vmx_enter(struct guest_info * info) { return -1; } + // Immediate exit from VM time bookkeeping + v3_time_exit_vm(info); + info->num_exits++; /* Update guest state */ diff --git a/test/geekos_test_vm/build/depend.mak b/test/geekos_test_vm/build/depend.mak deleted file mode 100644 index e69de29..0000000