SE/FS: Get rid of FULL_SYSTEM in dev.
[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 <poll.h>
39 #include <unistd.h>
40
41 #include <cctype>
42 #include <cerrno>
43 #include <fstream>
44 #include <iostream>
45 #include <sstream>
46 #include <string>
47
48 #include "base/atomicio.hh"
49 #include "base/misc.hh"
50 #include "base/output.hh"
51 #include "base/socket.hh"
52 #include "base/trace.hh"
53 #include "debug/Terminal.hh"
54 #include "debug/TerminalVerbose.hh"
55 #include "dev/platform.hh"
56 #include "dev/terminal.hh"
57 #include "dev/uart.hh"
58
59 using namespace std;
60
61
62 /*
63 * Poll event for the listen socket
64 */
65 Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
66 : PollEvent(fd, e), term(t)
67 {
68 }
69
70 void
71 Terminal::ListenEvent::process(int revent)
72 {
73 term->accept();
74 }
75
76 /*
77 * Poll event for the data socket
78 */
79 Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
80 : PollEvent(fd, e), term(t)
81 {
82 }
83
84 void
85 Terminal::DataEvent::process(int revent)
86 {
87 if (revent & POLLIN)
88 term->data();
89 else if (revent & POLLNVAL)
90 term->detach();
91 }
92
93 /*
94 * Terminal code
95 */
96 Terminal::Terminal(const Params *p)
97 : SimObject(p), listenEvent(NULL), dataEvent(NULL), number(p->number),
98 data_fd(-1), txbuf(16384), rxbuf(16384), outfile(NULL)
99 #if TRACING_ON == 1
100 , linebuf(16384)
101 #endif
102 {
103 if (p->output) {
104 outfile = simout.find(p->name);
105 outfile->setf(ios::unitbuf);
106 }
107
108 if (p->port)
109 listen(p->port);
110 }
111
112 Terminal::~Terminal()
113 {
114 if (data_fd != -1)
115 ::close(data_fd);
116
117 if (listenEvent)
118 delete listenEvent;
119
120 if (dataEvent)
121 delete dataEvent;
122 }
123
124 ///////////////////////////////////////////////////////////////////////
125 // socket creation and terminal attach
126 //
127
128 void
129 Terminal::listen(int port)
130 {
131 if (ListenSocket::allDisabled()) {
132 warn_once("Sockets disabled, not accepting terminal connections");
133 return;
134 }
135
136 while (!listener.listen(port, true)) {
137 DPRINTF(Terminal,
138 ": can't bind address terminal port %d inuse PID %d\n",
139 port, getpid());
140 port++;
141 }
142
143 int p1, p2;
144 p2 = name().rfind('.') - 1;
145 p1 = name().rfind('.', p2);
146 ccprintf(cerr, "Listening for %s connection on port %d\n",
147 name().substr(p1+1,p2-p1), port);
148
149 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
150 pollQueue.schedule(listenEvent);
151 }
152
153 void
154 Terminal::accept()
155 {
156 if (!listener.islistening())
157 panic("%s: cannot accept a connection if not listening!", name());
158
159 int fd = listener.accept(true);
160 if (data_fd != -1) {
161 char message[] = "terminal already attached!\n";
162 atomic_write(fd, message, sizeof(message));
163 ::close(fd);
164 return;
165 }
166
167 data_fd = fd;
168 dataEvent = new DataEvent(this, data_fd, POLLIN);
169 pollQueue.schedule(dataEvent);
170
171 stringstream stream;
172 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
173
174 // we need an actual carriage return followed by a newline for the
175 // terminal
176 stream << "\r\n";
177
178 write((const uint8_t *)stream.str().c_str(), stream.str().size());
179
180 DPRINTFN("attach terminal %d\n", number);
181
182 txbuf.readall(data_fd);
183 }
184
185 void
186 Terminal::detach()
187 {
188 if (data_fd != -1) {
189 ::close(data_fd);
190 data_fd = -1;
191 }
192
193 pollQueue.remove(dataEvent);
194 delete dataEvent;
195 dataEvent = NULL;
196
197 DPRINTFN("detach terminal %d\n", number);
198 }
199
200 void
201 Terminal::data()
202 {
203 uint8_t buf[1024];
204 int len;
205
206 len = read(buf, sizeof(buf));
207 if (len) {
208 rxbuf.write((char *)buf, len);
209 // Inform the UART there is data available
210 uart->dataAvailable();
211 }
212 }
213
214 size_t
215 Terminal::read(uint8_t *buf, size_t len)
216 {
217 if (data_fd < 0)
218 panic("Terminal not properly attached.\n");
219
220 size_t ret;
221 do {
222 ret = ::read(data_fd, buf, len);
223 } while (ret == -1 && errno == EINTR);
224
225
226 if (ret < 0)
227 DPRINTFN("Read failed.\n");
228
229 if (ret <= 0) {
230 detach();
231 return 0;
232 }
233
234 return ret;
235 }
236
237 // Terminal output.
238 size_t
239 Terminal::write(const uint8_t *buf, size_t len)
240 {
241 if (data_fd < 0)
242 panic("Terminal not properly attached.\n");
243
244 ssize_t ret = atomic_write(data_fd, buf, len);
245 if (ret < len)
246 detach();
247
248 return ret;
249 }
250
251 #define MORE_PENDING (ULL(1) << 61)
252 #define RECEIVE_SUCCESS (ULL(0) << 62)
253 #define RECEIVE_NONE (ULL(2) << 62)
254 #define RECEIVE_ERROR (ULL(3) << 62)
255
256 uint8_t
257 Terminal::in()
258 {
259 bool empty;
260 uint8_t c;
261
262 empty = rxbuf.empty();
263 assert(!empty);
264 rxbuf.read((char *)&c, 1);
265 empty = rxbuf.empty();
266
267
268 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
269 isprint(c) ? c : ' ', c, !empty);
270
271 return c;
272 }
273
274 uint64_t
275 Terminal::console_in()
276 {
277 uint64_t value;
278
279 if (dataAvailable()) {
280 value = RECEIVE_SUCCESS | in();
281 if (!rxbuf.empty())
282 value |= MORE_PENDING;
283 } else {
284 value = RECEIVE_NONE;
285 }
286
287 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
288
289 return value;
290 }
291
292 void
293 Terminal::out(char c)
294 {
295 #if TRACING_ON == 1
296 if (DTRACE(Terminal)) {
297 static char last = '\0';
298
299 if ((c != '\n' && c != '\r') || (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 }