xref: /kvmtool/hw/serial.c (revision 8dfae8be1660114bbf5b7726ba52d7abe9239d17)
1899fe063SPekka Enberg #include "kvm/8250-serial.h"
213a7760fSPekka Enberg 
38eb47d29SSasha Levin #include "kvm/read-write.h"
413a7760fSPekka Enberg #include "kvm/ioport.h"
54ef0f4d6SPekka Enberg #include "kvm/mutex.h"
646aa8d69SPekka Enberg #include "kvm/util.h"
705d1a2a6SAsias He #include "kvm/term.h"
8e557eef9SPekka Enberg #include "kvm/kvm.h"
913a7760fSPekka Enberg 
103fdf659dSSasha Levin #include <linux/types.h>
114e49b05bSCyrill Gorcunov #include <linux/serial_reg.h>
124e49b05bSCyrill Gorcunov 
132932c9ebSPekka Enberg #include <pthread.h>
1413a7760fSPekka Enberg 
1546aa8d69SPekka Enberg struct serial8250_device {
162932c9ebSPekka Enberg 	pthread_mutex_t		mutex;
171add4b76SSasha Levin 	u8			id;
182932c9ebSPekka Enberg 
193fdf659dSSasha Levin 	u16			iobase;
203fdf659dSSasha Levin 	u8			irq;
21f6b8ccc1SThomas Gleixner 	u8			irq_state;
22*8dfae8beSThomas Gleixner 	int			txcnt;
2376b4a122SPekka Enberg 
243fdf659dSSasha Levin 	u8			rbr;		/* receive buffer */
253fdf659dSSasha Levin 	u8			dll;
263fdf659dSSasha Levin 	u8			dlm;
273fdf659dSSasha Levin 	u8			iir;
283fdf659dSSasha Levin 	u8			ier;
293fdf659dSSasha Levin 	u8			fcr;
303fdf659dSSasha Levin 	u8			lcr;
313fdf659dSSasha Levin 	u8			mcr;
323fdf659dSSasha Levin 	u8			lsr;
333fdf659dSSasha Levin 	u8			msr;
343fdf659dSSasha Levin 	u8			scr;
3546aa8d69SPekka Enberg };
3646aa8d69SPekka Enberg 
37f3efa592SLiming Wang #define SERIAL_REGS_SETTING \
38f3efa592SLiming Wang 	.iir			= UART_IIR_NO_INT, \
39f3efa592SLiming Wang 	.lsr			= UART_LSR_TEMT | UART_LSR_THRE, \
40f3efa592SLiming Wang 	.msr			= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS, \
41f3efa592SLiming Wang 	.mcr			= UART_MCR_OUT2,
42f3efa592SLiming Wang 
43e62c18deSPekka Enberg static struct serial8250_device devices[] = {
44c6a69c61SPekka Enberg 	/* ttyS0 */
45c6a69c61SPekka Enberg 	[0]	= {
462932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
472932c9ebSPekka Enberg 
481add4b76SSasha Levin 		.id			= 0,
49c6a69c61SPekka Enberg 		.iobase			= 0x3f8,
50e557eef9SPekka Enberg 		.irq			= 4,
51e557eef9SPekka Enberg 
52f3efa592SLiming Wang 		SERIAL_REGS_SETTING
53c6a69c61SPekka Enberg 	},
54e62c18deSPekka Enberg 	/* ttyS1 */
55e62c18deSPekka Enberg 	[1]	= {
562932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
572932c9ebSPekka Enberg 
581add4b76SSasha Levin 		.id			= 1,
59e62c18deSPekka Enberg 		.iobase			= 0x2f8,
60e62c18deSPekka Enberg 		.irq			= 3,
61133bedc1SPekka Enberg 
62f3efa592SLiming Wang 		SERIAL_REGS_SETTING
63e62c18deSPekka Enberg 	},
64e62c18deSPekka Enberg 	/* ttyS2 */
65e62c18deSPekka Enberg 	[2]	= {
662932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
672932c9ebSPekka Enberg 
681add4b76SSasha Levin 		.id			= 2,
69e62c18deSPekka Enberg 		.iobase			= 0x3e8,
70e62c18deSPekka Enberg 		.irq			= 4,
71133bedc1SPekka Enberg 
72f3efa592SLiming Wang 		SERIAL_REGS_SETTING
73e62c18deSPekka Enberg 	},
74bf459c83SPekka Enberg 	/* ttyS3 */
75bf459c83SPekka Enberg 	[3]	= {
76bf459c83SPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
77bf459c83SPekka Enberg 
781add4b76SSasha Levin 		.id			= 3,
79bf459c83SPekka Enberg 		.iobase			= 0x2e8,
80bf459c83SPekka Enberg 		.irq			= 3,
81bf459c83SPekka Enberg 
82f3efa592SLiming Wang 		SERIAL_REGS_SETTING
83bf459c83SPekka Enberg 	},
8446aa8d69SPekka Enberg };
8546aa8d69SPekka Enberg 
86f6b8ccc1SThomas Gleixner static void serial8250_update_irq(struct kvm *kvm, struct serial8250_device *dev)
87f6b8ccc1SThomas Gleixner {
88f6b8ccc1SThomas Gleixner 	u8 iir = 0;
89f6b8ccc1SThomas Gleixner 
90f6b8ccc1SThomas Gleixner 	/* Data ready and rcv interrupt enabled ? */
91f6b8ccc1SThomas Gleixner 	if ((dev->ier & UART_IER_RDI) && (dev->lsr & UART_LSR_DR))
92f6b8ccc1SThomas Gleixner 		iir |= UART_IIR_RDI;
93f6b8ccc1SThomas Gleixner 
94f6b8ccc1SThomas Gleixner 	/* Transmitter empty and interrupt enabled ? */
95f6b8ccc1SThomas Gleixner 	if ((dev->ier & UART_IER_THRI) && (dev->lsr & UART_LSR_TEMT))
96f6b8ccc1SThomas Gleixner 		iir |= UART_IIR_THRI;
97f6b8ccc1SThomas Gleixner 
98f6b8ccc1SThomas Gleixner 	/* Now update the irq line, if necessary */
99f6b8ccc1SThomas Gleixner 	if (!iir) {
100f6b8ccc1SThomas Gleixner 		dev->iir = UART_IIR_NO_INT;
101f6b8ccc1SThomas Gleixner 		if (dev->irq_state)
102f6b8ccc1SThomas Gleixner 			kvm__irq_line(kvm, dev->irq, 0);
103f6b8ccc1SThomas Gleixner 	} else {
104f6b8ccc1SThomas Gleixner 		dev->iir = iir;
105f6b8ccc1SThomas Gleixner 		if (!dev->irq_state)
106f6b8ccc1SThomas Gleixner 			kvm__irq_line(kvm, dev->irq, 1);
107f6b8ccc1SThomas Gleixner 	}
108f6b8ccc1SThomas Gleixner 	dev->irq_state = iir;
109*8dfae8beSThomas Gleixner 
110*8dfae8beSThomas Gleixner 	/*
111*8dfae8beSThomas Gleixner 	 * If the kernel disabled the tx interrupt, we know that there
112*8dfae8beSThomas Gleixner 	 * is nothing more to transmit, so we can reset our tx logic
113*8dfae8beSThomas Gleixner 	 * here.
114*8dfae8beSThomas Gleixner 	 */
115*8dfae8beSThomas Gleixner 	if (!(dev->ier & UART_IER_THRI)) {
116*8dfae8beSThomas Gleixner 		dev->lsr |= UART_LSR_TEMT | UART_LSR_THRE;
117*8dfae8beSThomas Gleixner 		dev->txcnt = 0;
118*8dfae8beSThomas Gleixner 	}
119f6b8ccc1SThomas Gleixner }
120f6b8ccc1SThomas Gleixner 
121a428f72eSPekka Enberg #define SYSRQ_PENDING_NONE		0
122a428f72eSPekka Enberg #define SYSRQ_PENDING_BREAK		1
123a428f72eSPekka Enberg #define SYSRQ_PENDING_CMD		2
124a428f72eSPekka Enberg 
125a428f72eSPekka Enberg static int sysrq_pending;
126a428f72eSPekka Enberg 
12743835ac9SSasha Levin static void serial8250__sysrq(struct kvm *kvm, struct serial8250_device *dev)
128a428f72eSPekka Enberg {
129a428f72eSPekka Enberg 	switch (sysrq_pending) {
130a428f72eSPekka Enberg 	case SYSRQ_PENDING_BREAK:
131a428f72eSPekka Enberg 		dev->lsr |= UART_LSR_DR | UART_LSR_BI;
132a428f72eSPekka Enberg 
133a428f72eSPekka Enberg 		sysrq_pending = SYSRQ_PENDING_CMD;
134a428f72eSPekka Enberg 		break;
135a428f72eSPekka Enberg 	case SYSRQ_PENDING_CMD:
136a428f72eSPekka Enberg 		dev->rbr = 'p';
137a428f72eSPekka Enberg 		dev->lsr |= UART_LSR_DR;
138a428f72eSPekka Enberg 
139a428f72eSPekka Enberg 		sysrq_pending	= SYSRQ_PENDING_NONE;
140a428f72eSPekka Enberg 		break;
141a428f72eSPekka Enberg 	}
142a428f72eSPekka Enberg }
143a428f72eSPekka Enberg 
14443835ac9SSasha Levin static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev)
145251cf9a6SPekka Enberg {
146251cf9a6SPekka Enberg 	int c;
147251cf9a6SPekka Enberg 
148*8dfae8beSThomas Gleixner 	/*
149*8dfae8beSThomas Gleixner 	 * If the guest transmitted 16 chars in a row, we clear the
150*8dfae8beSThomas Gleixner 	 * TEMT/THRE bits to let the kernel escape from the 8250
151*8dfae8beSThomas Gleixner 	 * interrupt handler. We come here only once a ms, so that
152*8dfae8beSThomas Gleixner 	 * should give the kernel the desired pause.
153*8dfae8beSThomas Gleixner 	 */
154*8dfae8beSThomas Gleixner 	dev->lsr |= UART_LSR_TEMT | UART_LSR_THRE;
155*8dfae8beSThomas Gleixner 	dev->txcnt = 0;
156*8dfae8beSThomas Gleixner 
157db34045cSPekka Enberg 	if (dev->lsr & UART_LSR_DR)
158db34045cSPekka Enberg 		return;
159db34045cSPekka Enberg 
160a428f72eSPekka Enberg 	if (sysrq_pending) {
16143835ac9SSasha Levin 		serial8250__sysrq(kvm, dev);
162a428f72eSPekka Enberg 		return;
163a428f72eSPekka Enberg 	}
164a428f72eSPekka Enberg 
1651add4b76SSasha Levin 	if (!term_readable(CONSOLE_8250, dev->id))
166251cf9a6SPekka Enberg 		return;
167251cf9a6SPekka Enberg 
1681add4b76SSasha Levin 	c = term_getc(CONSOLE_8250, dev->id);
16905d1a2a6SAsias He 
170251cf9a6SPekka Enberg 	if (c < 0)
171251cf9a6SPekka Enberg 		return;
172251cf9a6SPekka Enberg 
173251cf9a6SPekka Enberg 	dev->rbr = c;
174251cf9a6SPekka Enberg 	dev->lsr |= UART_LSR_DR;
175251cf9a6SPekka Enberg }
176251cf9a6SPekka Enberg 
177f6b8ccc1SThomas Gleixner void serial8250__update_consoles(struct kvm *kvm)
1788bb34e0dSPekka Enberg {
179479de16fSCyrill Gorcunov 	unsigned int i;
1801add4b76SSasha Levin 
181479de16fSCyrill Gorcunov 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
1821add4b76SSasha Levin 		struct serial8250_device *dev = &devices[i];
183934c193bSPekka Enberg 
1844ef0f4d6SPekka Enberg 		mutex_lock(&dev->mutex);
1852932c9ebSPekka Enberg 
18643835ac9SSasha Levin 		serial8250__receive(kvm, dev);
187251cf9a6SPekka Enberg 
188f6b8ccc1SThomas Gleixner 		serial8250_update_irq(kvm, dev);
1892932c9ebSPekka Enberg 
1904ef0f4d6SPekka Enberg 		mutex_unlock(&dev->mutex);
1918bb34e0dSPekka Enberg 	}
1921add4b76SSasha Levin }
1938bb34e0dSPekka Enberg 
19443835ac9SSasha Levin void serial8250__inject_sysrq(struct kvm *kvm)
195a428f72eSPekka Enberg {
196a428f72eSPekka Enberg 	sysrq_pending	= SYSRQ_PENDING_BREAK;
197a428f72eSPekka Enberg }
198a428f72eSPekka Enberg 
1993fdf659dSSasha Levin static struct serial8250_device *find_device(u16 port)
200c6a69c61SPekka Enberg {
201c6a69c61SPekka Enberg 	unsigned int i;
202c6a69c61SPekka Enberg 
203c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
204c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
205c6a69c61SPekka Enberg 
206c6a69c61SPekka Enberg 		if (dev->iobase == (port & ~0x7))
207c6a69c61SPekka Enberg 			return dev;
208c6a69c61SPekka Enberg 	}
209c6a69c61SPekka Enberg 	return NULL;
210c6a69c61SPekka Enberg }
211c6a69c61SPekka Enberg 
212c9f6a037SXiao Guangrong static bool serial8250_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
21313a7760fSPekka Enberg {
214c6a69c61SPekka Enberg 	struct serial8250_device *dev;
2153fdf659dSSasha Levin 	u16 offset;
2162932c9ebSPekka Enberg 	bool ret = true;
21746aa8d69SPekka Enberg 
218c6a69c61SPekka Enberg 	dev = find_device(port);
219c6a69c61SPekka Enberg 	if (!dev)
220c6a69c61SPekka Enberg 		return false;
221c6a69c61SPekka Enberg 
2224ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
2232932c9ebSPekka Enberg 
224c6a69c61SPekka Enberg 	offset = port - dev->iobase;
225c6a69c61SPekka Enberg 
22646aa8d69SPekka Enberg 	switch (offset) {
227c59fa0c4SThomas Gleixner 	case UART_TX:
228c59fa0c4SThomas Gleixner 		if (!(dev->lcr & UART_LCR_DLAB)) {
22905d1a2a6SAsias He 			char *addr = data;
2300ea58e5bSPekka Enberg 
2310ea58e5bSPekka Enberg 			if (!(dev->mcr & UART_MCR_LOOP))
2321add4b76SSasha Levin 				term_putc(CONSOLE_8250, addr, size, dev->id);
233f6b8ccc1SThomas Gleixner 			/* else FIXME: Inject data into rcv path for LOOP */
2340ea58e5bSPekka Enberg 
235*8dfae8beSThomas Gleixner 			if (++dev->txcnt == 16)
236*8dfae8beSThomas Gleixner 				dev->lsr &= ~(UART_LSR_TEMT | UART_LSR_THRE);
237f6b8ccc1SThomas Gleixner 			break;
238c59fa0c4SThomas Gleixner 		} else {
239c59fa0c4SThomas Gleixner 			dev->dll = ioport__read8(data);
24046aa8d69SPekka Enberg 		}
241369c01c0SPekka Enberg 		break;
2424e49b05bSCyrill Gorcunov 	case UART_IER:
243f6b8ccc1SThomas Gleixner 		if (!(dev->lcr & UART_LCR_DLAB))
244369c01c0SPekka Enberg 			dev->ier = ioport__read8(data) & 0x3f;
245f6b8ccc1SThomas Gleixner 		else
246c59fa0c4SThomas Gleixner 			dev->dlm = ioport__read8(data);
247c59fa0c4SThomas Gleixner 		break;
248c59fa0c4SThomas Gleixner 	case UART_FCR:
249c59fa0c4SThomas Gleixner 		dev->fcr = ioport__read8(data);
25046aa8d69SPekka Enberg 		break;
251369c01c0SPekka Enberg 	case UART_LCR:
252369c01c0SPekka Enberg 		dev->lcr = ioport__read8(data);
253369c01c0SPekka Enberg 		break;
254369c01c0SPekka Enberg 	case UART_MCR:
255369c01c0SPekka Enberg 		dev->mcr = ioport__read8(data);
256369c01c0SPekka Enberg 		break;
257369c01c0SPekka Enberg 	case UART_LSR:
258369c01c0SPekka Enberg 		/* Factory test */
259369c01c0SPekka Enberg 		break;
260369c01c0SPekka Enberg 	case UART_MSR:
261369c01c0SPekka Enberg 		/* Not used */
262369c01c0SPekka Enberg 		break;
263369c01c0SPekka Enberg 	case UART_SCR:
264369c01c0SPekka Enberg 		dev->scr = ioport__read8(data);
265369c01c0SPekka Enberg 		break;
266369c01c0SPekka Enberg 	default:
2672932c9ebSPekka Enberg 		ret = false;
268d2ea115dSThomas Gleixner 		break;
26946aa8d69SPekka Enberg 	}
2702932c9ebSPekka Enberg 
271f6b8ccc1SThomas Gleixner 	serial8250_update_irq(kvm, dev);
272f6b8ccc1SThomas Gleixner 
2734ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
2742932c9ebSPekka Enberg 
2752932c9ebSPekka Enberg 	return ret;
27613a7760fSPekka Enberg }
27713a7760fSPekka Enberg 
278c9f6a037SXiao Guangrong static bool serial8250_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
27925af6674SPekka Enberg {
280c6a69c61SPekka Enberg 	struct serial8250_device *dev;
2813fdf659dSSasha Levin 	u16 offset;
2822932c9ebSPekka Enberg 	bool ret = true;
28346aa8d69SPekka Enberg 
284c6a69c61SPekka Enberg 	dev = find_device(port);
285c6a69c61SPekka Enberg 	if (!dev)
286c6a69c61SPekka Enberg 		return false;
287c6a69c61SPekka Enberg 
2884ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
2892932c9ebSPekka Enberg 
290c6a69c61SPekka Enberg 	offset = port - dev->iobase;
291c6a69c61SPekka Enberg 
29246aa8d69SPekka Enberg 	switch (offset) {
293251cf9a6SPekka Enberg 	case UART_RX:
294c59fa0c4SThomas Gleixner 		if (dev->lcr & UART_LCR_DLAB) {
295c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->dll);
296c59fa0c4SThomas Gleixner 		} else {
297251cf9a6SPekka Enberg 			ioport__write8(data, dev->rbr);
298369c01c0SPekka Enberg 			dev->lsr &= ~UART_LSR_DR;
299c59fa0c4SThomas Gleixner 		}
300369c01c0SPekka Enberg 		break;
301c59fa0c4SThomas Gleixner 	case UART_IER:
302c59fa0c4SThomas Gleixner 		if (dev->lcr & UART_LCR_DLAB)
303c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->dlm);
304c59fa0c4SThomas Gleixner 		else
305c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->ier);
306c59fa0c4SThomas Gleixner 		break;
307f6b8ccc1SThomas Gleixner 	case UART_IIR:
308f6b8ccc1SThomas Gleixner 		ioport__write8(data, dev->iir);
30946aa8d69SPekka Enberg 		break;
3104e49b05bSCyrill Gorcunov 	case UART_LCR:
311c6a69c61SPekka Enberg 		ioport__write8(data, dev->lcr);
31246aa8d69SPekka Enberg 		break;
3134e49b05bSCyrill Gorcunov 	case UART_MCR:
314c6a69c61SPekka Enberg 		ioport__write8(data, dev->mcr);
31546aa8d69SPekka Enberg 		break;
3164e49b05bSCyrill Gorcunov 	case UART_LSR:
317c6a69c61SPekka Enberg 		ioport__write8(data, dev->lsr);
31846aa8d69SPekka Enberg 		break;
3194e49b05bSCyrill Gorcunov 	case UART_MSR:
320369c01c0SPekka Enberg 		ioport__write8(data, dev->msr);
32146aa8d69SPekka Enberg 		break;
3224e49b05bSCyrill Gorcunov 	case UART_SCR:
323369c01c0SPekka Enberg 		ioport__write8(data, dev->scr);
32446aa8d69SPekka Enberg 		break;
325369c01c0SPekka Enberg 	default:
3262932c9ebSPekka Enberg 		ret = false;
327c59fa0c4SThomas Gleixner 		break;
32825af6674SPekka Enberg 	}
329f6b8ccc1SThomas Gleixner 
330f6b8ccc1SThomas Gleixner 	serial8250_update_irq(kvm, dev);
331f6b8ccc1SThomas Gleixner 
3324ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
3332932c9ebSPekka Enberg 
3342932c9ebSPekka Enberg 	return ret;
33513a7760fSPekka Enberg }
33613a7760fSPekka Enberg 
33746aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
33846aa8d69SPekka Enberg 	.io_in		= serial8250_in,
33946aa8d69SPekka Enberg 	.io_out		= serial8250_out,
340a93ec68bSPekka Enberg };
341a93ec68bSPekka Enberg 
342bc4b0ffeSPekka Enberg static void serial8250__device_init(struct kvm *kvm, struct serial8250_device *dev)
343bc4b0ffeSPekka Enberg {
3443d62dea6SSasha Levin 	ioport__register(dev->iobase, &serial8250_ops, 8, NULL);
345bc4b0ffeSPekka Enberg 	kvm__irq_line(kvm, dev->irq, 0);
346bc4b0ffeSPekka Enberg }
347bc4b0ffeSPekka Enberg 
348bc4b0ffeSPekka Enberg void serial8250__init(struct kvm *kvm)
34913a7760fSPekka Enberg {
350c6a69c61SPekka Enberg 	unsigned int i;
351c6a69c61SPekka Enberg 
352c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
353c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
354c6a69c61SPekka Enberg 
355bc4b0ffeSPekka Enberg 		serial8250__device_init(kvm, dev);
356c6a69c61SPekka Enberg 	}
35713a7760fSPekka Enberg }
358