Add very simple "network port UART" component to Renode.

Change-Id: I74c61847d25f26e4534cb410b9a943467bb4b71c
diff --git a/platforms/secure.repl b/platforms/secure.repl
index 2cbb2fe..68705f2 100644
--- a/platforms/secure.repl
+++ b/platforms/secure.repl
@@ -95,6 +95,11 @@
 
 // PATGEN            [‘h400E_0000 - ‘h400E_0FFF)   4KB PATGEN
 
+// Fake network-port-backed UART for testing in Renode
+net_uart: NetUart @ sysbus 0x400F0000
+    name: "NetUart"
+    port: 31415
+
 // RV_TIMER          [‘h4010_0000 - ‘h4010_0FFF)   4KB RV Timer for Security Core
 timer: Timers.OpenTitan_Timer@ sysbus 0x40100000
     frequency: 24000000
diff --git a/shodan.resc b/shodan.resc
index d2bfb53..4c49ea7 100644
--- a/shodan.resc
+++ b/shodan.resc
@@ -14,6 +14,7 @@
 include @sim/config/shodan_infrastructure/Mailbox.cs
 include @sim/renode/tools/sel4_extensions/seL4Extensions.cs
 include @sim/config/shodan_infrastructure/AddressRangeStub.cs
+include @sim/config/shodan_infrastructure/NetUart.cs
 
 machine LoadPlatformDescription @sim/config/platforms/shodan.repl
 
diff --git a/shodan_infrastructure/NetUart.cs b/shodan_infrastructure/NetUart.cs
new file mode 100644
index 0000000..d557345
--- /dev/null
+++ b/shodan_infrastructure/NetUart.cs
@@ -0,0 +1,115 @@
+//
+// Copyright (c) 2021 Google LLC
+//
+// This file is licensed under the MIT License.
+// Full license text is available in 'licenses/MIT.txt'.
+//
+using Antmicro.Renode.Core;
+using Antmicro.Renode.Logging;
+using Antmicro.Renode.Peripherals.Bus;
+
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Collections.Concurrent;
+
+// This is a very simple Renode periperal that provides a byte-oriented
+// communication port to the simulated SOC that's backed by a network port on
+// the host workstation. The port is intended for debugging only - it has no
+// real flow control, no interrupts, no error checking - but it should be around
+// 5x faster than using a real UART model to communicate with your SOC.
+//
+// Add this to your SOC .repl file:
+//
+// net_uart: NetUart @ sysbus <base_address>
+//     name: "NetUart"
+//     port: <network_port>
+//
+// Communicate with the port in your firmware like this:
+//
+// struct NetUart {
+//   uint32_t avail;
+//   uint32_t read;
+//   uint32_t write;
+// };
+// volatile NetUart* net_uart = reinterpret_cast<NetUart*>(base_address);
+//
+// uint8_t get_byte() {
+//   while (!net_uart->avail) {}
+//   return net_uart->read;
+// };
+//
+// void set_byte(uint8_t b) {
+//   net_uart->write = b;
+// };
+//
+// And then you can run "telnet localhost <network_port>" on your workstation to
+// communicate with your SOC.
+namespace Antmicro.Renode.Peripherals
+{
+  public class NetUart : IDoubleWordPeripheral, IKnownSize
+  {
+    public long Size => 0x12;
+
+    TcpListener listener = null;
+    Socket client = null;
+    byte[] buffer = null;
+    Thread accept_thread;
+    string name;
+
+    public NetUart(string name, int port) {
+      this.name = name;
+
+      listener = new TcpListener(port);
+      listener.Start();
+
+      this.Log(LogLevel.Info, "NetUart '{0}' starting at {1}", name, listener.LocalEndpoint);
+
+      buffer = new byte[4096];
+      accept_thread = new Thread(Accept);
+      accept_thread.Start();
+    }
+
+    public void Accept() {
+        while(true) {
+            Socket new_client = listener.AcceptSocket();
+            this.Log(LogLevel.Info, "NetUart '{0}' got connection from {1}", name, new_client.RemoteEndPoint);
+            lock (this) {
+                client = new_client;
+            }
+        }
+    }
+
+    public void Reset() {
+    }
+
+    public uint ReadDoubleWord(long offset) {
+        lock(this) {
+            if (client == null) {
+                return 0;
+            }
+            else if (offset == 0) {
+                return (uint)client.Available;
+            }
+            else if (offset == 4 && client.Available > 0) {
+                client.Receive(buffer, 1, 0);
+                return buffer[0];
+            }
+            else {
+                return 0;
+            }
+        }
+    }
+
+    public void WriteDoubleWord(long offset, uint value) {
+        lock(this) {
+            if (client != null && offset == 8) {
+                buffer[0] = (byte)value;
+                client.Send(buffer, 1, 0);
+            }
+        }
+    }
+  }
+}