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