Serial Consoles
TEMU provides a number of console models that bridge a serial port of the emulated system, normally a UART, to something on the host:
Console-
Logs the serial traffic to stdout or to a file, and can inject strings into the receiver. Typically called
tty<X>in the examples. ConsoleXTerm-
Opens an
xtermwindow running on the host. ConsolePTY-
Creates a pseudo terminal on the host, which a terminal program such as
screencan then attach to. ConsoleTelnet-
Listens on a TCP port and serves the console to a
telnetclient.
Connecting a Console
A console is connected to the transmit port of a UART model.
The connect command wires up both directions.
ConsolePTY and ConsoleTelnet read their host file descriptors
asynchronously, so they also need a TEMU time source:
import ConsoleTelnet
ConsoleTelnet.new name=telnet0
connect-timesource obj=telnet0 ts=cpu0
telnet0.start port=9023
connect a=apbuart0.tx b=telnet0:SerialIface
PTY Console
The ConsolePTY model allocates a pseudo terminal when the object is created.
The host path (e.g. /dev/pts/42) is printed in the log message emitted at
creation, and is also available as a read-only object property.
Input is always echoed back from the emulated side, so the terminal program
must not echo locally as well.
screen works without any extra flags, while other PTY clients may need one,
e.g. socat -,raw,echo=0 /dev/pts/42.
The following properties are available:
slavePath-
Read-only path of the allocated PTY slave device.
config.rawSlave-
Put the slave side into raw mode (default
1). In raw mode the host terminal driver does not add its own echo and line editing on top of what the target software already does. Set it to0if the attached program configures the terminal itself. config.txQueueLimit-
Maximum number of bytes buffered towards the PTY (default 4096).
config.rxQueueLimit-
Maximum number of bytes buffered towards the UART (default 4096).
While its transmit queue is full, the model reports that it is not clear to receive, so a UART that honours flow control (see Serial Flow Control) holds the data back instead.
import ConsolePTY
ConsolePTY.new name=pty0
connect-timesource obj=pty0 ts=cpu0
connect a=apbuart0.tx b=pty0:SerialIface
# Print the path to attach a terminal to
pty0.slavePath
Telnet Console
The ConsoleTelnet model serves the console over TCP.
The listen socket is not opened when the object is created,
but by the start command:
telnet0.start port=9023
If port is omitted, or 0 is given, the operating system selects a free
port.
The port that was actually bound can be read from the listenPort
property.
By default the model only binds the loopback interface.
Set config.bindAny to 1 to accept connections from other hosts.
The following properties are supported:
listenPort-
Read-only TCP port the model is listening on.
config.port-
Port used by
startwhen it is called without aportargument (default0, i.e. let the operating system pick one). config.bindAny-
Bind
0.0.0.0instead of127.0.0.1(default0). config.telnetMode-
Speak the telnet protocol (default
1). Set to0for a raw TCP byte stream. config.rfc2217Mode-
Announce the RFC 2217 com-port option (default
0). config.txQueueLimit-
Maximum number of bytes buffered towards the client (default 4096).
config.rxQueueLimit-
Maximum number of bytes buffered towards the UART (default 4096).
In telnet mode (the default) the model implements the option negotiation of RFC 854. On connect it offers and requests binary transmission and offers to do the input echoing itself. A normal telnet client can therefore be used directly:
$ telnet localhost 9023
Interactive commands (IAC sequences) from the client are interpreted and
removed from the byte stream handed to the UART.
Unless binary transmission has been negotiated, line endings are translated:
a CR NUL or CR LF from the client is passed on as a single CR,
and a bare CR from the target is sent as CR LF.
A target that already emits CR LF is not modified.
By default the server always offers binary transmission.
Setting config.rfc2217Mode to 1 additionally requests the com-port control
option (RFC 2217) from the client.
Baud rate, parity and other line settings are not applied to the UART model.
Only the negotiation itself is handled.
Serial Flow Control
A serial endpoint can tell its peer whether it is currently able to accept data. This is used both by the UART models and by the console models to apply back pressure instead of dropping bytes when one side is faster than the other.
The mechanism is an optional addition to the SerialIface interface:
typedef struct temu_SerialIface {
void (*write)(void *Obj, uint8_t Data);
//! Notify this endpoint that the peer may now be clear to receive data.
void (*cts)(void *Obj);
//! Return whether this endpoint is currently clear to receive data.
//! Optional. If NULL, we assume it is always clear to receive.
bool (*isClearToReceive)(void *Obj);
} temu_SerialIface;
Implementing isClearToReceive is optional.
An endpoint that leaves it NULL is always treated as ready,
which is how all serial endpoints behaved before.
The TEMU UART hardware device models and the consoles implement
isClearToReceive since TEMU 5.1.
Capturing Console Traffic
The Console model can keep the console output in memory in addition to
printing it. Setting config.recordTraffic to 1 enables the recording,
and the dump-traffic command prints what has been captured so far:
tty0.config.recordTraffic = 1
# Later, print everything that has been captured
tty0.dump-traffic
# ... or only the lines from line 20 and onwards
tty0.dump-traffic from=20
The command takes an optional from argument, which is the index of the first
line to print, so that a script or an external tool can poll for new output
without repeating what it has already seen.
This is how the MCP server gives an agent access to the
console output of the target software.