Deleted Added
sdiff udiff text old ( 12693:4db8d6442b44 ) new ( 14249:e6d5e7d248c5 )
full compact
1/*
2 * Copyright (c) 2019 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2001-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Nathan Binkert
41 * Ali Saidi
42 */
43
44/* @file
45 * Implements the user interface to a serial terminal
46 */
47
48#include <sys/ioctl.h>
49
50#if defined(__FreeBSD__)
51#include <termios.h>
52
53#else
54#include <sys/termios.h>
55
56#endif
57#include "dev/serial/terminal.hh"
58
59#include <poll.h>
60#include <unistd.h>
61
62#include <cctype>
63#include <cerrno>
64#include <fstream>
65#include <iostream>
66#include <sstream>
67#include <string>
68
69#include "base/atomicio.hh"
70#include "base/logging.hh"
71#include "base/output.hh"
72#include "base/socket.hh"
73#include "base/trace.hh"
74#include "debug/Terminal.hh"
75#include "debug/TerminalVerbose.hh"
76#include "dev/platform.hh"
77#include "dev/serial/uart.hh"
78
79using namespace std;
80
81
82/*
83 * Poll event for the listen socket
84 */
85Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e)
86 : PollEvent(fd, e), term(t)
87{
88}
89
90void
91Terminal::ListenEvent::process(int revent)
92{
93 term->accept();
94}
95
96/*
97 * Poll event for the data socket
98 */
99Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e)
100 : PollEvent(fd, e), term(t)
101{
102}
103
104void
105Terminal::DataEvent::process(int revent)
106{
107 // As a consequence of being called from the PollQueue, we might
108 // have been called from a different thread. Migrate to "our"
109 // thread.
110 EventQueue::ScopedMigration migrate(term->eventQueue());
111
112 if (revent & POLLIN)
113 term->data();
114 else if (revent & POLLNVAL)
115 term->detach();
116}
117
118/*
119 * Terminal code
120 */
121Terminal::Terminal(const Params *p)
122 : SerialDevice(p), listenEvent(NULL), dataEvent(NULL),
123 number(p->number), data_fd(-1), txbuf(16384), rxbuf(16384),
124 outfile(terminalDump(p))
125#if TRACING_ON == 1
126 , linebuf(16384)
127#endif
128{
129 if (outfile)
130 outfile->stream()->setf(ios::unitbuf);
131
132 if (p->port)
133 listen(p->port);
134}
135
136Terminal::~Terminal()
137{
138 if (data_fd != -1)
139 ::close(data_fd);
140
141 if (listenEvent)
142 delete listenEvent;
143
144 if (dataEvent)
145 delete dataEvent;
146}
147
148OutputStream *
149Terminal::terminalDump(const TerminalParams* p)
150{
151 switch (p->outfile) {
152 case Enums::TerminalDump::none:
153 return nullptr;
154 case Enums::TerminalDump::stdout:
155 return simout.findOrCreate("stdout");
156 case Enums::TerminalDump::stderr:
157 return simout.findOrCreate("stderr");
158 case Enums::TerminalDump::file:
159 return simout.findOrCreate(p->name);
160 default:
161 panic("Invalid option\n");
162 }
163}
164
165///////////////////////////////////////////////////////////////////////
166// socket creation and terminal attach
167//
168
169void
170Terminal::listen(int port)
171{
172 if (ListenSocket::allDisabled()) {
173 warn_once("Sockets disabled, not accepting terminal connections");
174 return;
175 }
176
177 while (!listener.listen(port, true)) {
178 DPRINTF(Terminal,
179 ": can't bind address terminal port %d inuse PID %d\n",
180 port, getpid());
181 port++;
182 }
183
184 ccprintf(cerr, "%s: Listening for connections on port %d\n",
185 name(), port);
186
187 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN);
188 pollQueue.schedule(listenEvent);
189}
190
191void
192Terminal::accept()
193{
194 if (!listener.islistening())
195 panic("%s: cannot accept a connection if not listening!", name());
196
197 int fd = listener.accept(true);
198 if (data_fd != -1) {
199 char message[] = "terminal already attached!\n";
200 atomic_write(fd, message, sizeof(message));
201 ::close(fd);
202 return;
203 }
204
205 data_fd = fd;
206 dataEvent = new DataEvent(this, data_fd, POLLIN);
207 pollQueue.schedule(dataEvent);
208
209 stringstream stream;
210 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number);
211
212 // we need an actual carriage return followed by a newline for the
213 // terminal
214 stream << "\r\n";
215
216 write((const uint8_t *)stream.str().c_str(), stream.str().size());
217
218 DPRINTFN("attach terminal %d\n", number);
219 char buf[1024];
220 for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) {
221 const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf)));
222 txbuf.peek(buf, i, chunk_len);
223 write((const uint8_t *)buf, chunk_len);
224 }
225}
226
227void
228Terminal::detach()
229{
230 if (data_fd != -1) {
231 ::close(data_fd);
232 data_fd = -1;
233 }
234
235 pollQueue.remove(dataEvent);
236 delete dataEvent;
237 dataEvent = NULL;
238
239 DPRINTFN("detach terminal %d\n", number);
240}
241
242void
243Terminal::data()
244{
245 uint8_t buf[1024];
246 int len;
247
248 len = read(buf, sizeof(buf));
249 if (len) {
250 rxbuf.write((char *)buf, len);
251 notifyInterface();
252 }
253}
254
255size_t
256Terminal::read(uint8_t *buf, size_t len)
257{
258 if (data_fd < 0)
259 panic("Terminal not properly attached.\n");
260
261 ssize_t ret;
262 do {
263 ret = ::read(data_fd, buf, len);
264 } while (ret == -1 && errno == EINTR);
265
266
267 if (ret < 0)
268 DPRINTFN("Read failed.\n");
269
270 if (ret <= 0) {
271 detach();
272 return 0;
273 }
274
275 return ret;
276}
277
278// Terminal output.
279size_t
280Terminal::write(const uint8_t *buf, size_t len)
281{
282 if (data_fd < 0)
283 panic("Terminal not properly attached.\n");
284
285 ssize_t ret = atomic_write(data_fd, buf, len);
286 if (ret < len)
287 detach();
288
289 return ret;
290}
291
292#define MORE_PENDING (ULL(1) << 61)
293#define RECEIVE_SUCCESS (ULL(0) << 62)
294#define RECEIVE_NONE (ULL(2) << 62)
295#define RECEIVE_ERROR (ULL(3) << 62)
296
297uint8_t
298Terminal::readData()
299{
300 uint8_t c;
301
302 assert(!rxbuf.empty());
303 rxbuf.read((char *)&c, 1);
304
305 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n",
306 isprint(c) ? c : ' ', c, !rxbuf.empty());
307
308 return c;
309}
310
311uint64_t
312Terminal::console_in()
313{
314 uint64_t value;
315
316 if (dataAvailable()) {
317 value = RECEIVE_SUCCESS | readData();
318 if (!rxbuf.empty())
319 value |= MORE_PENDING;
320 } else {
321 value = RECEIVE_NONE;
322 }
323
324 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value);
325
326 return value;
327}
328
329void
330Terminal::writeData(uint8_t c)
331{
332#if TRACING_ON == 1
333 if (DTRACE(Terminal)) {
334 static char last = '\0';
335
336 if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) {
337 if (c == '\n' || c == '\r') {
338 int size = linebuf.size();
339 char *buffer = new char[size + 1];
340 linebuf.read(buffer, size);
341 buffer[size] = '\0';
342 DPRINTF(Terminal, "%s\n", buffer);
343 delete [] buffer;
344 } else {
345 linebuf.write(&c, 1);
346 }
347 }
348
349 last = c;
350 }
351#endif
352
353 txbuf.write(&c, 1);
354
355 if (data_fd >= 0)
356 write(c);
357
358 if (outfile)
359 outfile->stream()->put((char)c);
360
361 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
362 isprint(c) ? c : ' ', (int)c);
363
364}
365
366Terminal *
367TerminalParams::create()
368{
369 return new Terminal(this);
370}