Make include paths explicit and update makefile accordingly.
[gem5.git] / dev / console.cc
1 /* $Id$ */
2
3 /* @file
4 * User Console Definitions
5 */
6
7 #include <sys/ioctl.h>
8 #include <sys/termios.h>
9 #include <sys/types.h>
10 #include <errno.h>
11 #include <poll.h>
12 #include <unistd.h>
13
14 #include <iostream>
15 #include <fstream>
16 #include <sstream>
17 #include <string>
18
19 #include "base/misc.hh"
20 #include "targetarch/ev5.hh"
21
22 #include "dev/console.hh"
23 #include "base/socket.hh"
24 #include "base/trace.hh"
25 #include "mem/functional_mem/memory_control.hh"
26
27 using namespace std;
28
29 // check whether an int is pending
30 inline bool
31 IntPending(int status, int mask)
32 { return (status & mask) != 0; }
33
34 inline bool
35 IntTransition(int ostaus, int omask, int nstatus, int nmask)
36 { return IntPending(ostaus, omask) != IntPending(nstatus, nmask); }
37
38 ////////////////////////////////////////////////////////////////////////
39 //
40 //
41
42 SimConsole::Event::Event(SimConsole *c, int fd, int e)
43 : PollEvent(fd, e), cons(c)
44 {
45 }
46
47 void
48 SimConsole::Event::process(int revent)
49 {
50 if (revent & POLLIN)
51 cons->data();
52 else if (revent & POLLNVAL)
53 cons->detach();
54 }
55
56 SimConsole::SimConsole(const string &name, const string &file, int num)
57 : SimObject(name), event(NULL), number(num), in_fd(-1), out_fd(-1),
58 listener(NULL), txbuf(16384), rxbuf(16384), outfile(NULL),
59 intr_status(0), intr_enable(0), intr(NULL)
60 {
61 if (!file.empty())
62 outfile = new ofstream(file.c_str());
63
64 if (outfile)
65 outfile->setf(ios::unitbuf);
66 }
67
68 SimConsole::~SimConsole()
69 {
70 close();
71
72 if (outfile)
73 delete outfile;
74 }
75
76 void
77 SimConsole::close()
78 {
79 if (in_fd != -1)
80 ::close(in_fd);
81
82 if (out_fd != in_fd && out_fd != -1)
83 ::close(out_fd);
84 }
85
86 void
87 SimConsole::attach(int in, int out, ConsoleListener *l)
88 {
89 in_fd = in;
90 out_fd = out;
91 listener = l;
92
93 event = new Event(this, in, POLLIN);
94 pollQueue.schedule(event);
95
96 stringstream stream;
97 ccprintf(stream, "==== Simplescalar slave console: Console %d ====",
98 number);
99 // we need an actual carriage return followed by a newline for the
100 // terminal
101 stream << "\r\n";
102
103 write((const uint8_t *)stream.str().c_str(), stream.str().size());
104
105
106 DPRINTFN("attach console %d\n", number);
107
108 txbuf.readall(out);
109 }
110
111 void
112 SimConsole::detach()
113 {
114 close();
115 in_fd = -1;
116 out_fd = -1;
117
118 pollQueue.remove(event);
119
120 if (listener) {
121 listener->add(this);
122 listener = NULL;
123 }
124
125 DPRINTFN("detach console %d\n", number);
126 }
127
128 void
129 SimConsole::data()
130 {
131 uint8_t buf[1024];
132 int len;
133
134 len = read(buf, sizeof(buf));
135 if (len) {
136 rxbuf.write((char *)buf, len);
137 raiseInt(ReceiveInterrupt);
138 }
139 }
140
141 size_t
142 SimConsole::read(uint8_t *buf, size_t len)
143 {
144 if (in_fd < 0)
145 panic("SimConsole(read): Console not properly attached.\n");
146
147 size_t ret;
148 do {
149 ret = ::read(in_fd, buf, len);
150 } while (ret == -1 && errno == EINTR);
151
152
153 if (ret < 0)
154 DPRINTFN("SimConsole(read): Read failed.\n");
155
156 if (ret <= 0) {
157 detach();
158 return 0;
159 }
160
161 return ret;
162 }
163
164 // Console output.
165 size_t
166 SimConsole::write(const uint8_t *buf, size_t len)
167 {
168 if (out_fd < 0)
169 panic("SimConsole(write): Console not properly attached.\n");
170
171 size_t ret;
172 for (;;) {
173 ret = ::write(out_fd, buf, len);
174
175 if (ret >= 0)
176 break;
177
178 if (errno != EINTR)
179 detach();
180 }
181
182 return ret;
183 }
184
185 ///////////////////////////////////////////////////////////////////////
186 // ConfigureTerm turns off all character processing by the host OS so
187 // the launched OS can control it.
188 //
189 // We ignore anything except stdin; the sconsole program runs this
190 // same code on the ttys for the slave consoles before connecting.
191 //
192 void
193 SimConsole::configTerm()
194 {
195 struct termios ios;
196
197 if (isatty(out_fd)) {
198 if (tcgetattr(out_fd, &ios) < 0) {
199 panic( "tcgetattr\n");
200 }
201 ios.c_iflag &= ~(ISTRIP|ICRNL|IGNCR|ICRNL|IXOFF|IXON);
202 ios.c_oflag &= ~(OPOST);
203 ios.c_oflag &= (ONLCR);
204 ios.c_lflag &= ~(ISIG|ICANON|ECHO);
205 ios.c_cc[VMIN] = 1;
206 ios.c_cc[VTIME] = 0;
207 if (tcsetattr(out_fd, TCSANOW, &ios) < 0) {
208 panic( "tcsetattr\n");
209 }
210 }
211 }
212
213
214 ///////////////////////////////////////////////////////////////////////
215 // console i/o
216 //
217
218 ///////////////////////////////////////////////////////////////////////
219 //
220 // Console input.
221 // Returns -1 if there is no character pending, otherwise returns the
222 // char. Calling this function clears the input int (if no further
223 // chars are pending).
224 //
225 int
226 SimConsole::in()
227 {
228 if (rxbuf.empty()) {
229 clearInt(ReceiveInterrupt);
230 return -1;
231 }
232
233 char c;
234 rxbuf.read(&c, 1);
235
236 DPRINTF(Console, "in: \'%c\' %#02x status: %#x\n",
237 isprint(c) ? c : ' ', c, intr_status);
238
239 return c;
240 }
241
242 ///////////////////////////////////////////////////////////////////////
243 //
244 // Console output.
245 // NOTE: this very rudimentary device generates a TX int as soon as
246 // a character is output, since it has unlimited TX buffer capacity.
247 //
248 // Console output.
249 // Uses sim_console_out to perform functionality similar to 'write'
250 void
251 SimConsole::out(char c)
252 {
253 txbuf.write(c);
254
255 if (out_fd >= 0)
256 write(c);
257
258 if (outfile)
259 outfile->write(&c, 1);
260
261 raiseInt(TransmitInterrupt);
262
263 DPRINTF(Console, "out: \'%c\' %#02x status: %#x\n",
264 isprint(c) ? c : ' ', (int)c, intr_status);
265 }
266
267 // Simple console output used by Alpha firmware (not by the OS) -
268 // outputs the character to console n, and doesn't raise any
269 // interrupts
270 void
271 SimConsole::simple(char c)
272 {
273 txbuf.write(c);
274
275 if (out_fd >= 0)
276 write(c);
277
278 if (outfile)
279 outfile->write(&c, 1);
280
281 DPRINTF(Console, "simple char: \'%c\' %#02x\n",
282 isprint(c) ? c : ' ', (int)c);
283 }
284
285 // Read the current interrupt status of this console.
286 int
287 SimConsole::intStatus()
288 {
289 #if 0
290 DPRINTF(Console, "interrupt %d status: %#x\n",
291 number, intr_status);
292 #endif
293
294 return intr_status;
295 }
296
297 int
298 SimConsole::clearInt(int i)
299 {
300 int old_status = intr_status;
301 intr_status &= ~i;
302 if (IntTransition(old_status, intr_enable, intr_status, intr_enable) &&
303 intr)
304 intr->clear(TheISA::INTLEVEL_IRQ0);
305 return old_status;
306 }
307
308 void
309 SimConsole::raiseInt(int i)
310 {
311 int old = intr_status;
312 intr_status |= i;
313 if (IntTransition(old, intr_enable, intr_status, intr_enable) && intr)
314 intr->post(TheISA::INTLEVEL_IRQ0);
315 }
316
317 void
318 SimConsole::initInt(IntrControl *i)
319 {
320 if (intr)
321 panic("Console has already been initialized.");
322
323 // note: intr_status and intr_enable will normally be 0, since
324 // cs is statically allocated. When restoring from a checkpoint,
325 // these fields will be set, so don't touch them here.
326 intr = i; // interrupt handler
327 }
328
329 // Set the interrupt enable bits.
330 void
331 SimConsole::setInt(int bits)
332 {
333 int old_enable;
334
335 if (bits & ~(TransmitInterrupt | ReceiveInterrupt))
336 panic("An interrupt was not set!");
337
338 old_enable = intr_enable;
339 intr_enable |= bits;
340
341 if (IntTransition(intr_status, old_enable, intr_status, intr_enable) &&
342 intr) {
343 if (IntPending(intr_status, intr_enable))
344 intr->post(TheISA::INTLEVEL_IRQ0);
345 else
346 intr->clear(TheISA::INTLEVEL_IRQ0);
347 }
348 }
349
350
351 void
352 SimConsole::serialize()
353 {
354 panic("Unimplemented");
355 }
356
357 void
358 SimConsole::unserialize(IniFile &db, const std::string &category,
359 ConfigNode *node)
360 {
361 panic("Unimplemented");
362 }
363
364
365 BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimConsole)
366
367 SimObjectParam<ConsoleListener *> listener;
368 SimObjectParam<IntrControl *> intr_control;
369 Param<string> output;
370 Param<int> number;
371
372 END_DECLARE_SIM_OBJECT_PARAMS(SimConsole)
373
374 BEGIN_INIT_SIM_OBJECT_PARAMS(SimConsole)
375
376 INIT_PARAM(listener, "console listener"),
377 INIT_PARAM(intr_control, "interrupt controller"),
378 INIT_PARAM_DFLT(output, "file to dump output to", ""),
379 INIT_PARAM_DFLT(number, "console number", 0)
380
381 END_INIT_SIM_OBJECT_PARAMS(SimConsole)
382
383 CREATE_SIM_OBJECT(SimConsole)
384 {
385 SimConsole *console = new SimConsole(getInstanceName(), output, number);
386 ((ConsoleListener *)listener)->add(console);
387 ((SimConsole *)console)->initInt(intr_control);
388 ((SimConsole *)console)->setInt(SimConsole::TransmitInterrupt |
389 SimConsole::ReceiveInterrupt);
390
391 return console;
392 }
393
394 REGISTER_SIM_OBJECT("SimConsole", SimConsole)
395
396 ////////////////////////////////////////////////////////////////////////
397 //
398 //
399
400 ConsoleListener::ConsoleListener(const string &name)
401 : SimObject(name), event(NULL)
402 {}
403
404 ConsoleListener::~ConsoleListener()
405 {
406 if (event)
407 delete event;
408 }
409
410 void
411 ConsoleListener::Event::process(int revent)
412 {
413 listener->accept();
414 }
415
416 ///////////////////////////////////////////////////////////////////////
417 // socket creation and console attach
418 //
419
420 void
421 ConsoleListener::listen(int port)
422 {
423 while (!listener.listen(port, true)) {
424 DPRINTF(Console, ": can't bind address console port %d inuse PID %d\n",
425 port, getpid());
426 port++;
427 }
428
429 cerr << "Listening for console connection on port " << port << endl;
430 event = new Event(this, listener.getfd(), POLLIN);
431 pollQueue.schedule(event);
432 }
433
434 void
435 ConsoleListener::add(SimConsole *cons)
436 { ConsoleList.push_back(cons);}
437
438 void
439 ConsoleListener::accept()
440 {
441 if (!listener.islistening())
442 panic("%s: cannot accept a connection if we're not listening!",
443 name());
444
445 int sfd = listener.accept(true);
446 if (sfd != -1) {
447 iter_t i = ConsoleList.begin();
448 iter_t end = ConsoleList.end();
449 if (i == end) {
450 close(sfd);
451 } else {
452 (*i)->attach(sfd, this);
453 i = ConsoleList.erase(i);
454 }
455 }
456 }
457
458 BEGIN_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener)
459
460 Param<int> port;
461
462 END_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener)
463
464 BEGIN_INIT_SIM_OBJECT_PARAMS(ConsoleListener)
465
466 INIT_PARAM_DFLT(port, "listen port", 3456)
467
468 END_INIT_SIM_OBJECT_PARAMS(ConsoleListener)
469
470 CREATE_SIM_OBJECT(ConsoleListener)
471 {
472 ConsoleListener *listener = new ConsoleListener(getInstanceName());
473 listener->listen(port);
474
475 return listener;
476 }
477
478 REGISTER_SIM_OBJECT("ConsoleListener", ConsoleListener)