xref: /kvmtool/hw/serial.c (revision 05d1a2a69d0fc68b7771a889e7acaaba2fda10b2)
1 #include "kvm/8250-serial.h"
2 
3 #include "kvm/read-write.h"
4 #include "kvm/ioport.h"
5 #include "kvm/util.h"
6 #include "kvm/term.h"
7 #include "kvm/kvm.h"
8 
9 #include <linux/serial_reg.h>
10 
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			msr;
26 	uint8_t			scr;
27 };
28 
29 static struct serial8250_device devices[] = {
30 	/* ttyS0 */
31 	[0]	= {
32 		.iobase			= 0x3f8,
33 		.irq			= 4,
34 
35 		.iir			= UART_IIR_NO_INT,
36 		.lsr			= UART_LSR_TEMT | UART_LSR_THRE,
37 		.msr			= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS,
38 		.mcr			= UART_MCR_OUT2,
39 	},
40 	/* ttyS1 */
41 	[1]	= {
42 		.iobase			= 0x2f8,
43 		.irq			= 3,
44 
45 		.iir			= UART_IIR_NO_INT,
46 	},
47 	/* ttyS2 */
48 	[2]	= {
49 		.iobase			= 0x3e8,
50 		.irq			= 4,
51 
52 		.iir			= UART_IIR_NO_INT,
53 	},
54 };
55 
56 static void serial8250__receive(struct kvm *self, struct serial8250_device *dev)
57 {
58 	int c;
59 
60 	if (dev->lsr & UART_LSR_DR)
61 		return;
62 
63 	if (!term_readable(CONSOLE_8250))
64 		return;
65 
66 	c		= term_getc(CONSOLE_8250);
67 
68 	if (c < 0)
69 		return;
70 
71 	dev->rbr	= c;
72 	dev->lsr	|= UART_LSR_DR;
73 }
74 
75 /*
76  * Interrupts are injected for ttyS0 only.
77  */
78 void serial8250__inject_interrupt(struct kvm *self)
79 {
80 	struct serial8250_device *dev = &devices[0];
81 
82 	serial8250__receive(self, dev);
83 
84 	if (dev->ier & UART_IER_RDI && dev->lsr & UART_LSR_DR)
85 		dev->iir		= UART_IIR_RDI;
86 	else if (dev->ier & UART_IER_THRI)
87 		dev->iir		= UART_IIR_THRI;
88 	else
89 		dev->iir		= UART_IIR_NO_INT;
90 
91 	if (dev->iir != UART_IIR_NO_INT) {
92 		kvm__irq_line(self, dev->irq, 0);
93 		kvm__irq_line(self, dev->irq, 1);
94 	}
95 }
96 
97 static struct serial8250_device *find_device(uint16_t port)
98 {
99 	unsigned int i;
100 
101 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
102 		struct serial8250_device *dev = &devices[i];
103 
104 		if (dev->iobase == (port & ~0x7))
105 			return dev;
106 	}
107 	return NULL;
108 }
109 
110 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
111 {
112 	struct serial8250_device *dev;
113 	uint16_t offset;
114 
115 	dev		= find_device(port);
116 	if (!dev)
117 		return false;
118 
119 	offset		= port - dev->iobase;
120 
121 	if (dev->lcr & UART_LCR_DLAB) {
122 		switch (offset) {
123 		case UART_DLL:
124 			dev->dll	= ioport__read8(data);
125 			break;
126 		case UART_DLM:
127 			dev->dlm	= ioport__read8(data);
128 			break;
129 		case UART_FCR:
130 			dev->fcr	= ioport__read8(data);
131 			break;
132 		case UART_LCR:
133 			dev->lcr	= ioport__read8(data);
134 			break;
135 		case UART_MCR:
136 			dev->mcr	= ioport__read8(data);
137 			break;
138 		case UART_LSR:
139 			/* Factory test */
140 			break;
141 		case UART_MSR:
142 			/* Not used */
143 			break;
144 		case UART_SCR:
145 			dev->scr	= ioport__read8(data);
146 			break;
147 		default:
148 			return false;
149 		}
150 	} else {
151 		switch (offset) {
152 		case UART_TX: {
153 			char *addr = data;
154 			if (!(dev->mcr & UART_MCR_LOOP)) {
155 				term_putc(CONSOLE_8250, addr, size * count);
156 			}
157 			dev->iir		= UART_IIR_NO_INT;
158 			break;
159 		}
160 		case UART_FCR:
161 			dev->fcr	= ioport__read8(data);
162 			break;
163 		case UART_IER:
164 			dev->ier	= ioport__read8(data) & 0x3f;
165 			break;
166 		case UART_LCR:
167 			dev->lcr	= ioport__read8(data);
168 			break;
169 		case UART_MCR:
170 			dev->mcr	= ioport__read8(data);
171 			break;
172 		case UART_LSR:
173 			/* Factory test */
174 			break;
175 		case UART_MSR:
176 			/* Not used */
177 			break;
178 		case UART_SCR:
179 			dev->scr	= ioport__read8(data);
180 			break;
181 		default:
182 			return false;
183 		}
184 	}
185 	return true;
186 }
187 
188 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
189 {
190 	struct serial8250_device *dev;
191 	uint16_t offset;
192 
193 	dev		= find_device(port);
194 	if (!dev)
195 		return false;
196 
197 	offset		= port - dev->iobase;
198 
199 	if (dev->lcr & UART_LCR_DLAB) {
200 		switch (offset) {
201 		case UART_DLL:
202 			ioport__write8(data, dev->dll);
203 			return true;
204 		case UART_DLM:
205 			ioport__write8(data, dev->dlm);
206 			return true;
207 		default:
208 			break;
209 		}
210 	} else {
211 		switch (offset) {
212 		case UART_RX:
213 			ioport__write8(data, dev->rbr);
214 			dev->lsr		&= ~UART_LSR_DR;
215 			dev->iir		= UART_IIR_NO_INT;
216 			return true;
217 		case UART_IER:
218 			ioport__write8(data, dev->ier);
219 			return true;
220 		default:
221 			break;
222 		}
223 	}
224 
225 	switch (offset) {
226 	case UART_IIR: {
227 		uint8_t iir = dev->iir;
228 
229 		if (dev->fcr & UART_FCR_ENABLE_FIFO)
230 			iir		|= 0xc0;
231 
232 		ioport__write8(data, iir);
233 		break;
234 	}
235 	case UART_LCR:
236 		ioport__write8(data, dev->lcr);
237 		break;
238 	case UART_MCR:
239 		ioport__write8(data, dev->mcr);
240 		break;
241 	case UART_LSR:
242 		ioport__write8(data, dev->lsr);
243 		dev->lsr		&= ~(UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI);
244 		break;
245 	case UART_MSR:
246 		ioport__write8(data, dev->msr);
247 		break;
248 	case UART_SCR:
249 		ioport__write8(data, dev->scr);
250 		break;
251 	default:
252 		return false;
253 	}
254 	return true;
255 }
256 
257 static struct ioport_operations serial8250_ops = {
258 	.io_in		= serial8250_in,
259 	.io_out		= serial8250_out,
260 };
261 
262 static void serial8250__device_init(struct kvm *kvm, struct serial8250_device *dev)
263 {
264 	ioport__register(dev->iobase, &serial8250_ops, 8);
265 	kvm__irq_line(kvm, dev->irq, 0);
266 }
267 
268 void serial8250__init(struct kvm *kvm)
269 {
270 	unsigned int i;
271 
272 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
273 		struct serial8250_device *dev = &devices[i];
274 
275 		serial8250__device_init(kvm, dev);
276 	}
277 }
278