diff --git a/Makefile.target b/Makefile.target index acd6cda..6e88704 100644 --- a/Makefile.target +++ b/Makefile.target @@ -393,7 +393,7 @@ ifeq ($(ARCH),alpha) endif # must use static linking to avoid leaving stuff in virtual address space -VL_OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o +VL_OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o pcap.o # XXX: suppress QEMU_TOOL tests ifdef CONFIG_WIN32 VL_OBJS+=block-raw-win32.o diff --git a/hw/eepro100.c b/hw/eepro100.c index 84d1e52..cfc9162 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -74,10 +74,12 @@ #define KiB 1024 /* debug EEPRO100 card */ -//~ #define DEBUG_EEPRO100 +#define DEBUG_EEPRO100 + +int e100_debug = 0; #ifdef DEBUG_EEPRO100 -#define logout(fmt, args...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ##args) +#define logout(fmt, args...) {if (e100_debug) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ##args);} #else #define logout(fmt, args...) ((void)0) #endif @@ -173,6 +175,15 @@ typedef struct { } eepro100_rx_t; typedef struct { + uint16_t count; + uint16_t pad0; + uint32_t link; + uint32_t buf_addr; + uint16_t size; + uint16_t pad1; +} eepro100_rbd_t; + +typedef struct { uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions, tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, tx_multiple_collisions, tx_total_collisions; @@ -243,6 +254,7 @@ typedef struct { /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */ uint32_t ru_base; /* RU base address */ uint32_t ru_offset; /* RU address offset */ + uint32_t rbd_offset; /* RBD address offset */ uint32_t statsaddr; /* pointer to eepro100_stats_t */ eepro100_stats_t statistics; /* statistical counters */ #if 0 @@ -724,20 +736,13 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val) uint16_t size = 0; uint32_t tbd_address = cb_address + 0x10; assert(tcb_bytes <= sizeof(buf)); - while (size < tcb_bytes) { - uint32_t tx_buffer_address = ldl_phys(tbd_address); - uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); - //~ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); - tbd_address += 8; + if (tbd_array == 0xffffffff) { + /* Simplified mode. */ logout ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", - tx_buffer_address, tx_buffer_size); - cpu_physical_memory_read(tx_buffer_address, &buf[size], - tx_buffer_size); - size += tx_buffer_size; - } - if (tbd_array == 0xffffffff) { - /* Simplified mode. Was already handled by code above. */ + tbd_address, tcb_bytes); + cpu_physical_memory_read(tbd_address, buf, tcb_bytes); + size = tcb_bytes; } else { /* Flexible mode. */ uint8_t tbd_count = 0; @@ -746,7 +751,7 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val) assert(tcb_bytes == 0); for (; tbd_count < 2; tbd_count++) { uint32_t tx_buffer_address = ldl_phys(tbd_address); - uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); + uint16_t tx_buffer_size = lduw_phys(tbd_address + 4) & 0x3fff; uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); tbd_address += 8; logout @@ -869,6 +874,16 @@ static void eepro100_ru_command(EEPRO100State * s, uint8_t val) } set_ru_state(s, ru_ready); s->ru_offset = s->pointer; + + eepro100_rx_t rx; + cpu_physical_memory_read(s->ru_base + s->ru_offset, (uint8_t *) & rx, + offsetof(eepro100_rx_t, packet)); + uint16_t rfd_command = le16_to_cpu(rx.command); + + /* If the SF bit is set, then use flexible mode. */ + if (rfd_command & 0x0008) + s->rbd_offset = le32_to_cpu(rx.rx_buf_addr); + logout("val=0x%02x (rx start)\n", val); break; case RX_RESUME: @@ -885,6 +900,10 @@ static void eepro100_ru_command(EEPRO100State * s, uint8_t val) logout("val=0x%02x (RU base address)\n", val); s->ru_base = s->pointer; break; + case RX_ABORT: + /* TODO XXX _AP_ need to generate interrupts and halt all activity. */ + set_ru_state(s, ru_idle); + break; default: logout("val=0x%02x (undefined RU command)\n", val); missing("Undefined SU command"); @@ -1545,7 +1564,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size) offsetof(eepro100_rx_t, packet)); uint16_t rfd_command = le16_to_cpu(rx.command); uint16_t rfd_size = le16_to_cpu(rx.size); - assert(size <= rfd_size); + if (size < 64) { rfd_status |= 0x0080; } @@ -1553,15 +1572,33 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size) rx.link, rx.rx_buf_addr, rfd_size); stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, status), rfd_status); - stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, count), size); /* Early receive interrupt not supported. */ //~ eepro100_er_interrupt(s); /* Receive CRC Transfer not supported. */ assert(!(s->configuration[18] & 4)); /* TODO: check stripping enable bit. */ //~ assert(!(s->configuration[17] & 1)); - cpu_physical_memory_write(s->ru_base + s->ru_offset + - offsetof(eepro100_rx_t, packet), buf, size); + + if (rfd_command & 0x0008) { + /* XXX TODO _AP_ support more links */ + eepro100_rbd_t rbd; + int wrote = 0; + do { + cpu_physical_memory_read(s->ru_base + s->rbd_offset, (uint8_t *) &rbd, sizeof(rbd)); + int w = (size - wrote) > rbd.size ? rbd.size : (size - wrote); + cpu_physical_memory_write(s->ru_base + rbd.buf_addr, buf + wrote, w); + stw_phys(s->ru_base + s->rbd_offset, w | 0x8000 | 0x4000); + wrote += w; + s->rbd_offset = le32_to_cpu(rbd.link); + } while(wrote < size); + } else { + /* Simplified mode. */ + assert(size <= rfd_size); + cpu_physical_memory_write(s->ru_base + s->ru_offset + + offsetof(eepro100_rx_t, packet), buf, size); + } + /* Update the EOF and F bits in addition to setting the size. */ + stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, count), size | 0x8000 | 0x4000); s->statistics.rx_good_frames++; eepro100_fr_interrupt(s); s->ru_offset = le32_to_cpu(rx.link); @@ -1631,6 +1668,7 @@ static int nic_load(QEMUFile * f, void *opaque, int version_id) qemu_get_be32s(f, &s->cu_offset); qemu_get_be32s(f, &s->ru_base); qemu_get_be32s(f, &s->ru_offset); + qemu_get_be32s(f, &s->rbd_offset); qemu_get_be32s(f, &s->statsaddr); /* Restore epro100_stats_t statistics */ qemu_get_be32s(f, &s->statistics.tx_good_frames); @@ -1710,6 +1748,7 @@ static void nic_save(QEMUFile * f, void *opaque) qemu_put_be32s(f, &s->cu_offset); qemu_put_be32s(f, &s->ru_base); qemu_put_be32s(f, &s->ru_offset); + qemu_put_be32s(f, &s->rbd_offset); qemu_put_be32s(f, &s->statsaddr); /* Save epro100_stats_t statistics */ qemu_put_be32s(f, &s->statistics.tx_good_frames); diff --git a/pcap.c b/pcap.c new file mode 100644 index 0000000..6585dba --- /dev/null +++ b/pcap.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +#include "pcap.h" + +static int started = 0; +static FILE *cap_file; + +static void +pcap_write(const char *b, int l) { + size_t c = fwrite(b, 1, l, cap_file); + assert(c == l); + fflush(cap_file); +} + +void +pcap_dump_init(const char *fname) { + pcap_hdr_t pcap_hdr; + + if (!fname) + fname = "slirp.cap"; + + cap_file = fopen(fname, "wb"); + if (!cap_file) { + perror("pcap_dump_init:"); + return; + } + + pcap_hdr.magic_number = PCAP_MAGIC; + pcap_hdr.version_major = PCAP_VMAJOR; + pcap_hdr.version_minor = PCAP_VMINOR; + pcap_hdr.thiszone = 0; + pcap_hdr.sigfigs = 0; + pcap_hdr.snaplen = 65535; + pcap_hdr.network = 1; // Ethernet + + pcap_write((char *)&pcap_hdr, sizeof(pcap_hdr)); + + started = 1; +} + +void +pcap_dump(const char *pkt, int len) { + pcaprec_hdr_t pkt_hdr; + struct timeval tv; + + if (!started) + return; + + gettimeofday(&tv, 0); + + pkt_hdr.ts_sec = tv.tv_sec; + pkt_hdr.ts_usec = tv.tv_usec; + pkt_hdr.incl_len = len; + pkt_hdr.orig_len = len; + + pcap_write((char *)&pkt_hdr, sizeof(pkt_hdr)); + pcap_write(pkt, len); +} + diff --git a/pcap.h b/pcap.h new file mode 100644 index 0000000..e801d29 --- /dev/null +++ b/pcap.h @@ -0,0 +1,34 @@ +#ifndef _PCAP_H_ +#define _PCAP_H_ + +#include "qemu-common.h" + +/* + * Used http://wiki.wireshark.org/Development/LibpcapFileFormat to get the pcap file format + */ + +#define PCAP_MAGIC 0xa1b2c3d4 +#define PCAP_VMAJOR 2 +#define PCAP_VMINOR 4 + +typedef struct pcap_hdr_s { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +} pcap_hdr_t; + +typedef struct pcaprec_hdr_s { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +} pcaprec_hdr_t; + +void pcap_dump_init(const char *fname); +void pcap_dump(const char *pkt, int len); + +#endif //_PCAP_H_ diff --git a/vl.c b/vl.c index a04edbc..dac4cce 100644 --- a/vl.c +++ b/vl.c @@ -37,6 +37,7 @@ #include "qemu-char.h" #include "block.h" #include "audio/audio.h" +#include "pcap.h" #include #include @@ -158,6 +159,8 @@ int inet_aton(const char *cp, struct in_addr *ia); /* XXX: use a two level table to limit memory usage */ #define MAX_IOPORTS 65536 +extern int e100_debug; + const char *bios_dir = CONFIG_QEMU_SHAREDIR; const char *bios_name = NULL; void *ioport_opaque[MAX_IOPORTS]; @@ -3686,6 +3689,7 @@ int slirp_can_output(void) void slirp_output(const uint8_t *pkt, int pkt_len) { + pcap_dump(pkt, pkt_len); #if 0 printf("slirp output:\n"); hex_dump(stdout, pkt, pkt_len); @@ -3697,6 +3701,7 @@ void slirp_output(const uint8_t *pkt, int pkt_len) static void slirp_receive(void *opaque, const uint8_t *buf, int size) { + pcap_dump(buf, size); #if 0 printf("slirp input:\n"); hex_dump(stdout, buf, size); @@ -7617,6 +7622,9 @@ static void help(int exitcode) #endif "-clock force the use of the given methods for timer alarm.\n" " To see what timers are available use -clock help\n" + "-pcap file_name\n" + " when -net user is enabled, dump packets to file_name,\n" + "-debug-e100 print E100 debug statments\n" "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -7719,6 +7727,9 @@ enum { QEMU_OPTION_old_param, QEMU_OPTION_clock, QEMU_OPTION_startdate, + + QEMU_OPTION_pcap, + QEMU_OPTION_debug_e100, }; typedef struct QEMUOption { @@ -7826,6 +7837,8 @@ const QEMUOption qemu_options[] = { { "old-param", 0, QEMU_OPTION_old_param }, #endif { "clock", HAS_ARG, QEMU_OPTION_clock }, + { "pcap", HAS_ARG, QEMU_OPTION_pcap }, + { "debug-e100", 0, QEMU_OPTION_debug_e100 }, { "startdate", HAS_ARG, QEMU_OPTION_startdate }, { NULL }, }; @@ -8661,6 +8674,12 @@ int main(int argc, char **argv) } } break; + case QEMU_OPTION_pcap: + pcap_dump_init(optarg); + break; + case QEMU_OPTION_debug_e100: + e100_debug = 1; + break; } } }