summaryrefslogtreecommitdiffstats
path: root/zpu/sw/simulator/com/zylin/zpu/simulator/gdb/GDBServer.java
diff options
context:
space:
mode:
Diffstat (limited to 'zpu/sw/simulator/com/zylin/zpu/simulator/gdb/GDBServer.java')
-rw-r--r--zpu/sw/simulator/com/zylin/zpu/simulator/gdb/GDBServer.java609
1 files changed, 609 insertions, 0 deletions
diff --git a/zpu/sw/simulator/com/zylin/zpu/simulator/gdb/GDBServer.java b/zpu/sw/simulator/com/zylin/zpu/simulator/gdb/GDBServer.java
new file mode 100644
index 0000000..2580ded
--- /dev/null
+++ b/zpu/sw/simulator/com/zylin/zpu/simulator/gdb/GDBServer.java
@@ -0,0 +1,609 @@
+/**
+ * Handles TCP/IP communication between simulator and GDB
+ */
+
+package com.zylin.zpu.simulator.gdb;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+
+import com.zylin.zpu.simulator.Host;
+import com.zylin.zpu.simulator.Sim;
+import com.zylin.zpu.simulator.SimApp;
+import com.zylin.zpu.simulator.exceptions.BadPacketException;
+import com.zylin.zpu.simulator.exceptions.CPUException;
+import com.zylin.zpu.simulator.exceptions.EndSessionException;
+import com.zylin.zpu.simulator.exceptions.GDBServerException;
+import com.zylin.zpu.simulator.exceptions.MemoryAccessException;
+import com.zylin.zpu.simulator.exceptions.UnsupportedSyscallException;
+
+public class GDBServer implements Host
+{
+ static final boolean UNKNOWN=false;
+ static final boolean ALL=false;
+ static final boolean CPUEXCEPTION = false;
+ static protected boolean MINIMAL=true;
+ static boolean PACKET=false;
+ static boolean REPLY=false;
+ static protected boolean IGNOREDEXCEPTIONS=false;
+ protected Throwable packetException;
+ protected Object packetReady=new Object();
+ private Packet packet;
+ boolean done;
+ private Thread asyncMessage;
+ private Object listenBreak=new Object();
+ private boolean listenForBreak;
+ private boolean sleeping;
+ private ByteBuffer readBuffer;
+ private ByteBuffer writeBuffer;
+ private SocketChannel sc;
+ private Selector selectorRead;
+ private Selector selectorWrite;
+ public boolean alive;
+ static private int sessionNr;
+ private SimApp app;
+ private boolean stopAsyncMessage;
+ Sim simulator;
+
+ public GDBServer(Sim simulator, SimApp app)
+ {
+ this.simulator=simulator;
+ this.app=app;
+ }
+
+ void print(boolean filter, String str)
+ {
+ if (filter)
+ {
+ System.out.println(str);
+ }
+ }
+
+ /** infinite loop that waits for debug sessions to be initiated via TCP/IP */
+ public void gdbServer() throws MemoryAccessException, IOException, GDBServerException, EndSessionException
+ {
+ try
+ {
+ asyncMessage = new Thread(new Runnable()
+ {
+ public void run()
+ {
+ asyncMessage();
+ }
+ });
+ asyncMessage.start();
+ try
+ {
+ readBuffer = ByteBuffer.allocate(1);
+ writeBuffer = ByteBuffer.allocate(128);
+ debugSession();
+ }
+ finally
+ {
+ /* tell it to stop waiting for break chars and wake up the thread */
+ stopAsyncMessage = true;
+ synchronized(listenBreak)
+ {
+ listenBreak.notify();
+ }
+
+ try
+ {
+ asyncMessage.join();
+ } catch (InterruptedException e3)
+ {
+ e3.printStackTrace();
+ }
+ }
+ } catch (IOException e)
+ {
+ // the session failed...
+ if (IGNOREDEXCEPTIONS)
+ {
+ e.printStackTrace();
+ }
+ } catch (GDBServerException e)
+ {
+ // connect failed...
+ if (IGNOREDEXCEPTIONS)
+ {
+ e.printStackTrace();
+ }
+ } catch (EndSessionException e)
+ {
+ } catch (Throwable e)
+ {
+ // some terrible unforseen failure.
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * We have to wait for break, but as soon as the main thread wants to wait
+ * for packets again, we have to stop waiting for a break.
+ *
+ * Tricky....
+ */
+ private void asyncMessage()
+ {
+ for (;;)
+ {
+ synchronized(listenBreak)
+ {
+ if (stopAsyncMessage)
+ {
+ /* shutting down */
+ return;
+ }
+ try
+ {
+ sleeping=true;
+ listenBreak.notify();
+
+ listenBreak.wait();
+ sleeping=false;
+ listenBreak.notify();
+ } catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ if (stopAsyncMessage)
+ {
+ /* shutting down */
+ return;
+ }
+ }
+
+ while (listenForBreak)
+ {
+ try
+ {
+ if (waitSelect(selectorRead, true))
+ {
+ int t = read();
+ if (t == 0x03)
+ {
+ // We received a ctrl-c while processing a package,
+ // this
+ // would be a suspend
+ simulator.suspend();
+ } else
+ {
+ // ignore garbage. Shouldn't happen.
+ }
+ } else
+ {
+ // we've been awoken since we're ready to send
+ // the reply to the package...
+// int x=0;
+ }
+ } catch (IOException e)
+ {
+ // Perfectly normal. This would happen if the connection
+ // is terminated.
+ }
+ }
+ }
+ }
+
+ /** wait for read/write ready */
+ private boolean waitSelect(Selector selector, boolean read) throws IOException
+ {
+ boolean gotit=false;
+
+ selector.select(1000);
+ if (!sc.isOpen())
+ {
+ throw new IOException("Channel closed");
+ }
+ if (!sc.isConnected())
+ {
+ throw new IOException("Channel not connected");
+ }
+
+ // Get list of selection keys with pending events
+ Iterator it = selector.selectedKeys().iterator();
+ // Process each key at a time
+ while (it.hasNext())
+ {
+ // Get the selection key
+ SelectionKey selKey = (SelectionKey) it.next();
+ // Remove it from the list to indicate that it is being
+ // processed
+ it.remove();
+ if (selKey.isValid() &&
+ ((read && selKey.isReadable()) || (!read && selKey.isWritable())))
+ {
+ gotit=true;
+ }
+
+ }
+ return gotit;
+ }
+
+
+ protected void sleepABit()
+ {
+ try
+ {
+ // just to avoid locking up the machine in a busy loop when
+ // debugging the Simulator
+ Thread.sleep(2000);
+ } catch (InterruptedException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+
+ private void debugSession() throws IOException, GDBServerException, EndSessionException, MemoryAccessException
+ {
+ print(MINIMAL, "GDB server waiting for connection " + sessionNr++ + "...");
+
+ writeBuffer.clear();
+ readBuffer.clear();
+
+
+ selectorRead = Selector.open();
+ try
+ {
+ selectorWrite = Selector.open();
+ try
+ {
+ sc = app.channel.accept();
+ try
+ {
+ sc.socket().setKeepAlive(true);
+ sc.configureBlocking(false);
+ sc.register(selectorRead, SelectionKey.OP_READ);
+ sc.register(selectorWrite, SelectionKey.OP_WRITE);
+
+ sessionStarted();
+
+ expect('+'); // connection ack.
+
+ sessionLoop();
+ } finally
+ {
+ sc.close();
+
+ print(MINIMAL, "Session ended");
+ }
+ } finally
+ {
+ selectorWrite.close();
+ }
+ } finally
+ {
+ selectorRead.close();
+ }
+
+ }
+
+ private void sessionStarted()
+ {
+ simulator.sessionStarted();
+ print(MINIMAL, "Session started");
+ }
+
+
+ private void sessionLoop() throws IOException, EndSessionException
+ {
+ alive=true;
+ while (alive)
+ {
+ try
+ {
+ /* wait for new packet to arrive and notify the packet execution thread... */
+ packet=new Packet(this);
+ packet.receive();
+
+ enterListenForCtrlC();
+
+ try
+ {
+ // During execution we can receive an abort/suspend command...
+ packet.parseAndExecute();
+ }
+ finally
+ {
+ leaveListenForCtrlC();
+ }
+
+ packet.sendReply();
+
+ if (!alive)
+ throw new EndSessionException();
+ } catch (BadPacketException e)
+ {
+ // do nothing.
+ if (IGNOREDEXCEPTIONS)
+ {
+ e.printStackTrace();
+ }
+ sleepABit();
+ } catch (GDBServerException e)
+ {
+ if (IGNOREDEXCEPTIONS)
+ {
+ e.printStackTrace();
+ }
+ // continue processing packets
+ sleepABit();
+ }
+ }
+ }
+
+ private void enterListenForCtrlC()
+ {
+ setBreakListen(true);
+ }
+
+ private void leaveListenForCtrlC()
+ {
+ /* we don't want to wait for the select to time out as that would make
+ * the protocol excruciatingly slow */
+ setBreakListen(false);
+ selectorRead.wakeup();
+ synchronized(listenBreak)
+ {
+ try
+ {
+ while (!sleeping)
+ {
+ listenBreak.notify();
+ listenBreak.wait();
+ }
+ } catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void setBreakListen(boolean state)
+ {
+ synchronized(listenBreak)
+ {
+ listenForBreak=state;
+ listenBreak.notify();
+ }
+ }
+
+
+
+ void expect(char nextChar) throws IOException, GDBServerException
+ {
+ int t = read();
+ if (t!=nextChar)
+ {
+ throw new BadPacketException();
+ }
+ }
+
+ int read() throws IOException
+ {
+ flush();
+ readBuffer.clear();
+ for (;;)
+ {
+ int n;
+ n = sc.read(readBuffer);
+ if (n == 1)
+ {
+ break;
+ }
+ while (!waitSelect(selectorRead, true));
+ }
+ readBuffer.flip();
+ int t = readBuffer.get(0);
+ return t;
+ }
+
+ /**
+ * @param value
+ * @return
+ */
+ protected String printHex(int value)
+ {
+ return formatHex(value, "00000000");
+ }
+
+ /**
+ * @param value
+ * @param pad TODO
+ * @return
+ */
+ protected String formatHex(int value, String pad)
+ {
+ String t=Integer.toHexString(value);
+ if (t.length()>pad.length())
+ {
+ t=t.substring(0, pad.length());
+ }
+ return pad.substring(0, pad.length()-t.length())+t;
+ }
+
+ public void write(byte[] bytes) throws IOException
+ {
+ int i=0;
+ while (i<bytes.length)
+ {
+ int len;
+
+ while ((len=Math.min(bytes.length-i, writeBuffer.capacity()-writeBuffer.position()))==0)
+ {
+ flush();
+ }
+
+ writeBuffer.put(bytes, i, len);
+
+ i+=len;
+ }
+ }
+
+ void flush() throws IOException
+ {
+ if (writeBuffer.position()>0)
+ {
+ writeBuffer.flip();
+ int len=writeBuffer.limit();
+
+ int j=0;
+ while (j<len)
+ {
+ int t=sc.write(writeBuffer);
+
+ if (t==0)
+ {
+ while (!waitSelect(selectorWrite, false));
+ }
+ j+=t;
+ }
+ writeBuffer.clear();
+ }
+ }
+
+
+ private boolean enterSyscall;
+
+
+
+ /* handle all sorts of IO calls, etc. by sending them to the
+ */
+ public void syscall(Sim s) throws CPUException
+ {
+ simulator.suspend();
+ enterSyscall=true;
+ }
+
+
+ protected void performSyscall()
+ {
+ enterSyscall=false;
+ try
+ {
+ int id;
+ id=simulator.getArg(1);
+ Packet syscall;
+ syscall=new Packet(this);
+ switch (id)
+ {
+ case Host.SYS_write:
+ syscall.invokeSyscall("write", 3, "iii");
+ break;
+ case Host.SYS_read:
+ syscall.invokeSyscall("read", 3, "iii");
+ break;
+ case Host.SYS_lseek:
+ syscall.invokeSyscall("lseek", 3, "iii");
+ break;
+ case Host.SYS_open:
+ syscall.invokeSyscall("open", 3, "sii");
+ break;
+ case Host.SYS_close:
+ syscall.invokeSyscall("close", 1, "i");
+ break;
+ case Host.SYS_fstat:
+ syscall.invokeSyscall("fstat", 2, "ii");
+ break;
+ case Host.SYS_stat:
+ syscall.invokeSyscall("stat", 2, "si");
+ break;
+ case Host.SYS_isatty:
+ syscall.invokeSyscall("isatty", 1, "i");
+ break;
+ case Host.SYS_unlink:
+ syscall.invokeSyscall("unlink", 1, "s");
+ break;
+ default:
+ simulator.suspend();
+ throw new UnsupportedSyscallException();
+ }
+ simulator.cpuWriteLong(simulator.getArg(0), syscall.syscallErrno);
+ simulator.cpuWriteLong(0, syscall.syscallRetval);
+ } catch (CPUException e)
+ {
+ e.printStackTrace();
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ } catch (GDBServerException e)
+ {
+ e.printStackTrace();
+ } catch (EndSessionException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean doneContinue()
+ {
+ if (!enterSyscall)
+ return true;
+ leaveListenForCtrlC();
+ try
+ {
+ performSyscall();
+ } finally
+ {
+ enterListenForCtrlC();
+ }
+ return false;
+ }
+
+ public int getSyscallArg(int i) throws CPUException
+ {
+ return simulator.getArg(i+2);
+ }
+
+
+ public void writeUART(int val)
+ {
+ System.out.print((char)val);
+ System.out.flush();
+ }
+ public int readUART() throws CPUException
+ {
+ try
+ {
+ if (System.in.available()<=0)
+ {
+ throw new MemoryAccessException();
+ }
+
+ return System.in.read();
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+
+ public int readFIFO()
+ {
+ try
+ {
+ return System.in.available()>0?0:1;
+ } catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ return 1;
+ }
+
+ public void halted()
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void running()
+ {
+ // TODO Auto-generated method stub
+
+ }
+}
OpenPOWER on IntegriCloud