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