xref: /kvmtool/hw/serial.c (revision 6d54df7482d9df411bd092830acbb81d4985ba15)
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 = (struct pollfd) {
96 		.fd	= fd,
97 		.events	= POLLIN,
98 	};
99 
100 	return poll(&pollfd, 1, 0) > 0;
101 }
102 
103 void serial8250__interrupt(struct kvm *self)
104 {
105 	if (!(device.lsr & UART_LSR_DR) && is_readable(fileno(stdin))) {
106 		int c;
107 
108 		c			= read_char(fileno(stdin));
109 		if (c >= 0) {
110 			device.thr		= c;
111 			device.lsr		|= UART_LSR_DR;
112 		}
113 	}
114 
115 	if (device.ier & UART_IER_THRI || device.lsr & UART_LSR_DR) {
116 		device.iir		&= ~UART_IIR_NO_INT;
117 		kvm__irq_line(self, device.irq, 1);
118 	}
119 }
120 
121 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
122 {
123 	uint16_t offset = port - device.iobase;
124 
125 	if (device.lcr & UART_LCR_DLAB) {
126 		switch (offset) {
127 		case DLL:
128 			device.dll		= ioport__read8(data);
129 			break;
130 		case DLM:
131 			device.dlm		= ioport__read8(data);
132 			break;
133 		case FCR:
134 			device.fcr		= ioport__read8(data);
135 			break;
136 		case LCR:
137 			device.lcr		= ioport__read8(data);
138 			break;
139 		default:
140 			return false;
141 		}
142 	} else {
143 		switch (offset) {
144 		case THR: {
145 			char *p = data;
146 			int i;
147 
148 			while (count--) {
149 				for (i = 0; i < size; i++)
150 					fprintf(stdout, "%c", *p++);
151 			}
152 			fflush(stdout);
153 
154 			device.iir		|= UART_IIR_NO_INT;
155 			kvm__irq_line(self, device.irq, 0);
156 
157 			break;
158 		}
159 		case IER:
160 			device.ier		= ioport__read8(data);
161 			break;
162 		case FCR:
163 			device.fcr		= ioport__read8(data);
164 			break;
165 		case LCR:
166 			device.lcr		= ioport__read8(data);
167 			break;
168 		case MCR:
169 			device.mcr		= ioport__read8(data);
170 			break;
171 		case SCR:
172 			device.scr		= ioport__read8(data);
173 			break;
174 		default:
175 			return false;
176 		}
177 	}
178 
179 	return true;
180 }
181 
182 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
183 {
184 	uint16_t offset = port - device.iobase;
185 
186 	if (device.lcr & UART_LCR_DLAB)
187 		return false;
188 
189 	switch (offset) {
190 	case THR:
191 		if (device.lsr & UART_LSR_DR) {
192 			device.lsr		&= ~UART_LSR_DR;
193 			ioport__write8(data, device.thr);
194 
195 			device.iir		|= UART_IIR_NO_INT;
196 			kvm__irq_line(self, device.irq, 0);
197 		}
198 		break;
199 	case IER:
200 		ioport__write8(data, device.ier);
201 		break;
202 	case IIR:
203 		ioport__write8(data, device.iir);
204 		break;
205 	case LCR:
206 		ioport__write8(data, device.lcr);
207 		break;
208 	case MCR:
209 		ioport__write8(data, device.mcr);
210 		break;
211 	case LSR:
212 		ioport__write8(data, device.lsr);
213 		break;
214 	case MSR:
215 		ioport__write8(data, UART_MSR_CTS);
216 		break;
217 	case SCR:
218 		ioport__write8(data, device.scr);
219 		break;
220 	default:
221 		return false;
222 	}
223 
224 	return true;
225 }
226 
227 static struct ioport_operations serial8250_ops = {
228 	.io_in		= serial8250_in,
229 	.io_out		= serial8250_out,
230 };
231 
232 void serial8250__init(void)
233 {
234 	ioport__register(device.iobase, &serial8250_ops, 8);
235 }
236