xref: /kvmtool/hw/serial.c (revision 19a2bb7d9fb82f04536805719710667a606970ef)
1 #include "kvm/8250-serial.h"
2 
3 #include "kvm/ioport.h"
4 #include "kvm/util.h"
5 #include "kvm/kvm.h"
6 
7 #include <linux/serial_reg.h>
8 
9 #include <stdbool.h>
10 #include <poll.h>
11 
12 struct serial8250_device {
13 	uint16_t		iobase;
14 	uint8_t			irq;
15 
16 	uint8_t			rbr;		/* receive buffer */
17 	uint8_t			dll;
18 	uint8_t			dlm;
19 	uint8_t			iir;
20 	uint8_t			ier;
21 	uint8_t			fcr;
22 	uint8_t			lcr;
23 	uint8_t			mcr;
24 	uint8_t			lsr;
25 	uint8_t			scr;
26 
27 	uint8_t			counter;
28 };
29 
30 static struct serial8250_device devices[] = {
31 	/* ttyS0 */
32 	[0]	= {
33 		.iobase			= 0x3f8,
34 		.irq			= 4,
35 
36 		.lsr			= UART_LSR_TEMT | UART_LSR_THRE,
37 	},
38 	/* ttyS1 */
39 	[1]	= {
40 		.iobase			= 0x2f8,
41 		.irq			= 3,
42 
43 		.iir			= UART_IIR_NO_INT,
44 	},
45 	/* ttyS2 */
46 	[2]	= {
47 		.iobase			= 0x3e8,
48 		.irq			= 4,
49 
50 		.iir			= UART_IIR_NO_INT,
51 	},
52 };
53 
54 static int read_char(int fd)
55 {
56 	int c;
57 
58 	if (read(fd, &c, 1) < 0)
59 		return -1;
60 
61 	return c;
62 }
63 
64 static bool is_readable(int fd)
65 {
66 	struct pollfd pollfd = (struct pollfd) {
67 		.fd	= fd,
68 		.events	= POLLIN,
69 	};
70 
71 	return poll(&pollfd, 1, 0) > 0;
72 }
73 
74 static void serial8250__receive(struct kvm *self, struct serial8250_device *dev)
75 {
76 	int c;
77 
78 	if (dev->lsr & UART_LSR_DR)
79 		return;
80 
81 	if (!is_readable(fileno(stdin)))
82 		return;
83 
84 	c		= read_char(fileno(stdin));
85 	if (c < 0)
86 		return;
87 
88 	dev->rbr	= c;
89 	dev->lsr	|= UART_LSR_DR;
90 }
91 
92 /*
93  * Interrupts are injected for ttyS0 only.
94  */
95 void serial8250__interrupt(struct kvm *self)
96 {
97 	struct serial8250_device *dev = &devices[0];
98 	uint8_t new_iir;
99 
100 	dev->iir	= UART_IIR_NO_INT;
101 
102 	/* No interrupts enabled. Exit... */
103 	if (!(dev->ier & (UART_IER_THRI|UART_IER_RDI)))
104 		return;
105 
106 	serial8250__receive(self, dev);
107 
108 	new_iir		= 0;
109 
110 	if (dev->lsr & UART_LSR_DR)
111 		new_iir			|= UART_IIR_RDI;
112 	else if (dev->ier & UART_IER_THRI)
113 		new_iir			|= UART_IIR_THRI;
114 
115 	/* Only send an IRQ if there's work to do. */
116 	if (new_iir) {
117 		dev->counter		= 0;
118 		dev->iir		= new_iir;
119 		kvm__irq_line(self, dev->irq, 0);
120 		kvm__irq_line(self, dev->irq, 1);
121 	}
122 }
123 
124 static struct serial8250_device *find_device(uint16_t port)
125 {
126 	unsigned int i;
127 
128 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
129 		struct serial8250_device *dev = &devices[i];
130 
131 		if (dev->iobase == (port & ~0x7))
132 			return dev;
133 	}
134 	return NULL;
135 }
136 
137 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
138 {
139 	struct serial8250_device *dev;
140 	uint16_t offset;
141 
142 	dev		= find_device(port);
143 	if (!dev)
144 		return false;
145 
146 	offset		= port - dev->iobase;
147 
148 	/* LSR is factory test and MSR is not used for writes */
149 	if (offset == UART_LSR || offset == UART_MSR)
150 		return true;
151 
152 	/* FCR, LCR, MCR, and SCR have the same meaning regardless of DLAB */
153 	switch (offset) {
154 	case UART_FCR:
155 		dev->fcr		= ioport__read8(data);
156 		return true;
157 	case UART_LCR:
158 		dev->lcr		= ioport__read8(data);
159 		return true;
160 	case UART_MCR:
161 		dev->mcr		= ioport__read8(data);
162 		return true;
163 	case UART_SCR:
164 		dev->scr		= ioport__read8(data);
165 		return true;
166 	default:
167 		break;
168 	}
169 
170 	if (dev->lcr & UART_LCR_DLAB) {
171 		switch (offset) {
172 		case UART_DLL:
173 			dev->dll		= ioport__read8(data);
174 			break;
175 		case UART_DLM:
176 			dev->dlm		= ioport__read8(data);
177 			break;
178 		}
179 	} else {
180 		switch (offset) {
181 		case UART_TX: {
182 			char *p = data;
183 			int i;
184 
185 			while (count--) {
186 				for (i = 0; i < size; i++)
187 					fprintf(stdout, "%c", *p++);
188 			}
189 			fflush(stdout);
190 
191 			if (dev->counter++ > 10)
192 				dev->iir		= UART_IIR_NO_INT;
193 
194 			break;
195 		}
196 		case UART_IER:
197 			dev->ier		= ioport__read8(data);
198 			break;
199 		}
200 	}
201 
202 	return true;
203 }
204 
205 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
206 {
207 	struct serial8250_device *dev;
208 	uint16_t offset;
209 
210 	dev		= find_device(port);
211 	if (!dev)
212 		return false;
213 
214 	offset		= port - dev->iobase;
215 
216 	/* DLAB only changes the meaning of the first two registers */
217 	if (dev->lcr & UART_LCR_DLAB) {
218 		switch (offset) {
219 		case UART_DLL:
220 			ioport__write8(data, dev->dll);
221 			return true;
222 		case UART_DLM:
223 			ioport__write8(data, dev->dlm);
224 			return true;
225 		}
226 	} else {
227 		switch (offset) {
228 		case UART_RX:
229 			if (dev->lsr & UART_LSR_DR) {
230 				dev->iir		= UART_IIR_NO_INT;
231 
232 				dev->lsr		&= ~UART_LSR_DR;
233 				ioport__write8(data, dev->rbr);
234 			}
235 			return true;
236 		case UART_IER:
237 			ioport__write8(data, dev->ier);
238 			return true;
239 		}
240 	}
241 
242 	/* All other registers have the same meaning regardless of DLAB */
243 	switch (offset) {
244 	case UART_IIR:
245 		dev->iir &= 0x3f; /* no FIFO for 8250 */
246 		ioport__write8(data, dev->iir);
247 		break;
248 	case UART_LCR:
249 		ioport__write8(data, dev->lcr);
250 		break;
251 	case UART_MCR:
252 		ioport__write8(data, dev->mcr);
253 		break;
254 	case UART_LSR:
255 		ioport__write8(data, dev->lsr);
256 		break;
257 	case UART_MSR:
258 		ioport__write8(data, UART_MSR_CTS);
259 		break;
260 	case UART_SCR:
261 		/* No SCR for 8250 */
262 		ioport__write8(data, 0);
263 		break;
264 	}
265 
266 	return true;
267 }
268 
269 static struct ioport_operations serial8250_ops = {
270 	.io_in		= serial8250_in,
271 	.io_out		= serial8250_out,
272 };
273 
274 void serial8250__init(void)
275 {
276 	unsigned int i;
277 
278 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
279 		struct serial8250_device *dev = &devices[i];
280 
281 		ioport__register(dev->iobase, &serial8250_ops, 8);
282 	}
283 }
284