summaryrefslogtreecommitdiff
path: root/emulators
diff options
context:
space:
mode:
authorthorpej <thorpej@pkgsrc.org>2020-10-07 00:43:04 +0000
committerthorpej <thorpej@pkgsrc.org>2020-10-07 00:43:04 +0000
commit250543d6af49908900257509450a16d14f01080d (patch)
treeeb2f78adf238507f2b8a643df106d18de95f8a39 /emulators
parente001d5288290ef4c0a9031f501e75d6497568da0 (diff)
downloadpkgsrc-250543d6af49908900257509450a16d14f01080d.tar.gz
- Add tap(4) networking support to gxemul. See the networking documentation
for it's theory of operation in gxemul and how to configure it. - Add address filtering to the LANCE and Tulip emulation. These changes have already been upstreamed in the main gxemul repository. Bump package version to gxemul-0.6.2nb1.
Diffstat (limited to 'emulators')
-rw-r--r--emulators/gxemul/Makefile3
-rw-r--r--emulators/gxemul/distinfo17
-rw-r--r--emulators/gxemul/patches/patch-doc_networking.html120
-rw-r--r--emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc890
-rw-r--r--emulators/gxemul/patches/patch-src_devices_dev_ether.cc74
-rw-r--r--emulators/gxemul/patches/patch-src_devices_dev_le.cc232
-rw-r--r--emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc51
-rw-r--r--emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc69
-rw-r--r--emulators/gxemul/patches/patch-src_devices_dev_sn.cc40
-rw-r--r--emulators/gxemul/patches/patch-src_include_net.h107
-rw-r--r--emulators/gxemul/patches/patch-src_net_Makefile.skel15
-rw-r--r--emulators/gxemul/patches/patch-src_net_net.cc318
-rw-r--r--emulators/gxemul/patches/patch-src_net_net_ether.cc150
-rw-r--r--emulators/gxemul/patches/patch-src_net_net_ip.cc238
-rw-r--r--emulators/gxemul/patches/patch-src_net_net_tap.cc192
-rw-r--r--emulators/gxemul/patches/patch-src_old_main_emul.cc14
-rw-r--r--emulators/gxemul/patches/patch-src_old_main_emul_parse.cc38
17 files changed, 2566 insertions, 2 deletions
diff --git a/emulators/gxemul/Makefile b/emulators/gxemul/Makefile
index b3341ec46a9..aaadbd95624 100644
--- a/emulators/gxemul/Makefile
+++ b/emulators/gxemul/Makefile
@@ -1,6 +1,7 @@
-# $NetBSD: Makefile,v 1.67 2019/08/23 13:13:49 ryoon Exp $
+# $NetBSD: Makefile,v 1.68 2020/10/07 00:43:04 thorpej Exp $
DISTNAME= gxemul-0.6.2
+PKGREVISION= 1
CATEGORIES= emulators
MASTER_SITES= ${MASTER_SITE_SOURCEFORGE:=gxemul/}
diff --git a/emulators/gxemul/distinfo b/emulators/gxemul/distinfo
index 9c416df4b05..48b59e47891 100644
--- a/emulators/gxemul/distinfo
+++ b/emulators/gxemul/distinfo
@@ -1,6 +1,21 @@
-$NetBSD: distinfo,v 1.60 2019/08/23 13:13:49 ryoon Exp $
+$NetBSD: distinfo,v 1.61 2020/10/07 00:43:04 thorpej Exp $
SHA1 (gxemul-0.6.2.tar.gz) = aabaeba783e70be952ab0056bf84d0f2b70c2155
RMD160 (gxemul-0.6.2.tar.gz) = ccac73d82446f89792b1fc803bee623813f3aab2
SHA512 (gxemul-0.6.2.tar.gz) = 4f389c509f9ecf39603ceed50e899e2bee285d3fefac9b3214076115ee71b5a7a68d1d92690b6debc8de5cf5f0303da83b3cc921a5c0b5eb4c7ad89baa730b59
Size (gxemul-0.6.2.tar.gz) = 5897883 bytes
+SHA1 (patch-doc_networking.html) = dd7a1519a678196fd5a835317a32ba483630ece8
+SHA1 (patch-src_devices_dev_dec21143.cc) = 52f36741038c76a2dbafc7da6737e816aed5c9f9
+SHA1 (patch-src_devices_dev_ether.cc) = 00221e09530743e81faedcc75ee951fa853d0e2c
+SHA1 (patch-src_devices_dev_le.cc) = a728e8008a7a9f33aaf95811a33ebac2cb86e80e
+SHA1 (patch-src_devices_dev_rtl8139c.cc) = ee6dbba7c7c9c62c50493c476297ee5ac89d2b83
+SHA1 (patch-src_devices_dev_sgi_mec.cc) = 24b1259350faf60265df7958f0f680302f475e8e
+SHA1 (patch-src_devices_dev_sn.cc) = e939521be1630f51e7ddc67abe90980de38e8837
+SHA1 (patch-src_include_net.h) = 4d31fcefe384fcc9d68255825240c89b45acc92e
+SHA1 (patch-src_net_Makefile.skel) = 4738229a928b9cb5a2531dfc357297f91e9fdc09
+SHA1 (patch-src_net_net.cc) = 57397c9a8197ee25e7faa8c0733273014e3e0670
+SHA1 (patch-src_net_net_ether.cc) = ef7464dbb0812a9cb8d5be806db07cc19853fc1e
+SHA1 (patch-src_net_net_ip.cc) = f5615f3b347e9bdcd256fa4b5b1594473fd2e5e4
+SHA1 (patch-src_net_net_tap.cc) = f913b3efb51bc4a8080420988d5fc845e8a38f73
+SHA1 (patch-src_old_main_emul.cc) = 0b1106745e7c5d320e93f9f7775d8ced6109c089
+SHA1 (patch-src_old_main_emul_parse.cc) = 23048bc3a0a83fd189b3bbd4656ef0e1a2c23b99
diff --git a/emulators/gxemul/patches/patch-doc_networking.html b/emulators/gxemul/patches/patch-doc_networking.html
new file mode 100644
index 00000000000..c19f221cb98
--- /dev/null
+++ b/emulators/gxemul/patches/patch-doc_networking.html
@@ -0,0 +1,120 @@
+$NetBSD: patch-doc_networking.html,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Document tap(4)-based networking.
+
+--- doc/networking.html.orig 2020-10-05 22:53:41.969487340 +0000
++++ doc/networking.html 2020-10-05 22:54:17.309695478 +0000
+@@ -46,6 +46,7 @@ SUCH DAMAGE.
+ <p><br>
+ <ul>
+ <li><a href="#intro">Introduction</a>
++ <li><a href="#tap">Virtual Ethernet switch using a tap device</a>
+ <li><a href="#multihost">Network across multiple hosts</a>
+ <li><a href="#direct_example_1">Direct-access example 1: udp_snoop</a>
+ </ul>
+@@ -60,9 +61,11 @@ SUCH DAMAGE.
+ <a name="intro"></a>
+ <h3>Introduction:</h3>
+
+-<p>GXemul's current networking layer supports two modes:
++<p>GXemul's current networking layer supports three modes:
+
+ <p><ol>
++ <li>A vitual Ethernet switch built on top of a <i>tap</i> device.
++ <p>
+ <li>A NAT-like layer, which allows guest OSes to access the outside
+ internet world (IPv4 only, so far). When only one machine is being
+ emulated, the following default values apply to the guest OS:<pre>
+@@ -78,7 +81,7 @@ SUCH DAMAGE.
+ ethernet packages from/to the emulator.
+ </ol>
+
+-<p><i>NOTE:</i> Both these modes have problems. The NAT-like layer is very
++<p><i>NOTE:</i> The latter two modes have problems. The NAT-like layer is very
+ "hackish" and was only meant as a proof-of-concept, to see if networking
+ like this would work with e.g. NetBSD as a guest OS. (If you are
+ interested in the technical details, and the reasons why NAT networking is
+@@ -120,6 +123,83 @@ href="machine_decstation.html#netbsdpmax
+
+
+
++<p><br>
++<a name="tap"></a>
++<h3>Virtual Ethernet switch using a <i>tap</i> device:</h3>
++
++<p>The simplest way to emulate a real Ethernet network is using a <i>tap</i>
++device. In this mode, the emulator disables the simulated NAT and
++direct-access machinery and internally treats all emulated NICs as if
++they are on a single Ethernet switch. In this mode, packets destined for
++the guest's specific MAC address as well as Ethernet multicast and broadcast
++packets are send to the individual guest instances. Individual NIC
++emulations may also apply their own multicast filtering; multcast filtering
++is implemented for the DEC 21143 and Lance NICs.
++
++<p>The <i>tap</i> interface on the host can be thought of as an upstream
++link on the virtual Ethernet switch. In addition to providing a "port"
++for the host, the <i>tap</i> interface can be bridged to a physical Ethernet
++port on the host, allowing the guests to access the host's connected LAN.
++
++<p>Networking services such as DHCP and DNS must be provided either by
++the host or by the host's connected LAN.
++
++<p>Support for the <i>tap</i> device was developed on NetBSD, but should
++also work on FreeBSD, OpenBSD, and Linux hosts.
++
++<p>Here is a simple example:
++
++<p><pre>
++<font color="#2020cf">! Configuration file for
++! virtual Ethernet switch networking
++! using a tap device.</font>
++
++<b>net(</b>
++ <b>tapdev(</b><font color="#ff003f">"/dev/tap0"</font><b>)</b>
++<b>)</b>
++<b>machine(</b>
++ <b>name(<font color="#ff003f">"guest machine"</font>)</b>
++
++ <b>type(<font color="#ff003f">"dec"</font>)</b>
++ <b>subtype(<font color="#ff003f">"5000/200"</font>)</b>
++
++ <font color="#2020cf">! Add a disk, etc.</font>
++<b>)</b>
++</pre>
++
++<p>Before starting the emulator, you will need to create the <i>tap</i>
++interface on the host. Here is an example for NetBSD:
++
++<p><pre>
++<b>#ifconfig tap0 create up</b>
++</pre>
++
++<p>If you wish to simply network the host and the guests together, then
++simply assign an IP address to the <i>tap</i> interface on the host:
++
++<p><pre>
++<b># ifconfig tap0 10.0.0.254</b>
++</pre>
++
++<p>You can now run a DHCP server on the host for the guests, or you can
++configure the guests manually.
++
++<p>If instead you would like to bridge to the host's connected LAN,
++Here is an example for NetBSD:
++
++<p><pre>
++<b># ifconfig bridge0 create up</b>
++<b># brconfig add tap0 add wm0</b>
++</pre>
++
++<p>Although it <i>is</i> possible to have more than one machine per
++configuration file, I strongly recommend against it. Please use one
++configuration file for one emulated machine. Each configuration file
++must have a unique <i>tap</i> instance, and machines in separate
++configuration files must use bridged <i>tap</i> devices if they wish
++to communicate with each other as if on the same LAN.
++
++
+
+ <p><br>
+ <a name="multihost"></a>
diff --git a/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc b/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc
new file mode 100644
index 00000000000..bd57987f55d
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc
@@ -0,0 +1,890 @@
+$NetBSD: patch-src_devices_dev_dec21143.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+- Add support for tap(4)-based networking.
+- Process the setup packet, and implement all of the Tulip receive
+ filtering modes.
+- Add ugly hack to deal with PCI vs. CPU views of main memory differences
+ between systems.
+
+--- src/devices/dev_dec21143.cc.orig 2020-10-05 22:54:55.903897678 +0000
++++ src/devices/dev_dec21143.cc 2020-10-05 22:55:15.711704852 +0000
+@@ -45,8 +45,6 @@
+ * o) Handle _writes_ to MII registers.
+ * o) Make it work with modern Linux kernels (as a guest OS).
+ * o) Endianness for descriptors? If necessary.
+- * o) Actually handle the "Setup" packet.
+- * o) MAC filtering on incoming packets.
+ * o) Don't hardcode as many values.
+ */
+
+@@ -76,16 +74,15 @@
+ #define ROM_WIDTH 6
+
+ struct dec21143_data {
++ /* NIC common data */
++ struct nic_data nic;
++
+ struct interrupt irq;
+ int irq_was_asserted;
+
+ /* PCI: */
+ int pci_little_endian;
+
+- /* Ethernet address, and a network which we are connected to: */
+- uint8_t mac[6];
+- struct net *net;
+-
+ /* SROM emulation: */
+ uint8_t srom[1 << (ROM_WIDTH + 1)];
+ int srom_curbit;
+@@ -105,19 +102,66 @@ struct dec21143_data {
+ uint32_t reg[N_REGS];
+
+ /* Internal TX state: */
+- uint64_t cur_tx_addr;
++ uint32_t cur_tx_addr;
+ unsigned char *cur_tx_buf;
+ int cur_tx_buf_len;
+ int tx_idling;
+ int tx_idling_threshold;
+
+ /* Internal RX state: */
+- uint64_t cur_rx_addr;
++ uint32_t cur_rx_addr;
+ unsigned char *cur_rx_buf;
+ int cur_rx_buf_len;
+ int cur_rx_offset;
++
++ /*
++ * Receive filter information. We keep our own copy of
++ * the promiscuous flag because to implement some of the
++ * filtering modes, we need to tell the network layer that
++ * we want all packets.
++ */
++ int (*drop_packet)(struct net *, struct dec21143_data *);
++ int allmulti;
++ int promiscuous;
++ int filter_needs_promiscuous;
++ uint8_t perfect_filter[6 * TULIP_MAXADDRS];
++
++ /* Only 16 bits are used per filter word. */
++#define MCHASH_NWORDS (TULIP_MCHASHSIZE / 16)
++ uint32_t hash_filter[MCHASH_NWORDS];
++ int hash_filter_saturated;
++
++ /*
++ * XXX XXX XXX
++ * XXX UGLY HACK. Need a proper way to deal with
++ * XXX different PCI vs. CPU views of RAM.
++ * XXX XXX XXX
++ */
++ uint32_t xxx_dma_to_phys_mask;
+ };
+
++/* XXX This is an UGLY hack. */
++static uint64_t dma_to_phys(const struct dec21143_data *d, uint32_t dma_addr)
++{
++ return dma_addr & d->xxx_dma_to_phys_mask;
++}
++
++
++static inline uint32_t load_le32(const uint8_t *buf)
++{
++ return buf[0] | ((uint32_t)buf[1] << 8) |
++ ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24);
++}
++
++
++static inline void store_le32(uint8_t *buf, uint32_t val)
++{
++ buf[0] = (uint8_t)val;
++ buf[1] = (uint8_t)(val >> 8);
++ buf[2] = (uint8_t)(val >> 16);
++ buf[3] = (uint8_t)(val >> 24);
++}
++
+
+ /* Internal states during MII data stream decode: */
+ #define MII_STATE_RESET 0
+@@ -130,6 +174,171 @@ struct dec21143_data {
+
+
+ /*
++ * The 21143 has multiple address matching modes:
++ *
++ * - Perfect Filtering: The chip interprets the descriptor buffer
++ * as a table of 16 MAC addresses that it should match. The
++ * station address and broadcast must be included in the list.
++ *
++ * - Hash Perfect Filtering: The chip interprets the descriptor buffer
++ * as a 512-bit hash table plus one perfect filter match. Multicast
++ * addresses only are matched against the hash table.
++ *
++ * - Inverse Filtering: Like Perfect Filtering, but the table is
++ * addresses NOT to match.
++ *
++ * - Hash-only Filtering: Like Hash Perfect, except without the Perfect.
++ * All addresses are matched against the hash table.
++ *
++ * The mode seleted by the TDCTL descriptor field is reflected in 3
++ * read-only bits in the OPMODE register.
++ *
++ * We implement all 4 (NetBSD, at least, is known to use Perfect and
++ * Hash Perfect on the 21143; it also uses Hash-only on the 21140).
++ */
++
++#define TDCTL_Tx_FT_MASK (TDCTL_Tx_FT1|TDCTL_Tx_FT0)
++
++#define dec21143_mchash(addr) \
++ (net_ether_crc32_le((addr), 6) & (TULIP_MCHASHSIZE - 1))
++
++static int dec21143_drop_packet_perfect(struct net *net,
++ struct dec21143_data *d)
++{
++ int i;
++
++ for (i = 0; i < TULIP_MAXADDRS; i++) {
++ if (net_ether_eq(d->cur_rx_buf, &d->perfect_filter[6 * i])) {
++ /* Match! */
++ return 0;
++ }
++ }
++
++ return 1;
++}
++
++static int dec21143_drop_packet_hashperfect(struct net *net,
++ struct dec21143_data *d)
++{
++
++ /*
++ * In this mode, we have the network layer match our station
++ * address, and we reflect the true promiscuous status there
++ * as well. This means that if it's not a multicast address,
++ * then it's already been sufficiently matched.
++ */
++ if (! net_ether_multicast(d->cur_rx_buf))
++ return 0;
++
++ /*
++ * Note that the broadcast address is also checked against
++ * the hash table in this mode!
++ */
++
++ const uint32_t hash = dec21143_mchash(d->cur_rx_buf);
++ if (d->hash_filter[hash >> 4] & (1U << (hash & 0xf))) {
++ /* Match! */
++ return 0;
++ }
++
++ return 1;
++}
++
++static int dec21143_drop_packet_inverse(struct net *net,
++ struct dec21143_data *d)
++{
++ return !dec21143_drop_packet_perfect(net, d);
++}
++
++static int dec21143_drop_packet_hashonly(struct net *net,
++ struct dec21143_data *d)
++{
++ const uint32_t hash = dec21143_mchash(d->cur_rx_buf);
++ if (d->hash_filter[hash >> 4] & (1U << (hash & 0xf))) {
++ /* Match! */
++ return 0;
++ }
++
++ return 1;
++}
++
++
++/*
++ * dec21143_rx_drop_packet():
++ *
++ * Implement the logic to determine if we should drop a packet
++ * before paassing it to the guest. Returns 1 if the packet
++ * was dropped.
++ */
++static int dec21143_rx_drop_packet(struct net *net, struct dec21143_data *d)
++{
++ /* Only implement filtering if using a tap device. */
++ if (net->tapdev == NULL)
++ return 0;
++
++ /*
++ * We might have told the network layer that we're promiscuous
++ * due to the chosen filtering mode, so check the truth here.
++ */
++ if (d->promiscuous)
++ return 0;
++
++ /*
++ * If the guest wants all multicast (either all the bits are
++ * set or the OPMODE_PM bit is set), then check to see if we
++ * can short-circuit the checks.
++ */
++ if (d->allmulti && net_ether_multicast(d->cur_rx_buf))
++ return 0;
++
++ /*
++ * Note that if we haven't gotten a setup packet yet, then
++ * d->drop_packet will be NULL. If this happens, we always
++ * drop. This is akin to the real hardware defaulting to
++ * Perfect filtering mode but not having any valid addresses
++ * in the list to match against.
++ */
++ if (d->drop_packet == NULL || d->drop_packet(net, d)) {
++ /* Not for us; drop the packet. */
++ free(d->cur_rx_buf);
++ d->cur_rx_buf = NULL;
++ d->cur_rx_buf_len = 0;
++ return 1;
++ }
++
++ return 0;
++}
++
++
++/*
++ * dec21143_update_rx_mode():
++ *
++ * Update promiscuous / allmulti indicators based on OPMODE
++ * and filter state.
++ */
++static void dec21143_update_rx_mode(struct dec21143_data *d)
++{
++ int opmode_pr = (d->reg[CSR_OPMODE / 8] & OPMODE_PR) != 0;
++ int opmode_pm = (d->reg[CSR_OPMODE / 8] & OPMODE_PM) != 0;
++
++ debug("[ dec21143 rx mode: opmode_pr = %d ]\n",
++ opmode_pr);
++ debug("[ dec21143 rx mode: filter_needs_promiscuous = %d ]\n",
++ d->filter_needs_promiscuous);
++ debug("[ dec21143 rx mode: opmode_pm = %d ]\n",
++ opmode_pm);
++ debug("[ dec21143 rx mode: filter_saturated = %d ]\n",
++ d->hash_filter_saturated);
++
++ d->promiscuous = opmode_pr;
++ d->nic.promiscuous_mode =
++ d->promiscuous || d->filter_needs_promiscuous;
++
++ d->allmulti = opmode_pm || d->hash_filter_saturated;
++}
++
++
++/*
+ * dec21143_rx():
+ *
+ * Receive a packet. (If there is no current packet, then check for newly
+@@ -138,20 +347,23 @@ struct dec21143_data {
+ */
+ int dec21143_rx(struct cpu *cpu, struct dec21143_data *d)
+ {
+- uint64_t addr = d->cur_rx_addr, bufaddr;
++ uint32_t addr = d->cur_rx_addr, bufaddr;
+ unsigned char descr[16];
+ uint32_t rdes0, rdes1, rdes2, rdes3;
+ int bufsize, buf1_size, buf2_size, i, writeback_len = 4, to_xfer;
+
+ /* No current packet? Then check for new ones. */
+- if (d->cur_rx_buf == NULL) {
++ while (d->cur_rx_buf == NULL) {
+ /* Nothing available? Then abort. */
+- if (!net_ethernet_rx_avail(d->net, d))
++ if (!net_ethernet_rx_avail(d->nic.net, &d->nic))
+ return 0;
+
+ /* Get the next packet into our buffer: */
+- net_ethernet_rx(d->net, d, &d->cur_rx_buf,
+- &d->cur_rx_buf_len);
++ net_ethernet_rx(d->nic.net, &d->nic,
++ &d->cur_rx_buf, &d->cur_rx_buf_len);
++
++ if (dec21143_rx_drop_packet(d->nic.net, d))
++ continue;
+
+ /* Append a 4 byte CRC: */
+ d->cur_rx_buf_len += 4;
+@@ -165,15 +377,14 @@ int dec21143_rx(struct cpu *cpu, struct
+ }
+
+ /* fatal("{ dec21143_rx: base = 0x%08x }\n", (int)addr); */
+- addr &= 0x7fffffff;
+
+- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t),
+- MEM_READ, PHYSICAL | NO_EXCEPTIONS)) {
++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr),
++ descr, sizeof(uint32_t), MEM_READ, PHYSICAL | NO_EXCEPTIONS)) {
+ fatal("[ dec21143_rx: memory_rw failed! ]\n");
+ return 0;
+ }
+
+- rdes0 = descr[0] + (descr[1]<<8) + (descr[2]<<16) + (descr[3]<<24);
++ rdes0 = load_le32(&descr[0]);
+
+ /* Only use descriptors owned by the 21143: */
+ if (!(rdes0 & TDSTAT_OWN)) {
+@@ -181,16 +392,17 @@ int dec21143_rx(struct cpu *cpu, struct
+ return 0;
+ }
+
+- if (!cpu->memory_rw(cpu, cpu->mem, addr + sizeof(uint32_t), descr +
+- sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ, PHYSICAL |
+- NO_EXCEPTIONS)) {
++ if (!cpu->memory_rw(cpu, cpu->mem,
++ dma_to_phys(d, addr + sizeof(uint32_t)),
++ descr + sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ,
++ PHYSICAL | NO_EXCEPTIONS)) {
+ fatal("[ dec21143_rx: memory_rw failed! ]\n");
+ return 0;
+ }
+
+- rdes1 = descr[4] + (descr[5]<<8) + (descr[6]<<16) + (descr[7]<<24);
+- rdes2 = descr[8] + (descr[9]<<8) + (descr[10]<<16) + (descr[11]<<24);
+- rdes3 = descr[12] + (descr[13]<<8) + (descr[14]<<16) + (descr[15]<<24);
++ rdes1 = load_le32(&descr[4]);
++ rdes2 = load_le32(&descr[8]);
++ rdes3 = load_le32(&descr[12]);
+
+ buf1_size = rdes1 & TDCTL_SIZE1;
+ buf2_size = (rdes1 & TDCTL_SIZE2) >> TDCTL_SIZE2_SHIFT;
+@@ -210,7 +422,6 @@ int dec21143_rx(struct cpu *cpu, struct
+
+ debug("{ RX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n",
+ (long long)addr, rdes0, rdes1, rdes2, rdes3, bufsize, (int)bufaddr);
+- bufaddr &= 0x7fffffff;
+
+ /* Turn off all status bits, and give up ownership: */
+ rdes0 = 0x00000000;
+@@ -221,7 +432,7 @@ int dec21143_rx(struct cpu *cpu, struct
+
+ /* DMA bytes from the packet into emulated physical memory: */
+ for (i=0; i<to_xfer; i++) {
+- cpu->memory_rw(cpu, cpu->mem, bufaddr + i,
++ cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, bufaddr + i),
+ d->cur_rx_buf + d->cur_rx_offset + i, 1, MEM_WRITE,
+ PHYSICAL | NO_EXCEPTIONS);
+ /* fatal(" %02x", d->cur_rx_buf[d->cur_rx_offset + i]); */
+@@ -253,19 +464,16 @@ int dec21143_rx(struct cpu *cpu, struct
+ }
+
+ /* Descriptor writeback: */
+- descr[ 0] = rdes0; descr[ 1] = rdes0 >> 8;
+- descr[ 2] = rdes0 >> 16; descr[ 3] = rdes0 >> 24;
++ store_le32(&descr[0], rdes0);
+ if (writeback_len > 1) {
+- descr[ 4] = rdes1; descr[ 5] = rdes1 >> 8;
+- descr[ 6] = rdes1 >> 16; descr[ 7] = rdes1 >> 24;
+- descr[ 8] = rdes2; descr[ 9] = rdes2 >> 8;
+- descr[10] = rdes2 >> 16; descr[11] = rdes2 >> 24;
+- descr[12] = rdes3; descr[13] = rdes3 >> 8;
+- descr[14] = rdes3 >> 16; descr[15] = rdes3 >> 24;
++ store_le32(&descr[4], rdes1);
++ store_le32(&descr[8], rdes2);
++ store_le32(&descr[12], rdes3);
+ }
+
+- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t)
+- * writeback_len, MEM_WRITE, PHYSICAL | NO_EXCEPTIONS)) {
++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr), descr,
++ sizeof(uint32_t) * writeback_len, MEM_WRITE,
++ PHYSICAL | NO_EXCEPTIONS)) {
+ fatal("[ dec21143_rx: memory_rw failed! ]\n");
+ return 0;
+ }
+@@ -275,6 +483,178 @@ int dec21143_rx(struct cpu *cpu, struct
+
+
+ /*
++ * dec21143_setup_copy_enaddr():
++ *
++ * Copy an Ethernet address out of the setup packet.
++ */
++static void dec21143_setup_copy_enaddr(uint8_t *enaddr,
++ const uint32_t *setup_packet)
++{
++ int i;
++
++ for (i = 0; i < 3; i++) {
++ enaddr[i*2 ] = (uint8_t)setup_packet[i];
++ enaddr[i*2 + 1] = (uint8_t)(setup_packet[i] >> 8);
++ }
++}
++
++
++/*
++ * dec21143_setup_perfect():
++ *
++ * Setup perfect filtering mode.
++ */
++static void dec21143_setup_perfect(struct dec21143_data *d,
++ const uint32_t *setup_packet)
++{
++ int i;
++
++ for (i = 0; i < TULIP_MAXADDRS; i++) {
++ dec21143_setup_copy_enaddr(&d->perfect_filter[i * 6],
++ &setup_packet[i * 3]);
++ debug("dec21143 PERFECT[%d] %02x:%02x:%02x:%02x:%02x:%02x\n",
++ i,
++ d->perfect_filter[i*6 + 0],
++ d->perfect_filter[i*6 + 1],
++ d->perfect_filter[i*6 + 2],
++ d->perfect_filter[i*6 + 3],
++ d->perfect_filter[i*6 + 4],
++ d->perfect_filter[i*6 + 5]);
++ }
++
++ d->drop_packet = dec21143_drop_packet_perfect;
++}
++
++
++/*
++ * dec21143_setup_hashperfect():
++ *
++ * Setup hash-perfect filtering mode.
++ */
++static void dec21143_setup_hashperfect(struct dec21143_data *d,
++ const uint32_t *setup_packet)
++{
++ int i;
++
++ debug("dec21143 HASHPERFECT:");
++ for (i = 0; i < MCHASH_NWORDS; i++) {
++ if ((i % 8) == 0)
++ debug("\n\t");
++ debug(" %04x", setup_packet[i]);
++ d->hash_filter[i] = setup_packet[i];
++ d->hash_filter_saturated |= (d->hash_filter[i] == 0xffff);
++ }
++ debug("\n");
++
++ dec21143_setup_copy_enaddr(d->nic.mac_address, &setup_packet[39]);
++ debug("dec21143 HASHPERFECT %02x:%02x:%02x:%02x:%02x:%02x\n",
++ d->nic.mac_address[0],
++ d->nic.mac_address[1],
++ d->nic.mac_address[2],
++ d->nic.mac_address[3],
++ d->nic.mac_address[4],
++ d->nic.mac_address[5]);
++
++ d->filter_needs_promiscuous = 0;
++ d->drop_packet = dec21143_drop_packet_hashperfect;
++}
++
++
++/*
++ * dec21143_setup_inverse():
++ *
++ * Setup inverse filtering mode.
++ */
++static void dec21143_setup_inverse(struct dec21143_data *d,
++ const uint32_t *setup_packet)
++{
++ dec21143_setup_perfect(d, setup_packet);
++ debug("dec21143 INVERSE ^^^^\n");
++ d->drop_packet = dec21143_drop_packet_inverse;
++}
++
++
++/*
++ * dec21143_setup_hashonly():
++ *
++ * Setup hash-only filtering mode.
++ */
++static void dec21143_setup_hashonly(struct dec21143_data *d,
++ const uint32_t *setup_packet)
++{
++ int i;
++
++ debug("dec21143 HASHONLY:");
++ for (i = 0; i < MCHASH_NWORDS; i++) {
++ if ((i % 8) == 0)
++ fatal("\n\t");
++ debug(" %04x", setup_packet[i]);
++ d->hash_filter[i] = setup_packet[i];
++ d->hash_filter_saturated |= (d->hash_filter[i] == 0xffff);
++ }
++ debug("\n");
++
++ d->drop_packet = dec21143_drop_packet_hashonly;
++}
++
++
++/*
++ * dec21143_process_setup_packet():
++ *
++ * Process the address filter setup packet.
++ */
++static void dec21143_process_setup_packet(struct cpu *cpu,
++ struct dec21143_data *d, uint32_t tdctl, uint32_t bufaddr)
++{
++ uint32_t setup_packet[TULIP_SETUP_PACKET_LEN / sizeof(uint32_t)];
++ uint8_t *cp = (uint8_t *)setup_packet;
++ uint32_t tmp;
++ int i;
++
++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, bufaddr), cp,
++ TULIP_SETUP_PACKET_LEN, MEM_READ, PHYSICAL | NO_EXCEPTIONS)) {
++ fatal("[ dec21143_process_setup_packet: memory_rw failed! ]\n");
++ return;
++ }
++
++ /* Ensure host order of each word. */
++ for (i = 0; i < TULIP_SETUP_PACKET_LEN; i += sizeof(uint32_t)) {
++ tmp = load_le32(&cp[i]);
++ setup_packet[i / sizeof(uint32_t)] = tmp;
++ }
++
++ /* Defaults. */
++ d->hash_filter_saturated = 0;
++ d->filter_needs_promiscuous = 1;
++
++ d->reg[CSR_OPMODE / 8] &= ~(OPMODE_HP | OPMODE_HO | OPMODE_IF);
++
++ switch (tdctl & TDCTL_Tx_FT_MASK) {
++ case TDCTL_Tx_FT_PERFECT:
++ dec21143_setup_perfect(d, setup_packet);
++ break;
++
++ case TDCTL_Tx_FT_HASH:
++ dec21143_setup_hashperfect(d, setup_packet);
++ d->reg[CSR_OPMODE / 8] |= OPMODE_HP;
++ break;
++
++ case TDCTL_Tx_FT_INVERSE:
++ dec21143_setup_inverse(d, setup_packet);
++ d->reg[CSR_OPMODE / 8] |= OPMODE_IF;
++ break;
++
++ case TDCTL_Tx_FT_HASHONLY:
++ dec21143_setup_hashonly(d, setup_packet);
++ d->reg[CSR_OPMODE / 8] |= OPMODE_HO;
++ break;
++ }
++
++ dec21143_update_rx_mode(d);
++}
++
++
++/*
+ * dec21143_tx():
+ *
+ * Transmit a packet, if the guest OS has marked a descriptor as containing
+@@ -282,20 +662,18 @@ int dec21143_rx(struct cpu *cpu, struct
+ */
+ int dec21143_tx(struct cpu *cpu, struct dec21143_data *d)
+ {
+- uint64_t addr = d->cur_tx_addr, bufaddr;
++ uint32_t addr = d->cur_tx_addr, bufaddr;
+ unsigned char descr[16];
+ uint32_t tdes0, tdes1, tdes2, tdes3;
+ int bufsize, buf1_size, buf2_size, i;
+
+- addr &= 0x7fffffff;
+-
+- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t),
+- MEM_READ, PHYSICAL | NO_EXCEPTIONS)) {
++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr), descr,
++ sizeof(uint32_t), MEM_READ, PHYSICAL | NO_EXCEPTIONS)) {
+ fatal("[ dec21143_tx: memory_rw failed! ]\n");
+ return 0;
+ }
+
+- tdes0 = descr[0] + (descr[1]<<8) + (descr[2]<<16) + (descr[3]<<24);
++ tdes0 = load_le32(&descr[0]);
+
+ /* fatal("{ dec21143_tx: base=0x%08x, tdes0=0x%08x }\n",
+ (int)addr, (int)tdes0); */
+@@ -310,16 +688,17 @@ int dec21143_tx(struct cpu *cpu, struct
+ return 0;
+ }
+
+- if (!cpu->memory_rw(cpu, cpu->mem, addr + sizeof(uint32_t), descr +
+- sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ, PHYSICAL |
+- NO_EXCEPTIONS)) {
++ if (!cpu->memory_rw(cpu, cpu->mem,
++ dma_to_phys(d, addr + sizeof(uint32_t)),
++ descr + sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ,
++ PHYSICAL | NO_EXCEPTIONS)) {
+ fatal("[ dec21143_tx: memory_rw failed! ]\n");
+ return 0;
+ }
+
+- tdes1 = descr[4] + (descr[5]<<8) + (descr[6]<<16) + (descr[7]<<24);
+- tdes2 = descr[8] + (descr[9]<<8) + (descr[10]<<16) + (descr[11]<<24);
+- tdes3 = descr[12] + (descr[13]<<8) + (descr[14]<<16) + (descr[15]<<24);
++ tdes1 = load_le32(&descr[4]);
++ tdes2 = load_le32(&descr[8]);
++ tdes3 = load_le32(&descr[12]);
+
+ buf1_size = tdes1 & TDCTL_SIZE1;
+ buf2_size = (tdes1 & TDCTL_SIZE2) >> TDCTL_SIZE2_SHIFT;
+@@ -338,10 +717,9 @@ int dec21143_tx(struct cpu *cpu, struct
+ }
+
+ /*
+- fatal("{ TX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n",
+- (long long)addr, tdes0, tdes1, tdes2, tdes3, bufsize, (int)bufaddr);
++ fatal("{ TX (%x): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n",
++ addr, tdes0, tdes1, tdes2, tdes3, bufsize, bufaddr);
+ */
+- bufaddr &= 0x7fffffff;
+
+ /* Assume no error: */
+ tdes0 &= ~ (TDSTAT_Tx_UF | TDSTAT_Tx_EC | TDSTAT_Tx_LC
+@@ -350,13 +728,13 @@ int dec21143_tx(struct cpu *cpu, struct
+ if (tdes1 & TDCTL_Tx_SET) {
+ /*
+ * Setup Packet.
+- *
+- * TODO. For now, just ignore it, and pretend it worked.
+ */
+ /* fatal("{ TX: setup packet }\n"); */
+- if (bufsize != 192)
++ if (bufsize != TULIP_SETUP_PACKET_LEN)
+ fatal("[ dec21143: setup packet len = %i, should be"
+- " 192! ]\n", (int)bufsize);
++ " %d! ]\n", (int)bufsize, TULIP_SETUP_PACKET_LEN);
++ else
++ dec21143_process_setup_packet(cpu, d, tdes1, bufaddr);
+ if (tdes1 & TDCTL_Tx_IC)
+ d->reg[CSR_STATUS/8] |= STATUS_TI;
+ /* New descriptor values, according to the docs: */
+@@ -388,7 +766,8 @@ int dec21143_tx(struct cpu *cpu, struct
+
+ /* "DMA" data from emulated physical memory into the buf: */
+ for (i=0; i<bufsize; i++) {
+- cpu->memory_rw(cpu, cpu->mem, bufaddr + i,
++ cpu->memory_rw(cpu, cpu->mem,
++ dma_to_phys(d, bufaddr + i),
+ d->cur_tx_buf + d->cur_tx_buf_len + i, 1, MEM_READ,
+ PHYSICAL | NO_EXCEPTIONS);
+ /* fatal(" %02x", d->cur_tx_buf[
+@@ -400,9 +779,9 @@ int dec21143_tx(struct cpu *cpu, struct
+ /* Last segment? Then actually transmit it: */
+ if (tdes1 & TDCTL_Tx_LS) {
+ /* fatal("{ TX: data frame complete. }\n"); */
+- if (d->net != NULL) {
+- net_ethernet_tx(d->net, d, d->cur_tx_buf,
+- d->cur_tx_buf_len);
++ if (d->nic.net != NULL) {
++ net_ethernet_tx(d->nic.net, &d->nic,
++ d->cur_tx_buf, d->cur_tx_buf_len);
+ } else {
+ static int warn = 0;
+ if (!warn)
+@@ -430,17 +809,13 @@ int dec21143_tx(struct cpu *cpu, struct
+ tdes0 |= TDSTAT_ES;
+
+ /* Descriptor writeback: */
+- descr[ 0] = tdes0; descr[ 1] = tdes0 >> 8;
+- descr[ 2] = tdes0 >> 16; descr[ 3] = tdes0 >> 24;
+- descr[ 4] = tdes1; descr[ 5] = tdes1 >> 8;
+- descr[ 6] = tdes1 >> 16; descr[ 7] = tdes1 >> 24;
+- descr[ 8] = tdes2; descr[ 9] = tdes2 >> 8;
+- descr[10] = tdes2 >> 16; descr[11] = tdes2 >> 24;
+- descr[12] = tdes3; descr[13] = tdes3 >> 8;
+- descr[14] = tdes3 >> 16; descr[15] = tdes3 >> 24;
++ store_le32(&descr[0], tdes0);
++ store_le32(&descr[4], tdes1);
++ store_le32(&descr[8], tdes2);
++ store_le32(&descr[12], tdes3);
+
+- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t)
+- * 4, MEM_WRITE, PHYSICAL | NO_EXCEPTIONS)) {
++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr), descr,
++ sizeof(uint32_t) * 4, MEM_WRITE, PHYSICAL | NO_EXCEPTIONS)) {
+ fatal("[ dec21143_tx: memory_rw failed! ]\n");
+ return 0;
+ }
+@@ -750,7 +1125,6 @@ static void srom_access(struct cpu *cpu,
+ */
+ static void dec21143_reset(struct cpu *cpu, struct dec21143_data *d)
+ {
+- int leaf;
+
+ if (d->cur_rx_buf != NULL)
+ free(d->cur_rx_buf);
+@@ -759,7 +1133,6 @@ static void dec21143_reset(struct cpu *c
+ d->cur_rx_buf = d->cur_tx_buf = NULL;
+
+ memset(d->reg, 0, sizeof(uint32_t) * N_REGS);
+- memset(d->srom, 0, sizeof(d->srom));
+ memset(d->mii_phy_reg, 0, sizeof(d->mii_phy_reg));
+
+ /* Register values at reset, according to the manual: */
+@@ -772,35 +1145,8 @@ static void dec21143_reset(struct cpu *c
+ d->tx_idling_threshold = 10;
+ d->cur_rx_addr = d->cur_tx_addr = 0;
+
+- /* Version (= 1) and Chip count (= 1): */
+- d->srom[TULIP_ROM_SROM_FORMAT_VERION] = 1;
+- d->srom[TULIP_ROM_CHIP_COUNT] = 1;
+-
+ /* Set the MAC address: */
+- memcpy(d->srom + TULIP_ROM_IEEE_NETWORK_ADDRESS, d->mac, 6);
+-
+- leaf = 30;
+- d->srom[TULIP_ROM_CHIPn_DEVICE_NUMBER(0)] = 0;
+- d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)] = leaf & 255;
+- d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)+1] = leaf >> 8;
+-
+- d->srom[leaf+TULIP_ROM_IL_SELECT_CONN_TYPE] = 0; /* Not used? */
+- d->srom[leaf+TULIP_ROM_IL_MEDIA_COUNT] = 2;
+- leaf += TULIP_ROM_IL_MEDIAn_BLOCK_BASE;
+-
+- d->srom[leaf] = 7; /* descriptor length */
+- d->srom[leaf+1] = TULIP_ROM_MB_21142_SIA;
+- d->srom[leaf+2] = TULIP_ROM_MB_MEDIA_100TX;
+- /* here comes 4 bytes of GPIO control/data settings */
+- leaf += d->srom[leaf];
+-
+- d->srom[leaf] = 15; /* descriptor length */
+- d->srom[leaf+1] = TULIP_ROM_MB_21142_MII;
+- d->srom[leaf+2] = 0; /* PHY nr */
+- d->srom[leaf+3] = 0; /* len of select sequence */
+- d->srom[leaf+4] = 0; /* len of reset sequence */
+- /* 5,6, 7,8, 9,10, 11,12, 13,14 = unused by GXemul */
+- leaf += d->srom[leaf];
++ memcpy(d->nic.mac_address, d->srom + TULIP_ROM_IEEE_NETWORK_ADDRESS, 6);
+
+ /* MII PHY initial state: */
+ d->mii_state = MII_STATE_RESET;
+@@ -814,12 +1160,13 @@ static void dec21143_reset(struct cpu *c
+ DEVICE_ACCESS(dec21143)
+ {
+ struct dec21143_data *d = (struct dec21143_data *) extra;
+- uint64_t idata = 0, odata = 0;
++ uint32_t idata = 0, odata = 0;
+ uint32_t oldreg = 0;
+ int regnr = relative_addr >> 3;
+
+ if (writeflag == MEM_WRITE)
+- idata = memory_readmax64(cpu, data, len | d->pci_little_endian);
++ idata = (uint32_t)memory_readmax64(cpu, data,
++ len | d->pci_little_endian);
+
+ if ((relative_addr & 7) == 0 && regnr < N_REGS) {
+ if (writeflag == MEM_READ) {
+@@ -916,8 +1263,15 @@ DEVICE_ACCESS(dec21143)
+ /* Turned off RX? Then go to stopped state: */
+ d->reg[CSR_STATUS/8] &= ~STATUS_RS;
+ }
++ /* Maintain r/o filter mode bits: */
++ d->reg[CSR_OPMODE/8] &=
++ ~(OPMODE_HP | OPMODE_HO | OPMODE_IF);
++ d->reg[CSR_OPMODE/8] |= oldreg &
++ (OPMODE_HP | OPMODE_HO | OPMODE_IF);
+ idata &= ~(OPMODE_HBD | OPMODE_SCR | OPMODE_PCS
+- | OPMODE_PS | OPMODE_SF | OPMODE_TTM | OPMODE_FD);
++ | OPMODE_PS | OPMODE_SF | OPMODE_TTM | OPMODE_FD
++ | OPMODE_IF | OPMODE_HO | OPMODE_HP | OPMODE_PR
++ | OPMODE_PM);
+ if (idata & OPMODE_PNIC_IT) {
+ idata &= ~OPMODE_PNIC_IT;
+ d->tx_idling = d->tx_idling_threshold;
+@@ -926,6 +1280,7 @@ DEVICE_ACCESS(dec21143)
+ fatal("[ dec21143: UNIMPLEMENTED OPMODE bits"
+ ": 0x%08x ]\n", (int)idata);
+ }
++ dec21143_update_rx_mode(d);
+ dev_dec21143_tick(cpu, extra);
+ }
+ break;
+@@ -976,6 +1331,7 @@ DEVINIT(dec21143)
+ {
+ struct dec21143_data *d;
+ char name2[100];
++ int leaf;
+
+ CHECK_ALLOCATION(d = (struct dec21143_data *) malloc(sizeof(struct dec21143_data)));
+ memset(d, 0, sizeof(struct dec21143_data));
+@@ -983,15 +1339,80 @@ DEVINIT(dec21143)
+ INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
+ d->pci_little_endian = devinit->pci_little_endian;
+
+- net_generate_unique_mac(devinit->machine, d->mac);
+- net_add_nic(devinit->machine->emul->net, d, d->mac);
+- d->net = devinit->machine->emul->net;
++ /* XXX XXX XXX */
++ switch (devinit->machine->machine_type) {
++ /*
++ * Footbridge systems -- this is actually configurable by
++ * system software, but this is the window setting that
++ * NetBSD uses.
++ */
++ case MACHINE_CATS:
++ case MACHINE_NETWINDER:
++ d->xxx_dma_to_phys_mask = ~0x20000000;
++ break;
++
++ /*
++ * V3 Semi PCI bus controller -- this is actually configurable
++ * by system sofware, but this is the value previously hard-coded
++ * for all platforms that did not work on Footbridge systems.
++ */
++ case MACHINE_ALGOR:
++ /* FALLTHROUGH */
++
++ /* Other known users of dc21143 that came along for the ride. */
++ case MACHINE_COBALT:
++ case MACHINE_PMPPC:
++ case MACHINE_PREP:
++ case MACHINE_MACPPC:
++ case MACHINE_MVMEPPC:
++ d->xxx_dma_to_phys_mask = 0x7fffffff;
++ break;
++
++ default:
++ fatal("[ dec21143: default DMA mask for unhandled machine %d\n",
++ devinit->machine->machine_type);
++ d->xxx_dma_to_phys_mask = 0xffffffff;
++ }
++
++ memset(d->srom, 0, sizeof(d->srom));
++
++ /* Version (= 1) and Chip count (= 1): */
++ d->srom[TULIP_ROM_SROM_FORMAT_VERION] = 1;
++ d->srom[TULIP_ROM_CHIP_COUNT] = 1;
++
++ leaf = 30;
++ d->srom[TULIP_ROM_CHIPn_DEVICE_NUMBER(0)] = 0;
++ d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)] = leaf & 255;
++ d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)+1] = leaf >> 8;
++
++ d->srom[leaf+TULIP_ROM_IL_SELECT_CONN_TYPE] = 0; /* Not used? */
++ d->srom[leaf+TULIP_ROM_IL_MEDIA_COUNT] = 2;
++ leaf += TULIP_ROM_IL_MEDIAn_BLOCK_BASE;
++
++ d->srom[leaf] = 7; /* descriptor length */
++ d->srom[leaf+1] = TULIP_ROM_MB_21142_SIA;
++ d->srom[leaf+2] = TULIP_ROM_MB_MEDIA_100TX;
++ /* here comes 4 bytes of GPIO control/data settings */
++ leaf += d->srom[leaf];
++
++ d->srom[leaf] = 15; /* descriptor length */
++ d->srom[leaf+1] = TULIP_ROM_MB_21142_MII;
++ d->srom[leaf+2] = 0; /* PHY nr */
++ d->srom[leaf+3] = 0; /* len of select sequence */
++ d->srom[leaf+4] = 0; /* len of reset sequence */
++ /* 5,6, 7,8, 9,10, 11,12, 13,14 = unused by GXemul */
++ leaf += d->srom[leaf];
++
++ net_generate_unique_mac(devinit->machine, d->nic.mac_address);
++ memcpy(d->srom + TULIP_ROM_IEEE_NETWORK_ADDRESS, d->nic.mac_address, 6);
++ net_add_nic(devinit->machine->emul->net, &d->nic);
+
+ dec21143_reset(devinit->machine->cpus[0], d);
+
+ snprintf(name2, sizeof(name2), "%s [%02x:%02x:%02x:%02x:%02x:%02x]",
+- devinit->name, d->mac[0], d->mac[1], d->mac[2], d->mac[3],
+- d->mac[4], d->mac[5]);
++ devinit->name, d->nic.mac_address[0], d->nic.mac_address[1],
++ d->nic.mac_address[2], d->nic.mac_address[3],
++ d->nic.mac_address[4], d->nic.mac_address[5]);
+
+ memory_device_register(devinit->machine->memory, name2,
+ devinit->addr, 0x100, dev_dec21143_access, d, DM_DEFAULT, NULL);
diff --git a/emulators/gxemul/patches/patch-src_devices_dev_ether.cc b/emulators/gxemul/patches/patch-src_devices_dev_ether.cc
new file mode 100644
index 00000000000..678ac1cf7a0
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_devices_dev_ether.cc
@@ -0,0 +1,74 @@
+$NetBSD: patch-src_devices_dev_ether.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- src/devices/dev_ether.cc.orig 2020-10-05 22:56:01.167967575 +0000
++++ src/devices/dev_ether.cc 2020-10-05 22:56:17.330782903 +0000
+@@ -49,6 +49,8 @@
+ #define DEV_ETHER_TICK_SHIFT 14
+
+ struct ether_data {
++ struct nic_data nic;
++
+ unsigned char buf[DEV_ETHER_BUFFER_SIZE];
+ unsigned char mac[6];
+
+@@ -66,7 +68,7 @@ DEVICE_TICK(ether)
+
+ d->status &= ~DEV_ETHER_STATUS_MORE_PACKETS_AVAILABLE;
+ if (cpu->machine->emul->net != NULL)
+- r = net_ethernet_rx_avail(cpu->machine->emul->net, d);
++ r = net_ethernet_rx_avail(cpu->machine->emul->net, &d->nic);
+ if (r)
+ d->status |= DEV_ETHER_STATUS_MORE_PACKETS_AVAILABLE;
+
+@@ -147,7 +149,7 @@ DEVICE_ACCESS(ether)
+ else {
+ d->status &= ~DEV_ETHER_STATUS_PACKET_RECEIVED;
+ if (net_ethernet_rx(cpu->machine->emul->net,
+- d, &incoming_ptr, &incoming_len)) {
++ &d->nic, &incoming_ptr, &incoming_len)) {
+ d->status |=
+ DEV_ETHER_STATUS_PACKET_RECEIVED;
+ if (incoming_len>DEV_ETHER_BUFFER_SIZE)
+@@ -167,7 +169,7 @@ DEVICE_ACCESS(ether)
+ fatal("[ ether: SEND but no net? ]\n");
+ else
+ net_ethernet_tx(cpu->machine->emul->net,
+- d, d->buf, d->packet_len);
++ &d->nic, d->buf, d->packet_len);
+ d->status &= ~DEV_ETHER_STATUS_PACKET_RECEIVED;
+ dev_ether_tick(cpu, d);
+ break;
+@@ -183,7 +185,7 @@ DEVICE_ACCESS(ether)
+ fatal("[ ether: read of MAC is not allowed! ]\n");
+ } else {
+ // Write out the MAC address to the address given.
+- cpu->memory_rw(cpu, cpu->mem, idata, d->mac,
++ cpu->memory_rw(cpu, cpu->mem, idata, d->nic.mac_address,
+ 6, MEM_WRITE, CACHE_NONE);
+ }
+ break;
+@@ -221,9 +223,11 @@ DEVINIT(ether)
+
+ INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
+
+- net_generate_unique_mac(devinit->machine, d->mac);
++ net_generate_unique_mac(devinit->machine, d->nic.mac_address);
+ snprintf(tmp, sizeof(tmp), "%02x:%02x:%02x:%02x:%02x:%02x",
+- d->mac[0], d->mac[1], d->mac[2], d->mac[3], d->mac[4], d->mac[5]);
++ d->nic.mac_address[0], d->nic.mac_address[1],
++ d->nic.mac_address[2], d->nic.mac_address[3],
++ d->nic.mac_address[4], d->nic.mac_address[5]);
+
+ snprintf(n1, nlen, "%s [%s]", devinit->name, tmp);
+ snprintf(n2, nlen, "%s [%s, control]", devinit->name, tmp);
+@@ -237,7 +241,7 @@ DEVINIT(ether)
+ DEV_ETHER_LENGTH-DEV_ETHER_BUFFER_SIZE, dev_ether_access, (void *)d,
+ DM_DEFAULT, NULL);
+
+- net_add_nic(devinit->machine->emul->net, d, d->mac);
++ net_add_nic(devinit->machine->emul->net, &d->nic);
+
+ machine_add_tickfunction(devinit->machine,
+ dev_ether_tick, d, DEV_ETHER_TICK_SHIFT);
diff --git a/emulators/gxemul/patches/patch-src_devices_dev_le.cc b/emulators/gxemul/patches/patch-src_devices_dev_le.cc
new file mode 100644
index 00000000000..8624436baa1
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_devices_dev_le.cc
@@ -0,0 +1,232 @@
+$NetBSD: patch-src_devices_dev_le.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+- Add support for tap(4)-based networking.
+- Process the setup packet, and implement address filtering.
+
+--- src/devices/dev_le.cc.orig 2020-10-05 22:56:40.808218933 +0000
++++ src/devices/dev_le.cc 2020-10-05 22:56:55.751440817 +0000
+@@ -72,9 +72,10 @@
+
+ extern int quiet_mode;
+
+-#define LE_MODE_LOOP 4
+-#define LE_MODE_DTX 2
+-#define LE_MODE_DRX 1
++#define LE_MODE_PROM 0x8000
++#define LE_MODE_LOOP 0x0004
++#define LE_MODE_DTX 0x0002
++#define LE_MODE_DRX 0x0001
+
+
+ #define N_REGISTERS 4
+@@ -83,6 +84,8 @@ extern int quiet_mode;
+
+
+ struct le_data {
++ struct nic_data nic;
++
+ struct interrupt irq;
+ int irq_asserted;
+
+@@ -101,13 +104,14 @@ struct le_data {
+ uint32_t init_block_addr;
+
+ uint16_t mode;
+- uint64_t padr; /* MAC address */
+- uint64_t ladrf;
++ uint16_t ladrf[4];
+ uint32_t rdra; /* receive descriptor ring address */
+ int rlen; /* nr of rx descriptors */
+ uint32_t tdra; /* transmit descriptor ring address */
+ int tlen; /* nr ot tx descriptors */
+
++ int allmulti;/* receive all multicast packets */
++
+ /* Current rx and tx descriptor indices: */
+ int rxp;
+ int txp;
+@@ -157,6 +161,9 @@ static void le_write_16bit(struct le_dat
+ */
+ static void le_chip_init(struct le_data *d)
+ {
++ uint16_t tmp;
++ uint8_t macaddr[6];
++
+ d->init_block_addr = (d->reg[1] & 0xffff) + ((d->reg[2] & 0xff) << 16);
+ if (d->init_block_addr & 1)
+ fatal("[ le: WARNING! initialization block address "
+@@ -165,13 +172,36 @@ static void le_chip_init(struct le_data
+ debug("[ le: d->init_block_addr = 0x%06x ]\n", d->init_block_addr);
+
+ d->mode = le_read_16bit(d, d->init_block_addr + 0);
+- d->padr = le_read_16bit(d, d->init_block_addr + 2);
+- d->padr += (le_read_16bit(d, d->init_block_addr + 4) << 16);
+- d->padr += (le_read_16bit(d, d->init_block_addr + 6) << 32);
+- d->ladrf = le_read_16bit(d, d->init_block_addr + 8);
+- d->ladrf += (le_read_16bit(d, d->init_block_addr + 10) << 16);
+- d->ladrf += (le_read_16bit(d, d->init_block_addr + 12) << 32);
+- d->ladrf += (le_read_16bit(d, d->init_block_addr + 14) << 48);
++
++ /*
++ * The MAC address is packed into the PADR field as 3 little-endian
++ * 16-bit words.
++ */
++ tmp = le_read_16bit(d, d->init_block_addr + 2);
++ macaddr[0] = (uint8_t)(tmp);
++ macaddr[1] = (uint8_t)(tmp >> 8);
++ tmp = le_read_16bit(d, d->init_block_addr + 4);
++ macaddr[2] = (uint8_t)(tmp);
++ macaddr[3] = (uint8_t)(tmp >> 8);
++ tmp = le_read_16bit(d, d->init_block_addr + 6);
++ macaddr[4] = (uint8_t)(tmp);
++ macaddr[5] = (uint8_t)(tmp >> 8);
++ memcpy(d->nic.mac_address, macaddr, sizeof(d->nic.mac_address));
++
++ /*
++ * The muticast address filter is packed into the LADRF field
++ * as 4 little-endian 16-bit words.
++ */
++ d->ladrf[0] = le_read_16bit(d, d->init_block_addr + 8);
++ d->ladrf[1] = le_read_16bit(d, d->init_block_addr + 10);
++ d->ladrf[2] = le_read_16bit(d, d->init_block_addr + 12);
++ d->ladrf[3] = le_read_16bit(d, d->init_block_addr + 14);
++ if (d->ladrf[0] == 0xffff && d->ladrf[1] == 0xffff &&
++ d->ladrf[2] == 0xffff && d->ladrf[3] == 0xffff)
++ d->allmulti = 1;
++ else
++ d->allmulti = 0;
++
+ d->rdra = le_read_16bit(d, d->init_block_addr + 16);
+ d->rdra += ((le_read_16bit(d, d->init_block_addr + 18) & 0xff) << 16);
+ d->rlen = 1 << ((le_read_16bit(d, d->init_block_addr + 18) >> 13) & 7);
+@@ -179,13 +209,16 @@ static void le_chip_init(struct le_data
+ d->tdra += ((le_read_16bit(d, d->init_block_addr + 22) & 0xff) << 16);
+ d->tlen = 1 << ((le_read_16bit(d, d->init_block_addr + 22) >> 13) & 7);
+
+- debug("[ le: DEBUG: mode %04x ]\n", d->mode);
+- debug("[ le: DEBUG: padr %016llx ]\n", (long long)d->padr);
+- debug("[ le: DEBUG: ladrf %016llx ]\n", (long long)d->ladrf);
+- debug("[ le: DEBUG: rdra %06llx ]\n", d->rdra);
+- debug("[ le: DEBUG: rlen %3i ]\n", d->rlen);
+- debug("[ le: DEBUG: tdra %06llx ]\n", d->tdra);
+- debug("[ le: DEBUG: tlen %3i ]\n", d->tlen);
++ debug("[ le: DEBUG: mode %04x ]\n", d->mode);
++ debug("[ le: DEBUG: padr %02x:%02x:%02x:%02x:%02x:%02x ]\n",
++ macaddr[0], macaddr[1], macaddr[2],
++ macaddr[3], macaddr[4], macaddr[5]);
++ debug("[ le: DEBUG: ladrf %04x:%04x:%04x:%04x ]\n",
++ d->ladrf[0], d->ladrf[1], d->ladrf[2], d->ladrf[3]);
++ debug("[ le: DEBUG: rdra %06llx ]\n", d->rdra);
++ debug("[ le: DEBUG: rlen %3i ]\n", d->rlen);
++ debug("[ le: DEBUG: tdra %06llx ]\n", d->tdra);
++ debug("[ le: DEBUG: tlen %3i ]\n", d->tlen);
+
+ /* Set TXON and RXON, unless they are disabled by 'mode': */
+ if (d->mode & LE_MODE_DTX)
+@@ -198,6 +231,9 @@ static void le_chip_init(struct le_data
+ else
+ d->reg[0] |= LE_RXON;
+
++ /* Initialize promiscuous mode. */
++ d->nic.promiscuous_mode = (d->mode & LE_MODE_PROM) ? 1 : 0;
++
+ /* Go to the start of the descriptor rings: */
+ d->rxp = d->txp = 0;
+
+@@ -308,7 +344,8 @@ static void le_tx(struct net *net, struc
+ * the packet.
+ */
+ if (enp) {
+- net_ethernet_tx(net, d, d->tx_packet, d->tx_packet_len);
++ net_ethernet_tx(net, &d->nic, d->tx_packet,
++ d->tx_packet_len);
+
+ free(d->tx_packet);
+ d->tx_packet = NULL;
+@@ -446,6 +483,62 @@ static void le_rx(struct net *net, struc
+
+
+ /*
++ * le_rx_drop_packet():
++ *
++ * Implement the logic to determine if we should drop a packet
++ * before passing it to the guest. Returns 1 if the packet was
++ * dropped.
++ */
++static int
++le_rx_drop_packet(struct net *net, struct le_data *d)
++{
++ /* Only implement filtering if using a tap device. */
++ if (net->tapdev == NULL)
++ return 0;
++
++ /*
++ * The network layer has already checked for our MAC address
++ * or promiscuous mode. We just need to check the multicast
++ * filter or broadcast.
++ */
++
++ /* If the packet is not multicast, we know it should be received. */
++ if (! net_ether_multicast(d->rx_packet))
++ return 0;
++
++ /*
++ * Optimization -- if the guest has set all of the filter
++ * bits, then we can skip additional checks.
++ */
++ if (d->allmulti)
++ return 0;
++
++ /* Check for broadcast. */
++ if (net_ether_broadcast(d->rx_packet))
++ return 0;
++
++ /*
++ * Check the multicast address filter. We pass the address
++ * through the little-endian Ethernet CRC generator. The
++ * high-order 6 bits are the index into the 64-bit filter.
++ * The upper 2 bits select the 16-bit filter word, and the
++ * remaining 4 select the bit in the word.
++ */
++ uint32_t crc = net_ether_crc32_le(d->rx_packet, 6);
++ crc >>= 26;
++ if (d->ladrf[crc >> 4] & (1 << (crc & 0xf)))
++ return 0;
++
++ /* Not for us; drop the packet. */
++ free(d->rx_packet);
++ d->rx_packet = NULL;
++ d->rx_packet_len = 0;
++
++ return 1;
++}
++
++
++/*
+ * le_register_fix():
+ */
+ static void le_register_fix(struct net *net, struct le_data *d)
+@@ -481,9 +574,12 @@ static void le_register_fix(struct net *
+ break;
+
+ if (d->rx_packet == NULL &&
+- net_ethernet_rx_avail(net, d))
+- net_ethernet_rx(net, d,
++ net_ethernet_rx_avail(net, &d->nic)) {
++ net_ethernet_rx(net, &d->nic,
+ &d->rx_packet, &d->rx_packet_len);
++ if (le_rx_drop_packet(net, d))
++ continue;
++ }
+ } while (d->rx_packet != NULL);
+ }
+
+@@ -813,6 +909,7 @@ void dev_le_init(struct machine *machine
+
+ machine_add_tickfunction(machine, dev_le_tick, d, LE_TICK_SHIFT);
+
+- net_add_nic(machine->emul->net, d, &d->rom[0]);
++ memcpy(d->nic.mac_address, &d->rom[0], sizeof(d->nic.mac_address));
++ net_add_nic(machine->emul->net, &d->nic);
+ }
+
diff --git a/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc b/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc
new file mode 100644
index 00000000000..586268d7248
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc
@@ -0,0 +1,51 @@
+$NetBSD: patch-src_devices_dev_rtl8139c.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Updates for tap(4)-related changes to Ethernet support.
+
+--- src/devices/dev_rtl8139c.cc.orig 2020-10-05 22:57:40.189047655 +0000
++++ src/devices/dev_rtl8139c.cc 2020-10-05 22:57:53.936646578 +0000
+@@ -50,8 +50,9 @@
+ #define EEPROM_SIZE 0x100
+
+ struct rtl8139c_data {
++ struct nic_data nic;
++
+ struct interrupt irq;
+- unsigned char macaddr[6];
+
+ /* Registers: */
+ uint8_t rl_command;
+@@ -205,25 +206,26 @@ DEVINIT(rtl8139c)
+
+ INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
+
+- net_generate_unique_mac(devinit->machine, d->macaddr);
++ net_generate_unique_mac(devinit->machine, d->nic.mac_address);
+
+ /* TODO: eeprom address width = 6 on 8129? */
+ d->eeprom_address_width = 8;
+ d->eeprom_reg[0] = 0x8139;
+- d->eeprom_reg[7] = d->macaddr[0] + (d->macaddr[1] << 8);
+- d->eeprom_reg[8] = d->macaddr[2] + (d->macaddr[3] << 8);
+- d->eeprom_reg[9] = d->macaddr[4] + (d->macaddr[5] << 8);
++ d->eeprom_reg[7] = d->nic.mac_address[0] + (d->nic.mac_address[1] << 8);
++ d->eeprom_reg[8] = d->nic.mac_address[2] + (d->nic.mac_address[3] << 8);
++ d->eeprom_reg[9] = d->nic.mac_address[4] + (d->nic.mac_address[5] << 8);
+
+ CHECK_ALLOCATION(name2 = (char *) malloc(nlen));
+ snprintf(name2, nlen, "%s [%02x:%02x:%02x:%02x:%02x:%02x]",
+- devinit->name, d->macaddr[0], d->macaddr[1], d->macaddr[2],
+- d->macaddr[3], d->macaddr[4], d->macaddr[5]);
++ devinit->name, d->nic.mac_address[0], d->nic.mac_address[1],
++ d->nic.mac_address[2], d->nic.mac_address[3],
++ d->nic.mac_address[4], d->nic.mac_address[5]);
+
+ memory_device_register(devinit->machine->memory, name2,
+ devinit->addr, DEV_RTL8139C_LENGTH, dev_rtl8139c_access, (void *)d,
+ DM_DEFAULT, NULL);
+
+- net_add_nic(devinit->machine->emul->net, d, d->macaddr);
++ net_add_nic(devinit->machine->emul->net, &d->nic);
+
+ return 1;
+ }
diff --git a/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc b/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc
new file mode 100644
index 00000000000..a50fc819f97
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc
@@ -0,0 +1,69 @@
+$NetBSD: patch-src_devices_dev_sgi_mec.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Updates for tap(4)-related changes to Ethernet support.
+
+--- src/devices/dev_sgi_mec.cc.orig 2020-10-05 22:58:11.248005164 +0000
++++ src/devices/dev_sgi_mec.cc 2020-10-05 22:58:31.644418157 +0000
+@@ -81,13 +81,13 @@
+ #define N_RX_ADDRESSES 16
+
+ struct sgi_mec_data {
++ struct nic_data nic;
++
+ uint64_t reg[DEV_SGI_MEC_LENGTH / sizeof(uint64_t)];
+
+ struct interrupt irq;
+ int prev_asserted;
+
+- unsigned char macaddr[6];
+-
+ unsigned char cur_tx_packet[MAX_TX_PACKET_LEN];
+ int cur_tx_packet_len;
+
+@@ -157,8 +157,8 @@ static int mec_try_rx(struct cpu *cpu, s
+ }
+
+ if (d->cur_rx_packet == NULL &&
+- net_ethernet_rx_avail(cpu->machine->emul->net, d))
+- net_ethernet_rx(cpu->machine->emul->net, d,
++ net_ethernet_rx_avail(cpu->machine->emul->net, &d->nic))
++ net_ethernet_rx(cpu->machine->emul->net, &d->nic,
+ &d->cur_rx_packet, &d->cur_rx_packet_len);
+
+ if (d->cur_rx_packet == NULL)
+@@ -343,7 +343,7 @@ static int mec_try_tx(struct cpu *cpu, s
+ if (j < len)
+ fatal("[ mec_try_tx: not enough data? ]\n");
+
+- net_ethernet_tx(cpu->machine->emul->net, d,
++ net_ethernet_tx(cpu->machine->emul->net, &d->nic,
+ d->cur_tx_packet, d->cur_tx_packet_len);
+
+ /* see openbsd's if_mec.c for details */
+@@ -675,13 +675,14 @@ void dev_sgi_mec_init(struct machine *ma
+ memset(d, 0, sizeof(struct sgi_mec_data));
+
+ INTERRUPT_CONNECT(irq_path, d->irq);
+- memcpy(d->macaddr, macaddr, 6);
++ memcpy(d->nic.mac_address, macaddr, 6);
+ mec_reset(d);
+
+ CHECK_ALLOCATION(name2 = (char *) malloc(nlen));
+ snprintf(name2, nlen, "mec [%02x:%02x:%02x:%02x:%02x:%02x]",
+- d->macaddr[0], d->macaddr[1], d->macaddr[2],
+- d->macaddr[3], d->macaddr[4], d->macaddr[5]);
++ d->nic.mac_address[0], d->nic.mac_address[1],
++ d->nic.mac_address[2], d->nic.mac_address[3],
++ d->nic.mac_address[4], d->nic.mac_address[5]);
+
+ memory_device_register(mem, name2, baseaddr,
+ DEV_SGI_MEC_LENGTH, dev_sgi_mec_access, (void *)d,
+@@ -690,7 +691,7 @@ void dev_sgi_mec_init(struct machine *ma
+ machine_add_tickfunction(machine, dev_sgi_mec_tick, d,
+ MEC_TICK_SHIFT);
+
+- net_add_nic(machine->emul->net, d, macaddr);
++ net_add_nic(machine->emul->net, &d->nic);
+ }
+
+
diff --git a/emulators/gxemul/patches/patch-src_devices_dev_sn.cc b/emulators/gxemul/patches/patch-src_devices_dev_sn.cc
new file mode 100644
index 00000000000..c25906f27b1
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_devices_dev_sn.cc
@@ -0,0 +1,40 @@
+$NetBSD: patch-src_devices_dev_sn.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Updates for tap(4)-related changes to Ethernet support.
+
+--- src/devices/dev_sn.cc.orig 2020-10-05 22:58:49.162038719 +0000
++++ src/devices/dev_sn.cc 2020-10-05 22:59:04.524312162 +0000
+@@ -48,8 +48,8 @@
+ #define DEV_SN_LENGTH 0x1000
+
+ struct sn_data {
++ struct nic_data nic;
+ struct interrupt irq;
+- unsigned char macaddr[6];
+ uint32_t reg[SONIC_NREGS];
+ };
+
+@@ -104,18 +104,19 @@ DEVINIT(sn)
+
+ INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
+
+- net_generate_unique_mac(devinit->machine, d->macaddr);
++ net_generate_unique_mac(devinit->machine, d->nic.mac_address);
+
+ CHECK_ALLOCATION(name2 = (char *) malloc(nlen));
+ snprintf(name2, nlen, "%s [%02x:%02x:%02x:%02x:%02x:%02x]",
+- devinit->name, d->macaddr[0], d->macaddr[1], d->macaddr[2],
+- d->macaddr[3], d->macaddr[4], d->macaddr[5]);
++ devinit->name, d->nic.mac_address[0], d->nic.mac_address[1],
++ d->nic.mac_address[2], d->nic.mac_address[3],
++ d->nic.mac_address[4], d->nic.mac_address[5]);
+
+ memory_device_register(devinit->machine->memory, name2,
+ devinit->addr, DEV_SN_LENGTH,
+ dev_sn_access, (void *)d, DM_DEFAULT, NULL);
+
+- net_add_nic(devinit->machine->emul->net, d, d->macaddr);
++ net_add_nic(devinit->machine->emul->net, &d->nic);
+
+ return 1;
+ }
diff --git a/emulators/gxemul/patches/patch-src_include_net.h b/emulators/gxemul/patches/patch-src_include_net.h
new file mode 100644
index 00000000000..c73357e4bc5
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_include_net.h
@@ -0,0 +1,107 @@
+$NetBSD: patch-src_include_net.h,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- src/include/net.h.orig 2020-10-05 22:59:17.124851904 +0000
++++ src/include/net.h 2020-10-05 22:59:31.468423021 +0000
+@@ -112,6 +112,15 @@ struct tcp_connection {
+
+ /*****************************************************************************/
+
++/* Common data for emulated Ethernet NICs. */
++
++struct nic_data {
++ struct net *net; /* net we belong to */
++ uint8_t mac_address[6]; /* our MAC address */
++ int promiscuous_mode;/* receive all packets */
++};
++
++/*****************************************************************************/
+
+ #define MAX_TCP_CONNECTIONS 100
+ #define MAX_UDP_CONNECTIONS 100
+@@ -120,13 +129,17 @@ struct net {
+ /* The emul struct which this net belong to: */
+ struct emul *emul;
+
++ /* The network's tap device, if we're using tap: */
++ const char *tapdev;
++ int tap_fd;
++
+ /* The network's addresses: */
+ struct in_addr netmask_ipv4;
+ int netmask_ipv4_len;
+
+ /* NICs connected to this network: */
+ int n_nics;
+- void **nic_extra; /* one void * per NIC */
++ struct nic_data **nic_data; /* one per NIC */
+
+ /* The "special machine": */
+ unsigned char gateway_ipv4_addr[4];
+@@ -151,6 +164,18 @@ struct net {
+ struct remote_net *remote_nets;
+ };
+
++/* net_tap.c: */
++void net_tap_rx_avail(struct net *net);
++void net_tap_tx(struct net *net, struct nic_data *nic, unsigned char *packet,
++ int len);
++int net_tap_init(struct net *net, const char *tapdev);
++
++/* net_ether.cc */
++int net_ether_eq(const uint8_t *a1, const uint8_t *a2);
++int net_ether_broadcast(const uint8_t *a);
++int net_ether_multicast(const uint8_t *a);
++uint32_t net_ether_crc32_le(const uint8_t *buf, size_t len);
++
+ /* net_misc.c: */
+ void net_debugaddr(void *addr, int type);
+ void net_generate_unique_mac(struct machine *, unsigned char *macbuf);
+@@ -162,25 +187,27 @@ void net_ip_checksum(unsigned char *ip_h
+ void net_ip_tcp_checksum(unsigned char *tcp_header, int chksumoffset,
+ int tcp_len, unsigned char *srcaddr, unsigned char *dstaddr,
+ int udpflag);
+-void net_ip_tcp_connectionreply(struct net *net, void *extra,
++void net_ip_tcp_connectionreply(struct net *net, struct nic_data *nic,
+ int con_id, int connecting, unsigned char *data, int datalen, int rst);
+-void net_ip_broadcast(struct net *net, void *extra,
++void net_ip_broadcast(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len);
+-void net_ip(struct net *net, void *extra, unsigned char *packet, int len);
+-void net_udp_rx_avail(struct net *net, void *extra);
+-void net_tcp_rx_avail(struct net *net, void *extra);
++void net_ip(struct net *net, struct nic_data *nic, unsigned char *packet,
++ int len);
++void net_udp_rx_avail(struct net *net, struct nic_data *nic);
++void net_tcp_rx_avail(struct net *net, struct nic_data *nic);
+
+ /* net.c: */
+ struct ethernet_packet_link *net_allocate_ethernet_packet_link(
+- struct net *net, void *extra, size_t len);
+-int net_ethernet_rx_avail(struct net *net, void *extra);
+-int net_ethernet_rx(struct net *net, void *extra,
++ struct net *net, struct nic_data *nic, size_t len);
++int net_ethernet_rx_avail(struct net *net, struct nic_data *nic);
++int net_ethernet_rx(struct net *net, struct nic_data *nic,
+ unsigned char **packetp, int *lenp);
+-void net_ethernet_tx(struct net *net, void *extra,
++void net_ethernet_tx(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len);
+ void net_dumpinfo(struct net *net);
+-void net_add_nic(struct net *net, void *extra, unsigned char *macaddr);
++void net_add_nic(struct net *net, struct nic_data *nic);
+ struct net *net_init(struct emul *emul, int init_flags,
++ const char *tapdev,
+ const char *ipv4addr, int netipv4len, char **remote, int n_remote,
+ int local_port, const char *settings_prefix);
+
+@@ -195,7 +222,7 @@ struct ethernet_packet_link {
+ struct ethernet_packet_link *prev;
+ struct ethernet_packet_link *next;
+
+- void *extra;
++ struct nic_data *nic;
+ unsigned char *data;
+ int len;
+ };
diff --git a/emulators/gxemul/patches/patch-src_net_Makefile.skel b/emulators/gxemul/patches/patch-src_net_Makefile.skel
new file mode 100644
index 00000000000..7e6abda9d83
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_net_Makefile.skel
@@ -0,0 +1,15 @@
+$NetBSD: patch-src_net_Makefile.skel,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- src/net/Makefile.skel.orig 2020-10-05 22:59:56.683092920 +0000
++++ src/net/Makefile.skel 2020-10-05 23:00:09.053165122 +0000
+@@ -4,7 +4,7 @@
+
+ CXXFLAGS=$(CWARNINGS) $(COPTIM) $(XINCLUDE) $(DINCLUDE)
+
+-OBJS=net.o net_ip.o net_misc.o
++OBJS=net.o net_ip.o net_misc.o net_tap.o net_ether.o
+
+ all: $(OBJS)
+
diff --git a/emulators/gxemul/patches/patch-src_net_net.cc b/emulators/gxemul/patches/patch-src_net_net.cc
new file mode 100644
index 00000000000..0cadc3c45dd
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_net_net.cc
@@ -0,0 +1,318 @@
+$NetBSD: patch-src_net_net.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- src/net/net.cc.orig 2020-10-05 23:00:24.839832619 +0000
++++ src/net/net.cc 2020-10-05 23:00:41.597469289 +0000
+@@ -30,10 +30,10 @@
+ * (Read the README file in this directory for more details.)
+ *
+ *
+- * NOTE: The 'extra' argument used in many functions in this file is a pointer
+- * to something unique for each NIC (i.e. the NIC itself :-), so that if
+- * multiple NICs are emulated concurrently, they will not get packets that
+- * are meant for some other controller.
++ * NOTE: The 'nic' argument used in many functions in this file is a pointer
++ * to the nic_data for each NIC, so that if multiple NICs are emulated
++ * concurrently, they will not get packets that are meant for some other
++ * controller.
+ */
+
+ #include <stdio.h>
+@@ -62,7 +62,7 @@
+ *
+ * This routine allocates an ethernet_packet_link struct, and adds it at
+ * the end of the packet chain. A data buffer is allocated, and the data,
+- * extra, and len fields of the link are set.
++ * nic, and len fields of the link are set.
+ *
+ * Note: The data buffer is not zeroed.
+ *
+@@ -70,7 +70,7 @@
+ * failure.
+ */
+ struct ethernet_packet_link *net_allocate_ethernet_packet_link(
+- struct net *net, void *extra, size_t len)
++ struct net *net, struct nic_data *nic, size_t len)
+ {
+ struct ethernet_packet_link *lp;
+
+@@ -78,7 +78,7 @@ struct ethernet_packet_link *net_allocat
+ malloc(sizeof(struct ethernet_packet_link)));
+
+ lp->len = len;
+- lp->extra = extra;
++ lp->nic = nic;
+ CHECK_ALLOCATION(lp->data = (unsigned char *) malloc(len));
+
+ lp->next = NULL;
+@@ -116,7 +116,7 @@ struct ethernet_packet_link *net_allocat
+ * An ARP request with the same from and to IP addresses should be ignored.
+ * (This would be a host testing to see if there is an IP collision.)
+ */
+-static void net_arp(struct net *net, void *extra,
++static void net_arp(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len, int reverse)
+ {
+ int q;
+@@ -161,7 +161,7 @@ static void net_arp(struct net *net, voi
+ break;
+
+ lp = net_allocate_ethernet_packet_link(
+- net, extra, 60 + 14);
++ net, nic, 60 + 14);
+
+ /* Copy the old packet first: */
+ memset(lp->data, 0, 60 + 14);
+@@ -186,7 +186,7 @@ static void net_arp(struct net *net, voi
+ break;
+ case 3: /* Reverse Request */
+ lp = net_allocate_ethernet_packet_link(
+- net, extra, 60 + 14);
++ net, nic, 60 + 14);
+
+ /* Copy the old packet first: */
+ memset(lp->data, 0, 60 + 14);
+@@ -242,7 +242,7 @@ static void net_arp(struct net *net, voi
+ /*
+ * net_ethernet_rx_avail():
+ *
+- * Return 1 if there is a packet available for this 'extra' pointer, otherwise
++ * Return 1 if there is a packet available for this nic, otherwise
+ * return 0.
+ *
+ * Appart from actually checking for incoming packets from the outside world,
+@@ -250,12 +250,21 @@ static void net_arp(struct net *net, voi
+ * a return value telling us whether there is a packet or not, we don't
+ * actually get the packet.
+ */
+-int net_ethernet_rx_avail(struct net *net, void *extra)
++int net_ethernet_rx_avail(struct net *net, struct nic_data *nic)
+ {
+ if (net == NULL)
+ return 0;
+
+ /*
++ * If we're using a tap device, check in with that and
++ * that's it.
++ */
++ if (net->tapdev) {
++ net_tap_rx_avail(net);
++ return net_ethernet_rx(net, nic, NULL, NULL);
++ }
++
++ /*
+ * If the network is distributed across multiple emulator processes,
+ * then receive incoming packets from those processes.
+ */
+@@ -282,7 +291,7 @@ int net_ethernet_rx_avail(struct net *ne
+ for (i=0; i<net->n_nics; i++) {
+ struct ethernet_packet_link *lp;
+ lp = net_allocate_ethernet_packet_link(
+- net, net->nic_extra[i], res);
++ net, net->nic_data[i], res);
+ memcpy(lp->data, buf, res);
+ }
+ }
+@@ -290,10 +299,10 @@ int net_ethernet_rx_avail(struct net *ne
+ }
+
+ /* IP protocol specific: */
+- net_udp_rx_avail(net, extra);
+- net_tcp_rx_avail(net, extra);
++ net_udp_rx_avail(net, nic);
++ net_tcp_rx_avail(net, nic);
+
+- return net_ethernet_rx(net, extra, NULL, NULL);
++ return net_ethernet_rx(net, nic, NULL, NULL);
+ }
+
+
+@@ -309,11 +318,11 @@ int net_ethernet_rx_avail(struct net *ne
+ * available, 0 is returned.
+ *
+ * If packetp is NULL, then the search is aborted as soon as a packet with
+- * the correct 'extra' field is found, and a 1 is returned, but as packetp
++ * the correct 'nic' field is found, and a 1 is returned, but as packetp
+ * is NULL we can't return the actual packet. (This is the internal form
+ * if net_ethernet_rx_avail().)
+ */
+-int net_ethernet_rx(struct net *net, void *extra,
++int net_ethernet_rx(struct net *net, struct nic_data *nic,
+ unsigned char **packetp, int *lenp)
+ {
+ struct ethernet_packet_link *lp, *prev;
+@@ -321,12 +330,12 @@ int net_ethernet_rx(struct net *net, voi
+ if (net == NULL)
+ return 0;
+
+- /* Find the first packet which has the right 'extra' field. */
++ /* Find the first packet which has the right 'nic' field. */
+
+ lp = net->first_ethernet_packet;
+ prev = NULL;
+ while (lp != NULL) {
+- if (lp->extra == extra) {
++ if (lp->nic == nic) {
+ /* We found a packet for this controller! */
+ if (packetp == NULL || lenp == NULL)
+ return 1;
+@@ -368,7 +377,7 @@ int net_ethernet_rx(struct net *net, voi
+ * If the packet can be handled here, it will not necessarily be transmitted
+ * to the outside world.
+ */
+-void net_ethernet_tx(struct net *net, void *extra,
++void net_ethernet_tx(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len)
+ {
+ int i, eth_type, for_the_gateway;
+@@ -376,8 +385,6 @@ void net_ethernet_tx(struct net *net, vo
+ if (net == NULL)
+ return;
+
+- for_the_gateway = !memcmp(packet, net->gateway_ethernet_addr, 6);
+-
+ /* Drop too small packets: */
+ if (len < 20) {
+ fatal("[ net_ethernet_tx: Warning: dropping tiny packet "
+@@ -386,15 +393,26 @@ void net_ethernet_tx(struct net *net, vo
+ }
+
+ /*
++ * If we're using a tap device, we send the packet that way
++ * and that's it.
++ */
++ if (net->tapdev) {
++ net_tap_tx(net, nic, packet, len);
++ return;
++ }
++
++ for_the_gateway = !memcmp(packet, net->gateway_ethernet_addr, 6);
++
++ /*
+ * Copy this packet to all other NICs on this network (except if
+ * it is aimed specifically at the gateway's ethernet address):
+ */
+- if (!for_the_gateway && extra != NULL && net->n_nics > 0) {
++ if (!for_the_gateway && nic != NULL && net->n_nics > 0) {
+ for (i=0; i<net->n_nics; i++)
+- if (extra != net->nic_extra[i]) {
++ if (nic != net->nic_data[i]) {
+ struct ethernet_packet_link *lp;
+ lp = net_allocate_ethernet_packet_link(net,
+- net->nic_extra[i], len);
++ net->nic_data[i], len);
+
+ /* Copy the entire packet: */
+ memcpy(lp->data, packet, len);
+@@ -438,7 +456,7 @@ void net_ethernet_tx(struct net *net, vo
+ if (eth_type == ETHERTYPE_IP) {
+ /* Routed via the gateway? */
+ if (for_the_gateway) {
+- net_ip(net, extra, packet, len);
++ net_ip(net, nic, packet, len);
+ return;
+ }
+
+@@ -446,7 +464,7 @@ void net_ethernet_tx(struct net *net, vo
+ if (packet[0] == 0xff && packet[1] == 0xff &&
+ packet[2] == 0xff && packet[3] == 0xff &&
+ packet[4] == 0xff && packet[5] == 0xff) {
+- net_ip_broadcast(net, extra, packet, len);
++ net_ip_broadcast(net, nic, packet, len);
+ return;
+ }
+
+@@ -465,13 +483,13 @@ void net_ethernet_tx(struct net *net, vo
+ if (len != 42 && len != 60)
+ fatal("[ net_ethernet_tx: WARNING! unusual "
+ "ARP len (%i) ]\n", len);
+- net_arp(net, extra, packet + 14, len - 14, 0);
++ net_arp(net, nic, packet + 14, len - 14, 0);
+ return;
+ }
+
+ /* RARP: */
+ if (eth_type == ETHERTYPE_REVARP) {
+- net_arp(net, extra, packet + 14, len - 14, 1);
++ net_arp(net, nic, packet + 14, len - 14, 1);
+ return;
+ }
+
+@@ -595,21 +613,28 @@ static void parse_resolvconf(struct net
+ * Add a NIC to a network. (All NICs on a network will see each other's
+ * packets.)
+ */
+-void net_add_nic(struct net *net, void *extra, unsigned char *macaddr)
++void net_add_nic(struct net *net, struct nic_data *nic)
+ {
+ if (net == NULL)
+ return;
+
+- if (extra == NULL) {
+- fprintf(stderr, "net_add_nic(): extra = NULL\n");
++ if (nic == NULL) {
++ fprintf(stderr, "net_add_nic(): nic = NULL\n");
+ exit(1);
+ }
+
+- net->n_nics ++;
+- CHECK_ALLOCATION(net->nic_extra = (void **)
+- realloc(net->nic_extra, sizeof(void *) * net->n_nics));
++ /*
++ * Set up some of the basics for this NIC. We assume the
++ * device has set up all of the other fields.
++ */
++ nic->net = net;
++ nic->promiscuous_mode = 0;
++
++ net->n_nics++;
++ CHECK_ALLOCATION(net->nic_data = (struct nic_data **)
++ realloc(net->nic_data, sizeof(struct nic_data *) * net->n_nics));
+
+- net->nic_extra[net->n_nics - 1] = extra;
++ net->nic_data[net->n_nics - 1] = nic;
+ }
+
+
+@@ -661,6 +686,12 @@ void net_dumpinfo(struct net *net)
+
+ debug_indentation(iadd);
+
++ if (net->tapdev) {
++ debug("tap device: %s\n", net->tapdev);
++ debug_indentation(-iadd);
++ return;
++ }
++
+ debug("simulated network: ");
+ net_debugaddr(&net->netmask_ipv4, NET_ADDR_IPV4);
+ debug("/%i", net->netmask_ipv4_len);
+@@ -718,6 +749,7 @@ void net_dumpinfo(struct net *net)
+ * On failure, exit() is called.
+ */
+ struct net *net_init(struct emul *emul, int init_flags,
++ const char *tapdev,
+ const char *ipv4addr, int netipv4len,
+ char **remote, int n_remote, int local_port,
+ const char *settings_prefix)
+@@ -734,6 +766,19 @@ struct net *net_init(struct emul *emul,
+ /* Sane defaults: */
+ net->timestamp = 0;
+ net->first_ethernet_packet = net->last_ethernet_packet = NULL;
++ net->tapdev = NULL;
++ net->tap_fd = -1;
++
++ /*
++ * If we're using a tap device, attempt to initialize it and
++ * none of the other stuff.
++ */
++ if (tapdev) {
++ if (! net_tap_init(net, tapdev))
++ exit(1);
++ net_dumpinfo(net);
++ return net;
++ }
+
+ #ifdef HAVE_INET_PTON
+ res = inet_pton(AF_INET, ipv4addr, &net->netmask_ipv4);
diff --git a/emulators/gxemul/patches/patch-src_net_net_ether.cc b/emulators/gxemul/patches/patch-src_net_net_ether.cc
new file mode 100644
index 00000000000..ed31349cfb6
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_net_net_ether.cc
@@ -0,0 +1,150 @@
+$NetBSD: patch-src_net_net_ether.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add some generic Ethernet routines used for address filtering.
+
+--- /dev/null 2020-10-05 22:44:11.028207457 +0000
++++ src/net/net_ether.cc 2020-10-05 23:01:48.744053911 +0000
+@@ -0,0 +1,143 @@
++/*
++ * Copyright (C) 2020 Jason R. Thorpe. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote products
++ * derived from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Common Ethernet support routines.
++ */
++
++#include <sys/types.h>
++#include <string.h>
++
++#include "misc.h"
++#include "net.h"
++
++
++/*
++ * net_ether_eq():
++ *
++ * Compare two Ethernet addresses for equality.
++ */
++int net_ether_eq(const uint8_t *a1, const uint8_t *a2)
++{
++
++ return a1[5] == a2[5] &&
++ a1[4] == a2[4] &&
++ a1[3] == a2[3] &&
++ a1[2] == a2[2] &&
++ a1[1] == a2[1] &&
++ a1[0] == a2[0];
++}
++
++
++/*
++ * net_ether_broadcast():
++ *
++ * Returns 1 if the specified destination address is the Ethernet
++ * broadcast address.
++ */
++int net_ether_broadcast(const uint8_t *a)
++{
++ static const uint8_t ether_broadcast[6] =
++ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++ return net_ether_eq(a, ether_broadcast);
++}
++
++
++/*
++ * net_ether_multicast():
++ *
++ * Returns 1 if the specfied destination address is an Ethernet
++ * multicast address.
++ *
++ * Note that this also matches Ethernet broadcast, which is just
++ * a special case of multicast.
++ */
++int net_ether_multicast(const uint8_t *a)
++{
++ return (*a & 0x01);
++}
++
++
++/*
++ * Copyright (c) 1982, 1989, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)if_ethersubr.c 8.2 (Berkeley) 4/4/96
++ */
++
++
++/*
++ * net_ether_crc32_le():
++ *
++ * Fast table-driven little-endian Ethernet CRC generator.
++ */
++uint32_t net_ether_crc32_le(const uint8_t *buf, size_t len)
++{
++ static const uint32_t crctab[] = {
++ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
++ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
++ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
++ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
++ };
++ uint32_t crc;
++ size_t i;
++
++ crc = 0xffffffffU; /* initial value */
++
++ for (i = 0; i < len; i++) {
++ crc ^= buf[i];
++ crc = (crc >> 4) ^ crctab[crc & 0xf];
++ crc = (crc >> 4) ^ crctab[crc & 0xf];
++ }
++
++ return crc;
++}
diff --git a/emulators/gxemul/patches/patch-src_net_net_ip.cc b/emulators/gxemul/patches/patch-src_net_net_ip.cc
new file mode 100644
index 00000000000..89e8b9370c1
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_net_net_ip.cc
@@ -0,0 +1,238 @@
+$NetBSD: patch-src_net_net_ip.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- src/net/net_ip.cc.orig 2020-10-05 23:02:21.375165006 +0000
++++ src/net/net_ip.cc 2020-10-05 23:02:33.181030722 +0000
+@@ -155,7 +155,7 @@ void net_ip_tcp_checksum(unsigned char *
+ * 1c1d1e1f202122232425262728292a2b
+ * 2c2d2e2f3031323334353637
+ */
+-static void net_ip_icmp(struct net *net, void *extra,
++static void net_ip_icmp(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len)
+ {
+ int type;
+@@ -166,7 +166,7 @@ static void net_ip_icmp(struct net *net,
+ switch (type) {
+ case 8: /* ECHO request */
+ debug("[ ICMP echo ]\n");
+- lp = net_allocate_ethernet_packet_link(net, extra, len);
++ lp = net_allocate_ethernet_packet_link(net, nic, len);
+
+ /* Copy the old packet first: */
+ memcpy(lp->data + 12, packet + 12, len - 12);
+@@ -225,7 +225,7 @@ static void tcp_closeconnection(struct n
+ * This creates an ethernet packet for the guest OS with an ACK to the
+ * initial SYN packet.
+ */
+-void net_ip_tcp_connectionreply(struct net *net, void *extra,
++void net_ip_tcp_connectionreply(struct net *net, struct nic_data *nic,
+ int con_id, int connecting, unsigned char *data, int datalen, int rst)
+ {
+ struct ethernet_packet_link *lp;
+@@ -238,7 +238,7 @@ void net_ip_tcp_connectionreply(struct n
+ net->tcp_connections[con_id].tcp_id ++;
+ tcp_length = 20 + option_len + datalen;
+ ip_len = 20 + tcp_length;
+- lp = net_allocate_ethernet_packet_link(net, extra, 14 + ip_len);
++ lp = net_allocate_ethernet_packet_link(net, nic, 14 + ip_len);
+
+ /* Ethernet header: */
+ memcpy(lp->data + 0, net->tcp_connections[con_id].ethernet_address, 6);
+@@ -376,7 +376,7 @@ void net_ip_tcp_connectionreply(struct n
+ * http://www.networksorcery.com/enp/protocol/tcp.htm
+ * http://www.tcpipguide.com/free/t_TCPIPTransmissionControlProtocolTCP.htm
+ */
+-static void net_ip_tcp(struct net *net, void *extra,
++static void net_ip_tcp(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len)
+ {
+ int con_id, free_con_id, i, res;
+@@ -585,7 +585,7 @@ static void net_ip_tcp(struct net *net,
+
+ if (rst) {
+ debug("[ 'rst': disconnecting TCP connection %i ]\n", con_id);
+- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1);
++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 1);
+ tcp_closeconnection(net, con_id);
+ return;
+ }
+@@ -596,7 +596,7 @@ static void net_ip_tcp(struct net *net,
+ "connection %i ]\n", con_id);
+
+ /* Send an RST? (TODO, this is wrong...) */
+- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1);
++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 1);
+
+ /* ... and forget about this connection: */
+ tcp_closeconnection(net, con_id);
+@@ -610,7 +610,7 @@ static void net_ip_tcp(struct net *net,
+
+ /* Send an ACK: */
+ net->tcp_connections[con_id].state = TCP_OUTSIDE_CONNECTED;
+- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0);
++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 0);
+ net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2;
+ return;
+ }
+@@ -620,7 +620,7 @@ static void net_ip_tcp(struct net *net,
+ con_id);
+
+ /* Send ACK: */
+- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0);
++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 0);
+ net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2;
+
+ /* Return and send FIN: */
+@@ -725,7 +725,7 @@ debug(" all acked\n");
+
+ ret:
+ /* Send an ACK (or FIN) to the guest OS: */
+- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0);
++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 0);
+ }
+
+
+@@ -746,7 +746,7 @@ ret:
+ * srcport=fffc dstport=0035 length=0028 chksum=76b6
+ * 43e20100000100000000000003667470066e6574627364036f726700001c0001
+ */
+-static void net_ip_udp(struct net *net, void *extra,
++static void net_ip_udp(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len)
+ {
+ int con_id, free_con_id, i, srcport, dstport, udp_len;
+@@ -882,7 +882,8 @@ static void net_ip_udp(struct net *net,
+ *
+ * Handle an IP packet, coming from the emulated NIC.
+ */
+-void net_ip(struct net *net, void *extra, unsigned char *packet, int len)
++void net_ip(struct net *net, struct nic_data *nic, unsigned char *packet,
++ int len)
+ {
+ #if 1
+ int i;
+@@ -913,13 +914,13 @@ void net_ip(struct net *net, void *extra
+ /* IPv4: */
+ switch (packet[23]) {
+ case 1: /* ICMP */
+- net_ip_icmp(net, extra, packet, len);
++ net_ip_icmp(net, nic, packet, len);
+ break;
+ case 6: /* TCP */
+- net_ip_tcp(net, extra, packet, len);
++ net_ip_tcp(net, nic, packet, len);
+ break;
+ case 17:/* UDP */
+- net_ip_udp(net, extra, packet, len);
++ net_ip_udp(net, nic, packet, len);
+ break;
+ default:
+ fatal("[ net: IP: UNIMPLEMENTED protocol %i ]\n",
+@@ -939,7 +940,7 @@ void net_ip(struct net *net, void *extra
+ * Read http://tools.ietf.org/html/rfc2131 for details on DHCP.
+ * (And http://users.telenet.be/mydotcom/library/network/dhcp.htm.)
+ */
+-static void net_ip_broadcast_dhcp(struct net *net, void *extra,
++static void net_ip_broadcast_dhcp(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len)
+ {
+ /*
+@@ -1008,7 +1009,7 @@ static void net_ip_broadcast_dhcp(struct
+ fatal(" ]\n");
+
+ reply_len = 307;
+- lp = net_allocate_ethernet_packet_link(net, extra, reply_len);
++ lp = net_allocate_ethernet_packet_link(net, nic, reply_len);
+
+ /* From old packet, copy everything before options field: */
+ memcpy(lp->data, packet, 278);
+@@ -1130,7 +1131,7 @@ packet = lp->data;
+ * Handle an IP broadcast packet, coming from the emulated NIC.
+ * (This is usually a DHCP request, or similar.)
+ */
+-void net_ip_broadcast(struct net *net, void *extra,
++void net_ip_broadcast(struct net *net, struct nic_data *nic,
+ unsigned char *packet, int len)
+ {
+ unsigned char *p = (unsigned char *) &net->netmask_ipv4;
+@@ -1193,7 +1194,7 @@ void net_ip_broadcast(struct net *net, v
+ packet[23] == 0x11 && /* UDP */
+ packet[34] == 0 && packet[35] == 68 && /* DHCP client */
+ packet[36] == 0 && packet[37] == 67) { /* DHCP server */
+- net_ip_broadcast_dhcp(net, extra, packet, len);
++ net_ip_broadcast_dhcp(net, nic, packet, len);
+ return;
+ }
+
+@@ -1222,7 +1223,7 @@ void net_ip_broadcast(struct net *net, v
+ *
+ * Receive any available UDP packets (from the outside world).
+ */
+-void net_udp_rx_avail(struct net *net, void *extra)
++void net_udp_rx_avail(struct net *net, struct nic_data *nic)
+ {
+ int received_packets_this_tick = 0;
+ int max_packets_this_tick = 200;
+@@ -1326,7 +1327,7 @@ void net_udp_rx_avail(struct net *net, v
+
+ ip_len = 20 + this_packets_data_length;
+
+- lp = net_allocate_ethernet_packet_link(net, extra,
++ lp = net_allocate_ethernet_packet_link(net, nic,
+ 14 + 20 + this_packets_data_length);
+
+ /* Ethernet header: */
+@@ -1381,7 +1382,7 @@ void net_udp_rx_avail(struct net *net, v
+ *
+ * Receive any available TCP packets (from the outside world).
+ */
+-void net_tcp_rx_avail(struct net *net, void *extra)
++void net_tcp_rx_avail(struct net *net, struct nic_data *nic)
+ {
+ int received_packets_this_tick = 0;
+ int max_packets_this_tick = 200;
+@@ -1445,7 +1446,7 @@ void net_tcp_rx_avail(struct net *net, v
+ net->tcp_connections[con_id].state =
+ TCP_OUTSIDE_CONNECTED;
+ debug("CHANGING TO TCP_OUTSIDE_CONNECTED\n");
+- net_ip_tcp_connectionreply(net, extra, con_id, 1,
++ net_ip_tcp_connectionreply(net, nic, con_id, 1,
+ NULL, 0, 0);
+ }
+
+@@ -1477,7 +1478,7 @@ void net_tcp_rx_avail(struct net *net, v
+ net->tcp_connections[con_id].
+ incoming_buf_seqnr;
+
+- net_ip_tcp_connectionreply(net, extra, con_id,
++ net_ip_tcp_connectionreply(net, nic, con_id,
+ 0, net->tcp_connections[con_id].
+ incoming_buf,
+ net->tcp_connections[con_id].
+@@ -1519,21 +1520,21 @@ void net_tcp_rx_avail(struct net *net, v
+ memcpy(net->tcp_connections[con_id].incoming_buf,
+ buf, res);
+
+- net_ip_tcp_connectionreply(net, extra, con_id, 0,
++ net_ip_tcp_connectionreply(net, nic, con_id, 0,
+ buf, res, 0);
+ } else if (res == 0) {
+ net->tcp_connections[con_id].state =
+ TCP_OUTSIDE_DISCONNECTED;
+ debug("CHANGING TO TCP_OUTSIDE_DISCONNECTED, read"
+ " res=0\n");
+- net_ip_tcp_connectionreply(net, extra, con_id, 0,
++ net_ip_tcp_connectionreply(net, nic, con_id, 0,
+ NULL, 0, 0);
+ } else {
+ net->tcp_connections[con_id].state =
+ TCP_OUTSIDE_DISCONNECTED;
+ fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED, "
+ "read res<=0, errno = %i\n", errno);
+- net_ip_tcp_connectionreply(net, extra, con_id, 0,
++ net_ip_tcp_connectionreply(net, nic, con_id, 0,
+ NULL, 0, 0);
+ }
+
diff --git a/emulators/gxemul/patches/patch-src_net_net_tap.cc b/emulators/gxemul/patches/patch-src_net_net_tap.cc
new file mode 100644
index 00000000000..cf7d9e061e5
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_net_net_tap.cc
@@ -0,0 +1,192 @@
+$NetBSD: patch-src_net_net_tap.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- /dev/null 2020-10-05 22:44:11.028207457 +0000
++++ src/net/net_tap.cc 2020-10-05 23:03:15.768748362 +0000
+@@ -0,0 +1,185 @@
++/*
++ * Copyright (C) 2020 Jason R. Thorpe. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote products
++ * derived from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Support for Ethernet tap interfaces.
++ *
++ * A single tap instance is used for the entire simulated network.
++ * We treat this as sort of virtual Ethernet switch, with the tap
++ * being the upstream port. This is very simple, conceptually, and
++ * fits in nicely with the rest of the network simulation model in
++ * GXemul.
++ *
++ * Use of the tap interface is completely optional, but if it is used
++ * the all of the virtual IP network support is bypassed completely.
++ */
++
++#include <sys/types.h>
++#include <sys/ioctl.h>
++#include <fcntl.h>
++#include <string.h>
++#include <unistd.h>
++
++#include "misc.h"
++#include "net.h"
++
++/*
++ * net_tap_rx_for_nic():
++ *
++ * Receive a packet from the virtual Ethernet switch for this NIC.
++ */
++static void net_tap_rx_for_nic(struct net *net, struct nic_data *nic,
++ unsigned char *buf, ssize_t size)
++{
++ struct ethernet_packet_link *lp;
++
++ /*
++ * We should deliver to the interface if:
++ *
++ * ==> The interface is in promiscuous mode.
++ * -- or --
++ * ==> The packet is broadcast or multicast (the emulated device
++ * can further apply a multicast filter if it wishes).
++ * -- or --
++ * ==> The destination MAC address matches the NIC MAC address.
++ *
++ * Note that normally a switch would not know if an interface
++ * is in promiscuous mode, but this is a bit of extra magic
++ * we implement because we can for the sake of convenience.
++ * Also, some emulated interfaces may want to see all packets
++ * so as to implement their own filtering logic.
++ *
++ * Also note that testing for multicast also catches the broadcast
++ * case.
++ */
++
++ if (nic->promiscuous_mode ||
++ net_ether_multicast(buf) || net_ether_eq(nic->mac_address, buf)) {
++ lp = net_allocate_ethernet_packet_link(net, nic, (int)size);
++ memcpy(lp->data, buf, size);
++ }
++}
++
++/*
++ * net_tap_rx_avail():
++ *
++ * We poll the net-shared tap device and link up any available packets to
++ * their destination interfaces, acting like a virtual Ethernet switch.
++ */
++void net_tap_rx_avail(struct net *net)
++{
++ int received_packets_this_tick = 0;
++ int max_packets_this_tick = 200;
++
++ for (;;) {
++ unsigned char buf[1518];
++ ssize_t bytes_read;
++ int i;
++
++ if (received_packets_this_tick > max_packets_this_tick)
++ break;
++
++ /* Read one packet from the tap device. */
++ bytes_read = read(net->tap_fd, buf, sizeof(buf));
++
++ if (bytes_read < 0) {
++ /* No more packets available on the tap. */
++ break;
++ }
++
++ /*
++ * Drop runt packets now; allow other layers to assume
++ * valid Ethernet frames. This really should be 64, but
++ * 20 is used in the transmit path.
++ */
++ if (bytes_read < 20)
++ continue;
++
++ for (i = 0; i < net->n_nics; i++) {
++ net_tap_rx_for_nic(net, net->nic_data[i],
++ buf, bytes_read);
++ }
++ }
++}
++
++/*
++ * net_tap_tx():
++ *
++ * Transmit an ethernet packet, as seen from the emulated ethernet controller,
++ * to the net-shared tap device. Even if the packet is destined only for
++ * a NIC on the local virtual Ethernet switch, we always send it to the
++ * tap device so that the host system can monitor traffic by running tcpdump
++ * on its view of the tap.
++ */
++void net_tap_tx(struct net *net, struct nic_data *nic,
++ unsigned char *packet, int len)
++{
++ int i;
++
++ for (i = 0; i < net->n_nics; i++) {
++ if (nic == net->nic_data[i])
++ continue;
++ net_tap_rx_for_nic(net, net->nic_data[i], packet, len);
++ }
++
++ /*
++ * Don't bother checking for errors here. The tap driver in the
++ * kernel will either take the entire packet or none of it, and
++ * there isn't any useful error recovery for us anyway.
++ */
++ write(net->tap_fd, packet, len);
++}
++
++/*
++ * net_tap_init():
++ *
++ * Initialize the tap interface. Returns 1 if successful, 0 otherwise.
++ */
++int net_tap_init(struct net *net, const char *tapdev)
++{
++ int fd;
++ int one = 1;
++
++ fd = open(tapdev, O_RDWR);
++ if (fd < 0) {
++ fatal("[ net: unable to open tap device '%s': %s ]\n",
++ tapdev, strerror(errno));
++ return 0;
++ }
++
++ if (ioctl(fd, FIONBIO, &one) < 0) {
++ fatal("[ net: unable to set non-blocking mode on "
++ "tap device '%s': %s ]\n", tapdev, strerror(errno));
++ close(fd);
++ return 0;
++ }
++
++ net->tapdev = strdup(tapdev);
++ net->tap_fd = fd;
++
++ return 1;
++}
diff --git a/emulators/gxemul/patches/patch-src_old_main_emul.cc b/emulators/gxemul/patches/patch-src_old_main_emul.cc
new file mode 100644
index 00000000000..d096fb655a2
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_old_main_emul.cc
@@ -0,0 +1,14 @@
+$NetBSD: patch-src_old_main_emul.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- src/old_main/emul.cc.orig 2020-10-05 23:04:14.559513959 +0000
++++ src/old_main/emul.cc 2020-10-05 23:04:26.418738637 +0000
+@@ -748,6 +748,7 @@ void emul_simple_init(struct emul *emul)
+
+ /* Create a simple network: */
+ emul->net = net_init(emul, NET_INIT_FLAG_GATEWAY,
++ NULL,
+ NET_DEFAULT_IPV4_MASK,
+ NET_DEFAULT_IPV4_LEN,
+ NULL, 0, 0, NULL);
diff --git a/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc b/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc
new file mode 100644
index 00000000000..e4240e1dd14
--- /dev/null
+++ b/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc
@@ -0,0 +1,38 @@
+$NetBSD: patch-src_old_main_emul_parse.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $
+
+Add support for tap(4)-based networking.
+
+--- src/old_main/emul_parse.cc.orig 2020-10-05 23:04:38.529354586 +0000
++++ src/old_main/emul_parse.cc 2020-10-05 23:04:50.653298084 +0000
+@@ -197,6 +197,7 @@ static void read_one_word(FILE *f, char
+ #define PARSESTATE_NET 2
+ #define PARSESTATE_MACHINE 3
+
++static char cur_net_tapdev[50];
+ static char cur_net_ipv4net[50];
+ static char cur_net_ipv4len[50];
+ static char cur_net_local_port[10];
+@@ -315,6 +316,7 @@ static void parse__emul(struct emul *e,
+ line, EXPECT_LEFT_PARENTHESIS);
+
+ /* Default net: */
++ strlcpy(cur_net_tapdev, "", sizeof(cur_net_tapdev));
+ strlcpy(cur_net_ipv4net, NET_DEFAULT_IPV4_MASK,
+ sizeof(cur_net_ipv4net));
+ snprintf(cur_net_ipv4len, sizeof(cur_net_ipv4len), "%i",
+@@ -391,6 +393,7 @@ static void parse__net(struct emul *e, F
+ sizeof(cur_net_local_port));
+
+ e->net = net_init(e, NET_INIT_FLAG_GATEWAY,
++ cur_net_tapdev[0] ? cur_net_tapdev : NULL,
+ cur_net_ipv4net, atoi(cur_net_ipv4len),
+ cur_net_remote, cur_net_n_remote,
+ atoi(cur_net_local_port), NULL);
+@@ -410,6 +413,7 @@ static void parse__net(struct emul *e, F
+ return;
+ }
+
++ WORD("tapdev", cur_net_tapdev);
+ WORD("ipv4net", cur_net_ipv4net);
+ WORD("ipv4len", cur_net_ipv4len);
+ WORD("local_port", cur_net_local_port);