diff --git a/0readmeAsynchIO.txt b/0readmeAsynchIO.txt index b41cf4f9..28a8205b 100644 --- a/0readmeAsynchIO.txt +++ b/0readmeAsynchIO.txt @@ -5,6 +5,9 @@ Theory of operation. Features. - Optional Use. Build with or without SIM_ASYNCH_IO defined and simulators will still build and perform correctly when run. + Additionmally, a simulator built with SIM_ASYNCH_IO defined can + dynamically disable and reenable asynchronous operation with + the scp commands SET NOASYNCH and SET ASYNCH respectively. - Consistent Save/Restore state. The state of a simulator saved on a simulator with (or without) Asynch support can be restored on any simulator of the same version with or without Asynch diff --git a/0readme_ethernet.txt b/0readme_ethernet.txt index 9c8c7401..eb1c66f0 100644 --- a/0readme_ethernet.txt +++ b/0readme_ethernet.txt @@ -43,9 +43,9 @@ bridge, or route TAP devices for you. Integrated Universal TUN/TAP support can be used for host<->simulator network traffic (on the platforms where it is available) by using the SIMH command: "attach xq tap:tapN" (i.e. attach xq tap:tap0). Platforms that this has been -tested on include: Linux, FreeBSD, OpenBSD, NetBSD. Each of these platforms -has some way to create a tap pseudo device and then bridge it with a physical -network interface. +tested on include: Linux, FreeBSD, OpenBSD, NetBSD and OSX. Each of these +platforms has some way to create a tap pseudo device (and possibly then to +bridge it with a physical network interface). The following steps were performed to get a working SIMH vax simulator sharing a physical NIC and allowing Host<->SIMH vax communications: @@ -117,6 +117,53 @@ NetBSD (NetBSD 5.0.2) # Run simulator and "attach xq tap:tap0" +OSX (Snow Leopard) + OSX Does NOT have native support for tun/tap interfaces. It also does not have native + support for bridging. + + Mattias Nissler has created tun/tap functionality available at http://tuntaposx,sourceforge.net/ + + We'll punt on bridging for the sake of this example and move on to use a basic tap + based internal network so a host and guest can communicate directly. + + Download the install package from: + http://sourceforge.net/projects/tuntaposx/files/tuntap/20090913/tuntap_20090913.tar.gz + + Expand the tarball to a directory. + Invoke the package installer tuntap_20090913.pkg + Click through the various prompts accepting things and eventually installing the package. + + # Build and Run simulator and: + sim> attach xq tap:tap0 + sim> ! ifconfig tap0 192.168.6.1 netmask 255.255.255.0 + + Simulated system uses IP address 192.168.6.2 and host uses 192.167.6.1 + and things work. + You must run as root for this to work. + +------------------------------------------------------------------------------- +An alternative to direct pcap and tun/tap networking on *nix environments is +VDE (Virtual Distributed Ethernet). + +Note 1: Using vde based networking is likely more flexible, but it isn't + nearly as efficient. Host OS overhead will always be higher when + vde networking is used as compared to native pcap and/or tun/tap + networking. +Note 2: Root access will likely be needed to configure or start the vde + environment prior to starting a simulator which may use it. +Note 3: Simulators running using VDE networking can run without root + privilege. + +Linux (Ubuntu 10.04): + apt-get install libvdeplug-dev + apt-get install vde2 + + vde_switch -s /tmp/switch1 -tap tap0 -m 666 + ifconfig tap0 192.168.6.1 netmask 255.255.255.0 up + + # Build and Run simulator and: + sim> attach xq vde:/tmp/switch1 #simulator uses IP address 192.168.6.2 + ------------------------------------------------------------------------------- Windows notes: @@ -134,6 +181,11 @@ Windows notes: Building on Windows: + Building with MinGW can use the provided makefile following the instructions + below. Alternatively, you can use the free Visual C++ Express 2008 or 2010 + interactive development environments. Read the file + ".\Visual Studio Projects\0ReadMe_Projects.txt" for details. + 1. Install WinPCAP 4.x runtime and the WinPCAP Developer's kit. 2. Put the required .h files (bittypes,devioctl,ip6_misc,pcap,pcap-stdinc @@ -159,8 +211,11 @@ Linux, {Free|Net|Open}BSD, OS/X, and Un*x notes: ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- Sim_Ether has been reworked to be more universal; because of this, you will -need to get a version of libpcap that is 0.9 or greater. This can be -downloaded from www.tcpdump.org - see the comments at the top of Sim_ether.c +need to get a version of libpcap that is 0.9 or greater. All current Linux +distributions provide a libpcap-dev package which has the needed version +of libpcap and the required components to build applications using it. +If you are running an older Linux OS, you can download and build the required +library from www.tcpdump.org - see the comments at the top of Sim_ether.c for details. ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- @@ -187,11 +242,13 @@ for details. will be welcomed. 2. If you want to use TAP devices, and any surrounding system network/bridge - setup must be done before running SIMH. + setup must be done before running SIMH. However, once that is done + (possibly at system boot time), using the TAP devices can be done without + root privileges. Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: - 1. Get/make/install the libpcap package for your operating system. Sources: + 1. Get/make/install the libpcap-dev package for your operating system. Sources: All : http://www.tcpdump.org/ Older versions of libpcap can be found, for various systems, at: Linux : search for your variant on http://rpmfind.net @@ -214,7 +271,7 @@ Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: ------------------------------------------------------------------------------- -OpenVMS Alpha notes: +OpenVMS Alpha and OpenVMS Integrety (IA64) notes: 1. Ethernet support will only work on Alpha VMS 7.3-1 or later, which is when required VCI promiscuous mode support was added. Hobbyists can get the required version of VMS from the OpenVMS Alpha Hobbyist Kit 3.0. @@ -247,10 +304,10 @@ OpenVMS Alpha notes: adapter prior trying to connect with SIMH, or the host may crash. The execlet is not written to create an I/O structure for the device. -Building on OpenVMS Alpha: +Building on OpenVMS Alpha and OpenVMS Integrety (IA64): The current descrip.mms file will build simulators capable of using Ethernet support with them automatically. These currently are: VAX, - PDP11, and PDP10. The descrip.mms driven builds will also build the + VAX780, and PDP11. The descrip.mms driven builds will also build the pcap library and build and install the VCI execlet. 1. Fetch the VMS-PCAP zip file from: @@ -320,7 +377,18 @@ Dave =============================================================================== Change Log =============================================================================== - + + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking + 29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX + 17-Aug-11 RMS Fix from Sergey Oboguev relating to XU and XQ Auto Config and + vector assignments + 12-Aug-11 MP Cleaned up payload length determination + Fixed race condition detecting reflections when threaded + reading and writing is enabled + 07-Jul-11 MB VMS Pcap (from Mike Burke) + - Fixed Alpha issues + - Added OpenVMS Integrety support + 20-Apr-11 MP Fixed save/restore behavior 12-Jan-11 DTH Added SHOW XU FILTERS modifier 11-Jan-11 DTH Corrected DEUNA/DELUA SELFTEST command, enabling use by VMS 3.7, VMS 4.7, and Ultrix 1.1 diff --git a/PDP11/pdp11_tu.c b/PDP11/pdp11_tu.c index bec28535..f4b2144f 100644 --- a/PDP11/pdp11_tu.c +++ b/PDP11/pdp11_tu.c @@ -469,7 +469,7 @@ den = GET_DEN (tutc); /* get density */ uptr = tu_dev.units + drv; /* get unit */ if (DEBUG_PRS (tu_dev)) { fprintf (sim_deb, ">>TU%d STRT: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=", - drv, tu_fname[fnc], tufc, tufs, tuer, uptr->pos); + drv, tu_fname[fnc], tufc, tufs, tuer); fprint_val (sim_deb, uptr->pos, 10, T_ADDR_W, PV_LEFT); fprintf (sim_deb, "\n"); } diff --git a/PDP11/pdp11_xq.c b/PDP11/pdp11_xq.c index ec5e2a0e..c4eb46e7 100644 --- a/PDP11/pdp11_xq.c +++ b/PDP11/pdp11_xq.c @@ -450,10 +450,10 @@ MTAB xq_mod[] = { { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEQNA|DELQA|DELQA-T}", &xq_set_type, &xq_show_type, NULL }, #ifdef USE_READER_THREAD - { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500|DELAY=nnn}", + { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500|DELAY=nnn}", &xq_set_poll, &xq_show_poll, NULL }, #else - { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500}", + { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|DISABLED|4..2500}", &xq_set_poll, &xq_show_poll, NULL }, #endif { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}", @@ -2584,6 +2584,7 @@ t_stat xq_attach(UNIT* uptr, char* cptr) if (xq->var->poll == 0) { status = eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); if (status != SCPE_OK) { + eth_close(xq->var->etherface); free(tptr); free(xq->var->etherface); xq->var->etherface = NULL; diff --git a/PDP11/pdp11_xq.h b/PDP11/pdp11_xq.h index 5f786975..b71a725e 100644 --- a/PDP11/pdp11_xq.h +++ b/PDP11/pdp11_xq.h @@ -87,7 +87,7 @@ extern int32 int_req[IPL_HLVL]; #define XQ_QUE_MAX 500 /* read queue size in packets */ #define XQ_FILTER_MAX 14 /* number of filters allowed */ -#if defined SIM_ASYNCH_IO +#if defined(SIM_ASYNCH_IO) && defined(USE_READER_THREAD) #define XQ_SERVICE_INTERVAL 0 /* polling interval - No Polling with Asynch I/O */ #else #define XQ_SERVICE_INTERVAL 100 /* polling interval - X per second */ diff --git a/PDP8/pdp8_fpp.c b/PDP8/pdp8_fpp.c index 6845dbe8..4fb04c01 100644 --- a/PDP8/pdp8_fpp.c +++ b/PDP8/pdp8_fpp.c @@ -1,6 +1,6 @@ /* pdp8_fpp.c: PDP-8 floating point processor (FPP8A) - Copyright (c) 2007-2011, Robert M Supnik + Copyright (c) 2007-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP8/pdp8_sys.c b/PDP8/pdp8_sys.c index ed7ff85b..3b550941 100644 --- a/PDP8/pdp8_sys.c +++ b/PDP8/pdp8_sys.c @@ -1,6 +1,6 @@ /* pdp8_sys.c: PDP-8 simulator interface - Copyright (c) 1993-2011, Robert M Supnik + Copyright (c) 1993-2009, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -24,7 +24,7 @@ in this Software without prior written authorization from Robert M Supnik. 24-Mar-09 RMS Added link to FPP - 24-Jun-08 RMS Fixed bug in new rim loader (Don North) + 24-Jun-08 RMS Fixed bug in new rim loader (found by Don North) 24-May-08 RMS Fixed signed/unsigned declaration inconsistency 03-Sep-07 RMS Added FPP8 support Rewrote rim and binary loaders diff --git a/PDP8/pdp8_td.c b/PDP8/pdp8_td.c index d02ee571..46703def 100644 --- a/PDP8/pdp8_td.c +++ b/PDP8/pdp8_td.c @@ -28,6 +28,7 @@ td TD8E/TU56 DECtape + 23-Mar-11 RMS Fixed SDLC to clear AC (from Dave Gesswein) 23-Jun-06 RMS Fixed switch conflict in ATTACH 16-Aug-05 RMS Fixed C++ declaration and cast problems 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR @@ -302,6 +303,7 @@ switch (pulse) { if (td_newsa (td_cmd)) /* new command */ return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON); } + AC = 0; break; case 05: /* SDLD */ diff --git a/VAX/vax730_stddev.c b/VAX/vax730_stddev.c index f68b7a7e..429e3a30 100644 --- a/VAX/vax730_stddev.c +++ b/VAX/vax730_stddev.c @@ -30,11 +30,34 @@ todr TODR clock tmr interval timer + 28-Sep-11 MP Generalized setting TODR for all OSes. + Unbound the TODR value from the 100hz clock tick + interrupt. TODR now behaves like the original + battery backed-up clock and runs with the wall + clock, not the simulated instruction clock. + Two operational modes are available: + - Default VMS mode, which is similar to the previous + behavior in that without initializing the TODR it + would default to the value VMS would set it to if + VMS knew the correct time. This would be correct + almost all the time unless a VMS disk hadn't been + booted from for more than a year. This mode + produces strange time results for non VMS OSes on + each system boot. + - OS Agnostic mode. This mode behaves precisely like + the VAX780 TODR and works correctly for all OSes. + This mode is enabled by attaching the TODR to a + battery backup state file for the TOY clock + (i.e. sim> attach TODR TOY_CLOCK). When operating + in OS Agnostic mode, the TODR will initially start + counting from 0 and be adjusted differently when an + OS specifically writes to the TODR. VMS will prompt + to set the time on each boot unless the SYSGEN + parameter TIMEPROMPTWAIT is set to 0. 29-Mar-2011 MB First Version */ #include "vax_defs.h" -#include /* Terminal definitions */ @@ -159,6 +182,11 @@ int32 clk_tps = 100; /* ticks/second */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 todr_reg = 0; /* TODR register */ +struct todr_battery_info { + uint32 toy_gmtbase; /* GMT base of set value */ + uint32 toy_gmtbasemsec; /* The milliseconds of the set value */ + }; +typedef struct todr_battery_info TOY; int32 td_swait = 100; /* seek, per block */ int32 td_cwait = 150; /* command time */ @@ -187,6 +215,8 @@ t_stat tmr_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr); +t_stat clk_attach (UNIT *uptr, char *cptr); +t_stat clk_detach (UNIT *uptr); t_stat tmr_reset (DEVICE *dptr); t_stat td_svc (UNIT *uptr); t_stat td_reset (DEVICE *dptr); @@ -269,7 +299,7 @@ DEVICE tto_dev = { /* TODR and TMR data structures */ -UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; /* 100Hz */ +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */ REG clk_reg[] = { { DRDATA (TODR, todr_reg, 32), PV_LEFT }, @@ -280,9 +310,9 @@ REG clk_reg[] = { DEVICE clk_dev = { "TODR", &clk_unit, clk_reg, NULL, - 1, 0, 0, 0, 0, 0, + 1, 0, 8, 1, 0, 0, NULL, NULL, &clk_reset, - NULL, NULL, NULL, + NULL, &clk_attach, &clk_detach, NULL, 0 }; @@ -763,7 +793,6 @@ t_stat clk_svc (UNIT *uptr) tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, tmr_poll); /* reactivate unit */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ -todr_reg = todr_reg + 1; /* incr TODR */ if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */ tmr_incr (TMR_INC); /* do timer service */ return SCPE_OK; @@ -836,9 +865,44 @@ t_stat clk_reset (DEVICE *dptr) tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */ sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ +if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */ + clk_unit.filebuf = calloc(sizeof(TOY), 1); + if (clk_unit.filebuf == NULL) + return SCPE_MEM; + todr_resync (); + } return SCPE_OK; } +/* CLK attach */ + +t_stat clk_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE); +memset (uptr->filebuf, 0, (size_t)uptr->capac); +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +else + uptr->hwmark = (uint32) uptr->capac; +return r; +} + +/* CLK detach */ + +t_stat clk_detach (UNIT *uptr) +{ +t_stat r; + +r = detach_unit (uptr); +if ((uptr->flags & UNIT_ATT) == 0) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +return r; +} + + /* Interval timer reset */ t_stat tmr_reset (DEVICE *dptr) @@ -857,32 +921,58 @@ return SCPE_OK; int32 todr_rd (void) { -return todr_reg; +TOY *toy = (TOY *)clk_unit.filebuf; +struct timespec base, now, val; + +clock_gettime(CLOCK_REALTIME, &now); /* get curr time */ +base.tv_sec = toy->toy_gmtbase; +base.tv_nsec = toy->toy_gmtbasemsec * 1000000; +sim_timespec_diff (&val, &now, &base); +return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */ } void todr_wr (int32 data) { -todr_reg = data; -return; +TOY *toy = (TOY *)clk_unit.filebuf; +struct timespec now, val, base; + +/* Save the GMT time when set value was 0 to record the base for future + read operations in "battery backed-up" state */ + +if (-1 == clock_gettime(CLOCK_REALTIME, &now)) /* get curr time */ + return; /* error? */ +val.tv_sec = ((uint32)data) / 100; +val.tv_nsec = (((uint32)data) % 100) * 10000000; +sim_timespec_diff (&base, &now, &val); /* base = now - data */ +toy->toy_gmtbase = base.tv_sec; +toy->toy_gmtbasemsec = base.tv_nsec/1000000; } t_stat todr_resync (void) { -uint32 base; -time_t curr; -struct tm *ctm; +TOY *toy = (TOY *)clk_unit.filebuf; -curr = time (NULL); /* get curr time */ -if (curr == (time_t) -1) /* error? */ - return SCPE_NOFNC; -ctm = localtime (&curr); /* decompose */ -if (ctm == NULL) /* error? */ - return SCPE_NOFNC; -base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ - ctm->tm_hour) * 60) + - ctm->tm_min) * 60) + - ctm->tm_sec; -todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ +if (clk_unit.flags & UNIT_ATT) { /* Attached means behave like real VAX780 */ + if (!toy->toy_gmtbase) /* Never set? */ + todr_wr (0); /* Start ticking from 0 */ + } +else { /* Not-Attached means */ + uint32 base; /* behave like simh VMS default */ + time_t curr; + struct tm *ctm; + + curr = time (NULL); /* get curr time */ + if (curr == (time_t) -1) /* error? */ + return SCPE_NOFNC; + ctm = localtime (&curr); /* decompose */ + if (ctm == NULL) /* error? */ + return SCPE_NOFNC; + base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ + ctm->tm_hour) * 60) + + ctm->tm_min) * 60) + + ctm->tm_sec; + todr_wr ((base * 100) + 0x10000000); /* use VMS form */ + } return SCPE_OK; } diff --git a/VAX/vax780_stddev.c b/VAX/vax780_stddev.c index 9c9b09ce..2288b481 100644 --- a/VAX/vax780_stddev.c +++ b/VAX/vax780_stddev.c @@ -29,6 +29,30 @@ todr TODR clock tmr interval timer + 28-Sep-11 MP Generalized setting TODR for all OSes. + Unbound the TODR value from the 100hz clock tick + interrupt. TODR now behaves like the original + battery backed-up clock and runs with the wall + clock, not the simulated instruction clock. + Two operational modes are available: + - Default VMS mode, which is similar to the previous + behavior in that without initializing the TODR it + would default to the value VMS would set it to if + VMS knew the correct time. This would be correct + almost all the time unless a VMS disk hadn't been + booted from for more than a year. This mode + produces strange time results for non VMS OSes on + each system boot. + - OS Agnostic mode. This mode behaves precisely like + the VAX780 TODR and works correctly for all OSes. + This mode is enabled by attaching the TODR to a + battery backup state file for the TOY clock + (i.e. sim> attach TODR TOY_CLOCK). When operating + in OS Agnostic mode, the TODR will initially start + counting from 0 and be adjusted differently when an + OS specifically writes to the TODR. VMS will prompt + to set the time on each boot unless the SYSGEN + parameter TIMEPROMPTWAIT is set to 0. 21-Mar-11 RMS Added reboot capability 17-Aug-08 RMS Resync TODR on any clock reset 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock @@ -56,7 +80,7 @@ */ #include "vax_defs.h" -#include + /* Terminal definitions */ @@ -171,6 +195,11 @@ int32 clk_tps = 100; /* ticks/second */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 todr_reg = 0; /* TODR register */ +struct todr_battery_info { + uint32 toy_gmtbase; /* GMT base of set value */ + uint32 toy_gmtbasemsec; /* The milliseconds of the set value */ + }; +typedef struct todr_battery_info TOY; int32 fl_fnc = 0; /* function */ int32 fl_esr = 0; /* error status */ @@ -197,6 +226,8 @@ t_stat tmr_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr); +t_stat clk_attach (UNIT *uptr, char *cptr); +t_stat clk_detach (UNIT *uptr); t_stat tmr_reset (DEVICE *dptr); t_stat fl_svc (UNIT *uptr); t_stat fl_reset (DEVICE *dptr); @@ -281,7 +312,7 @@ DEVICE tto_dev = { /* TODR and TMR data structures */ -UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; /* 100Hz */ +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */ REG clk_reg[] = { { DRDATA (TODR, todr_reg, 32), PV_LEFT }, @@ -296,9 +327,9 @@ REG clk_reg[] = { DEVICE clk_dev = { "TODR", &clk_unit, clk_reg, NULL, - 1, 0, 0, 0, 0, 0, + 1, 0, 8, 1, 0, 0, NULL, NULL, &clk_reset, - NULL, NULL, NULL, + NULL, &clk_attach, &clk_detach, NULL, 0 }; @@ -578,7 +609,6 @@ tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, tmr_poll); /* reactivate unit */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */ -todr_reg = todr_reg + 1; /* incr TODR */ if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */ tmr_incr (TMR_INC); /* do timer service */ return SCPE_OK; @@ -651,9 +681,44 @@ t_stat clk_reset (DEVICE *dptr) tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */ sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ +if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */ + clk_unit.filebuf = calloc(sizeof(TOY), 1); + if (clk_unit.filebuf == NULL) + return SCPE_MEM; + todr_resync (); + } return SCPE_OK; } +/* CLK attach */ + +t_stat clk_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE); +memset (uptr->filebuf, 0, (size_t)uptr->capac); +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +else + uptr->hwmark = (uint32) uptr->capac; +return r; +} + +/* CLK detach */ + +t_stat clk_detach (UNIT *uptr) +{ +t_stat r; + +r = detach_unit (uptr); +if ((uptr->flags & UNIT_ATT) == 0) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +return r; +} + + /* Interval timer reset */ t_stat tmr_reset (DEVICE *dptr) @@ -672,32 +737,59 @@ return SCPE_OK; int32 todr_rd (void) { -return todr_reg; +TOY *toy = (TOY *)clk_unit.filebuf; +struct timespec base, now, val; + +clock_gettime(CLOCK_REALTIME, &now); /* get curr time */ +base.tv_sec = toy->toy_gmtbase; +base.tv_nsec = toy->toy_gmtbasemsec * 1000000; +sim_timespec_diff (&val, &now, &base); +return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */ } + void todr_wr (int32 data) { -todr_reg = data; -return; +TOY *toy = (TOY *)clk_unit.filebuf; +struct timespec now, val, base; + +/* Save the GMT time when set value was 0 to record the base for future + read operations in "battery backed-up" state */ + +if (-1 == clock_gettime(CLOCK_REALTIME, &now)) /* get curr time */ + return; /* error? */ +val.tv_sec = ((uint32)data) / 100; +val.tv_nsec = (((uint32)data) % 100) * 10000000; +sim_timespec_diff (&base, &now, &val); /* base = now - data */ +toy->toy_gmtbase = base.tv_sec; +toy->toy_gmtbasemsec = base.tv_nsec/1000000; } t_stat todr_resync (void) { -uint32 base; -time_t curr; -struct tm *ctm; +TOY *toy = (TOY *)clk_unit.filebuf; -curr = time (NULL); /* get curr time */ -if (curr == (time_t) -1) /* error? */ - return SCPE_NOFNC; -ctm = localtime (&curr); /* decompose */ -if (ctm == NULL) /* error? */ - return SCPE_NOFNC; -base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ - ctm->tm_hour) * 60) + - ctm->tm_min) * 60) + - ctm->tm_sec; -todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ +if (clk_unit.flags & UNIT_ATT) { /* Attached means behave like real VAX780 */ + if (!toy->toy_gmtbase) /* Never set? */ + todr_wr (0); /* Start ticking from 0 */ + } +else { /* Not-Attached means */ + uint32 base; /* behave like simh VMS default */ + time_t curr; + struct tm *ctm; + + curr = time (NULL); /* get curr time */ + if (curr == (time_t) -1) /* error? */ + return SCPE_NOFNC; + ctm = localtime (&curr); /* decompose */ + if (ctm == NULL) /* error? */ + return SCPE_NOFNC; + base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ + ctm->tm_hour) * 60) + + ctm->tm_min) * 60) + + ctm->tm_sec; + todr_wr ((base * 100) + 0x10000000); /* use VMS form */ + } return SCPE_OK; } diff --git a/VAX/vax_stddev.c b/VAX/vax_stddev.c index 881fcefb..254f4080 100644 --- a/VAX/vax_stddev.c +++ b/VAX/vax_stddev.c @@ -27,6 +27,31 @@ tto terminal output clk 100Hz and TODR clock + 28-Sep-11 MP Generalized setting TODR for all OSes. + Unbound the TODR value from the 100hz clock tick + interrupt. TODR now behaves like the original + battery backed-up clock and runs with the wall + clock, not the simulated instruction clock + (except when running ROM diagnostics). + Two operational modes are available: + - Default VMS mode, which is similar to the previous + behavior in that without initializing the TODR it + would default to the value VMS would set it to if + VMS knew the correct time. This would be correct + almost all the time unless a VMS disk hadn't been + booted from for more than a year. This mode + produces strange time results for non VMS OSes on + each system boot. + - OS Agnostic mode. This mode behaves precisely like + the VAX780 TODR and works correctly for all OSes. + This mode is enabled by attaching the TODR to a + battery backup state file for the TOY clock + (i.e. sim> attach TODR TOY_CLOCK). When operating + in OS Agnostic mode, the TODR will initially start + counting from 0 and be adjusted differently when an + OS specifically writes to the TODR. VMS will prompt + to set the time on each boot unless the SYSGEN + parameter TIMEPROMPTWAIT is set to 0. 05-Jan-11 MP Added Asynch I/O support 17-Aug-08 RMS Resync TODR on any clock reset 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock @@ -70,6 +95,11 @@ int32 clk_csr = 0; /* control/status */ int32 clk_tps = 100; /* ticks/second */ int32 todr_reg = 0; /* TODR register */ int32 todr_blow = 1; /* TODR battery low */ +struct todr_battery_info { + uint32 toy_gmtbase; /* GMT base of set value */ + uint32 toy_gmtbasemsec; /* The milliseconds of the set value */ + }; +typedef struct todr_battery_info TOY; int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ @@ -79,9 +109,12 @@ t_stat clk_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat clk_reset (DEVICE *dptr); +t_stat clk_attach (UNIT *uptr, char *cptr); +t_stat clk_detach (UNIT *uptr); t_stat todr_resync (void); extern int32 sysd_hlt_enb (void); +extern int32 fault_PC; /* TTI data structures @@ -168,7 +201,7 @@ DEVICE tto_dev = { DIB clk_dib = { 0, 0, NULL, NULL, 1, IVCL (CLK), SCB_INTTIM, { NULL } }; -UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */ REG clk_reg[] = { { HRDATA (CSR, clk_csr, 16) }, @@ -180,6 +213,7 @@ REG clk_reg[] = { { DRDATA (POLL, tmr_poll, 24), REG_NZ + PV_LEFT + REG_HRO }, { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, #if defined (SIM_ASYNCH_IO) + { DRDATA (ASYNCH, sim_asynch_enabled, 1), PV_LEFT }, { DRDATA (LATENCY, sim_asynch_latency, 32), PV_LEFT }, { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT }, #endif @@ -193,16 +227,15 @@ MTAB clk_mod[] = { DEVICE clk_dev = { "CLK", &clk_unit, clk_reg, clk_mod, - 1, 0, 0, 0, 0, 0, + 1, 0, 8, 1, 0, 0, NULL, NULL, &clk_reset, - NULL, NULL, NULL, + NULL, &clk_attach, &clk_detach, &clk_dib, 0 }; /* Clock and terminal MxPR routines iccs_rd/wr interval timer - todr_rd/wr time of year clock rxcs_rd/wr input control/status rxdb_rd input buffer txcs_rd/wr output control/status @@ -214,11 +247,6 @@ int32 iccs_rd (void) return (clk_csr & CLKCSR_IMP); } -int32 todr_rd (void) -{ -return todr_reg; -} - int32 rxcs_rd (void) { return (tti_csr & TTICSR_IMP); @@ -247,14 +275,6 @@ clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW); return; } -void todr_wr (int32 data) -{ -todr_reg = data; -if (data) - todr_blow = 0; -return; -} - void rxcs_wr (int32 data) { if ((data & CSR_IE) == 0) @@ -357,7 +377,8 @@ return SCPE_OK; clk_svc process event (clock tick) clk_reset process reset - todr_powerup powerup for TODR (get date from system) + todr_rd/wr time of year clock + todr_resync powerup for TODR (get date from system) */ t_stat clk_svc (UNIT *uptr) @@ -370,8 +391,8 @@ t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ sim_activate (&clk_unit, t); /* reactivate unit */ tmr_poll = t; /* set tmr poll */ tmxr_poll = t * TMXR_MULT; /* set mux poll */ -if (!todr_blow) /* incr TODR */ - todr_reg = todr_reg + 1; +if (!todr_blow && todr_reg) /* if running? */ + todr_reg = todr_reg + 1; /* incr TODR */ return SCPE_OK; } @@ -385,26 +406,80 @@ t = sim_is_active (&clk_unit); return (t? t - 1: wait); } +int32 todr_rd (void) +{ +TOY *toy = (TOY *)clk_unit.filebuf; +struct timespec base, now, val; + +if ((fault_PC&0xFFFE0000) == 0x20040000) /* running from ROM? */ + return todr_reg; /* return counted value for ROM diags */ + +if (0 == todr_reg) /* clock running? */ + return todr_reg; + +/* Maximum number of seconds which can be represented as 10ms ticks + in the 32bit TODR. This is the 33bit value 0x100000000/100 to get seconds */ +#define TOY_MAX_SECS (0x40000000/25) + +clock_gettime(CLOCK_REALTIME, &now); /* get curr time */ +base.tv_sec = toy->toy_gmtbase; +base.tv_nsec = toy->toy_gmtbasemsec * 1000000; +sim_timespec_diff (&val, &now, &base); + +if (val.tv_sec >= TOY_MAX_SECS) /* todr overflowed? */ + return todr_reg = 0; /* stop counting */ + +return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */ +} + + +void todr_wr (int32 data) +{ +TOY *toy = (TOY *)clk_unit.filebuf; +struct timespec now, val, base; + +/* Save the GMT time when set value was 0 to record the base for future + read operations in "battery backed-up" state */ + +if (-1 == clock_gettime(CLOCK_REALTIME, &now)) /* get curr time */ + return; /* error? */ +val.tv_sec = ((uint32)data) / 100; +val.tv_nsec = (((uint32)data) % 100) * 10000000; +sim_timespec_diff (&base, &now, &val); /* base = now - data */ +toy->toy_gmtbase = base.tv_sec; +toy->toy_gmtbasemsec = base.tv_nsec/1000000; +todr_reg = data; +if (data) + todr_blow = 0; +} + /* TODR resync routine */ t_stat todr_resync (void) { -uint32 base; -time_t curr; -struct tm *ctm; +TOY *toy = (TOY *)clk_unit.filebuf; -curr = time (NULL); /* get curr time */ -if (curr == (time_t) -1) /* error? */ - return SCPE_NOFNC; -ctm = localtime (&curr); /* decompose */ -if (ctm == NULL) /* error? */ - return SCPE_NOFNC; -base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ - ctm->tm_hour) * 60) + - ctm->tm_min) * 60) + - ctm->tm_sec; -todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ -todr_blow = 0; +if (clk_unit.flags & UNIT_ATT) { /* Attached means behave like real VAX780 */ + if (!toy->toy_gmtbase) /* Never set? */ + todr_wr (0); /* Start ticking from 0 */ + } +else { /* Not-Attached means */ + uint32 base; /* behave like simh VMS default */ + time_t curr; + struct tm *ctm; + + curr = time (NULL); /* get curr time */ + if (curr == (time_t) -1) /* error? */ + return SCPE_NOFNC; + ctm = localtime (&curr); /* decompose */ + if (ctm == NULL) /* error? */ + return SCPE_NOFNC; + base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ + ctm->tm_hour) * 60) + + ctm->tm_min) * 60) + + ctm->tm_sec; + todr_wr ((base * 100) + 0x10000000); /* use VMS form */ + } return SCPE_OK; } @@ -414,13 +489,46 @@ t_stat clk_reset (DEVICE *dptr) { int32 t; -todr_resync (); /* resync clock */ clk_csr = 0; CLR_INT (CLK); t = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init timer */ sim_activate_abs (&clk_unit, t); /* activate unit */ tmr_poll = t; /* set tmr poll */ tmxr_poll = t * TMXR_MULT; /* set mux poll */ +if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */ + clk_unit.filebuf = calloc(sizeof(TOY), 1); + if (clk_unit.filebuf == NULL) + return SCPE_MEM; + todr_resync (); + } return SCPE_OK; } +/* CLK attach */ + +t_stat clk_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE); +memset (uptr->filebuf, 0, (size_t)uptr->capac); +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +else + uptr->hwmark = (uint32) uptr->capac; +return r; +} + +/* CLK detach */ + +t_stat clk_detach (UNIT *uptr) +{ +t_stat r; + +r = detach_unit (uptr); +if ((uptr->flags & UNIT_ATT) == 0) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +return r; +} + diff --git a/Visual Studio Projects/VAX730.vcproj b/Visual Studio Projects/VAX730.vcproj index 1b150d31..3fb08fd5 100644 --- a/Visual Studio Projects/VAX730.vcproj +++ b/Visual Studio Projects/VAX730.vcproj @@ -134,7 +134,7 @@ FavorSizeOrSpeed="1" OmitFramePointers="true" AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;../../winpcap/Wpdpack/Include;"../../pthreads/Pre-built.2/include"" - PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_730;USE_SHARED;_CRT_SECURE_NO_WARNINGS;USE_READER_THREAD" + PreprocessorDefinitions="USE_INT64;USE_ADDR64;VM_VAX;VAX_730;USE_SHARED;_CRT_SECURE_NO_WARNINGS;USE_READER_THREAD;SIM_ASYNCH_IO" StringPooling="true" RuntimeLibrary="0" EnableFunctionLevelLinking="true" diff --git a/Visual Studio Projects/VAX780.vcproj b/Visual Studio Projects/VAX780.vcproj index 55ee4893..e0139d7a 100644 --- a/Visual Studio Projects/VAX780.vcproj +++ b/Visual Studio Projects/VAX780.vcproj @@ -27,7 +27,7 @@ set breakpoints\n" "set nobreak clear breakpoints\n" - "set throttle x{M|K|%%} set simulation rate\n" + "set throttle {x{M|K|%}}|{x/t}\n" + " set simulation rate\n" "set nothrottle set simulation rate to maximum\n" + "set asynch enable asynchronous I/O\n" + "set noasynch disable asynchronous I/O\n" "set OCT|DEC|HEX set device display radix\n" "set ENABLED enable device\n" "set DISABLED disable device\n" @@ -628,6 +644,7 @@ static CTAB cmd_table[] = { "sh{ow} q{ueue} show event queue\n" "sh{ow} ti{me} show simulated time\n" "sh{ow} th{rottle} show simulation rate\n" + "sh{ow} a{synch} show asynchronouse I/O state\n" "sh{ow} ve{rsion} show simulator version\n" "sh{ow} RADIX show device display radix\n" "sh{ow} DEBUG show device debug flags\n" @@ -1006,13 +1023,6 @@ do { (cmdp->action != &on_cmd) && (cmdp->action != &echo_cmd))) sim_last_cmd_stat = stat; /* save command error status */ - staying = (stat != SCPE_EXIT) && /* decide if staying */ - (stat != SCPE_AFAIL) && - (!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP)); - if ((stat == SCPE_AFAIL) && /* handle special case AFAIL */ - sim_on_check[sim_do_depth] && /* and use trap action if defined */ - sim_on_actions[sim_do_depth][stat]) /* otherwise exit */ - staying = TRUE; if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */ (stat != SCPE_STEP)) { if (!echo && !sim_quiet && /* report if not echoing */ @@ -1023,8 +1033,23 @@ do { } stat = stat & ~SCPE_DOFAILED; /* remove possible flag */ } + switch (stat) { + case SCPE_OK: + case SCPE_STEP: + break; + case SCPE_AFAIL: + staying = (sim_on_check[sim_do_depth] && /* if trap action defined */ + sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */ + break; + case SCPE_EXIT: + staying = FALSE; + break; + default: + staying = sim_on_check[sim_do_depth]; + break; + } if ((staying || !interactive) && /* report error if staying */ - (stat >= SCPE_BASE)) { /* or in cmdline file */ + (stat >= SCPE_BASE) && !isdo) { /* or in cmdline file */ printf ("%s\n", sim_error_text (stat)); if (sim_log) fprintf (sim_log, "%s\n", sim_error_text (stat)); @@ -1033,7 +1058,7 @@ do { (sim_on_check[sim_do_depth]) && (stat != SCPE_OK) && (stat != SCPE_STEP)) - if (sim_on_actions[sim_do_depth][stat]) + if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat]) sim_brk_act[sim_do_depth] = sim_on_actions[sim_do_depth][stat]; else sim_brk_act[sim_do_depth] = sim_on_actions[sim_do_depth][0]; @@ -1281,6 +1306,61 @@ if ((sim_do_depth != 0) && return SCPE_OK; } +/* Set asynch/noasynch routine */ + +t_stat sim_set_asynch (int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) /* now eol? */ + return SCPE_2MARG; +#ifdef SIM_ASYNCH_IO +if (flag == sim_asynch_enabled) /* already set correctly? */ + return SCPE_OK; +sim_asynch_enabled = flag; +if (1) { + uint32 i, j; + DEVICE *dptr; + UNIT *uptr; + + /* Call unit flush routines to report asynch status change to device layer */ + for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files */ + for (j = 0; j < dptr->numunits; j++) { /* if not buffered in mem */ + uptr = dptr->units + j; + if ((uptr->flags & UNIT_ATT) && /* attached, */ + !(uptr->flags & UNIT_BUF) && /* not buffered, */ + (uptr->fileref)) /* real file, */ + if (uptr->io_flush) /* unit specific flush routine */ + uptr->io_flush (uptr); + } + } + } +if (!sim_quiet) + printf ("Asynchronous I/O %sabled\n", sim_asynch_enabled ? "en" : "dis"); +if (sim_log) + fprintf (sim_log, "Asynchronous I/O %sabled\n", sim_asynch_enabled ? "en" : "dis"); +return SCPE_OK; +#else +if (!sim_quiet) + printf ("Asynchronous I/O is not available in this simulator\n"); +if (sim_log) + fprintf (sim_log, "Asynchronous I/O is not available in this simulator\n"); +return SCPE_NOFNC; +#endif +} + +/* Show asynch routine */ + +t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +#ifdef SIM_ASYNCH_IO +fprintf (st, "Asynchronous I/O is %sabled\n", (sim_asynch_enabled) ? "en" : "dis"); +#else +fprintf (st, "Asynchronous I/O is not available in this simulator\n"); +#endif +return SCPE_OK; +} + /* Set command */ t_stat set_cmd (int32 flag, char *cptr) @@ -1305,6 +1385,8 @@ static CTAB set_glob_tab[] = { { "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */ { "THROTTLE", &sim_set_throt, 1 }, { "NOTHROTTLE", &sim_set_throt, 0 }, + { "ASYNCH", &sim_set_asynch, 1 }, + { "NOASYNCH", &sim_set_asynch, 0 }, { "ON", &set_on, 1 }, { "NOON", &set_on, 0 }, { NULL, NULL, 0 } @@ -1570,6 +1652,7 @@ static SHTAB show_glob_tab[] = { { "TELNET", &sim_show_telnet, 0 }, /* deprecated */ { "DEBUG", &sim_show_debug, 0 }, /* deprecated */ { "THROTTLE", &sim_show_throt, 0 }, + { "ASYNCH", &sim_show_asynch, 0 }, { "ON", &show_on, 0 }, { NULL, NULL, 0 } }; @@ -2878,7 +2961,9 @@ for (j=0, r = SCPE_OK; j= 0) { /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ if (i) { /* if delayed */ - printf ("Running\n"); /* print transition */ + printf ("Running\r\n"); /* print transition */ fflush (stdout); + if (sim_log) { /* log file? */ + fprintf (sim_log, "Running\n"); + fflush (sim_log); + } } return SCPE_OK; /* ready to proceed */ } @@ -652,8 +661,12 @@ for (i = 0; i < sec; i++) { /* loop */ if ((c == SCPE_STOP) || stop_cpu) return SCPE_STOP; if ((i % 10) == 0) { /* Status every 10 sec */ - printf ("Waiting for console Telnet connection\n"); + printf ("Waiting for console Telnet connection\r\n"); fflush (stdout); + if (sim_log) { /* log file? */ + fprintf (sim_log, "Waiting for console Telnet connection\n"); + fflush (sim_log); + } } sim_os_sleep (1); /* wait 1 second */ } diff --git a/sim_defs.h b/sim_defs.h index 91abc4ef..4df30c98 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -565,6 +565,7 @@ extern pthread_cond_t sim_asynch_wake; extern pthread_t sim_asynch_main_threadid; extern struct sim_unit *sim_asynch_queue; extern t_bool sim_idle_wait; +extern t_bool sim_asynch_enabled; extern int32 sim_asynch_check; extern int32 sim_asynch_latency; extern int32 sim_asynch_inst_latency; diff --git a/sim_disk.c b/sim_disk.c index 5dbbb012..cf5ff17c 100644 --- a/sim_disk.c +++ b/sim_disk.c @@ -408,15 +408,17 @@ return SCPE_NOFNC; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; pthread_attr_t attr; -ctx->asynch_io = 1; +ctx->asynch_io = sim_asynch_enabled; ctx->asynch_io_latency = latency; -pthread_mutex_init (&ctx->io_lock, NULL); -pthread_cond_init (&ctx->io_cond, NULL); -pthread_attr_init(&attr); -pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); -pthread_create (&ctx->io_thread, &attr, _disk_io, (void *)uptr); -pthread_attr_destroy(&attr); -uptr->a_check_completion = _disk_completion_dispatch; +if (ctx->asynch_io) { + pthread_mutex_init (&ctx->io_lock, NULL); + pthread_cond_init (&ctx->io_cond, NULL); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + pthread_create (&ctx->io_thread, &attr, _disk_io, (void *)uptr); + pthread_attr_destroy(&attr); + uptr->a_check_completion = _disk_completion_dispatch; + } #endif return SCPE_OK; } @@ -723,7 +725,8 @@ uint32 f = DK_GET_FMT (uptr); #if defined (SIM_ASYNCH_IO) sim_disk_clr_async (uptr); -sim_disk_set_async (uptr, 0); +if (sim_asynch_enabled) + sim_disk_set_async (uptr, 0); #endif switch (f) { /* case on format */ case DKUF_F_STD: /* Simh */ @@ -1332,7 +1335,7 @@ if (strchr (openmode, 'r')) DesiredAccess |= GENERIC_READ; if (strchr (openmode, 'w') || strchr (openmode, '+')) DesiredAccess |= GENERIC_WRITE; -Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); +Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_WRITE_THROUGH, NULL); if (Handle == INVALID_HANDLE_VALUE) { _set_errno_from_status (GetLastError ()); return NULL; @@ -1418,17 +1421,21 @@ static t_stat sim_os_disk_unload_raw (FILE *Disk) { #ifdef IOCTL_STORAGE_EJECT_MEDIA DWORD BytesReturned; +uint32 Removable = FALSE; -if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ - IOCTL_STORAGE_EJECT_MEDIA, /* dwIoControlCode */ - NULL, /* lpInBuffer */ - 0, /* nInBufferSize */ - NULL, /* lpOutBuffer */ - 0, /* nOutBufferSize */ - (LPDWORD) &BytesReturned, /* number of bytes returned */ - (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ - _set_errno_from_status (GetLastError ()); - return SCPE_IOERR; +sim_os_disk_info_raw (Disk, NULL, &Removable); +if (Removable) { + if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ + IOCTL_STORAGE_EJECT_MEDIA, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + NULL, /* lpOutBuffer */ + 0, /* nOutBufferSize */ + (LPDWORD) &BytesReturned, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ + _set_errno_from_status (GetLastError ()); + return SCPE_IOERR; + } } return SCPE_OK; #else @@ -1440,17 +1447,21 @@ static t_bool sim_os_disk_isavailable_raw (FILE *Disk) { #ifdef IOCTL_STORAGE_EJECT_MEDIA DWORD BytesReturned; +uint32 Removable = FALSE; -if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ - IOCTL_STORAGE_CHECK_VERIFY, /* dwIoControlCode */ - NULL, /* lpInBuffer */ - 0, /* nInBufferSize */ - NULL, /* lpOutBuffer */ - 0, /* nOutBufferSize */ - (LPDWORD) &BytesReturned, /* number of bytes returned */ - (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ - _set_errno_from_status (GetLastError ()); - return FALSE; +sim_os_disk_info_raw (Disk, NULL, &Removable); +if (Removable) { + if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ + IOCTL_STORAGE_CHECK_VERIFY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + NULL, /* lpOutBuffer */ + 0, /* nOutBufferSize */ + (LPDWORD) &BytesReturned, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ + _set_errno_from_status (GetLastError ()); + return FALSE; + } } #endif return TRUE; @@ -2646,6 +2657,7 @@ if (SizeInBytes > ((uint64)(1024*1024*1024))*2040) { } if (File = sim_fopen (szVHDPath, "rb")) { fclose (File); + File = NULL; Status = EEXIST; goto Cleanup_Return; } @@ -2773,7 +2785,8 @@ if (WriteFilePosition(File, Cleanup_Return: free (BAT); -fclose (File); +if (File) + fclose (File); if (Status) { if (Status != EEXIST) remove (szVHDPath); diff --git a/sim_ether.c b/sim_ether.c index e15c1ad3..3ca7eefa 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -138,6 +138,15 @@ at open time. This functionality is only useful/needed on *nix platforms since native sharing of Windows NIC devices works with no external magic. + USE_VDE_NETWORK - Specifies that support for vde networking should be + included. This can be leveraged, along with OS bridging + capabilities to share a single LAN interface. It also + can allow a simulator to have useful networking + functionality when running without root access. This + allows device names of the form vde:/tmp/switch to be + specified at open time. This functionality is only + available on *nix platforms since the vde api isn't + available on Windows. NEED_PCAP_SENDPACKET - Specifies that you are using an older version of libpcap @@ -151,6 +160,11 @@ Modification history: + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking + 29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX + 12-Aug-11 MP Cleaned up payload length determination + Fixed race condition detecting reflections when threaded + reading and writing is enabled 18-Apr-11 MP Fixed race condition with self loopback packets in multithreaded environments 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and @@ -787,12 +801,16 @@ void eth_show_dev (FILE* st, ETH_DEV* dev) #elif defined(USE_BSDTUNTAP) #include #include -#include +#include #else /* We don't know how to do this on the current platform */ #undef USE_TAP_NETWORK #endif #endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK +#include +#endif /* USE_VDE_NETWORK */ + /* Allows windows to look up user-defined adapter names */ #if defined(_WIN32) #include @@ -1092,40 +1110,42 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, char *devname) if (!pcap_mac_if_win32(devname, (UCHAR *)&dev->host_nic_phy_hw_addr)) dev->have_host_nic_phy_addr = 1; #elif !defined (__VMS) -{ - char command[1024]; - FILE *f; + if (1) { + char command[1024]; + FILE *f; - memset(command, 0, sizeof(command)); - snprintf(command, sizeof(command)-1, "ifconfig %s | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F] >NIC.hwaddr", devname); - system(command); - if (f = fopen("NIC.hwaddr", "r")) { - if (fgets(command, sizeof(command)-1, f)) { - char *p1, *p2; + if (0 == strncmp("vde:", devname, 4)) + return; + memset(command, 0, sizeof(command)); + snprintf(command, sizeof(command)-1, "ifconfig %s | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F] >NIC.hwaddr", devname); + system(command); + if (f = fopen("NIC.hwaddr", "r")) { + if (fgets(command, sizeof(command)-1, f)) { + char *p1, *p2; - p1 = strchr(command, ':'); - while (p1) { - p2 = strchr(p1+1, ':'); - if (p2 == p1+3) { - int mac_bytes[6]; - if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { - dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; - dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; - dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; - dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; - dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; - dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; - dev->have_host_nic_phy_addr = 1; + p1 = strchr(command, ':'); + while (p1) { + p2 = strchr(p1+1, ':'); + if (p2 == p1+3) { + int mac_bytes[6]; + if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { + dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; + dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; + dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; + dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; + dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; + dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; + dev->have_host_nic_phy_addr = 1; + } + break; + } + p1 = p2; } - break; } - p1 = p2; + fclose(f); + remove("NIC.hwaddr"); } } - fclose(f); - remove("NIC.hwaddr"); - } -} #endif } @@ -1148,84 +1168,116 @@ int sched_policy; struct sched_param sched_priority; #if defined (_WIN32) HANDLE hWait = pcap_getevent ((pcap_t*)dev->handle); -#endif -#if defined (MUST_DO_SELECT) -struct timeval timeout; +#else int sel_ret; -int pcap_fd; +int do_select = 0; +int select_fd; -if (dev->pcap_mode) - pcap_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); -else - pcap_fd = dev->fd_handle; +switch (dev->eth_api) { + case ETH_API_PCAP: +#if defined (MUST_DO_SELECT) + do_select = 1; + select_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); +#endif + break; + case ETH_API_TAP: + case ETH_API_VDE: + do_select = 1; + select_fd = dev->fd_handle; + break; + } #endif - sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); +sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); - /* Boost Priority for this I/O thread vs the CPU instruction execution - thread which in general won't be readily yielding the processor when - this thread needs to run */ - pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); - ++sched_priority.sched_priority; - pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); +/* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ +pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); +++sched_priority.sched_priority; +pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); - while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { -#if defined (MUST_DO_SELECT) - fd_set setl; +while (dev->handle) { +#if defined (_WIN32) + if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { +#else + fd_set setl; + struct timeval timeout; + + if (do_select) { FD_ZERO(&setl); - FD_SET(pcap_fd, &setl); + FD_SET(select_fd, &setl); timeout.tv_sec = 0; timeout.tv_usec = 250*1000; - sel_ret = select(1+pcap_fd, &setl, NULL, NULL, &timeout); - if (sel_ret < 0 && errno != EINTR) break; - if (sel_ret > 0) { -#else /* !MUST_DO_SELECT */ -#if defined (_WIN32) - if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { -#else - if (1) { -#endif -#endif /* MUST_DO_SELECT */ - if ((dev->pcap_mode) && (!dev->handle)) - break; - if ((!dev->pcap_mode) && (!dev->fd_handle)) - break; - /* dispatch read request queue available packets */ - if (dev->pcap_mode) + sel_ret = select(1+select_fd, &setl, NULL, NULL, &timeout); + } + else + sel_ret = 1; + if (sel_ret < 0 && errno != EINTR) break; + if (sel_ret > 0) { +#endif /* _WIN32 */ + if (!dev->handle) + break; + /* dispatch read request queue available packets */ + switch (dev->eth_api) { + case ETH_API_PCAP: status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev); + break; #ifdef USE_TAP_NETWORK - else { - struct pcap_pkthdr header; - int len; - u_char buf[ETH_MAX_JUMBO_FRAME]; + case ETH_API_TAP: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; - memset(&header, 0, sizeof(header)); - len = read(dev->fd_handle, buf, sizeof(buf)); - if (len > 0) { - status = 1; - header.caplen = header.len = len; - _eth_callback((u_char *)dev, &header, buf); - } else { - status = 0; - } - } -#endif /* USE_TAP_NETWORK */ - - if ((status > 0) && (dev->asynch_io)) { - int wakeup_needed; - pthread_mutex_lock (&dev->lock); - wakeup_needed = (dev->read_queue.count != 0); - pthread_mutex_unlock (&dev->lock); - if (wakeup_needed) { - sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); - sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; } + break; +#endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = vde_recv((VDECONN *)dev->handle, buf, sizeof(buf), 0); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_VDE_NETWORK */ + } + if ((status > 0) && (dev->asynch_io)) { + int wakeup_needed; + + pthread_mutex_lock (&dev->lock); + wakeup_needed = (dev->read_queue.count != 0); + pthread_mutex_unlock (&dev->lock); + if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); } } + } } - sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); - return NULL; +sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); +return NULL; } static void * @@ -1236,208 +1288,257 @@ struct write_request *request; int sched_policy; struct sched_param sched_priority; - /* Boost Priority for this I/O thread vs the CPU instruction execution - thread which in general won't be readily yielding the processor when - this thread needs to run */ - pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); - ++sched_priority.sched_priority; - pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); +/* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ +pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); +++sched_priority.sched_priority; +pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); - sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); +sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); - pthread_mutex_lock (&dev->writer_lock); - while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { - pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); - while (request = dev->write_requests) { - /* Pull buffer off request list */ - dev->write_requests = request->next; - pthread_mutex_unlock (&dev->writer_lock); - - dev->write_status = _eth_write(dev, &request->packet, NULL); - - pthread_mutex_lock (&dev->writer_lock); - /* Put buffer on free buffer list */ - request->next = dev->write_buffers; - dev->write_buffers = request; +pthread_mutex_lock (&dev->writer_lock); +while (dev->handle) { + pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); + while (request = dev->write_requests) { + /* Pull buffer off request list */ + dev->write_requests = request->next; + pthread_mutex_unlock (&dev->writer_lock); + + dev->write_status = _eth_write(dev, &request->packet, NULL); + + pthread_mutex_lock (&dev->writer_lock); + /* Put buffer on free buffer list */ + request->next = dev->write_buffers; + dev->write_buffers = request; } } - pthread_mutex_unlock (&dev->writer_lock); +pthread_mutex_unlock (&dev->writer_lock); - sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n"); - return NULL; +sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n"); +return NULL; } #endif t_stat eth_set_async (ETH_DEV *dev, int latency) { #if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) - char *msg = "Eth: can't operate asynchronously, must poll\r\n"; - printf ("%s", msg); - if (sim_log) fprintf (sim_log, "%s", msg); - return SCPE_NOFNC; +char *msg = "Eth: can't operate asynchronously, must poll\r\n"; +printf ("%s", msg); +if (sim_log) fprintf (sim_log, "%s", msg); +return SCPE_NOFNC; #else - int wakeup_needed; +int wakeup_needed; - dev->asynch_io = 1; - dev->asynch_io_latency = latency; - pthread_mutex_lock (&dev->lock); - wakeup_needed = (dev->read_queue.count != 0); - pthread_mutex_unlock (&dev->lock); - if (wakeup_needed) { - sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); - sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); +dev->asynch_io = 1; +dev->asynch_io_latency = latency; +pthread_mutex_lock (&dev->lock); +wakeup_needed = (dev->read_queue.count != 0); +pthread_mutex_unlock (&dev->lock); +if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); } #endif - return SCPE_OK; +return SCPE_OK; } t_stat eth_clr_async (ETH_DEV *dev) { #if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) - return SCPE_NOFNC; +return SCPE_NOFNC; #else - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - dev->asynch_io = 0; - return SCPE_OK; +dev->asynch_io = 0; +return SCPE_OK; #endif } t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) { - int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; - char errbuf[PCAP_ERRBUF_SIZE]; - char temp[1024]; - char* savname = name; - int num; - char* msg; +int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; +char errbuf[PCAP_ERRBUF_SIZE]; +char temp[1024]; +char* savname = name; +int num; +char* msg; - if (bufsz < ETH_MAX_JUMBO_FRAME) - bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ +if (bufsz < ETH_MAX_JUMBO_FRAME) + bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ - /* initialize device */ - eth_zero(dev); +/* initialize device */ +eth_zero(dev); - /* translate name of type "ethX" to real device name */ - if ((strlen(name) == 4) - && (tolower(name[0]) == 'e') - && (tolower(name[1]) == 't') - && (tolower(name[2]) == 'h') - && isdigit(name[3]) - ) { - num = atoi(&name[3]); - savname = eth_getname(num, temp); +/* translate name of type "ethX" to real device name */ +if ((strlen(name) == 4) + && (tolower(name[0]) == 'e') + && (tolower(name[1]) == 't') + && (tolower(name[2]) == 'h') + && isdigit(name[3]) + ) { + num = atoi(&name[3]); + savname = eth_getname(num, temp); + if (savname == 0) /* didn't translate */ + return SCPE_OPENERR; + } +else { + /* are they trying to use device description? */ + savname = eth_getname_bydesc(name, temp); + if (savname == 0) { /* didn't translate */ + /* probably is not ethX and has no description */ + savname = eth_getname_byname(name, temp); if (savname == 0) /* didn't translate */ - return SCPE_OPENERR; - } else { - /* are they trying to use device description? */ - savname = eth_getname_bydesc(name, temp); - if (savname == 0) { /* didn't translate */ - /* probably is not ethX and has no description */ - savname = eth_getname_byname(name, temp); - if (savname == 0) /* didn't translate */ - savname = name; + savname = name; } } - /* attempt to connect device */ - memset(errbuf, 0, sizeof(errbuf)); - if (strncmp("tap:", savname, 4)) { +/* attempt to connect device */ +memset(errbuf, 0, sizeof(errbuf)); +if (0 == strncmp("tap:", savname, 4)) { + int tun = -1; /* TUN/TAP Socket */ + int on = 1; + char dev_name[64] = ""; + +#if defined(USE_TAP_NETWORK) + if (!strcmp(savname, "tap:tapN")) { + msg = "Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } +#endif +#if defined(__linux) && defined(USE_TAP_NETWORK) + if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { + struct ifreq ifr; /* Interface Requests */ + + memset(&ifr, 0, sizeof(ifr)); + /* Set up interface flags */ + strcpy(ifr.ifr_name, savname+4); + ifr.ifr_flags = IFF_TAP|IFF_NO_PI; + + /* Send interface requests to TUN/TAP driver. */ + if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } + else { + dev->fd_handle = tun; + strcpy(savname, ifr.ifr_name); + } + } + else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + } + else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); +#elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK) + snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4); + dev_name[sizeof(dev_name)-1] = '\0'; + + if ((tun = open(dev_name, O_RDWR)) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } + else { + dev->fd_handle = tun; + strcpy(savname, savname+4); + } +#if defined (__APPLE__) + if (1) { + struct ifreq ifr; + int s; + + memset (&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, savname+4, sizeof(ifr.ifr_name)); + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) >= 0) { + ifr.ifr_flags |= IFF_UP; + ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr); + } + close(s); + } + } +#endif + } + else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); +#else + strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1); +#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ + if (0 == errbuf[0]) { + dev->eth_api = ETH_API_TAP; + dev->handle = (void *)1; /* Flag used to indicated open */ + } + } +else + if (0 == strncmp("vde:", savname, 4)) { +#if defined(USE_VDE_NETWORK) + struct vde_open_args voa; + + memset(&voa, 0, sizeof(voa)); + if (!strcmp(savname, "vde:vdedevice")) { + msg = "Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + if (!(dev->handle = (void*) vde_open(savname+4, "simh", &voa))) + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + else { + dev->eth_api = ETH_API_VDE; + dev->fd_handle = vde_datafd((VDECONN*)dev->handle); + } +#else + strncpy(errbuf, "No support for vde: network devices", sizeof(errbuf)-1); +#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ + } + else { dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); if (!dev->handle) { /* can't open device */ msg = "Eth: pcap_open_live error - %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); return SCPE_OPENERR; - } - dev->pcap_mode = 1; - } else { - int tun = -1; /* TUN/TAP Socket */ - int on = 1; - char dev_name[64] = ""; - -#if defined(USE_TAP_NETWORK) - if (!strcmp(savname, "tap:tapN")) { - msg = "Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - return SCPE_OPENERR; - } -#endif -#if defined(__linux) && defined(USE_TAP_NETWORK) - if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { - struct ifreq ifr; /* Interface Requests */ - - memset(&ifr, 0, sizeof(ifr)); - /* Set up interface flags */ - strcpy(ifr.ifr_name, savname+4); - ifr.ifr_flags = IFF_TAP|IFF_NO_PI; - - // Send interface requests to TUN/TAP driver. - if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { - if (ioctl(tun, FIONBIO, &on)) { - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); - close(tun); - } else { - dev->fd_handle = tun; - strcpy(savname, ifr.ifr_name); - } - } else - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); - } else - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); -#elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK) - snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4); - dev_name[sizeof(dev_name)-1] = '\0'; - - if ((tun = open(dev_name, O_RDWR)) >= 0) { - if (ioctl(tun, FIONBIO, &on)) { - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); - close(tun); - } else { - dev->fd_handle = tun; - strcpy(savname, savname+4); } - } else { - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + dev->eth_api = ETH_API_PCAP; } -#else - strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1); -#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ +if (errbuf[0]) { + msg = "Eth: open error - %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; } - if (errbuf[0]) { - msg = "Eth: open error - %s\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - return SCPE_OPENERR; - } - msg = "Eth: opened OS device %s\r\n"; - printf (msg, savname); - if (sim_log) fprintf (sim_log, msg, savname); +msg = "Eth: opened OS device %s\r\n"; +printf (msg, savname); +if (sim_log) fprintf (sim_log, msg, savname); - /* get the NIC's hardware MAC address */ - eth_get_nic_hw_addr(dev, savname); +/* get the NIC's hardware MAC address */ +eth_get_nic_hw_addr(dev, savname); - /* save name of device */ - dev->name = malloc(strlen(savname)+1); - strcpy(dev->name, savname); +/* save name of device */ +dev->name = malloc(strlen(savname)+1); +strcpy(dev->name, savname); - /* save debugging information */ - dev->dptr = dptr; - dev->dbit = dbit; +/* save debugging information */ +dev->dptr = dptr; +dev->dbit = dbit; #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) - /* Tell the kernel that the header is fully-formed when it gets it. - This is required in order to fake the src address. */ - if (dev->pcap_mode) { - int one = 1; - ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); +/* Tell the kernel that the header is fully-formed when it gets it. + This is required in order to fake the src address. */ +if (dev->eth_api == ETH_API_PCAP) { + int one = 1; + ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); } #endif /* xBSD */ #if defined (USE_READER_THREAD) - { +if (1) { pthread_attr_t attr; #if defined(_WIN32) @@ -1456,318 +1557,344 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) } #else /* !defined (USE_READER_THREAD */ #ifdef USE_SETNONBLOCK - /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ - if (dev->pcap_mode && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) { - msg = "Eth: Failed to set non-blocking: %s\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); +/* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ +if ((dev->eth_api == ETH_API_PCAP) && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) { + msg = "Eth: Failed to set non-blocking: %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); } #endif #endif /* !defined (USE_READER_THREAD */ #if defined (__APPLE__) - if (1) { - /* Deliver packets immediately, needed for OS X 10.6.2 and later - * (Snow-Leopard). - * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on - * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110 - */ - int v = 1; - ioctl(pcap_fileno(dev->handle), BIOCIMMEDIATE, &v); +if (dev->eth_api == ETH_API_PCAP) { + /* Deliver packets immediately, needed for OS X 10.6.2 and later + * (Snow-Leopard). + * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on + * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110 + */ + int v = 1; + ioctl(pcap_fileno(dev->handle), BIOCIMMEDIATE, &v); } #endif - return SCPE_OK; +return SCPE_OK; } t_stat eth_close(ETH_DEV* dev) { - char* msg = "Eth: closed %s\r\n"; - pcap_t *pcap; - int pcap_fd = dev->fd_handle; +char* msg = "Eth: closed %s\r\n"; +pcap_t *pcap; +int pcap_fd = dev->fd_handle; - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* close the device */ - pcap = (pcap_t *)dev->handle; - dev->handle = NULL; - dev->fd_handle = 0; - dev->have_host_nic_phy_addr = 0; +/* close the device */ +pcap = (pcap_t *)dev->handle; +dev->handle = NULL; +dev->fd_handle = 0; +dev->have_host_nic_phy_addr = 0; #if defined (USE_READER_THREAD) - pthread_join (dev->reader_thread, NULL); - pthread_mutex_destroy (&dev->lock); - pthread_cond_signal (&dev->writer_cond); - pthread_join (dev->writer_thread, NULL); - pthread_mutex_destroy (&dev->self_lock); - pthread_mutex_destroy (&dev->writer_lock); - pthread_cond_destroy (&dev->writer_cond); - if (1) { - struct write_request *buffer; - - while (buffer = dev->write_buffers) { - dev->write_buffers = buffer->next; - free(buffer); +pthread_join (dev->reader_thread, NULL); +pthread_mutex_destroy (&dev->lock); +pthread_cond_signal (&dev->writer_cond); +pthread_join (dev->writer_thread, NULL); +pthread_mutex_destroy (&dev->self_lock); +pthread_mutex_destroy (&dev->writer_lock); +pthread_cond_destroy (&dev->writer_cond); +if (1) { + struct write_request *buffer; + while (buffer = dev->write_buffers) { + dev->write_buffers = buffer->next; + free(buffer); } - while (buffer = dev->write_requests) { - dev->write_requests = buffer->next; - free(buffer); + while (buffer = dev->write_requests) { + dev->write_requests = buffer->next; + free(buffer); } } - ethq_destroy (&dev->read_queue); /* release FIFO queue */ +ethq_destroy (&dev->read_queue); /* release FIFO queue */ #endif - if (dev->pcap_mode) +switch (dev->eth_api) { + case ETH_API_PCAP: pcap_close(pcap); + break; #ifdef USE_TAP_NETWORK - else + case ETH_API_TAP: close(pcap_fd); + break; #endif - printf (msg, dev->name); - if (sim_log) fprintf (sim_log, msg, dev->name); +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + vde_close((VDECONN*)pcap); + break; +#endif + } +printf (msg, dev->name); +if (sim_log) fprintf (sim_log, msg, dev->name); - /* clean up the mess */ - free(dev->name); - eth_zero(dev); +/* clean up the mess */ +free(dev->name); +eth_zero(dev); - return SCPE_OK; +return SCPE_OK; } t_stat eth_check_address_conflict (ETH_DEV* dev, ETH_MAC* const mac) { - ETH_PACK send, recv; - t_stat status; - int responses = 0; - char mac_string[32]; +ETH_PACK send, recv; +t_stat status; +int responses = 0; +char mac_string[32]; - eth_mac_fmt(mac, mac_string); - sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string); +eth_mac_fmt(mac, mac_string); +sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string); - /* build a loopback forward request packet */ - memset (&send, 0, sizeof(ETH_PACK)); - send.len = ETH_MIN_PACKET; /* minimum packet size */ - memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ - memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ - send.msg[12] = 0x90; /* loopback packet type */ - send.msg[14] = 0; /* Offset */ - send.msg[16] = 2; /* Forward */ - memcpy(&send.msg[18], mac, sizeof(ETH_MAC)); /* Forward Destination */ - send.msg[24] = 1; /* Reply */ +/* build a loopback forward request packet */ +memset (&send, 0, sizeof(ETH_PACK)); +send.len = ETH_MIN_PACKET; /* minimum packet size */ +memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ +memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ +send.msg[12] = 0x90; /* loopback packet type */ +send.msg[14] = 0; /* Offset */ +send.msg[16] = 2; /* Forward */ +memcpy(&send.msg[18], mac, sizeof(ETH_MAC)); /* Forward Destination */ +send.msg[24] = 1; /* Reply */ - eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); +eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); - /* send the packet */ - status = _eth_write (dev, &send, NULL); - if (status != SCPE_OK) { - char *msg; - msg = "Eth: Error Transmitting packet: %s\r\n" - "You may need to run as root, or install a libpcap version\r\n" - "which is at least 0.9 from www.tcpdump.org\r\n"; - printf(msg, strerror(errno)); - if (sim_log) fprintf (sim_log, msg, strerror(errno)); - return status; +/* send the packet */ +status = _eth_write (dev, &send, NULL); +if (status != SCPE_OK) { + char *msg; + msg = "Eth: Error Transmitting packet: %s\r\n" + "You may need to run as root, or install a libpcap version\r\n" + "which is at least 0.9 from www.tcpdump.org\r\n"; + printf(msg, strerror(errno)); + if (sim_log) fprintf (sim_log, msg, strerror(errno)); + return status; } - sim_os_ms_sleep (300); /* time for a conflicting host to respond */ +sim_os_ms_sleep (300); /* time for a conflicting host to respond */ - /* empty the read queue and count the responses */ - do { - memset (&recv, 0, sizeof(ETH_PACK)); - status = eth_read (dev, &recv, NULL); - if (memcmp(send.msg, recv.msg, 14)== 0) - responses++; +/* empty the read queue and count the responses */ +do { + memset (&recv, 0, sizeof(ETH_PACK)); + status = eth_read (dev, &recv, NULL); + if (memcmp(send.msg, recv.msg, 14)== 0) + responses++; } while (recv.len > 0); - sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses); - return responses; +sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses); +return responses; } t_stat eth_reflect(ETH_DEV* dev) { - /* Test with an address no NIC should have. */ - /* We do this to avoid reflections from the wire, */ - /* in the event that a simulated NIC has a MAC address conflict. */ - ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe}; +/* Test with an address no NIC should have. */ +/* We do this to avoid reflections from the wire, */ +/* in the event that a simulated NIC has a MAC address conflict. */ +ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe}; - sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n"); +sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n"); - dev->reflections = 0; - dev->reflections = eth_check_address_conflict (dev, &mac); +dev->reflections = 0; +dev->reflections = eth_check_address_conflict (dev, &mac); - sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); - return dev->reflections; +sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); +return dev->reflections; } static t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { - int status = 1; /* default to failure */ +int status = 1; /* default to failure */ - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* make sure packet exists */ - if (!packet) return SCPE_ARG; +/* make sure packet exists */ +if (!packet) return SCPE_ARG; - /* make sure packet is acceptable length */ - if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { - int loopback_self_frame = LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg); +/* make sure packet is acceptable length */ +if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { + int loopback_self_frame = LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg); - eth_packet_trace (dev, packet->msg, packet->len, "writing"); + eth_packet_trace (dev, packet->msg, packet->len, "writing"); - /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */ - if (loopback_self_frame) { + /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */ + if (loopback_self_frame) { #ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->self_lock); + pthread_mutex_lock (&dev->self_lock); #endif - dev->loopback_self_sent += dev->reflections; - dev->loopback_self_sent_total++; + dev->loopback_self_sent += dev->reflections; + dev->loopback_self_sent_total++; #ifdef USE_READER_THREAD - pthread_mutex_unlock (&dev->self_lock); + pthread_mutex_unlock (&dev->self_lock); #endif } /* dispatch write request (synchronous; no need to save write info to dev) */ - if (dev->pcap_mode) + switch (dev->eth_api) { + case ETH_API_PCAP: status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); + break; #ifdef USE_TAP_NETWORK - else + case ETH_API_TAP: status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); + break; #endif - - /* On error, correct loopback bookkeeping */ - if ((status != 0) && loopback_self_frame) { -#ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->self_lock); +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + status = vde_send((VDECONN*)dev->handle, (void *)packet->msg, packet->len, 0); + if ((status == packet->len) || (status == 0)) + status = 0; + else + if ((status == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + status = 0; + else + status = 1; + break; #endif - dev->loopback_self_sent -= dev->reflections; - dev->loopback_self_sent_total--; + } + /* On error, correct loopback bookkeeping */ + if ((status != 0) && loopback_self_frame) { #ifdef USE_READER_THREAD - pthread_mutex_unlock (&dev->self_lock); + pthread_mutex_lock (&dev->self_lock); +#endif + dev->loopback_self_sent -= dev->reflections; + dev->loopback_self_sent_total--; +#ifdef USE_READER_THREAD + pthread_mutex_unlock (&dev->self_lock); #endif } } /* if packet->len */ - /* call optional write callback function */ - if (routine) - (routine)(status); +/* call optional write callback function */ +if (routine) + (routine)(status); - return ((status == 0) ? SCPE_OK : SCPE_IOERR); +return ((status == 0) ? SCPE_OK : SCPE_IOERR); } t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { #ifdef USE_READER_THREAD - struct write_request *request; - int write_queue_size = 1; +struct write_request *request; +int write_queue_size = 1; - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* Get a buffer */ - pthread_mutex_lock (&dev->writer_lock); - if (request = dev->write_buffers) - dev->write_buffers = request->next; - pthread_mutex_unlock (&dev->writer_lock); - if (!request) - request = malloc(sizeof(*request)); +/* Get a buffer */ +pthread_mutex_lock (&dev->writer_lock); +if (request = dev->write_buffers) + dev->write_buffers = request->next; +pthread_mutex_unlock (&dev->writer_lock); +if (!request) + request = malloc(sizeof(*request)); - /* Copy buffer contents */ - request->packet.len = packet->len; - request->packet.used = packet->used; - request->packet.status = packet->status; - request->packet.crc_len = packet->crc_len; - memcpy(request->packet.msg, packet->msg, packet->len); +/* Copy buffer contents */ +request->packet.len = packet->len; +request->packet.used = packet->used; +request->packet.status = packet->status; +request->packet.crc_len = packet->crc_len; +memcpy(request->packet.msg, packet->msg, packet->len); - /* Insert buffer at the end of the write list (to make sure that */ - /* packets make it to the wire in the order they were presented here) */ - pthread_mutex_lock (&dev->writer_lock); - request->next = NULL; - if (dev->write_requests) { - struct write_request *last_request = dev->write_requests; +/* Insert buffer at the end of the write list (to make sure that */ +/* packets make it to the wire in the order they were presented here) */ +pthread_mutex_lock (&dev->writer_lock); +request->next = NULL; +if (dev->write_requests) { + struct write_request *last_request = dev->write_requests; + ++write_queue_size; + while (last_request->next) { + last_request = last_request->next; ++write_queue_size; - while (last_request->next) { - last_request = last_request->next; - ++write_queue_size; } - last_request->next = request; - } else + last_request->next = request; + } +else dev->write_requests = request; - if (write_queue_size > dev->write_queue_peak) - dev->write_queue_peak = write_queue_size; - pthread_mutex_unlock (&dev->writer_lock); +if (write_queue_size > dev->write_queue_peak) + dev->write_queue_peak = write_queue_size; +pthread_mutex_unlock (&dev->writer_lock); - /* Awaken writer thread to perform actual write */ - pthread_cond_signal (&dev->writer_cond); +/* Awaken writer thread to perform actual write */ +pthread_cond_signal (&dev->writer_cond); - /* Return with a status from some prior write */ - if (routine) - (routine)(dev->write_status); - return dev->write_status; +/* Return with a status from some prior write */ +if (routine) + (routine)(dev->write_status); +return dev->write_status; #else - return _eth_write(dev, packet, routine); +return _eth_write(dev, packet, routine); #endif } static int _eth_hash_lookup(ETH_MULTIHASH hash, const u_char* data) { - int key = 0x3f & (eth_crc32(0, data, 6) >> 26); +int key = 0x3f & (eth_crc32(0, data, 6) >> 26); - key ^= 0x3f; - return (hash[key>>3] & (1 << (key&0x7))); +key ^= 0x3f; +return (hash[key>>3] & (1 << (key&0x7))); } static int _eth_hash_validate(ETH_MAC *MultiCastList, int count, ETH_MULTIHASH hash) { - ETH_MULTIHASH lhash; - int i; +ETH_MULTIHASH lhash; +int i; - memset(lhash, 0, sizeof(lhash)); - for (i=0; i> 26); +memset(lhash, 0, sizeof(lhash)); +for (i=0; i> 26); - key ^= 0x3F; - printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", - MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], - key, key>>3, (1 << (key&0x7))); - lhash[key>>3] |= (1 << (key&0x7)); + key ^= 0x3F; + printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", + MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], + key, key>>3, (1 << (key&0x7))); + lhash[key>>3] |= (1 << (key&0x7)); } - if (memcmp(hash, lhash, sizeof(lhash))) { - printf("Inconsistent Computed Hash:\n"); - printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", - hash[0], hash[1], hash[2], hash[3], - hash[4], hash[5], hash[6], hash[7]); - printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", - lhash[0], lhash[1], lhash[2], lhash[3], - lhash[4], lhash[5], lhash[6], lhash[7]); +if (memcmp(hash, lhash, sizeof(lhash))) { + printf("Inconsistent Computed Hash:\n"); + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); } - printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", - hash[0], hash[1], hash[2], hash[3], - hash[4], hash[5], hash[6], hash[7]); - printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", - lhash[0], lhash[1], lhash[2], lhash[3], - lhash[4], lhash[5], lhash[6], lhash[7]); - return 0; +else { + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); + } +return 0; } static void _eth_test_multicast_hash() { - ETH_MAC tMacs[] = { - {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10}, - {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00}, - {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F}, - {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04}, - {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}}; - ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00}; +ETH_MAC tMacs[] = { + {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10}, + {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00}, + {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}}; +ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00}; - _eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash); +_eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash); } /* The IP header */ @@ -1790,7 +1917,7 @@ struct IPHeader { uint16 checksum; /* IP checksum */ uint32 source_ip; /* Source Address */ uint32 dest_ip; /* Destination Address */ -}; + }; /* ICMP header */ struct ICMPHeader { @@ -1798,14 +1925,14 @@ struct ICMPHeader { uint8 code; /* Type sub code */ uint16 checksum; /* ICMP Checksum */ uint32 otherstuff[1];/* optional data */ -}; + }; struct UDPHeader { uint16 source_port; uint16 dest_port; uint16 length; /* The length of the entire UDP datagram, including both header and Data fields. */ uint16 checksum; -}; + }; struct TCPHeader { uint16 source_port; @@ -1827,7 +1954,7 @@ struct TCPHeader { uint16 checksum; uint16 urgent; uint16 otherstuff[1]; /* The rest of the packet */ -}; + }; #ifndef IPPROTO_TCP #define IPPROTO_TCP 6 /* tcp */ @@ -1842,484 +1969,516 @@ struct TCPHeader { static uint16 ip_checksum(uint16 *buffer, int size) { - unsigned long cksum = 0; +unsigned long cksum = 0; - /* Sum all the words together, adding the final byte if size is odd */ - while (size > 1) { - cksum += *buffer++; - size -= sizeof(*buffer); - } - if (size) { - uint8 endbytes[2]; +/* Sum all the words together, adding the final byte if size is odd */ +while (size > 1) { + cksum += *buffer++; + size -= sizeof(*buffer); +} +if (size) { + uint8 endbytes[2]; - endbytes[0] = *((uint8 *)buffer); - endbytes[1] = 0; - cksum += *((uint16 *)endbytes); + endbytes[0] = *((uint8 *)buffer); + endbytes[1] = 0; + cksum += *((uint16 *)endbytes); } - /* Do a little shuffling */ - cksum = (cksum >> 16) + (cksum & 0xffff); - cksum += (cksum >> 16); +/* Do a little shuffling */ +cksum = (cksum >> 16) + (cksum & 0xffff); +cksum += (cksum >> 16); - /* Return the bitwise complement of the resulting mishmash */ - return (uint16)(~cksum); +/* Return the bitwise complement of the resulting mishmash */ +return (uint16)(~cksum); } static uint16 pseudo_checksum(uint16 len, uint16 proto, uint16 *src_addr, uint16 *dest_addr, uint8 *buff) { - uint32 sum; +uint32 sum; - /* Sum the data first */ - sum = 0xffff&(~ip_checksum((uint16 *)buff, len)); +/* Sum the data first */ +sum = 0xffff&(~ip_checksum((uint16 *)buff, len)); - /* add the pseudo header which contains the IP source and destinationn addresses */ - sum += src_addr[0]; - sum += src_addr[1]; - sum += dest_addr[0]; - sum += dest_addr[1]; - /* and the protocol number and the length of the UDP packet */ - sum = sum + htons(proto) + htons(len); +/* add the pseudo header which contains the IP source and destinationn addresses */ +sum += src_addr[0]; +sum += src_addr[1]; +sum += dest_addr[0]; +sum += dest_addr[1]; +/* and the protocol number and the length of the UDP packet */ +sum = sum + htons(proto) + htons(len); - /* Do a little shuffling */ - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); +/* Do a little shuffling */ +sum = (sum >> 16) + (sum & 0xffff); +sum += (sum >> 16); - /* Return the bitwise complement of the resulting mishmash */ - return (uint16)(~sum); +/* Return the bitwise complement of the resulting mishmash */ +return (uint16)(~sum); } static void _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) { - unsigned short* proto = (unsigned short*) &msg[12]; - struct IPHeader *IP; - struct TCPHeader *TCP = NULL; - struct UDPHeader *UDP; - struct ICMPHeader *ICMP; - uint16 orig_checksum; - uint16 payload_len; - uint16 mtu_payload; - uint16 ip_flags; - uint16 frag_offset; - struct pcap_pkthdr header; - uint16 orig_tcp_flags; +unsigned short* proto = (unsigned short*) &msg[12]; +struct IPHeader *IP; +struct TCPHeader *TCP = NULL; +struct UDPHeader *UDP; +struct ICMPHeader *ICMP; +uint16 orig_checksum; +uint16 payload_len; +uint16 mtu_payload; +uint16 ip_flags; +uint16 frag_offset; +struct pcap_pkthdr header; +uint16 orig_tcp_flags; - /* Only interested in IP frames */ - if (ntohs(*proto) != 0x0800) { - ++dev->jumbo_dropped; /* Non IP Frames are dropped */ - return; +/* Only interested in IP frames */ +if (ntohs(*proto) != 0x0800) { + ++dev->jumbo_dropped; /* Non IP Frames are dropped */ + return; } - IP = (struct IPHeader *)&msg[14]; - if (IP_VERSION(IP) != 4) { - ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */ - return; +IP = (struct IPHeader *)&msg[14]; +if (IP_VERSION(IP) != 4) { + ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */ + return; } - if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) { - ++dev->jumbo_dropped; /* Bogus header length frames are dropped */ - return; +if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) { + ++dev->jumbo_dropped; /* Bogus header length frames are dropped */ + return; } - if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) { - ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */ - return; +if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) { + ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */ + return; } - switch (IP->proto) { - case IPPROTO_UDP: - UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); - if (ntohs(UDP->length) > (len-IP_HLEN(IP))) { - ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */ - return; - } - if (UDP->checksum == 0) - break; /* UDP Checksums are disabled */ - orig_checksum = UDP->checksum; - UDP->checksum = 0; - UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); - if (orig_checksum != UDP->checksum) - eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed"); - break; - case IPPROTO_ICMP: - ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_checksum = ICMP->checksum; - ICMP->checksum = 0; - ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); - if (orig_checksum != ICMP->checksum) - eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed"); - break; - case IPPROTO_TCP: - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) { - ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */ - return; - } - /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */ - break; - default: - ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */ +switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) { + ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */ return; + } + if (UDP->checksum == 0) + break; /* UDP Checksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) { + ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */ + return; + } + /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */ + break; + default: + ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */ + return; } - /* Reasonable Checksums are now in the jumbo packet, but we've got to actually */ - /* deliver ONLY standard sized ethernet frames. Our job here is to now act as */ - /* a router might have to and fragment these IPv4 frames as they are delivered */ - /* into the virtual NIC. We do this by walking down the packet and dispatching */ - /* a chunk at a time recomputing an appropriate header for each chunk. For */ - /* datagram oriented protocols (UDP and ICMP) this is done by simple packet */ - /* fragmentation. For TCP this is done by breaking large packets into separate */ - /* TCP packets. */ - memset(&header, 0, sizeof(header)); - switch (IP->proto) { - case IPPROTO_UDP: - case IPPROTO_ICMP: - ++dev->jumbo_fragmented; - /* When we're performing LSO (Large Send Offload), we're given a - 'template' header which may not include a value being populated - in the IP header length (which is only 16 bits). - We process as payload everything which isn't known header data. */ - payload_len = len - (14 + IP_HLEN(IP)); - mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP)); - frag_offset = 0; - while (payload_len > 0) { - ip_flags = frag_offset; - if (payload_len > mtu_payload) { - ip_flags |= IP_MF_FLAG; - IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); - } else { - IP->total_len = htons(payload_len + IP_HLEN(IP)); +/* Reasonable Checksums are now in the jumbo packet, but we've got to actually */ +/* deliver ONLY standard sized ethernet frames. Our job here is to now act as */ +/* a router might have to and fragment these IPv4 frames as they are delivered */ +/* into the virtual NIC. We do this by walking down the packet and dispatching */ +/* a chunk at a time recomputing an appropriate header for each chunk. For */ +/* datagram oriented protocols (UDP and ICMP) this is done by simple packet */ +/* fragmentation. For TCP this is done by breaking large packets into separate */ +/* TCP packets. */ +memset(&header, 0, sizeof(header)); +switch (IP->proto) { + case IPPROTO_UDP: + case IPPROTO_ICMP: + ++dev->jumbo_fragmented; + /* When we're performing LSO (Large Send Offload), we're given a + 'template' header which may not include a value being populated + in the IP header length (which is only 16 bits). + We process as payload everything which isn't known header data. */ + payload_len = len - (14 + IP_HLEN(IP)); + mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP)); + frag_offset = 0; + while (payload_len > 0) { + ip_flags = frag_offset; + if (payload_len > mtu_payload) { + ip_flags |= IP_MF_FLAG; + IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); } - IP->flags = htons(ip_flags); - IP->checksum = 0; - IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); - header.caplen = header.len = 14 + ntohs(IP->total_len); - eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); + else { + IP->total_len = htons(payload_len + IP_HLEN(IP)); + } + IP->flags = htons(ip_flags); + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + header.caplen = header.len = 14 + ntohs(IP->total_len); + eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET - { /* Debugging is easier if we read packets directly with pcap - (i.e. we can use Wireshark to verify packet contents) - we don't want to do this all the time for 2 reasons: - 1) sending through pcap involves kernel transitions and - 2) if the current system reflects sent packets, the - recieving side will receive and process 2 copies of - any packets sent this way. */ - ETH_PACK pkt; + if (1) { + /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ + ETH_PACK pkt; - memset(&pkt, 0, sizeof(pkt)); - memcpy(pkt.msg, ((u_char *)IP)-14, header.len); - pkt.len = header.len; - _eth_write(dev, &pkt, NULL); - } + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } #else - _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); #endif - payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP)); - frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3; - if (payload_len > 0) { - /* Move the MAC and IP headers down to just prior to the next payload segment */ - memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP)); - IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP)); + payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP)); + frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3; + if (payload_len > 0) { + /* Move the MAC and IP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP)); } } - break; - case IPPROTO_TCP: - ++dev->jumbo_fragmented; - eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit); - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_tcp_flags = ntohs(TCP->data_offset_and_flags); - /* When we're performing LSO (Large Send Offload), we're given a - 'template' header which may not include a value being populated - in the IP header length (which is only 16 bits). - We process as payload everything which isn't known header data. */ - payload_len = len - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - while (payload_len > 0) { - if (payload_len > mtu_payload) { - TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); - IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - } else { - TCP->data_offset_and_flags = htons(orig_tcp_flags); - IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + break; + case IPPROTO_TCP: + ++dev->jumbo_fragmented; + eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit); + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_tcp_flags = ntohs(TCP->data_offset_and_flags); + /* When we're performing LSO (Large Send Offload), we're given a + 'template' header which may not include a value being populated + in the IP header length (which is only 16 bits). + We process as payload everything which isn't known header data. */ + payload_len = len - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + while (payload_len > 0) { + if (payload_len > mtu_payload) { + TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); + IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); } - IP->checksum = 0; - IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); - TCP->checksum = 0; - TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); - header.caplen = header.len = 14 + ntohs(IP->total_len); - eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); + else { + TCP->data_offset_and_flags = htons(orig_tcp_flags); + IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + } + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + header.caplen = header.len = 14 + ntohs(IP->total_len); + eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET - { /* Debugging is easier if we read packets directly with pcap - (i.e. we can use Wireshark to verify packet contents) - we don't want to do this all the time for 2 reasons: - 1) sending through pcap involves kernel transitions and - 2) if the current system reflects sent packets, the - recieving side will receive and process 2 copies of - any packets sent this way. */ - ETH_PACK pkt; + if (1) { + /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ + ETH_PACK pkt; - memset(&pkt, 0, sizeof(pkt)); - memcpy(pkt.msg, ((u_char *)IP)-14, header.len); - pkt.len = header.len; - _eth_write(dev, &pkt, NULL); - } + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } #else - _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); #endif - payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); - if (payload_len > 0) { - /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */ - memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number)); + payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + if (payload_len > 0) { + /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number)); } } - break; + break; } } static void _eth_fix_ip_xsum_offload(ETH_DEV* dev, u_char* msg, int len) { - unsigned short* proto = (unsigned short*) &msg[12]; - struct IPHeader *IP; - struct TCPHeader *TCP; - struct UDPHeader *UDP; - struct ICMPHeader *ICMP; - uint16 orig_checksum; +unsigned short* proto = (unsigned short*) &msg[12]; +struct IPHeader *IP; +struct TCPHeader *TCP; +struct UDPHeader *UDP; +struct ICMPHeader *ICMP; +uint16 orig_checksum; - /* Only need to process locally originated packets */ - if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6))) - return; - /* Only interested in IP frames */ - if (ntohs(*proto) != 0x0800) - return; - IP = (struct IPHeader *)&msg[14]; - if (IP_VERSION(IP) != 4) - return; /* Only interested in IPv4 frames */ - if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) - return; /* Bogus header length */ - orig_checksum = IP->checksum; - IP->checksum = 0; - IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); - if (orig_checksum != IP->checksum) - eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed"); - if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) - return; /* Insufficient data to compute payload checksum */ - switch (IP->proto) { - case IPPROTO_UDP: - UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); - if (ntohs(UDP->length) > (len-IP_HLEN(IP))) - return; /* packet contained length exceeds packet size */ - if (UDP->checksum == 0) - return; /* UDP Checksums are disabled */ - orig_checksum = UDP->checksum; - UDP->checksum = 0; - UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); - if (orig_checksum != UDP->checksum) - eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed"); - break; - case IPPROTO_TCP: - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_checksum = TCP->checksum; - TCP->checksum = 0; - TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); - if (orig_checksum != TCP->checksum) - eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed"); - break; - case IPPROTO_ICMP: - ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_checksum = ICMP->checksum; - ICMP->checksum = 0; - ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); - if (orig_checksum != ICMP->checksum) - eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed"); - break; +/* Only need to process locally originated packets */ +if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6))) + return; +/* Only interested in IP frames */ +if (ntohs(*proto) != 0x0800) + return; +IP = (struct IPHeader *)&msg[14]; +if (IP_VERSION(IP) != 4) + return; /* Only interested in IPv4 frames */ +if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) + return; /* Bogus header length */ +orig_checksum = IP->checksum; +IP->checksum = 0; +IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); +if (orig_checksum != IP->checksum) + eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed"); +if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) + return; /* Insufficient data to compute payload checksum */ +switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) + return; /* packet contained length exceeds packet size */ + if (UDP->checksum == 0) + return; /* UDP Checksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = TCP->checksum; + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + if (orig_checksum != TCP->checksum) + eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed"); + break; } } static void _eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) { - ETH_DEV* dev = (ETH_DEV*) info; +ETH_DEV* dev = (ETH_DEV*) info; +int to_me; +int from_me = 0; +int i; +int bpf_used; + +switch (dev->eth_api) { + case ETH_API_PCAP: #ifdef USE_BPF - int to_me = 1; - - /* AUTODIN II hash mode? */ - if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) - to_me = _eth_hash_lookup(dev->hash, data); -#else /* !USE_BPF */ - int to_me = 0; - int from_me = 0; - int i; - - eth_packet_trace (dev, data, header->len, "received"); -f - for (i = 0; i < dev->addr_count; i++) { - if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; - if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; - } - - /* all multicast mode? */ - if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; - - /* promiscuous mode? */ - if (dev->promiscuous) to_me = 1; - - /* AUTODIN II hash mode? */ - if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) - to_me = _eth_hash_lookup(dev->hash, data); + bpf_used = 1; + to_me = 1; + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) + to_me = _eth_hash_lookup(dev->hash, data); + break; #endif /* USE_BPF */ + case ETH_API_TAP: + case ETH_API_VDE: + bpf_used = 0; + to_me = 0; + eth_packet_trace (dev, data, header->len, "received"); - /* detect reception of loopback packet to our physical address */ - if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) { + for (i = 0; i < dev->addr_count; i++) { + if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; + if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; + } + + /* all multicast mode? */ + if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; + + /* promiscuous mode? */ + if (dev->promiscuous) to_me = 1; + + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) + to_me = _eth_hash_lookup(dev->hash, data); + break; + } + +/* detect reception of loopback packet to our physical address */ +if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) { #ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->self_lock); + pthread_mutex_lock (&dev->self_lock); #endif - dev->loopback_self_rcvd_total++; - /* lower reflection count - if already zero, pass it on */ - if (dev->loopback_self_sent > 0) { - eth_packet_trace (dev, data, header->len, "ignored"); - dev->loopback_self_sent--; - to_me = 0; - } -#ifndef USE_BPF - else + dev->loopback_self_rcvd_total++; + /* lower reflection count - if already zero, pass it on */ + if (dev->loopback_self_sent > 0) { + eth_packet_trace (dev, data, header->len, "ignored"); + dev->loopback_self_sent--; + to_me = 0; + } + else + if (!bpf_used) from_me = 0; -#endif #ifdef USE_READER_THREAD - pthread_mutex_unlock (&dev->self_lock); + pthread_mutex_unlock (&dev->self_lock); #endif } -#ifdef USE_BPF - if (to_me) { -#else /* !USE_BPF */ - if (to_me && !from_me) { -#endif - if (header->len > ETH_MIN_JUMBO_FRAME) { - if (header->len <= header->caplen) /* Whole Frame captured? */ - _eth_fix_ip_jumbo_offload(dev, data, header->len); - else - ++dev->jumbo_truncated; - return; +if (bpf_used ? to_me : (to_me && !from_me)) { + if (header->len > ETH_MIN_JUMBO_FRAME) { + if (header->len <= header->caplen) /* Whole Frame captured? */ + _eth_fix_ip_jumbo_offload(dev, data, header->len); + else + ++dev->jumbo_truncated; + return; } #if defined (USE_READER_THREAD) - if (1) { - int crc_len = 0; - uint8 crc_data[4]; - uint32 len = header->len; - u_char* moved_data = NULL; + if (1) { + int crc_len = 0; + uint8 crc_data[4]; + uint32 len = header->len; + u_char* moved_data = NULL; - if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ - moved_data = malloc(ETH_MIN_PACKET); - memcpy(moved_data, data, len); - memset(moved_data + len, 0, ETH_MIN_PACKET-len); - len = ETH_MIN_PACKET; - data = moved_data; + if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ + moved_data = malloc(ETH_MIN_PACKET); + memcpy(moved_data, data, len); + memset(moved_data + len, 0, ETH_MIN_PACKET-len); + len = ETH_MIN_PACKET; + data = moved_data; } - /* If necessary, fix IP header checksums for packets originated locally */ - /* but were presumed to be traversing a NIC which was going to handle that task */ - /* This must be done before any needed CRC calculation */ - _eth_fix_ip_xsum_offload(dev, (u_char*)data, len); - - if (dev->need_crc) - crc_len = eth_get_packet_crc32_data(data, len, crc_data); - - eth_packet_trace (dev, data, len, "rcvqd"); - - pthread_mutex_lock (&dev->lock); - ethq_insert_data(&dev->read_queue, 2, data, 0, len, crc_len, crc_data, 0); - pthread_mutex_unlock (&dev->lock); - free(moved_data); - } -#else - /* set data in passed read packet */ - dev->read_packet->len = header->len; - memcpy(dev->read_packet->msg, data, header->len); - /* Handle runt case and pad with zeros. */ - /* The real NIC won't hand us runts from the wire, BUT we may be getting */ - /* some packets looped back before they actually traverse the wire */ - /* (by an internal bridge device for instance) */ - if (header->len < ETH_MIN_PACKET) { - memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len); - dev->read_packet->len = ETH_MIN_PACKET; - } - /* If necessary, fix IP header checksums for packets originated by the local host */ + /* If necessary, fix IP header checksums for packets originated locally */ /* but were presumed to be traversing a NIC which was going to handle that task */ /* This must be done before any needed CRC calculation */ - _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len); + _eth_fix_ip_xsum_offload(dev, (u_char*)data, len); + if (dev->need_crc) - dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len); - else - dev->read_packet->crc_len = 0; + crc_len = eth_get_packet_crc32_data(data, len, crc_data); - eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); + eth_packet_trace (dev, data, len, "rcvqd"); - /* call optional read callback function */ - if (dev->read_callback) - (dev->read_callback)(0); + pthread_mutex_lock (&dev->lock); + ethq_insert_data(&dev->read_queue, 2, data, 0, len, crc_len, crc_data, 0); + pthread_mutex_unlock (&dev->lock); + free(moved_data); + } +#else /* !USE_READER_THREAD */ + /* set data in passed read packet */ + dev->read_packet->len = header->len; + memcpy(dev->read_packet->msg, data, header->len); + /* Handle runt case and pad with zeros. */ + /* The real NIC won't hand us runts from the wire, BUT we may be getting */ + /* some packets looped back before they actually traverse the wire */ + /* (by an internal bridge device for instance) */ + if (header->len < ETH_MIN_PACKET) { + memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len); + dev->read_packet->len = ETH_MIN_PACKET; + } + /* If necessary, fix IP header checksums for packets originated by the local host */ + /* but were presumed to be traversing a NIC which was going to handle that task */ + /* This must be done before any needed CRC calculation */ + _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len); + if (dev->need_crc) + dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len); + else + dev->read_packet->crc_len = 0; + + eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); + + /* call optional read callback function */ + if (dev->read_callback) + (dev->read_callback)(0); #endif } } int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { - int status; +int status; - /* make sure device exists */ +/* make sure device exists */ - if (!dev) return 0; +if (!dev) return 0; - /* make sure packet exists */ - if (!packet) return 0; +/* make sure packet exists */ +if (!packet) return 0; - packet->len = 0; +packet->len = 0; #if !defined (USE_READER_THREAD) - /* set read packet */ - dev->read_packet = packet; +/* set read packet */ +dev->read_packet = packet; - /* set optional callback routine */ - dev->read_callback = routine; +/* set optional callback routine */ +dev->read_callback = routine; - /* dispatch read request to either receive a filtered packet or timeout */ - do { - if (dev->pcap_mode) +/* dispatch read request to either receive a filtered packet or timeout */ +do { + switch (dev->eth_api) { + case ETH_API_PCAP: status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev); + break; #ifdef USE_TAP_NETWORK - else { - struct pcap_pkthdr header; - int len; - u_char buf[ETH_MAX_JUMBO_FRAME]; + case ETH_API_TAP: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; - memset(&header, 0, sizeof(header)); - len = read(dev->fd_handle, buf, sizeof(buf)); - if (len > 0) { - status = 1; - header.caplen = header.len = len; - _eth_callback((u_char *)dev, &header, buf); - } else { - status = 0; - } - } + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; #endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = vde_recv((VDECONN*)dev->handle, buf, sizeof(buf), 0); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_VDE_NETWORK */ + } } while ((status) && (0 == packet->len)); #else /* USE_READER_THREAD */ - status = 0; - pthread_mutex_lock (&dev->lock); - if (dev->read_queue.count > 0) { - ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; - packet->len = item->packet.len; - packet->crc_len = item->packet.crc_len; - memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len)); - status = 1; - ethq_remove(&dev->read_queue); - } - pthread_mutex_unlock (&dev->lock); - if ((status) && (routine)) - routine(0); + status = 0; + pthread_mutex_lock (&dev->lock); + if (dev->read_queue.count > 0) { + ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; + packet->len = item->packet.len; + packet->crc_len = item->packet.crc_len; + memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len)); + status = 1; + ethq_remove(&dev->read_queue); + } + pthread_mutex_unlock (&dev->lock); + if ((status) && (routine)) + routine(0); #endif - return status; +return status; } t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, @@ -2334,181 +2493,182 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash) { - int i; - bpf_u_int32 bpf_subnet, bpf_netmask; - char buf[114+66*ETH_FILTER_MAX]; - char errbuf[PCAP_ERRBUF_SIZE]; - char mac[20]; - char* buf2; - t_stat status; +int i; +bpf_u_int32 bpf_subnet, bpf_netmask; +char buf[114+66*ETH_FILTER_MAX]; +char errbuf[PCAP_ERRBUF_SIZE]; +char mac[20]; +char* buf2; +t_stat status; #ifdef USE_BPF - struct bpf_program bpf; - char* msg; +struct bpf_program bpf; +char* msg; #endif - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* filter count OK? */ - if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) - return SCPE_ARG; - else - if (!addresses) return SCPE_ARG; +/* filter count OK? */ +if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) + return SCPE_ARG; +else + if (!addresses) return SCPE_ARG; - /* test reflections. This is done early in this routine since eth_reflect */ - /* calls eth_filter recursively and thus changes the state of the device. */ - if (dev->reflections == -1) - status = eth_reflect(dev); +/* test reflections. This is done early in this routine since eth_reflect */ +/* calls eth_filter recursively and thus changes the state of the device. */ +if (dev->reflections == -1) + status = eth_reflect(dev); - /* set new filter addresses */ - for (i = 0; i < addr_count; i++) - memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); - dev->addr_count = addr_count; +/* set new filter addresses */ +for (i = 0; i < addr_count; i++) + memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); +dev->addr_count = addr_count; - /* store other flags */ - dev->all_multicast = all_multicast; - dev->promiscuous = promiscuous; +/* store other flags */ +dev->all_multicast = all_multicast; +dev->promiscuous = promiscuous; - /* store multicast hash data */ - dev->hash_filter = (hash != NULL); - if (hash) { - memcpy(dev->hash, hash, sizeof(*hash)); - sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", - dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], - dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]); +/* store multicast hash data */ +dev->hash_filter = (hash != NULL); +if (hash) { + memcpy(dev->hash, hash, sizeof(*hash)); + sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", + dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], + dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]); } - /* print out filter information if debugging */ - if (dev->dptr->dctrl & dev->dbit) { - sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); - for (i = 0; i < addr_count; i++) { - char mac[20]; - eth_mac_fmt(&dev->filter_address[i], mac); - sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); +/* print out filter information if debugging */ +if (dev->dptr->dctrl & dev->dbit) { + sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); + for (i = 0; i < addr_count; i++) { + char mac[20]; + eth_mac_fmt(&dev->filter_address[i], mac); + sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); } - if (dev->all_multicast) - sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); - if (dev->promiscuous) - sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); + if (dev->all_multicast) + sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); + if (dev->promiscuous) + sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); } - /* setup BPF filters and other fields to minimize packet delivery */ - strcpy(buf, ""); +/* setup BPF filters and other fields to minimize packet delivery */ +strcpy(buf, ""); - /* construct destination filters - since the real ethernet interface was set - into promiscuous mode by eth_open(), we need to filter out the packets that - our simulated interface doesn't want. */ - if (!dev->promiscuous) { - for (i = 0; i < addr_count; i++) { - eth_mac_fmt(&dev->filter_address[i], mac); - if (!strstr(buf, mac)) /* eliminate duplicates */ - sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac); +/* construct destination filters - since the real ethernet interface was set + into promiscuous mode by eth_open(), we need to filter out the packets that + our simulated interface doesn't want. */ +if (!dev->promiscuous) { + for (i = 0; i < addr_count; i++) { + eth_mac_fmt(&dev->filter_address[i], mac); + if (!strstr(buf, mac)) /* eliminate duplicates */ + sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac); } - if (dev->all_multicast || dev->hash_filter) - sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "(("); - if (strlen(buf) > 0) - sprintf(&buf[strlen(buf)], ")"); - } - - /* construct source filters - this prevents packets from being reflected back - by systems where WinPcap and libpcap cause packet reflections. Note that - some systems do not reflect packets at all. This *assumes* that the - simulated NIC will not send out packets with multicast source fields. */ - if ((addr_count > 0) && (dev->reflections > 0)) { - if (strlen(buf) > 0) - sprintf(&buf[strlen(buf)], " and "); - sprintf (&buf[strlen(buf)], "not ("); - buf2 = &buf[strlen(buf)]; - for (i = 0; i < addr_count; i++) { - if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ - eth_mac_fmt(&dev->filter_address[i], mac); - if (!strstr(buf2, mac)) /* eliminate duplicates */ - sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); - } - sprintf (&buf[strlen(buf)], ")"); - } + if (dev->all_multicast || dev->hash_filter) + sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "(("); if (strlen(buf) > 0) sprintf(&buf[strlen(buf)], ")"); - /* When changing the Physical Address on a LAN interface, VMS sends out a - loopback packet with the source and destination addresses set to the same - value as the Physical Address which is being setup. This packet is - designed to find and help diagnose MAC address conflicts (which also - include DECnet address conflicts). Normally, this packet would not be - seen by the sender, only by the other machine that has the same Physical - Address (or possibly DECnet address). If the ethernet subsystem is - reflecting packets, the network startup will fail to start if it sees the - reflected packet, since it thinks another system is using this Physical - Address (or DECnet address). We have to let these packets through, so - that if another machine has the same Physical Address (or DECnet address) - that we can detect it. Both eth_write() and _eth_callback() help by - checking the reflection count - eth_write() adds the reflection count to - dev->loopback_self_sent, and _eth_callback() check the value - if the - dev->loopback_self_sent count is zero, then the packet has come from - another machine with the same address, and needs to be passed on to the - simulated machine. */ - memset(dev->physical_addr, 0, sizeof(ETH_MAC)); - dev->loopback_self_sent = 0; - /* check for physical address in filters */ - if ((addr_count) && (dev->reflections > 0)) { - for (i = 0; i < addr_count; i++) { - if (dev->filter_address[i][0]&1) - continue; /* skip all multicast addresses */ - eth_mac_fmt(&dev->filter_address[i], mac); - if (strcmp(mac, "00:00:00:00:00:00") != 0) { - memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC)); - /* let packets through where dst and src are the same as our physical address */ - sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); - break; + } + +/* construct source filters - this prevents packets from being reflected back + by systems where WinPcap and libpcap cause packet reflections. Note that + some systems do not reflect packets at all. This *assumes* that the + simulated NIC will not send out packets with multicast source fields. */ +if ((addr_count > 0) && (dev->reflections > 0)) { + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], " and "); + sprintf (&buf[strlen(buf)], "not ("); + buf2 = &buf[strlen(buf)]; + for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ + eth_mac_fmt(&dev->filter_address[i], mac); + if (!strstr(buf2, mac)) /* eliminate duplicates */ + sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); + } + sprintf (&buf[strlen(buf)], ")"); + } +if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], ")"); +/* When changing the Physical Address on a LAN interface, VMS sends out a + loopback packet with the source and destination addresses set to the same + value as the Physical Address which is being setup. This packet is + designed to find and help diagnose MAC address conflicts (which also + include DECnet address conflicts). Normally, this packet would not be + seen by the sender, only by the other machine that has the same Physical + Address (or possibly DECnet address). If the ethernet subsystem is + reflecting packets, the network startup will fail to start if it sees the + reflected packet, since it thinks another system is using this Physical + Address (or DECnet address). We have to let these packets through, so + that if another machine has the same Physical Address (or DECnet address) + that we can detect it. Both eth_write() and _eth_callback() help by + checking the reflection count - eth_write() adds the reflection count to + dev->loopback_self_sent, and _eth_callback() check the value - if the + dev->loopback_self_sent count is zero, then the packet has come from + another machine with the same address, and needs to be passed on to the + simulated machine. */ +memset(dev->physical_addr, 0, sizeof(ETH_MAC)); +dev->loopback_self_sent = 0; +/* check for physical address in filters */ +if ((addr_count) && (dev->reflections > 0)) { + for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0]&1) + continue; /* skip all multicast addresses */ + eth_mac_fmt(&dev->filter_address[i], mac); + if (strcmp(mac, "00:00:00:00:00:00") != 0) { + memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC)); + /* let packets through where dst and src are the same as our physical address */ + sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); + break; } } } - if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */ - strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */ - sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); +if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */ + strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */ +sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); - /* get netmask, which is required for compiling */ - if (dev->pcap_mode && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0)) { - bpf_netmask = 0; - } +/* get netmask, which is required for compiling */ +if ((dev->eth_api == ETH_API_PCAP) && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0)) + bpf_netmask = 0; #ifdef USE_BPF - if (dev->pcap_mode) { - /* compile filter string */ - if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { +if (dev->eth_api == ETH_API_PCAP) { + /* compile filter string */ + if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { + sprintf(errbuf, "%s", pcap_geterr(dev->handle)); + msg = "Eth: pcap_compile error: %s\r\n"; + printf(msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_compile error: %s\n", errbuf); + /* show erroneous BPF string */ + msg = "Eth: BPF string is: |%s|\r\n"; + printf (msg, buf); + if (sim_log) fprintf (sim_log, msg, buf); + } + else { + /* apply compiled filter string */ + if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_compile error: %s\r\n"; + msg = "Eth: pcap_setfilter error: %s\r\n"; printf(msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); - sim_debug(dev->dbit, dev->dptr, "Eth: pcap_compile error: %s\n", errbuf); - /* show erroneous BPF string */ - msg = "Eth: BPF string is: |%s|\r\n"; - printf (msg, buf); - if (sim_log) fprintf (sim_log, msg, buf); - } else { - /* apply compiled filter string */ - if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { - sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_setfilter error: %s\r\n"; - printf(msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf); - } else { + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf); + } + else { #ifdef USE_SETNONBLOCK - /* set file non-blocking */ - status = pcap_setnonblock (dev->handle, 1, errbuf); + /* set file non-blocking */ + status = pcap_setnonblock (dev->handle, 1, errbuf); #endif /* USE_SETNONBLOCK */ } - pcap_freecode(&bpf); + pcap_freecode(&bpf); } #ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->lock); - ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ - pthread_mutex_unlock (&dev->lock); + pthread_mutex_lock (&dev->lock); + ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ + pthread_mutex_unlock (&dev->lock); #endif } #endif /* USE_BPF */ - return SCPE_OK; +return SCPE_OK; } /* @@ -2532,149 +2692,157 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, */ int eth_host_devices(int used, int max, ETH_LIST* list) { - pcap_t* conn; - int i, j, datalink; - char errbuf[PCAP_ERRBUF_SIZE]; +pcap_t* conn; +int i, j, datalink; +char errbuf[PCAP_ERRBUF_SIZE]; - for (i=0; i sizeof(regval))) { - RegCloseKey (reghnd); - continue; + /* make sure value is the right type, bail if not acceptable */ + if ((regtype != REG_SZ) || (reglen > sizeof(regval))) { + RegCloseKey (reghnd); + continue; } - /* registry value seems OK, finish up and replace description */ - RegCloseKey (reghnd ); - sprintf (list[i].desc, "%s", regval); + /* registry value seems OK, finish up and replace description */ + RegCloseKey (reghnd ); + sprintf (list[i].desc, "%s", regval); } } /* for */ #endif #ifdef USE_TAP_NETWORK - if (used < max) { - list[used].num = used; +if (used < max) { + list[used].num = used; #if defined(__OpenBSD__) - sprintf(list[used].name, "%s", "tap:tunN"); + sprintf(list[used].name, "%s", "tap:tunN"); #else - sprintf(list[used].name, "%s", "tap:tapN"); + sprintf(list[used].name, "%s", "tap:tapN"); #endif - sprintf(list[used].desc, "%s", "Integrated Tun/Tap support"); - ++used; + sprintf(list[used].desc, "%s", "Integrated Tun/Tap support"); + ++used; + } +#endif +#ifdef USE_VDE_NETWORK +if (used < max) { + list[used].num = used; + sprintf(list[used].name, "%s", "vde:device"); + sprintf(list[used].desc, "%s", "Integrated VDE support"); + ++used; } #endif - return used; +return used; } int eth_devices(int max, ETH_LIST* list) { - pcap_if_t* alldevs; - pcap_if_t* dev; - int i = 0; - char errbuf[PCAP_ERRBUF_SIZE]; - +int i = 0; #ifndef DONT_USE_PCAP_FINDALLDEVS - /* retrieve the device list */ - if (pcap_findalldevs(&alldevs, errbuf) == -1) { - char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - } else { - /* copy device list into the passed structure */ - for (i=0, dev=alldevs; dev; dev=dev->next) { - if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; - list[i].num = i; - sprintf(list[i].name, "%s", dev->name); - if (dev->description) - sprintf(list[i].desc, "%s", dev->description); - else - sprintf(list[i].desc, "%s", "No description available"); - if (i++ >= max) break; +pcap_if_t* alldevs; +pcap_if_t* dev; +char errbuf[PCAP_ERRBUF_SIZE]; + +/* retrieve the device list */ +if (pcap_findalldevs(&alldevs, errbuf) == -1) { + char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } +else { + /* copy device list into the passed structure */ + for (i=0, dev=alldevs; dev; dev=dev->next) { + if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; + list[i].num = i; + sprintf(list[i].name, "%s", dev->name); + if (dev->description) + sprintf(list[i].desc, "%s", dev->description); + else + sprintf(list[i].desc, "%s", "No description available"); + if (i++ >= max) break; } - /* free device list */ - pcap_freealldevs(alldevs); + /* free device list */ + pcap_freealldevs(alldevs); } #endif - /* Add any host specific devices and/or validate those already found */ - i = eth_host_devices(i, max, list); +/* Add any host specific devices and/or validate those already found */ +i = eth_host_devices(i, max, list); - /* return device count */ - return i; +/* return device count */ +return i; } void eth_show_dev (FILE *st, ETH_DEV* dev) { - fprintf(st, "Ethernet Device:\n"); - if (!dev) { - fprintf(st, "-- Not Attached\n"); - return; +fprintf(st, "Ethernet Device:\n"); +if (!dev) { + fprintf(st, "-- Not Attached\n"); + return; } - fprintf(st, " Name: %s\n", dev->name); - fprintf(st, " Reflections: %d\n", dev->reflections); - fprintf(st, " Self Loopbacks Sent: %d\n", dev->loopback_self_sent_total); - fprintf(st, " Self Loopbacks Rcvd: %d\n", dev->loopback_self_rcvd_total); - if (dev->have_host_nic_phy_addr) { - char hw_mac[20]; +fprintf(st, " Name: %s\n", dev->name); +fprintf(st, " Reflections: %d\n", dev->reflections); +fprintf(st, " Self Loopbacks Sent: %d\n", dev->loopback_self_sent_total); +fprintf(st, " Self Loopbacks Rcvd: %d\n", dev->loopback_self_rcvd_total); +if (dev->have_host_nic_phy_addr) { + char hw_mac[20]; - eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac); - fprintf(st, " Host NIC Address: %s\n", hw_mac); + eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac); + fprintf(st, " Host NIC Address: %s\n", hw_mac); } - if (dev->jumbo_dropped) - fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); - if (dev->jumbo_fragmented) - fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); - if (dev->jumbo_truncated) - fprintf(st, " Jumbo Truncated: %d\n", dev->jumbo_truncated); +if (dev->jumbo_dropped) + fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); +if (dev->jumbo_fragmented) + fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); +if (dev->jumbo_truncated) + fprintf(st, " Jumbo Truncated: %d\n", dev->jumbo_truncated); #if defined(USE_READER_THREAD) - fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); - if (dev->asynch_io) - fprintf(st, " Interrupt Latency: %d uSec\n", dev->asynch_io_latency); - fprintf(st, " Read Queue: Count: %d\n", dev->read_queue.count); - fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); - fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); - fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); +fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); +if (dev->asynch_io) + fprintf(st, " Interrupt Latency: %d uSec\n", dev->asynch_io_latency); +fprintf(st, " Read Queue: Count: %d\n", dev->read_queue.count); +fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); +fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); +fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); #endif } #endif /* USE_NETWORK */ diff --git a/sim_ether.h b/sim_ether.h index de155548..1478c40c 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -28,6 +28,7 @@ Modification history: + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking 18-Apr-11 MP Fixed race condition with self loopback packets in multithreaded environments 09-Dec-10 MP Added support to determine if network address conflicts exist @@ -89,13 +90,13 @@ #if defined (USE_READER_THREAD) #if defined (USE_SETNONBLOCK) #undef USE_SETNONBLOCK -#endif +#endif /* USE_SETNONBLOCK */ #undef PCAP_READ_TIMEOUT #define PCAP_READ_TIMEOUT 15 -#if !defined (xBSD) && !defined(_WIN32) && !defined(VMS) -#define MUST_DO_SELECT -#endif +#if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS)) || defined (USE_TAP_NETWORK) || defined (USE_VDE_NETWORK) +#define MUST_DO_SELECT 1 #endif +#endif /* USE_READER_THREAD */ /* USE_BPF is defined to let this code leverage the libpcap/OS kernel provided @@ -175,7 +176,10 @@ struct eth_device { char* name; /* name of ethernet device */ void* handle; /* handle of implementation-specific device */ int fd_handle; /* fd to kernel device (where needed) */ - int pcap_mode; /* Flag indicating if pcap API are being used to move packets */ + int eth_api; /* Designator for which API is being used to move packets */ +#define ETH_API_PCAP 0 /* Pcap API in use */ +#define ETH_API_TAP 1 /* tun/tap API in use */ +#define ETH_API_VDE 2 /* VDE API in use */ ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK write_callback; /* write callback function */ ETH_PACK* read_packet; /* read packet */ diff --git a/sim_tape.c b/sim_tape.c index d50a4887..a71aa54b 100644 --- a/sim_tape.c +++ b/sim_tape.c @@ -320,14 +320,16 @@ return SCPE_NOFNC; struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; pthread_attr_t attr; -ctx->asynch_io = 1; +ctx->asynch_io = sim_asynch_enabled; ctx->asynch_io_latency = latency; -pthread_mutex_init (&ctx->io_lock, NULL); -pthread_cond_init (&ctx->io_cond, NULL); -pthread_attr_init(&attr); -pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); -pthread_create (&ctx->io_thread, &attr, _tape_io, (void *)uptr); -pthread_attr_destroy(&attr); +if (ctx->asynch_io) { + pthread_mutex_init (&ctx->io_lock, NULL); + pthread_cond_init (&ctx->io_cond, NULL); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + pthread_create (&ctx->io_thread, &attr, _tape_io, (void *)uptr); + pthread_attr_destroy(&attr); + } uptr->a_check_completion = _tape_completion_dispatch; #endif return SCPE_OK; @@ -362,7 +364,8 @@ static void _sim_tape_io_flush (UNIT *uptr) { #if defined (SIM_ASYNCH_IO) sim_tape_clr_async (uptr); -sim_tape_set_async (uptr, 0); +if (sim_asynch_enabled) + sim_tape_set_async (uptr, 0); #endif fflush (uptr->fileref); } diff --git a/sim_timer.c b/sim_timer.c index 0d3feedd..2a364cdf 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -23,6 +23,28 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 21-Oct-11 MP Fixed throttling in several ways: + - Sleep for the observed clock tick size while throttling + - Recompute the throttling wait once every 10 seconds + to account for varying instruction mixes during + different phases of a simulator execution or to + accommodate the presence of other load on the host + system. + - Each of the pre-existing throttling modes (Kcps, + Mcps, and %) all compute the appropriate throttling + interval dynamically. These dynamic computations + assume that 100% of the host CPU is dedicated to + the current simulator during this computation. + This assumption may not always be true and under + certain conditions may never provide a way to + correctly determine the appropriate throttling + wait. An additional throttling mode has been added + which allows the simulator operator to explicitly + state the desired throttling wait parameters. + These are specified by: + SET THROT insts/delay + where 'insts' is the number of instructions to + execute before sleeping for 'delay' milliseconds. 22-Apr-11 MP Fixed Asynch I/O support to reasonably account cycles when an idle wait is terminated by an external event 05-Jan-11 MP Added Asynch I/O support @@ -67,6 +89,7 @@ static uint32 sim_throt_ms_stop = 0; static uint32 sim_throt_type = 0; static uint32 sim_throt_val = 0; static uint32 sim_throt_state = 0; +static uint32 sim_throt_sleep_time = 0; static int32 sim_throt_wait = 0; extern int32 sim_interval, sim_switches; extern FILE *sim_log; @@ -152,8 +175,7 @@ return sim_os_msec () - stime; } #if defined(SIM_ASYNCH_IO) -#ifndef CLOCK_REALTIME -#define CLOCK_REALTIME 1 +#ifdef NEED_CLOCK_GETTIME int clock_gettime(int clk_id, struct timespec *tp) { uint32 secs, ns, tod[2], unixbase[2] = {0xd53e8000, 0x019db1de}; @@ -225,12 +247,13 @@ Sleep (msec); return sim_os_msec () - stime; } -#if !defined(CLOCK_REALTIME) && defined (SIM_ASYNCH_IO) -#define CLOCK_REALTIME 1 +#if defined(NEED_CLOCK_GETTIME) int clock_gettime(int clk_id, struct timespec *tp) { t_uint64 now, unixbase; +if (clk_id != CLOCK_REALTIME) + return -1; unixbase = 116444736; unixbase *= 1000000000; GetSystemTimeAsFileTime((FILETIME*)&now); @@ -315,8 +338,7 @@ treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; return sim_os_msec () - stime; } -#if !defined(CLOCK_REALTIME) && defined (SIM_ASYNCH_IO) -#define CLOCK_REALTIME 1 +#if defined(NEED_CLOCK_GETTIME) int clock_gettime(int clk_id, struct timespec *tp) { struct timeval cur; @@ -377,8 +399,7 @@ if (tim > SIM_IDLE_MAX) return tim; } #if !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO) -#ifndef CLOCK_REALTIME -#define CLOCK_REALTIME 1 +#ifdef NEED_CLOCK_GETTIME typedef int clockid_t; int clock_gettime(clockid_t clk_id, struct timespec *tp) { @@ -408,6 +429,21 @@ return sim_os_msec () - stime; #endif +/* diff = min - sub */ +void +sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub) +{ +/* move the minuend value to the difference and operate there. */ +*diff = *min; +/* Borrow as needed for the nsec value */ +if (sub->tv_nsec > min->tv_nsec) { + --diff->tv_sec; + diff->tv_nsec += 1000000000; + } +diff->tv_nsec -= sub->tv_nsec; +diff->tv_sec -= sub->tv_sec; +} + #if defined(SIM_ASYNCH_IO) uint32 sim_idle_ms_sleep (unsigned int msec) { @@ -628,8 +664,11 @@ return SCPE_OK; t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) { -if (sim_idle_enab) - fprintf (st, "idle enabled, stability wait = %ds, minimum sleep resolution = %dms", sim_idle_stable, sim_idle_rate_ms); +if (sim_idle_enab) { + fprintf (st, "idle enabled"); + if (sim_switches & SWMASK ('D')) + fprintf (st, ", stability wait = %ds, minimum sleep resolution = %dms", sim_idle_stable, sim_idle_rate_ms); + } else fputs ("idle disabled", st); return SCPE_OK; } @@ -639,7 +678,7 @@ return SCPE_OK; t_stat sim_set_throt (int32 arg, char *cptr) { char *tptr, c; -t_value val; +t_value val, val2; if (arg == 0) { if ((cptr != 0) && (*cptr != 0)) @@ -653,8 +692,11 @@ else { val = strtotv (cptr, &tptr, 10); if (cptr == tptr) return SCPE_ARG; + sim_throt_sleep_time = sim_idle_rate_ms; c = toupper (*tptr++); - if (*tptr != 0) + if (c == '/') + val2 = strtotv (tptr, &tptr, 10); + if ((*tptr != 0) || (val == 0)) return SCPE_ARG; if (c == 'M') sim_throt_type = SIM_THROT_MCYC; @@ -662,6 +704,9 @@ else { sim_throt_type = SIM_THROT_KCYC; else if ((c == '%') && (val > 0) && (val < 100)) sim_throt_type = SIM_THROT_PCT; + else if ((c == '/') && (val2 != 0)) { + sim_throt_type = SIM_THROT_SPC; + } else return SCPE_ARG; if (sim_idle_enab) { printf ("Idling disabled\n"); @@ -670,6 +715,14 @@ else { sim_clr_idle (NULL, 0, NULL, NULL); } sim_throt_val = (uint32) val; + if (sim_throt_type == SIM_THROT_SPC) { + if (val2 >= sim_idle_rate_ms) + sim_throt_sleep_time = (uint32) val2; + else { + sim_throt_sleep_time = (uint32) (val2 * sim_idle_rate_ms); + sim_throt_val = (uint32) (val * sim_idle_rate_ms); + } + } } return SCPE_OK; } @@ -693,13 +746,17 @@ else { fprintf (st, "Throttle = %d%%\n", sim_throt_val); break; + case SIM_THROT_SPC: + fprintf (st, "Throttle = %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val); + break; + default: fprintf (st, "Throttling disabled\n"); break; } if (sim_switches & SWMASK ('D')) { - fprintf (st, "Wait rate = %d ms\n", sim_idle_rate_ms); + fprintf (st, "minimum sleep resolution = %d ms\n", sim_idle_rate_ms); if (sim_throt_type != 0) fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait); } @@ -712,7 +769,6 @@ void sim_throt_sched (void) sim_throt_state = 0; if (sim_throt_type) sim_activate (&sim_throt_unit, SIM_THROT_WINIT); -return; } void sim_throt_cancel (void) @@ -722,11 +778,12 @@ sim_cancel (&sim_throt_unit); /* Throttle service - Throttle service has three distinct states + Throttle service has three distinct states used while dynamically + determining a throttling interval: - 0 take initial measurement - 1 take final measurement, calculate wait values - 2 periodic waits to slow down the CPU + 0 take initial measurement + 1 take final measurement, calculate wait values + 2 periodic waits to slow down the CPU */ t_stat sim_throt_svc (UNIT *uptr) @@ -734,12 +791,16 @@ t_stat sim_throt_svc (UNIT *uptr) uint32 delta_ms; double a_cps, d_cps; +if (sim_throt_type == SIM_THROT_SPC) { /* Non dynamic? */ + sim_throt_state = 2; /* force state */ + sim_throt_wait = sim_throt_val; + } switch (sim_throt_state) { case 0: /* take initial reading */ sim_throt_ms_start = sim_os_msec (); sim_throt_wait = SIM_THROT_WST; - sim_throt_state++; /* next state */ + sim_throt_state = 1; /* next state */ break; /* reschedule */ case 1: /* take final reading */ @@ -761,24 +822,32 @@ switch (sim_throt_state) { d_cps = (double) sim_throt_val * 1000.0; else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; if (d_cps >= a_cps) { - sim_throt_state = 0; + sim_throt_sched (); /* start over */ return SCPE_OK; } sim_throt_wait = (int32) /* time between waits */ ((a_cps * d_cps * ((double) sim_idle_rate_ms)) / (1000.0 * (a_cps - d_cps))); if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */ - sim_throt_state = 0; + sim_throt_sched (); /* start over */ return SCPE_OK; } - sim_throt_state++; + sim_throt_ms_start = sim_throt_ms_stop; + sim_throt_state = 2; // fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n", // a_cps, d_cps, sim_throt_wait); } break; case 2: /* throttling */ - sim_os_ms_sleep (1); + sim_os_ms_sleep (sim_throt_sleep_time); + delta_ms = sim_os_msec () - sim_throt_ms_start; + if ((sim_throt_type != SIM_THROT_SPC) && /* when dynamic throttling */ + (delta_ms >= 10000)) { /* recompute every 10 sec */ + sim_throt_ms_start = sim_os_msec (); + sim_throt_wait = SIM_THROT_WST; + sim_throt_state = 1; /* next state */ + } break; } diff --git a/sim_timer.h b/sim_timer.h index 8447cbdd..56896f92 100644 --- a/sim_timer.h +++ b/sim_timer.h @@ -31,6 +31,26 @@ #ifndef _SIM_TIMER_H_ #define _SIM_TIMER_H_ 0 +#include + +#if defined (__APPLE__) +#define HAVE_STRUCT_TIMESPEC 1 /* OSX defined the structure but doesn't tell us */ +#endif + +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 1 +#define NEED_CLOCK_GETTIME 1 +#ifndef HAVE_STRUCT_TIMESPEC +#define HAVE_STRUCT_TIMESPEC 1 +struct timespec { + long tv_sec; + long tv_nsec; +}; +#endif /* HAVE_STRUCT_TIMESPEC */ +int clock_gettime(int clock_id, struct timespec *tp); +#endif + + #define SIM_NTIMERS 8 /* # timers */ #define SIM_TMAX 500 /* max timer makeup */ @@ -46,11 +66,13 @@ #define SIM_THROT_WMIN 100 /* min wait */ #define SIM_THROT_MSMIN 10 /* min for measurement */ #define SIM_THROT_NONE 0 /* throttle parameters */ -#define SIM_THROT_MCYC 1 -#define SIM_THROT_KCYC 2 -#define SIM_THROT_PCT 3 +#define SIM_THROT_MCYC 1 /* MegaCycles Per Sec */ +#define SIM_THROT_KCYC 2 /* KiloCycles Per Sec */ +#define SIM_THROT_PCT 3 /* Max Percent of host CPU */ +#define SIM_THROT_SPC 4 /* Specific periodic Delay */ t_bool sim_timer_init (void); +void sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub); int32 sim_rtcn_init (int32 time, int32 tmr); void sim_rtcn_init_all (void); int32 sim_rtcn_calb (int32 ticksper, int32 tmr);