1
#include <deque>
2
#include <errno.h>
3
#include <fcntl.h>
4
#include <riscv/mmio_plugin.h>
5
#include <stdint.h>
6
#include <stdio.h>
7
#include <string.h>
8
#include <string>
9
#include <sys/types.h>
10
#include <termios.h>
11
#include <unistd.h>
12
13
namespace {
14
15
constexpr reg_t UART_REG_THR_RBR_DLL = 0x00;
16
constexpr reg_t UART_REG_IER_DLM = 0x01;
17
constexpr reg_t UART_REG_IIR_FCR = 0x02;
18
constexpr reg_t UART_REG_LCR = 0x03;
19
constexpr reg_t UART_REG_MCR = 0x04;
20
constexpr reg_t UART_REG_LSR = 0x05;
21
constexpr reg_t UART_REG_MSR = 0x06;
22
constexpr reg_t UART_REG_SCR = 0x07;
23
24
constexpr uint8_t UART_LCR_DLAB = 0x80;
25
26
constexpr uint8_t UART_LSR_DR = 0x01;
27
constexpr uint8_t UART_LSR_THRE = 0x20;
28
constexpr uint8_t UART_LSR_TEMT = 0x40;
29
30
class ns16550a_plugin_t {
31
public:
32
explicit ns16550a_plugin_t(const std::string &args) : args_(args) {
33
const bool use_tty = args_.find("tty") != std::string::npos;
34
if (use_tty) {
35
int tty_in = open("/dev/tty", O_RDONLY | O_CLOEXEC);
36
int tty_out = open("/dev/tty", O_WRONLY | O_CLOEXEC);
37
38
if (tty_in >= 0 && tty_out >= 0) {
39
stdin_fd_ = tty_in;
40
stdout_fd_ = tty_out;
41
close_stdin_ = true;
42
close_stdout_ = true;
43
} else {
44
if (tty_in >= 0) {
45
close(tty_in);
46
}47
if (tty_out >= 0) {
48
close(tty_out);
49
}50
51
stdin_fd_ = STDIN_FILENO;
52
stdout_fd_ = STDOUT_FILENO;
53
}54
} else {
55
stdin_fd_ = STDIN_FILENO;
56
stdout_fd_ = STDOUT_FILENO;
57
}58
59
if (isatty(stdin_fd_) == 1 && tcgetattr(stdin_fd_, &stdin_termios_) == 0) {
60
stdin_termios_saved_ = true;
61
62
struct termios raw = stdin_termios_;
63
raw.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
64
raw.c_iflag &= ~(IXON | IXOFF | ICRNL | INLCR | IGNCR);
65
raw.c_cc[VMIN] = 1;
66
raw.c_cc[VTIME] = 0;
67
(void)tcsetattr(stdin_fd_, TCSANOW, &raw);
68
}69
70
stdin_flags_ = fcntl(stdin_fd_, F_GETFL, 0);
71
if (stdin_flags_ >= 0) {
72
(void)fcntl(stdin_fd_, F_SETFL, stdin_flags_ | O_NONBLOCK);
73
}74
}75
76
~ns16550a_plugin_t() {
77
if (stdin_flags_ >= 0) {
78
(void)fcntl(stdin_fd_, F_SETFL, stdin_flags_);
79
}80
81
if (stdin_termios_saved_) {
82
(void)tcsetattr(stdin_fd_, TCSANOW, &stdin_termios_);
83
}84
85
if (close_stdin_) {
86
close(stdin_fd_);
87
}88
if (close_stdout_) {
89
close(stdout_fd_);
90
}91
}92
93
bool load(reg_t addr, size_t len, uint8_t *bytes) {
94
if (!bytes || len == 0) {
95
return false;
96
}97
98
for (size_t i = 0; i < len; i++) {
99
if (!load8(addr + i, &bytes[i])) {
100
return false;
101
}102
}103
104
return true;
105
}106
107
bool store(reg_t addr, size_t len, const uint8_t *bytes) {
108
if (!bytes || len == 0) {
109
return false;
110
}111
112
for (size_t i = 0; i < len; i++) {
113
if (!store8(addr + i, bytes[i])) {
114
return false;
115
}116
}117
118
return true;
119
}120
121
private:
122
bool load8(reg_t addr, uint8_t *value) {
123
if (!value) {
124
return false;
125
}126
127
pump_stdin();
128
129
const reg_t reg = addr & 0x7;
130
switch (reg) {
131
case UART_REG_THR_RBR_DLL:
132
if (lcr_ & UART_LCR_DLAB) {
133
*value = dll_;
134
return true;
135
}136
137
if (rx_fifo_.empty()) {
138
*value = 0;
139
return true;
140
}141
142
*value = rx_fifo_.front();
143
rx_fifo_.pop_front();
144
return true;
145
146
case UART_REG_IER_DLM:
147
*value = (lcr_ & UART_LCR_DLAB) ? dlm_ : ier_;
148
return true;
149
150
case UART_REG_IIR_FCR:
151
*value = rx_fifo_.empty() ? 0x01 : 0x04;
152
return true;
153
154
case UART_REG_LCR:
155
*value = lcr_;
156
return true;
157
158
case UART_REG_MCR:
159
*value = mcr_;
160
return true;
161
162
case UART_REG_LSR: {
163
uint8_t lsr = UART_LSR_THRE | UART_LSR_TEMT;
164
if (!rx_fifo_.empty()) {
165
lsr |= UART_LSR_DR;
166
}167
*value = lsr;
168
return true;
169
}170
171
case UART_REG_MSR:
172
*value = 0xB0;
173
return true;
174
175
case UART_REG_SCR:
176
*value = scr_;
177
return true;
178
179
default:
180
*value = 0;
181
return true;
182
}183
}184
185
bool store8(reg_t addr, uint8_t value) {
186
const reg_t reg = addr & 0x7;
187
switch (reg) {
188
case UART_REG_THR_RBR_DLL:
189
if (lcr_ & UART_LCR_DLAB) {
190
dll_ = value;
191
return true;
192
}193
194
return write_stdout(value);
195
196
case UART_REG_IER_DLM:
197
if (lcr_ & UART_LCR_DLAB) {
198
dlm_ = value;
199
} else {
200
ier_ = value;
201
}202
return true;
203
204
case UART_REG_IIR_FCR:
205
return true;
206
207
case UART_REG_LCR:
208
lcr_ = value;
209
return true;
210
211
case UART_REG_MCR:
212
mcr_ = value;
213
return true;
214
215
case UART_REG_SCR:
216
scr_ = value;
217
return true;
218
219
default:
220
return true;
221
}222
}223
224
bool write_stdout(uint8_t ch) {
225
ssize_t wrote = write(stdout_fd_, &ch, 1);
226
if (wrote == 1) {
227
return true;
228
}229
230
if (wrote < 0 && (errno == EINTR || errno == EAGAIN)) {
231
return true;
232
}233
234
return false;
235
}236
237
void pump_stdin() {
238
if (stdin_fd_ < 0) {
239
return;
240
}241
242
uint8_t buf[256];
243
while (true) {
244
ssize_t n = read(stdin_fd_, buf, sizeof(buf));
245
if (n > 0) {
246
for (ssize_t i = 0; i < n; i++) {
247
rx_fifo_.push_back(buf[i]);
248
}249
continue;
250
}251
252
if (n == 0) {
253
return;
254
}255
256
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
257
return;
258
}259
260
return;
261
}262
}263
264
std::string args_;
265
std::deque<uint8_t> rx_fifo_;
266
int stdin_fd_ = -1;
267
int stdout_fd_ = -1;
268
int stdin_flags_ = -1;
269
bool close_stdin_ = false;
270
bool close_stdout_ = false;
271
bool stdin_termios_saved_ = false;
272
struct termios stdin_termios_ = {};
273
274
uint8_t ier_ = 0;
275
uint8_t lcr_ = 0;
276
uint8_t mcr_ = 0;
277
uint8_t scr_ = 0;
278
uint8_t dll_ = 0;
279
uint8_t dlm_ = 0;
280
};
281
282
mmio_plugin_registration_t<ns16550a_plugin_t> reg_ns16550a_plugin("ns16550a");
283
284
} // namespace