Updated Copyright with information in bitkeeper changelogs
[gem5.git] / dev / console.cc
1 /* $Id$ */
2
3
4 /* @file
5 * User Console Definitions
6 */
7
8 #include <sys/ioctl.h>
9 #include <sys/termios.h>
10 #include <sys/types.h>
11 #include <errno.h>
12 #include <poll.h>
13 #include <unistd.h>
14
15 #include <iostream>
16 #include <fstream>
17 #include <sstream>
18 #include <string>
19
20 #include "base/misc.hh"
21 #include "base/socket.hh"
22 #include "base/trace.hh"
23 #include "dev/console.hh"
24 #include "mem/functional_mem/memory_control.hh"
25 #include "sim/builder.hh"
26 #include "targetarch/ev5.hh"
27 #include "dev/platform.hh"
28
29 using namespace std;
30
31 ////////////////////////////////////////////////////////////////////////
32 //
33 //
34
35 SimConsole::Event::Event(SimConsole *c, int fd, int e)
36 : PollEvent(fd, e), cons(c)
37 {
38 }
39
40 void
41 SimConsole::Event::process(int revent)
42 {
43 if (revent & POLLIN)
44 cons->data();
45 else if (revent & POLLNVAL)
46 cons->detach();
47 }
48
49 SimConsole::SimConsole(const string &name, const string &file, int num)
50 : SimObject(name), event(NULL), number(num), in_fd(-1), out_fd(-1),
51 listener(NULL), txbuf(16384), rxbuf(16384), outfile(NULL),
52 #if TRACING_ON == 1
53 linebuf(16384),
54 #endif
55 _status(0), _enable(0), intr(NULL), platform(NULL)
56 {
57 if (!file.empty())
58 outfile = new ofstream(file.c_str());
59
60 if (outfile)
61 outfile->setf(ios::unitbuf);
62 }
63
64 SimConsole::~SimConsole()
65 {
66 close();
67
68 if (outfile)
69 delete outfile;
70 }
71
72 void
73 SimConsole::close()
74 {
75 if (in_fd != -1)
76 ::close(in_fd);
77
78 if (out_fd != in_fd && out_fd != -1)
79 ::close(out_fd);
80 }
81
82 void
83 SimConsole::attach(int in, int out, ConsoleListener *l)
84 {
85 in_fd = in;
86 out_fd = out;
87 listener = l;
88
89 event = new Event(this, in, POLLIN);
90 pollQueue.schedule(event);
91
92 stringstream stream;
93 ccprintf(stream, "==== m5 slave console: Console %d ====", number);
94
95 // we need an actual carriage return followed by a newline for the
96 // terminal
97 stream << "\r\n";
98
99 write((const uint8_t *)stream.str().c_str(), stream.str().size());
100
101
102 DPRINTFN("attach console %d\n", number);
103
104 txbuf.readall(out);
105 }
106
107 void
108 SimConsole::detach()
109 {
110 close();
111 in_fd = -1;
112 out_fd = -1;
113
114 pollQueue.remove(event);
115
116 if (listener) {
117 listener->add(this);
118 listener = NULL;
119 }
120
121 DPRINTFN("detach console %d\n", number);
122 }
123
124 void
125 SimConsole::data()
126 {
127 uint8_t buf[1024];
128 int len;
129
130 len = read(buf, sizeof(buf));
131 if (len) {
132 rxbuf.write((char *)buf, len);
133 raiseInt(ReceiveInterrupt);
134 }
135 }
136
137 size_t
138 SimConsole::read(uint8_t *buf, size_t len)
139 {
140 if (in_fd < 0)
141 panic("SimConsole(read): Console not properly attached.\n");
142
143 size_t ret;
144 do {
145 ret = ::read(in_fd, buf, len);
146 } while (ret == -1 && errno == EINTR);
147
148
149 if (ret < 0)
150 DPRINTFN("SimConsole(read): Read failed.\n");
151
152 if (ret <= 0) {
153 detach();
154 return 0;
155 }
156
157 return ret;
158 }
159
160 // Console output.
161 size_t
162 SimConsole::write(const uint8_t *buf, size_t len)
163 {
164 if (out_fd < 0)
165 panic("SimConsole(write): Console not properly attached.\n");
166
167 size_t ret;
168 for (;;) {
169 ret = ::write(out_fd, buf, len);
170
171 if (ret >= 0)
172 break;
173
174 if (errno != EINTR)
175 detach();
176 }
177
178 return ret;
179 }
180
181 void
182 SimConsole::configTerm()
183 {
184 struct termios ios;
185
186 if (isatty(out_fd)) {
187 if (tcgetattr(out_fd, &ios) < 0) {
188 panic( "tcgetattr\n");
189 }
190 ios.c_iflag &= ~(ISTRIP|ICRNL|IGNCR|ICRNL|IXOFF|IXON);
191 ios.c_oflag &= ~(OPOST);
192 ios.c_oflag &= (ONLCR);
193 ios.c_lflag &= ~(ISIG|ICANON|ECHO);
194 ios.c_cc[VMIN] = 1;
195 ios.c_cc[VTIME] = 0;
196 if (tcsetattr(out_fd, TCSANOW, &ios) < 0) {
197 panic( "tcsetattr\n");
198 }
199 }
200 }
201
202 #define MORE_PENDING (ULL(1) << 61)
203 #define RECEIVE_SUCCESS (ULL(0) << 62)
204 #define RECEIVE_NONE (ULL(2) << 62)
205 #define RECEIVE_ERROR (ULL(3) << 62)
206
207 bool
208 SimConsole::in(uint8_t &c)
209 {
210 bool empty, ret;
211
212 empty = rxbuf.empty();
213 ret = !empty;
214 if (!empty) {
215 rxbuf.read((char *)&c, 1);
216 empty = rxbuf.empty();
217 }
218
219 if (empty)
220 clearInt(ReceiveInterrupt);
221
222 DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d, return: %d\n",
223 isprint(c) ? c : ' ', c, !empty, ret);
224
225 return ret;
226 }
227
228 uint64_t
229 SimConsole::console_in()
230 {
231 uint8_t c;
232 uint64_t value;
233
234 if (in(c)) {
235 value = RECEIVE_SUCCESS | c;
236 if (!rxbuf.empty())
237 value |= MORE_PENDING;
238 } else {
239 value = RECEIVE_NONE;
240 }
241
242 DPRINTF(ConsoleVerbose, "console_in: return: %#x\n", value);
243
244 return value;
245 }
246
247 void
248 SimConsole::out(char c, bool raise_int)
249 {
250 #if TRACING_ON == 1
251 if (DTRACE(Console)) {
252 static char last = '\0';
253
254 if (c != '\n' && c != '\r' ||
255 last != '\n' && last != '\r') {
256 if (c == '\n' || c == '\r') {
257 int size = linebuf.size();
258 char *buffer = new char[size + 1];
259 linebuf.read(buffer, size);
260 buffer[size] = '\0';
261 DPRINTF(Console, "%s\n", buffer);
262 delete [] buffer;
263 } else {
264 linebuf.write(c);
265 }
266 }
267
268 last = c;
269 }
270 #endif
271
272 txbuf.write(c);
273
274 if (out_fd >= 0)
275 write(c);
276
277 if (outfile)
278 outfile->write(&c, 1);
279
280 if (raise_int)
281 raiseInt(TransmitInterrupt);
282
283 DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x",
284 isprint(c) ? c : ' ', (int)c);
285
286 if (raise_int)
287 DPRINTF(ConsoleVerbose, "status: %#x\n", _status);
288 else
289 DPRINTF(ConsoleVerbose, "\n");
290 }
291
292 inline bool
293 MaskStatus(int status, int mask)
294 { return (status & mask) != 0; }
295
296 int
297 SimConsole::clearInt(int i)
298 {
299 int old = _status;
300 _status &= ~i;
301 //if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr)
302 platform->clearConsoleInt();
303
304 return old;
305 }
306
307 void
308 SimConsole::raiseInt(int i)
309 {
310 //int old = _status;
311 _status |= i;
312 //if (MaskStatus(old, _enable) != MaskStatus(_status, _enable) && intr)
313 platform->postConsoleInt();
314 }
315
316 void
317 SimConsole::initInt(IntrControl *i)
318 {
319 if (intr)
320 panic("Console has already been initialized.");
321
322 intr = i;
323 }
324
325 void
326 SimConsole::setInt(int bits)
327 {
328 int old;
329
330 if (bits & ~(TransmitInterrupt | ReceiveInterrupt))
331 panic("An interrupt was not set!");
332
333 old = _enable;
334 _enable |= bits;
335
336 //if (MaskStatus(_status, old) != MaskStatus(_status, _enable) && intr) {
337 if (intr) {
338 if (MaskStatus(_status, _enable))
339 platform->postConsoleInt();
340 else
341 platform->clearConsoleInt();
342 }
343 }
344
345 void
346 SimConsole::setPlatform(Platform *p)
347 {
348 platform = p;
349 platform->cons = this;
350 }
351
352 void
353 SimConsole::serialize(ostream &os)
354 {
355 SERIALIZE_SCALAR(_status);
356 SERIALIZE_SCALAR(_enable);
357 }
358
359 void
360 SimConsole::unserialize(Checkpoint *cp, const std::string &section)
361 {
362 UNSERIALIZE_SCALAR(_status);
363 UNSERIALIZE_SCALAR(_enable);
364 }
365
366
367 BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimConsole)
368
369 SimObjectParam<ConsoleListener *> listener;
370 SimObjectParam<IntrControl *> intr_control;
371 SimObjectParam<Platform *> platform;
372 Param<string> output;
373 Param<bool> append_name;
374 Param<int> number;
375
376 END_DECLARE_SIM_OBJECT_PARAMS(SimConsole)
377
378 BEGIN_INIT_SIM_OBJECT_PARAMS(SimConsole)
379
380 INIT_PARAM(listener, "console listener"),
381 INIT_PARAM(intr_control, "interrupt controller"),
382 INIT_PARAM(platform, "platform"),
383 INIT_PARAM_DFLT(output, "file to dump output to", ""),
384 INIT_PARAM_DFLT(append_name, "append name() to filename", true),
385 INIT_PARAM_DFLT(number, "console number", 0)
386
387 END_INIT_SIM_OBJECT_PARAMS(SimConsole)
388
389 CREATE_SIM_OBJECT(SimConsole)
390 {
391 string filename = output;
392 if (filename.empty()) {
393 if (!outputDirectory.empty())
394 filename = outputDirectory + getInstanceName();
395 } else {
396 if (append_name)
397 filename += "." + getInstanceName();
398 if (!outputDirectory.empty())
399 filename = outputDirectory + filename;
400 }
401
402 SimConsole *console = new SimConsole(getInstanceName(), filename, number);
403 ((ConsoleListener *)listener)->add(console);
404 ((SimConsole *)console)->initInt(intr_control);
405 ((SimConsole *)console)->setPlatform(platform);
406 //((SimConsole *)console)->setInt(SimConsole::TransmitInterrupt |
407 // SimConsole::ReceiveInterrupt);
408
409 return console;
410 }
411
412 REGISTER_SIM_OBJECT("SimConsole", SimConsole)
413
414 ////////////////////////////////////////////////////////////////////////
415 //
416 //
417
418 ConsoleListener::ConsoleListener(const string &name)
419 : SimObject(name), event(NULL)
420 {}
421
422 ConsoleListener::~ConsoleListener()
423 {
424 if (event)
425 delete event;
426 }
427
428 void
429 ConsoleListener::Event::process(int revent)
430 {
431 listener->accept();
432 }
433
434 ///////////////////////////////////////////////////////////////////////
435 // socket creation and console attach
436 //
437
438 void
439 ConsoleListener::listen(int port)
440 {
441 while (!listener.listen(port, true)) {
442 DPRINTF(Console,
443 ": can't bind address console port %d inuse PID %d\n",
444 port, getpid());
445 port++;
446 }
447
448 ccprintf(cerr, "Listening for console connection on port %d\n", port);
449
450 event = new Event(this, listener.getfd(), POLLIN);
451 pollQueue.schedule(event);
452 }
453
454 void
455 ConsoleListener::add(SimConsole *cons)
456 { ConsoleList.push_back(cons);}
457
458 void
459 ConsoleListener::accept()
460 {
461 if (!listener.islistening())
462 panic("%s: cannot accept a connection if not listening!", name());
463
464 int sfd = listener.accept(true);
465 if (sfd != -1) {
466 iter_t i = ConsoleList.begin();
467 iter_t end = ConsoleList.end();
468 if (i == end) {
469 close(sfd);
470 } else {
471 (*i)->attach(sfd, this);
472 i = ConsoleList.erase(i);
473 }
474 }
475 }
476
477 BEGIN_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener)
478
479 Param<int> port;
480
481 END_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener)
482
483 BEGIN_INIT_SIM_OBJECT_PARAMS(ConsoleListener)
484
485 INIT_PARAM_DFLT(port, "listen port", 3456)
486
487 END_INIT_SIM_OBJECT_PARAMS(ConsoleListener)
488
489 CREATE_SIM_OBJECT(ConsoleListener)
490 {
491 ConsoleListener *listener = new ConsoleListener(getInstanceName());
492 listener->listen(port);
493
494 return listener;
495 }
496
497 REGISTER_SIM_OBJECT("ConsoleListener", ConsoleListener)