xref: /kvmtool/hw/serial.c (revision 76b4a12288e7d78618a30c26f71ef4099793a2f5)
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 <stdbool.h>
8 #include <poll.h>
9 
10 /* Transmitter holding register */
11 #define THR             0
12 
13 /* Receive buffer register */
14 #define RBR             0
15 
16 /* Divisor latch low byte */
17 #define DLL		0
18 
19 /* Divisor latch high byte */
20 #define DLM		1
21 
22 /* Interrupt enable register */
23 #define IER		1
24 
25 #define UART_IER_RDI		0x01
26 #define UART_IER_THRI		0x02
27 
28 /* Interrupt identification register */
29 #define IIR		2
30 
31 #define UART_IIR_NO_INT		0x01
32 #define UART_IIR_THRI		0x02
33 
34 /* 16550 FIFO Control Register */
35 #define FCR		2
36 
37 /* Line control register */
38 #define LCR		3
39 
40 #define UART_LCR_DLAB		0x80
41 
42 /* Modem control register */
43 #define MCR		4
44 
45 /* Line status register */
46 #define LSR		5
47 
48 #define UART_LSR_DR		0x01
49 #define UART_LSR_THRE		0x20
50 
51 /* Modem status register */
52 #define MSR		6
53 
54 #define UART_MSR_CTS		0x10
55 
56 /* Scratch register */
57 #define SCR		7
58 
59 struct serial8250_device {
60 	uint16_t		iobase;
61 	uint8_t			irq;
62 
63 	uint8_t			thr;
64 	uint8_t			dll;
65 	uint8_t			dlm;
66 	uint8_t			iir;
67 	uint8_t			ier;
68 	uint8_t			fcr;
69 	uint8_t			lcr;
70 	uint8_t			mcr;
71 	uint8_t			lsr;
72 	uint8_t			scr;
73 };
74 
75 static struct serial8250_device device = {
76 	.iobase			= 0x3f8,	/* ttyS0 */
77 	.irq			= 4,
78 
79 	.iir			= UART_IIR_NO_INT,
80 	.lsr			= UART_LSR_THRE,
81 };
82 
83 static int read_char(int fd)
84 {
85 	int c;
86 
87 	if (read(fd, &c, 1) < 0)
88 		return -1;
89 
90 	return c;
91 }
92 
93 static bool is_readable(int fd)
94 {
95 	struct pollfd pollfd;
96 	int err;
97 
98 	pollfd		= (struct pollfd) {
99 		.fd		= fd,
100 		.events		= POLLIN,
101 	};
102 
103 	err		= poll(&pollfd, 1, 0);
104 	return err > 0;
105 }
106 
107 void serial8250__interrupt(struct kvm *self)
108 {
109 	if (!(device.lsr & UART_LSR_DR) && is_readable(fileno(stdin))) {
110 		int c;
111 
112 		c			= read_char(fileno(stdin));
113 		if (c >= 0) {
114 			device.thr		= c;
115 			device.lsr		|= UART_LSR_DR;
116 		}
117 	}
118 
119 	if (device.ier & UART_IER_THRI || device.lsr & UART_LSR_DR) {
120 		device.iir		&= ~UART_IIR_NO_INT;
121 		kvm__irq_line(self, device.irq, 1);
122 	}
123 }
124 
125 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
126 {
127 	uint16_t offset = port - device.iobase;
128 
129 	if (device.lcr & UART_LCR_DLAB) {
130 		switch (offset) {
131 		case DLL:
132 			device.dll		= ioport__read8(data);
133 			break;
134 		case DLM:
135 			device.dlm		= ioport__read8(data);
136 			break;
137 		case FCR:
138 			device.fcr		= ioport__read8(data);
139 			break;
140 		case LCR:
141 			device.lcr		= ioport__read8(data);
142 			break;
143 		default:
144 			return false;
145 		}
146 	} else {
147 		switch (offset) {
148 		case THR: {
149 			char *p = data;
150 			int i;
151 
152 			while (count--) {
153 				for (i = 0; i < size; i++)
154 					fprintf(stdout, "%c", *p++);
155 			}
156 			fflush(stdout);
157 
158 			device.iir		|= UART_IIR_NO_INT;
159 			kvm__irq_line(self, device.irq, 0);
160 
161 			break;
162 		}
163 		case IER:
164 			device.ier		= ioport__read8(data);
165 			break;
166 		case FCR:
167 			device.fcr		= ioport__read8(data);
168 			break;
169 		case LCR:
170 			device.lcr		= ioport__read8(data);
171 			break;
172 		case MCR:
173 			device.mcr		= ioport__read8(data);
174 			break;
175 		case SCR:
176 			device.scr		= ioport__read8(data);
177 			break;
178 		default:
179 			return false;
180 		}
181 	}
182 
183 	return true;
184 }
185 
186 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
187 {
188 	uint16_t offset = port - device.iobase;
189 
190 	if (device.lcr & UART_LCR_DLAB)
191 		return false;
192 
193 	switch (offset) {
194 	case THR:
195 		if (device.lsr & UART_LSR_DR) {
196 			device.lsr		&= ~UART_LSR_DR;
197 			ioport__write8(data, device.thr);
198 
199 			device.iir		|= UART_IIR_NO_INT;
200 			kvm__irq_line(self, device.irq, 0);
201 		}
202 		break;
203 	case IER:
204 		ioport__write8(data, device.ier);
205 		break;
206 	case IIR:
207 		ioport__write8(data, device.iir);
208 		break;
209 	case LCR:
210 		ioport__write8(data, device.lcr);
211 		break;
212 	case MCR:
213 		ioport__write8(data, device.mcr);
214 		break;
215 	case LSR:
216 		ioport__write8(data, device.lsr);
217 		break;
218 	case MSR:
219 		ioport__write8(data, UART_MSR_CTS);
220 		break;
221 	case SCR:
222 		ioport__write8(data, device.scr);
223 		break;
224 	default:
225 		return false;
226 	}
227 
228 	return true;
229 }
230 
231 static struct ioport_operations serial8250_ops = {
232 	.io_in		= serial8250_in,
233 	.io_out		= serial8250_out,
234 };
235 
236 void serial8250__init(void)
237 {
238 	ioport__register(device.iobase, &serial8250_ops, 8);
239 }
240