rename AlphaConsole to AlphaBackdoor
[gem5.git] / src / dev / terminal.cc
1 /*
2 * Copyright (c) 2001-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Nathan Binkert
29 * Ali Saidi
30 */
31
32 /* @file
33 * Implements the user interface to a serial terminal
34 */
35
36 #include <sys/ioctl.h>
37 #include <sys/termios.h>
38 #include <sys/types.h>
39 #include <errno.h>
40 #include <poll.h>
41 #include <unistd.h>
42
43 #include <iostream>
44 #include <fstream>
45 #include <sstream>
46 #include <string>
47
48 #include "base/misc.hh"
49 #include "base/output.hh"
50 #include "base/socket.hh"
51 #include "base/trace.hh"
52 #include "dev/platform.hh"
53 #include "dev/terminal.hh"
54 #include "dev/uart.hh"
55
56 using namespace std;
57
58
59 /*
60 * Poll event for the listen socket
61 */
62 Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
63 : PollEvent(fd, e), term(t)
64 {
65 }
66
67 void
68 Terminal::ListenEvent::process(int revent)
69 {
70 term->accept();
71 }
72
73 /*
74 * Poll event for the data socket
75 */
76 Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
77 : PollEvent(fd, e), term(t)
78 {
79 }
80
81 void
82 Terminal::DataEvent::process(int revent)
83 {
84 if (revent & POLLIN)
85 term->data();
86 else if (revent & POLLNVAL)
87 term->detach();
88 }
89
90 /*
91 * Terminal code
92 */
93 Terminal::Terminal(const Params *p)
94 : SimObject(p), listenEvent(NULL), dataEvent(NULL), number(p->number),
95 data_fd(-1), txbuf(16384), rxbuf(16384), outfile(NULL)
96 #if TRACING_ON == 1
97 , linebuf(16384)
98 #endif
99 {
100 if (p->output) {
101 outfile = simout.find(p->name);
102 outfile->setf(ios::unitbuf);
103 }
104
105 if (p->port)
106 listen(p->port);
107 }
108
109 Terminal::~Terminal()
110 {
111 if (data_fd != -1)
112 ::close(data_fd);
113
114 if (listenEvent)
115 delete listenEvent;
116
117 if (dataEvent)
118 delete dataEvent;
119 }
120
121 ///////////////////////////////////////////////////////////////////////
122 // socket creation and terminal attach
123 //
124
125 void
126 Terminal::listen(int port)
127 {
128 while (!listener.listen(port, true)) {
129 DPRINTF(Terminal,
130 ": can't bind address terminal port %d inuse PID %d\n",
131 port, getpid());
132 port++;
133 }
134
135 int p1, p2;
136 p2 = name().rfind('.') - 1;
137 p1 = name().rfind('.', p2);
138 ccprintf(cerr, "Listening for %s connection on port %d\n",
139 name().substr(p1+1,p2-p1), port);
140
141 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
142 pollQueue.schedule(listenEvent);
143 }
144
145 void
146 Terminal::accept()
147 {
148 if (!listener.islistening())
149 panic("%s: cannot accept a connection if not listening!", name());
150
151 int fd = listener.accept(true);
152 if (data_fd != -1) {
153 char message[] = "terminal already attached!\n";
154 ::write(fd, message, sizeof(message));
155 ::close(fd);
156 return;
157 }
158
159 data_fd = fd;
160 dataEvent = new DataEvent(this, data_fd, POLLIN);
161 pollQueue.schedule(dataEvent);
162
163 stringstream stream;
164 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
165
166 // we need an actual carriage return followed by a newline for the
167 // terminal
168 stream << "\r\n";
169
170 write((const uint8_t *)stream.str().c_str(), stream.str().size());
171
172 DPRINTFN("attach terminal %d\n", number);
173
174 txbuf.readall(data_fd);
175 }
176
177 void
178 Terminal::detach()
179 {
180 if (data_fd != -1) {
181 ::close(data_fd);
182 data_fd = -1;
183 }
184
185 pollQueue.remove(dataEvent);
186 delete dataEvent;
187 dataEvent = NULL;
188
189 DPRINTFN("detach terminal %d\n", number);
190 }
191
192 void
193 Terminal::data()
194 {
195 uint8_t buf[1024];
196 int len;
197
198 len = read(buf, sizeof(buf));
199 if (len) {
200 rxbuf.write((char *)buf, len);
201 // Inform the UART there is data available
202 uart->dataAvailable();
203 }
204 }
205
206 size_t
207 Terminal::read(uint8_t *buf, size_t len)
208 {
209 if (data_fd < 0)
210 panic("Terminal not properly attached.\n");
211
212 size_t ret;
213 do {
214 ret = ::read(data_fd, buf, len);
215 } while (ret == -1 && errno == EINTR);
216
217
218 if (ret < 0)
219 DPRINTFN("Read failed.\n");
220
221 if (ret <= 0) {
222 detach();
223 return 0;
224 }
225
226 return ret;
227 }
228
229 // Terminal output.
230 size_t
231 Terminal::write(const uint8_t *buf, size_t len)
232 {
233 if (data_fd < 0)
234 panic("Terminal not properly attached.\n");
235
236 size_t ret;
237 for (;;) {
238 ret = ::write(data_fd, buf, len);
239
240 if (ret >= 0)
241 break;
242
243 if (errno != EINTR)
244 detach();
245 }
246
247 return ret;
248 }
249
250 #define MORE_PENDING (ULL(1) << 61)
251 #define RECEIVE_SUCCESS (ULL(0) << 62)
252 #define RECEIVE_NONE (ULL(2) << 62)
253 #define RECEIVE_ERROR (ULL(3) << 62)
254
255 uint8_t
256 Terminal::in()
257 {
258 bool empty;
259 uint8_t c;
260
261 empty = rxbuf.empty();
262 assert(!empty);
263 rxbuf.read((char *)&c, 1);
264 empty = rxbuf.empty();
265
266
267 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
268 isprint(c) ? c : ' ', c, !empty);
269
270 return c;
271 }
272
273 uint64_t
274 Terminal::console_in()
275 {
276 uint64_t value;
277
278 if (dataAvailable()) {
279 value = RECEIVE_SUCCESS | in();
280 if (!rxbuf.empty())
281 value |= MORE_PENDING;
282 } else {
283 value = RECEIVE_NONE;
284 }
285
286 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
287
288 return value;
289 }
290
291 void
292 Terminal::out(char c)
293 {
294 #if TRACING_ON == 1
295 if (DTRACE(Terminal)) {
296 static char last = '\0';
297
298 if (c != '\n' && c != '\r' ||
299 last != '\n' && last != '\r') {
300 if (c == '\n' || c == '\r') {
301 int size = linebuf.size();
302 char *buffer = new char[size + 1];
303 linebuf.read(buffer, size);
304 buffer[size] = '\0';
305 DPRINTF(Terminal, "%s\n", buffer);
306 delete [] buffer;
307 } else {
308 linebuf.write(c);
309 }
310 }
311
312 last = c;
313 }
314 #endif
315
316 txbuf.write(c);
317
318 if (data_fd >= 0)
319 write(c);
320
321 if (outfile)
322 outfile->write(&c, 1);
323
324 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
325 isprint(c) ? c : ' ', (int)c);
326
327 }
328
329 Terminal *
330 TerminalParams::create()
331 {
332 return new Terminal(this);
333 }