Major changes to how SimObjects are created and initialized. Almost all
[gem5.git] / src / 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 * Authors: Nathan Binkert
29 * Ali Saidi
30 */
31
32 /* @file
33 * Implements the user interface to a serial console
34 */
35
36 #include <sys/ioctl.h>
37 #include <sys/termios.h>
38 #include <sys/types.h>
39 #include <errno.h>
40 #include <poll.h>
41 #include <unistd.h>
42
43 #include <iostream>
44 #include <fstream>
45 #include <sstream>
46 #include <string>
47
48 #include "base/misc.hh"
49 #include "base/output.hh"
50 #include "base/socket.hh"
51 #include "base/trace.hh"
52 #include "dev/platform.hh"
53 #include "dev/simconsole.hh"
54 #include "dev/uart.hh"
55 #include "params/SimConsole.hh"
56
57 using namespace std;
58
59
60 /*
61 * Poll event for the listen socket
62 */
63 SimConsole::ListenEvent::ListenEvent(SimConsole *c, int fd, int e)
64 : PollEvent(fd, e), cons(c)
65 {
66 }
67
68 void
69 SimConsole::ListenEvent::process(int revent)
70 {
71 cons->accept();
72 }
73
74 /*
75 * Poll event for the data socket
76 */
77 SimConsole::DataEvent::DataEvent(SimConsole *c, int fd, int e)
78 : PollEvent(fd, e), cons(c)
79 {
80 }
81
82 void
83 SimConsole::DataEvent::process(int revent)
84 {
85 if (revent & POLLIN)
86 cons->data();
87 else if (revent & POLLNVAL)
88 cons->detach();
89 }
90
91 /*
92 * SimConsole code
93 */
94 SimConsole::SimConsole(const string &name, ostream *os, int num, int port)
95 : SimObject(name), listenEvent(NULL), dataEvent(NULL), number(num),
96 data_fd(-1), txbuf(16384), rxbuf(16384), outfile(os)
97 #if TRACING_ON == 1
98 , linebuf(16384)
99 #endif
100 {
101 if (outfile)
102 outfile->setf(ios::unitbuf);
103
104 if (port)
105 listen(port);
106 }
107
108 SimConsole::~SimConsole()
109 {
110 if (data_fd != -1)
111 ::close(data_fd);
112
113 if (listenEvent)
114 delete listenEvent;
115
116 if (dataEvent)
117 delete dataEvent;
118 }
119
120 ///////////////////////////////////////////////////////////////////////
121 // socket creation and console attach
122 //
123
124 void
125 SimConsole::listen(int port)
126 {
127 while (!listener.listen(port, true)) {
128 DPRINTF(Console,
129 ": can't bind address console port %d inuse PID %d\n",
130 port, getpid());
131 port++;
132 }
133
134 int p1, p2;
135 p2 = name().rfind('.') - 1;
136 p1 = name().rfind('.', p2);
137 ccprintf(cerr, "Listening for %s connection on port %d\n",
138 name().substr(p1+1,p2-p1), port);
139
140 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
141 pollQueue.schedule(listenEvent);
142 }
143
144 void
145 SimConsole::accept()
146 {
147 if (!listener.islistening())
148 panic("%s: cannot accept a connection if not listening!", name());
149
150 int fd = listener.accept(true);
151 if (data_fd != -1) {
152 char message[] = "console already attached!\n";
153 ::write(fd, message, sizeof(message));
154 ::close(fd);
155 return;
156 }
157
158 data_fd = fd;
159 dataEvent = new DataEvent(this, data_fd, POLLIN);
160 pollQueue.schedule(dataEvent);
161
162 stringstream stream;
163 ccprintf(stream, "==== m5 slave console: Console %d ====", number);
164
165 // we need an actual carriage return followed by a newline for the
166 // terminal
167 stream << "\r\n";
168
169 write((const uint8_t *)stream.str().c_str(), stream.str().size());
170
171 DPRINTFN("attach console %d\n", number);
172
173 txbuf.readall(data_fd);
174 }
175
176 void
177 SimConsole::detach()
178 {
179 if (data_fd != -1) {
180 ::close(data_fd);
181 data_fd = -1;
182 }
183
184 pollQueue.remove(dataEvent);
185 delete dataEvent;
186 dataEvent = NULL;
187
188 DPRINTFN("detach console %d\n", number);
189 }
190
191 void
192 SimConsole::data()
193 {
194 uint8_t buf[1024];
195 int len;
196
197 len = read(buf, sizeof(buf));
198 if (len) {
199 rxbuf.write((char *)buf, len);
200 // Inform the UART there is data available
201 uart->dataAvailable();
202 }
203 }
204
205 size_t
206 SimConsole::read(uint8_t *buf, size_t len)
207 {
208 if (data_fd < 0)
209 panic("Console not properly attached.\n");
210
211 size_t ret;
212 do {
213 ret = ::read(data_fd, buf, len);
214 } while (ret == -1 && errno == EINTR);
215
216
217 if (ret < 0)
218 DPRINTFN("Read failed.\n");
219
220 if (ret <= 0) {
221 detach();
222 return 0;
223 }
224
225 return ret;
226 }
227
228 // Console output.
229 size_t
230 SimConsole::write(const uint8_t *buf, size_t len)
231 {
232 if (data_fd < 0)
233 panic("Console not properly attached.\n");
234
235 size_t ret;
236 for (;;) {
237 ret = ::write(data_fd, buf, len);
238
239 if (ret >= 0)
240 break;
241
242 if (errno != EINTR)
243 detach();
244 }
245
246 return ret;
247 }
248
249 #define MORE_PENDING (ULL(1) << 61)
250 #define RECEIVE_SUCCESS (ULL(0) << 62)
251 #define RECEIVE_NONE (ULL(2) << 62)
252 #define RECEIVE_ERROR (ULL(3) << 62)
253
254 uint8_t
255 SimConsole::in()
256 {
257 bool empty;
258 uint8_t c;
259
260 empty = rxbuf.empty();
261 assert(!empty);
262 rxbuf.read((char *)&c, 1);
263 empty = rxbuf.empty();
264
265
266 DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d\n",
267 isprint(c) ? c : ' ', c, !empty);
268
269 return c;
270 }
271
272 uint64_t
273 SimConsole::console_in()
274 {
275 uint64_t value;
276
277 if (dataAvailable()) {
278 value = RECEIVE_SUCCESS | in();
279 if (!rxbuf.empty())
280 value |= MORE_PENDING;
281 } else {
282 value = RECEIVE_NONE;
283 }
284
285 DPRINTF(ConsoleVerbose, "console_in: return: %#x\n", value);
286
287 return value;
288 }
289
290 void
291 SimConsole::out(char c)
292 {
293 #if TRACING_ON == 1
294 if (DTRACE(Console)) {
295 static char last = '\0';
296
297 if (c != '\n' && c != '\r' ||
298 last != '\n' && last != '\r') {
299 if (c == '\n' || c == '\r') {
300 int size = linebuf.size();
301 char *buffer = new char[size + 1];
302 linebuf.read(buffer, size);
303 buffer[size] = '\0';
304 DPRINTF(Console, "%s\n", buffer);
305 delete [] buffer;
306 } else {
307 linebuf.write(c);
308 }
309 }
310
311 last = c;
312 }
313 #endif
314
315 txbuf.write(c);
316
317 if (data_fd >= 0)
318 write(c);
319
320 if (outfile)
321 outfile->write(&c, 1);
322
323 DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x\n",
324 isprint(c) ? c : ' ', (int)c);
325
326 }
327
328 SimConsole *
329 SimConsoleParams::create()
330 {
331 string filename = output;
332 ostream *stream = NULL;
333
334 if (!filename.empty()) {
335 if (append_name)
336 filename += "." + name;
337 stream = simout.find(filename);
338 }
339
340 return new SimConsole(name, stream, number, port);
341 }