xref: /kvmtool/hw/serial.c (revision f6b8ccc18fa40f99d9c074cd1ace5c765ea8d05e)
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;
21*f6b8ccc1SThomas Gleixner 	u8			irq_state;
2276b4a122SPekka Enberg 
233fdf659dSSasha Levin 	u8			rbr;		/* receive buffer */
243fdf659dSSasha Levin 	u8			dll;
253fdf659dSSasha Levin 	u8			dlm;
263fdf659dSSasha Levin 	u8			iir;
273fdf659dSSasha Levin 	u8			ier;
283fdf659dSSasha Levin 	u8			fcr;
293fdf659dSSasha Levin 	u8			lcr;
303fdf659dSSasha Levin 	u8			mcr;
313fdf659dSSasha Levin 	u8			lsr;
323fdf659dSSasha Levin 	u8			msr;
333fdf659dSSasha Levin 	u8			scr;
3446aa8d69SPekka Enberg };
3546aa8d69SPekka Enberg 
36f3efa592SLiming Wang #define SERIAL_REGS_SETTING \
37f3efa592SLiming Wang 	.iir			= UART_IIR_NO_INT, \
38f3efa592SLiming Wang 	.lsr			= UART_LSR_TEMT | UART_LSR_THRE, \
39f3efa592SLiming Wang 	.msr			= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS, \
40f3efa592SLiming Wang 	.mcr			= UART_MCR_OUT2,
41f3efa592SLiming Wang 
42e62c18deSPekka Enberg static struct serial8250_device devices[] = {
43c6a69c61SPekka Enberg 	/* ttyS0 */
44c6a69c61SPekka Enberg 	[0]	= {
452932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
462932c9ebSPekka Enberg 
471add4b76SSasha Levin 		.id			= 0,
48c6a69c61SPekka Enberg 		.iobase			= 0x3f8,
49e557eef9SPekka Enberg 		.irq			= 4,
50e557eef9SPekka Enberg 
51f3efa592SLiming Wang 		SERIAL_REGS_SETTING
52c6a69c61SPekka Enberg 	},
53e62c18deSPekka Enberg 	/* ttyS1 */
54e62c18deSPekka Enberg 	[1]	= {
552932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
562932c9ebSPekka Enberg 
571add4b76SSasha Levin 		.id			= 1,
58e62c18deSPekka Enberg 		.iobase			= 0x2f8,
59e62c18deSPekka Enberg 		.irq			= 3,
60133bedc1SPekka Enberg 
61f3efa592SLiming Wang 		SERIAL_REGS_SETTING
62e62c18deSPekka Enberg 	},
63e62c18deSPekka Enberg 	/* ttyS2 */
64e62c18deSPekka Enberg 	[2]	= {
652932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
662932c9ebSPekka Enberg 
671add4b76SSasha Levin 		.id			= 2,
68e62c18deSPekka Enberg 		.iobase			= 0x3e8,
69e62c18deSPekka Enberg 		.irq			= 4,
70133bedc1SPekka Enberg 
71f3efa592SLiming Wang 		SERIAL_REGS_SETTING
72e62c18deSPekka Enberg 	},
73bf459c83SPekka Enberg 	/* ttyS3 */
74bf459c83SPekka Enberg 	[3]	= {
75bf459c83SPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
76bf459c83SPekka Enberg 
771add4b76SSasha Levin 		.id			= 3,
78bf459c83SPekka Enberg 		.iobase			= 0x2e8,
79bf459c83SPekka Enberg 		.irq			= 3,
80bf459c83SPekka Enberg 
81f3efa592SLiming Wang 		SERIAL_REGS_SETTING
82bf459c83SPekka Enberg 	},
8346aa8d69SPekka Enberg };
8446aa8d69SPekka Enberg 
85*f6b8ccc1SThomas Gleixner static void serial8250_update_irq(struct kvm *kvm, struct serial8250_device *dev)
86*f6b8ccc1SThomas Gleixner {
87*f6b8ccc1SThomas Gleixner 	u8 iir = 0;
88*f6b8ccc1SThomas Gleixner 
89*f6b8ccc1SThomas Gleixner 	/* Data ready and rcv interrupt enabled ? */
90*f6b8ccc1SThomas Gleixner 	if ((dev->ier & UART_IER_RDI) && (dev->lsr & UART_LSR_DR))
91*f6b8ccc1SThomas Gleixner 		iir |= UART_IIR_RDI;
92*f6b8ccc1SThomas Gleixner 
93*f6b8ccc1SThomas Gleixner 	/* Transmitter empty and interrupt enabled ? */
94*f6b8ccc1SThomas Gleixner 	if ((dev->ier & UART_IER_THRI) && (dev->lsr & UART_LSR_TEMT))
95*f6b8ccc1SThomas Gleixner 		iir |= UART_IIR_THRI;
96*f6b8ccc1SThomas Gleixner 
97*f6b8ccc1SThomas Gleixner 	/* Now update the irq line, if necessary */
98*f6b8ccc1SThomas Gleixner 	if (!iir) {
99*f6b8ccc1SThomas Gleixner 		dev->iir = UART_IIR_NO_INT;
100*f6b8ccc1SThomas Gleixner 		if (dev->irq_state)
101*f6b8ccc1SThomas Gleixner 			kvm__irq_line(kvm, dev->irq, 0);
102*f6b8ccc1SThomas Gleixner 	} else {
103*f6b8ccc1SThomas Gleixner 		dev->iir = iir;
104*f6b8ccc1SThomas Gleixner 		if (!dev->irq_state)
105*f6b8ccc1SThomas Gleixner 			kvm__irq_line(kvm, dev->irq, 1);
106*f6b8ccc1SThomas Gleixner 	}
107*f6b8ccc1SThomas Gleixner 	dev->irq_state = iir;
108*f6b8ccc1SThomas Gleixner }
109*f6b8ccc1SThomas Gleixner 
110a428f72eSPekka Enberg #define SYSRQ_PENDING_NONE		0
111a428f72eSPekka Enberg #define SYSRQ_PENDING_BREAK		1
112a428f72eSPekka Enberg #define SYSRQ_PENDING_CMD		2
113a428f72eSPekka Enberg 
114a428f72eSPekka Enberg static int sysrq_pending;
115a428f72eSPekka Enberg 
11643835ac9SSasha Levin static void serial8250__sysrq(struct kvm *kvm, struct serial8250_device *dev)
117a428f72eSPekka Enberg {
118a428f72eSPekka Enberg 	switch (sysrq_pending) {
119a428f72eSPekka Enberg 	case SYSRQ_PENDING_BREAK:
120a428f72eSPekka Enberg 		dev->lsr |= UART_LSR_DR | UART_LSR_BI;
121a428f72eSPekka Enberg 
122a428f72eSPekka Enberg 		sysrq_pending = SYSRQ_PENDING_CMD;
123a428f72eSPekka Enberg 		break;
124a428f72eSPekka Enberg 	case SYSRQ_PENDING_CMD:
125a428f72eSPekka Enberg 		dev->rbr = 'p';
126a428f72eSPekka Enberg 		dev->lsr |= UART_LSR_DR;
127a428f72eSPekka Enberg 
128a428f72eSPekka Enberg 		sysrq_pending	= SYSRQ_PENDING_NONE;
129a428f72eSPekka Enberg 		break;
130a428f72eSPekka Enberg 	}
131a428f72eSPekka Enberg }
132a428f72eSPekka Enberg 
13343835ac9SSasha Levin static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev)
134251cf9a6SPekka Enberg {
135251cf9a6SPekka Enberg 	int c;
136251cf9a6SPekka Enberg 
137db34045cSPekka Enberg 	if (dev->lsr & UART_LSR_DR)
138db34045cSPekka Enberg 		return;
139db34045cSPekka Enberg 
140a428f72eSPekka Enberg 	if (sysrq_pending) {
14143835ac9SSasha Levin 		serial8250__sysrq(kvm, dev);
142a428f72eSPekka Enberg 		return;
143a428f72eSPekka Enberg 	}
144a428f72eSPekka Enberg 
1451add4b76SSasha Levin 	if (!term_readable(CONSOLE_8250, dev->id))
146251cf9a6SPekka Enberg 		return;
147251cf9a6SPekka Enberg 
1481add4b76SSasha Levin 	c = term_getc(CONSOLE_8250, dev->id);
14905d1a2a6SAsias He 
150251cf9a6SPekka Enberg 	if (c < 0)
151251cf9a6SPekka Enberg 		return;
152251cf9a6SPekka Enberg 
153251cf9a6SPekka Enberg 	dev->rbr = c;
154251cf9a6SPekka Enberg 	dev->lsr |= UART_LSR_DR;
155251cf9a6SPekka Enberg }
156251cf9a6SPekka Enberg 
157*f6b8ccc1SThomas Gleixner void serial8250__update_consoles(struct kvm *kvm)
1588bb34e0dSPekka Enberg {
159479de16fSCyrill Gorcunov 	unsigned int i;
1601add4b76SSasha Levin 
161479de16fSCyrill Gorcunov 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
1621add4b76SSasha Levin 		struct serial8250_device *dev = &devices[i];
163934c193bSPekka Enberg 
1644ef0f4d6SPekka Enberg 		mutex_lock(&dev->mutex);
1652932c9ebSPekka Enberg 
16643835ac9SSasha Levin 		serial8250__receive(kvm, dev);
167251cf9a6SPekka Enberg 
168*f6b8ccc1SThomas Gleixner 		serial8250_update_irq(kvm, dev);
1692932c9ebSPekka Enberg 
1704ef0f4d6SPekka Enberg 		mutex_unlock(&dev->mutex);
1718bb34e0dSPekka Enberg 	}
1721add4b76SSasha Levin }
1738bb34e0dSPekka Enberg 
17443835ac9SSasha Levin void serial8250__inject_sysrq(struct kvm *kvm)
175a428f72eSPekka Enberg {
176a428f72eSPekka Enberg 	sysrq_pending	= SYSRQ_PENDING_BREAK;
177a428f72eSPekka Enberg }
178a428f72eSPekka Enberg 
1793fdf659dSSasha Levin static struct serial8250_device *find_device(u16 port)
180c6a69c61SPekka Enberg {
181c6a69c61SPekka Enberg 	unsigned int i;
182c6a69c61SPekka Enberg 
183c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
184c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
185c6a69c61SPekka Enberg 
186c6a69c61SPekka Enberg 		if (dev->iobase == (port & ~0x7))
187c6a69c61SPekka Enberg 			return dev;
188c6a69c61SPekka Enberg 	}
189c6a69c61SPekka Enberg 	return NULL;
190c6a69c61SPekka Enberg }
191c6a69c61SPekka Enberg 
192c9f6a037SXiao Guangrong static bool serial8250_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
19313a7760fSPekka Enberg {
194c6a69c61SPekka Enberg 	struct serial8250_device *dev;
1953fdf659dSSasha Levin 	u16 offset;
1962932c9ebSPekka Enberg 	bool ret = true;
19746aa8d69SPekka Enberg 
198c6a69c61SPekka Enberg 	dev = find_device(port);
199c6a69c61SPekka Enberg 	if (!dev)
200c6a69c61SPekka Enberg 		return false;
201c6a69c61SPekka Enberg 
2024ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
2032932c9ebSPekka Enberg 
204c6a69c61SPekka Enberg 	offset = port - dev->iobase;
205c6a69c61SPekka Enberg 
20646aa8d69SPekka Enberg 	switch (offset) {
207c59fa0c4SThomas Gleixner 	case UART_TX:
208c59fa0c4SThomas Gleixner 		if (!(dev->lcr & UART_LCR_DLAB)) {
20905d1a2a6SAsias He 			char *addr = data;
2100ea58e5bSPekka Enberg 
2110ea58e5bSPekka Enberg 			if (!(dev->mcr & UART_MCR_LOOP))
2121add4b76SSasha Levin 				term_putc(CONSOLE_8250, addr, size, dev->id);
213*f6b8ccc1SThomas Gleixner 			/* else FIXME: Inject data into rcv path for LOOP */
2140ea58e5bSPekka Enberg 
215*f6b8ccc1SThomas Gleixner 			/*
216*f6b8ccc1SThomas Gleixner 			 * Set transmitter and transmit hold register
217*f6b8ccc1SThomas Gleixner 			 * empty.  We have no FIFO at the moment and
218*f6b8ccc1SThomas Gleixner 			 * on the TX side it's only interesting, when
219*f6b8ccc1SThomas Gleixner 			 * we could coalesce port io on the kernel
220*f6b8ccc1SThomas Gleixner 			 * kernel.
221*f6b8ccc1SThomas Gleixner 			 */
222*f6b8ccc1SThomas Gleixner 			dev->lsr |= UART_LSR_TEMT | UART_LSR_THRE;
223*f6b8ccc1SThomas Gleixner 			break;
224c59fa0c4SThomas Gleixner 		} else {
225c59fa0c4SThomas Gleixner 			dev->dll = ioport__read8(data);
22646aa8d69SPekka Enberg 		}
227369c01c0SPekka Enberg 		break;
2284e49b05bSCyrill Gorcunov 	case UART_IER:
229*f6b8ccc1SThomas Gleixner 		if (!(dev->lcr & UART_LCR_DLAB))
230369c01c0SPekka Enberg 			dev->ier = ioport__read8(data) & 0x3f;
231*f6b8ccc1SThomas Gleixner 		else
232c59fa0c4SThomas Gleixner 			dev->dlm = ioport__read8(data);
233c59fa0c4SThomas Gleixner 		break;
234c59fa0c4SThomas Gleixner 	case UART_FCR:
235c59fa0c4SThomas Gleixner 		dev->fcr = ioport__read8(data);
23646aa8d69SPekka Enberg 		break;
237369c01c0SPekka Enberg 	case UART_LCR:
238369c01c0SPekka Enberg 		dev->lcr = ioport__read8(data);
239369c01c0SPekka Enberg 		break;
240369c01c0SPekka Enberg 	case UART_MCR:
241369c01c0SPekka Enberg 		dev->mcr = ioport__read8(data);
242369c01c0SPekka Enberg 		break;
243369c01c0SPekka Enberg 	case UART_LSR:
244369c01c0SPekka Enberg 		/* Factory test */
245369c01c0SPekka Enberg 		break;
246369c01c0SPekka Enberg 	case UART_MSR:
247369c01c0SPekka Enberg 		/* Not used */
248369c01c0SPekka Enberg 		break;
249369c01c0SPekka Enberg 	case UART_SCR:
250369c01c0SPekka Enberg 		dev->scr = ioport__read8(data);
251369c01c0SPekka Enberg 		break;
252369c01c0SPekka Enberg 	default:
2532932c9ebSPekka Enberg 		ret = false;
254d2ea115dSThomas Gleixner 		break;
25546aa8d69SPekka Enberg 	}
2562932c9ebSPekka Enberg 
257*f6b8ccc1SThomas Gleixner 	serial8250_update_irq(kvm, dev);
258*f6b8ccc1SThomas Gleixner 
2594ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
2602932c9ebSPekka Enberg 
2612932c9ebSPekka Enberg 	return ret;
26213a7760fSPekka Enberg }
26313a7760fSPekka Enberg 
264c9f6a037SXiao Guangrong static bool serial8250_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
26525af6674SPekka Enberg {
266c6a69c61SPekka Enberg 	struct serial8250_device *dev;
2673fdf659dSSasha Levin 	u16 offset;
2682932c9ebSPekka Enberg 	bool ret = true;
26946aa8d69SPekka Enberg 
270c6a69c61SPekka Enberg 	dev = find_device(port);
271c6a69c61SPekka Enberg 	if (!dev)
272c6a69c61SPekka Enberg 		return false;
273c6a69c61SPekka Enberg 
2744ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
2752932c9ebSPekka Enberg 
276c6a69c61SPekka Enberg 	offset = port - dev->iobase;
277c6a69c61SPekka Enberg 
27846aa8d69SPekka Enberg 	switch (offset) {
279251cf9a6SPekka Enberg 	case UART_RX:
280c59fa0c4SThomas Gleixner 		if (dev->lcr & UART_LCR_DLAB) {
281c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->dll);
282c59fa0c4SThomas Gleixner 		} else {
283251cf9a6SPekka Enberg 			ioport__write8(data, dev->rbr);
284369c01c0SPekka Enberg 			dev->lsr &= ~UART_LSR_DR;
285c59fa0c4SThomas Gleixner 		}
286369c01c0SPekka Enberg 		break;
287c59fa0c4SThomas Gleixner 	case UART_IER:
288c59fa0c4SThomas Gleixner 		if (dev->lcr & UART_LCR_DLAB)
289c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->dlm);
290c59fa0c4SThomas Gleixner 		else
291c59fa0c4SThomas Gleixner 			ioport__write8(data, dev->ier);
292c59fa0c4SThomas Gleixner 		break;
293*f6b8ccc1SThomas Gleixner 	case UART_IIR:
294*f6b8ccc1SThomas Gleixner 		ioport__write8(data, dev->iir);
29546aa8d69SPekka Enberg 		break;
2964e49b05bSCyrill Gorcunov 	case UART_LCR:
297c6a69c61SPekka Enberg 		ioport__write8(data, dev->lcr);
29846aa8d69SPekka Enberg 		break;
2994e49b05bSCyrill Gorcunov 	case UART_MCR:
300c6a69c61SPekka Enberg 		ioport__write8(data, dev->mcr);
30146aa8d69SPekka Enberg 		break;
3024e49b05bSCyrill Gorcunov 	case UART_LSR:
303c6a69c61SPekka Enberg 		ioport__write8(data, dev->lsr);
30446aa8d69SPekka Enberg 		break;
3054e49b05bSCyrill Gorcunov 	case UART_MSR:
306369c01c0SPekka Enberg 		ioport__write8(data, dev->msr);
30746aa8d69SPekka Enberg 		break;
3084e49b05bSCyrill Gorcunov 	case UART_SCR:
309369c01c0SPekka Enberg 		ioport__write8(data, dev->scr);
31046aa8d69SPekka Enberg 		break;
311369c01c0SPekka Enberg 	default:
3122932c9ebSPekka Enberg 		ret = false;
313c59fa0c4SThomas Gleixner 		break;
31425af6674SPekka Enberg 	}
315*f6b8ccc1SThomas Gleixner 
316*f6b8ccc1SThomas Gleixner 	serial8250_update_irq(kvm, dev);
317*f6b8ccc1SThomas Gleixner 
3184ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
3192932c9ebSPekka Enberg 
3202932c9ebSPekka Enberg 	return ret;
32113a7760fSPekka Enberg }
32213a7760fSPekka Enberg 
32346aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
32446aa8d69SPekka Enberg 	.io_in		= serial8250_in,
32546aa8d69SPekka Enberg 	.io_out		= serial8250_out,
326a93ec68bSPekka Enberg };
327a93ec68bSPekka Enberg 
328bc4b0ffeSPekka Enberg static void serial8250__device_init(struct kvm *kvm, struct serial8250_device *dev)
329bc4b0ffeSPekka Enberg {
3303d62dea6SSasha Levin 	ioport__register(dev->iobase, &serial8250_ops, 8, NULL);
331bc4b0ffeSPekka Enberg 	kvm__irq_line(kvm, dev->irq, 0);
332bc4b0ffeSPekka Enberg }
333bc4b0ffeSPekka Enberg 
334bc4b0ffeSPekka Enberg void serial8250__init(struct kvm *kvm)
33513a7760fSPekka Enberg {
336c6a69c61SPekka Enberg 	unsigned int i;
337c6a69c61SPekka Enberg 
338c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
339c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
340c6a69c61SPekka Enberg 
341bc4b0ffeSPekka Enberg 		serial8250__device_init(kvm, dev);
342c6a69c61SPekka Enberg 	}
34313a7760fSPekka Enberg }
344