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