More documentation for 1.1 release.
[gem5.git] / dev / simconsole.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
29 /* @file
30 * Implements the user interface to a serial console
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/output.hh"
47 #include "base/socket.hh"
48 #include "base/trace.hh"
49 #include "dev/platform.hh"
50 #include "dev/simconsole.hh"
51 #include "dev/uart.hh"
52 #include "mem/functional/memory_control.hh"
53 #include "sim/builder.hh"
54
55 using namespace std;
56
57 ////////////////////////////////////////////////////////////////////////
58 //
59 //
60
61 SimConsole::Event::Event(SimConsole *c, int fd, int e)
62 : PollEvent(fd, e), cons(c)
63 {
64 }
65
66 void
67 SimConsole::Event::process(int revent)
68 {
69 if (revent & POLLIN)
70 cons->data();
71 else if (revent & POLLNVAL)
72 cons->detach();
73 }
74
75 SimConsole::SimConsole(const string &name, ostream *os, int num)
76 : SimObject(name), event(NULL), number(num), in_fd(-1), out_fd(-1),
77 listener(NULL), txbuf(16384), rxbuf(16384), outfile(os)
78 #if TRACING_ON == 1
79 , linebuf(16384)
80 #endif
81 {
82 if (outfile)
83 outfile->setf(ios::unitbuf);
84 }
85
86 SimConsole::~SimConsole()
87 {
88 close();
89 }
90
91 void
92 SimConsole::close()
93 {
94 if (in_fd != -1)
95 ::close(in_fd);
96
97 if (out_fd != in_fd && out_fd != -1)
98 ::close(out_fd);
99 }
100
101 void
102 SimConsole::attach(int in, int out, ConsoleListener *l)
103 {
104 in_fd = in;
105 out_fd = out;
106 listener = l;
107
108 event = new Event(this, in, POLLIN);
109 pollQueue.schedule(event);
110
111 stringstream stream;
112 ccprintf(stream, "==== m5 slave console: Console %d ====", number);
113
114 // we need an actual carriage return followed by a newline for the
115 // terminal
116 stream << "\r\n";
117
118 write((const uint8_t *)stream.str().c_str(), stream.str().size());
119
120
121 DPRINTFN("attach console %d\n", number);
122
123 txbuf.readall(out);
124 }
125
126 void
127 SimConsole::detach()
128 {
129 close();
130 in_fd = -1;
131 out_fd = -1;
132
133 pollQueue.remove(event);
134
135 if (listener) {
136 listener->add(this);
137 listener = NULL;
138 }
139
140 DPRINTFN("detach console %d\n", number);
141 }
142
143 void
144 SimConsole::data()
145 {
146 uint8_t buf[1024];
147 int len;
148
149 len = read(buf, sizeof(buf));
150 if (len) {
151 rxbuf.write((char *)buf, len);
152 // Inform the UART there is data available
153 uart->dataAvailable();
154 }
155 }
156
157 size_t
158 SimConsole::read(uint8_t *buf, size_t len)
159 {
160 if (in_fd < 0)
161 panic("Console not properly attached.\n");
162
163 size_t ret;
164 do {
165 ret = ::read(in_fd, buf, len);
166 } while (ret == -1 && errno == EINTR);
167
168
169 if (ret < 0)
170 DPRINTFN("Read failed.\n");
171
172 if (ret <= 0) {
173 detach();
174 return 0;
175 }
176
177 return ret;
178 }
179
180 // Console output.
181 size_t
182 SimConsole::write(const uint8_t *buf, size_t len)
183 {
184 if (out_fd < 0)
185 panic("Console not properly attached.\n");
186
187 size_t ret;
188 for (;;) {
189 ret = ::write(out_fd, buf, len);
190
191 if (ret >= 0)
192 break;
193
194 if (errno != EINTR)
195 detach();
196 }
197
198 return ret;
199 }
200
201 #define MORE_PENDING (ULL(1) << 61)
202 #define RECEIVE_SUCCESS (ULL(0) << 62)
203 #define RECEIVE_NONE (ULL(2) << 62)
204 #define RECEIVE_ERROR (ULL(3) << 62)
205
206 bool
207 SimConsole::in(uint8_t &c)
208 {
209 bool empty, ret;
210
211 empty = rxbuf.empty();
212 ret = !empty;
213 if (!empty) {
214 rxbuf.read((char *)&c, 1);
215 empty = rxbuf.empty();
216 }
217
218 DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d, return: %d\n",
219 isprint(c) ? c : ' ', c, !empty, ret);
220
221 return ret;
222 }
223
224 uint64_t
225 SimConsole::console_in()
226 {
227 uint8_t c;
228 uint64_t value;
229
230 if (in(c)) {
231 value = RECEIVE_SUCCESS | c;
232 if (!rxbuf.empty())
233 value |= MORE_PENDING;
234 } else {
235 value = RECEIVE_NONE;
236 }
237
238 DPRINTF(ConsoleVerbose, "console_in: return: %#x\n", value);
239
240 return value;
241 }
242
243 void
244 SimConsole::out(char c)
245 {
246 #if TRACING_ON == 1
247 if (DTRACE(Console)) {
248 static char last = '\0';
249
250 if (c != '\n' && c != '\r' ||
251 last != '\n' && last != '\r') {
252 if (c == '\n' || c == '\r') {
253 int size = linebuf.size();
254 char *buffer = new char[size + 1];
255 linebuf.read(buffer, size);
256 buffer[size] = '\0';
257 DPRINTF(Console, "%s\n", buffer);
258 delete [] buffer;
259 } else {
260 linebuf.write(c);
261 }
262 }
263
264 last = c;
265 }
266 #endif
267
268 txbuf.write(c);
269
270 if (out_fd >= 0)
271 write(c);
272
273 if (outfile)
274 outfile->write(&c, 1);
275
276 DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x\n",
277 isprint(c) ? c : ' ', (int)c);
278
279 }
280
281
282 void
283 SimConsole::serialize(ostream &os)
284 {
285 }
286
287 void
288 SimConsole::unserialize(Checkpoint *cp, const std::string &section)
289 {
290 }
291
292
293 BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimConsole)
294
295 SimObjectParam<ConsoleListener *> listener;
296 SimObjectParam<IntrControl *> intr_control;
297 Param<string> output;
298 Param<bool> append_name;
299 Param<int> number;
300
301 END_DECLARE_SIM_OBJECT_PARAMS(SimConsole)
302
303 BEGIN_INIT_SIM_OBJECT_PARAMS(SimConsole)
304
305 INIT_PARAM(listener, "console listener"),
306 INIT_PARAM(intr_control, "interrupt controller"),
307 INIT_PARAM(output, "file to dump output to"),
308 INIT_PARAM_DFLT(append_name, "append name() to filename", true),
309 INIT_PARAM_DFLT(number, "console number", 0)
310
311 END_INIT_SIM_OBJECT_PARAMS(SimConsole)
312
313 CREATE_SIM_OBJECT(SimConsole)
314 {
315 string filename = output;
316 ostream *stream = NULL;
317
318 if (!filename.empty()) {
319 if (append_name)
320 filename += "." + getInstanceName();
321 stream = simout.find(filename);
322 }
323
324 SimConsole *console = new SimConsole(getInstanceName(), stream, number);
325 ((ConsoleListener *)listener)->add(console);
326
327 return console;
328 }
329
330 REGISTER_SIM_OBJECT("SimConsole", SimConsole)
331
332 ////////////////////////////////////////////////////////////////////////
333 //
334 //
335
336 ConsoleListener::ConsoleListener(const string &name)
337 : SimObject(name), event(NULL)
338 {}
339
340 ConsoleListener::~ConsoleListener()
341 {
342 if (event)
343 delete event;
344 }
345
346 void
347 ConsoleListener::Event::process(int revent)
348 {
349 listener->accept();
350 }
351
352 ///////////////////////////////////////////////////////////////////////
353 // socket creation and console attach
354 //
355
356 void
357 ConsoleListener::listen(int port)
358 {
359 while (!listener.listen(port, true)) {
360 DPRINTF(Console,
361 ": can't bind address console port %d inuse PID %d\n",
362 port, getpid());
363 port++;
364 }
365
366 ccprintf(cerr, "Listening for console connection on port %d\n", port);
367
368 event = new Event(this, listener.getfd(), POLLIN);
369 pollQueue.schedule(event);
370 }
371
372 void
373 ConsoleListener::add(SimConsole *cons)
374 { ConsoleList.push_back(cons);}
375
376 void
377 ConsoleListener::accept()
378 {
379 if (!listener.islistening())
380 panic("%s: cannot accept a connection if not listening!", name());
381
382 int sfd = listener.accept(true);
383 if (sfd != -1) {
384 iter_t i = ConsoleList.begin();
385 iter_t end = ConsoleList.end();
386 if (i == end) {
387 close(sfd);
388 } else {
389 (*i)->attach(sfd, this);
390 i = ConsoleList.erase(i);
391 }
392 }
393 }
394
395 BEGIN_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener)
396
397 Param<int> port;
398
399 END_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener)
400
401 BEGIN_INIT_SIM_OBJECT_PARAMS(ConsoleListener)
402
403 INIT_PARAM_DFLT(port, "listen port", 3456)
404
405 END_INIT_SIM_OBJECT_PARAMS(ConsoleListener)
406
407 CREATE_SIM_OBJECT(ConsoleListener)
408 {
409 ConsoleListener *listener = new ConsoleListener(getInstanceName());
410 listener->listen(port);
411
412 return listener;
413 }
414
415 REGISTER_SIM_OBJECT("ConsoleListener", ConsoleListener)