xref: /kvmtool/hw/serial.c (revision 610a82c46678006a35ebb5246af2187a17781f1d)
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;
2176b4a122SPekka Enberg 
223fdf659dSSasha Levin 	u8			rbr;		/* receive buffer */
233fdf659dSSasha Levin 	u8			dll;
243fdf659dSSasha Levin 	u8			dlm;
253fdf659dSSasha Levin 	u8			iir;
263fdf659dSSasha Levin 	u8			ier;
273fdf659dSSasha Levin 	u8			fcr;
283fdf659dSSasha Levin 	u8			lcr;
293fdf659dSSasha Levin 	u8			mcr;
303fdf659dSSasha Levin 	u8			lsr;
313fdf659dSSasha Levin 	u8			msr;
323fdf659dSSasha Levin 	u8			scr;
3346aa8d69SPekka Enberg };
3446aa8d69SPekka Enberg 
35f3efa592SLiming Wang #define SERIAL_REGS_SETTING \
36f3efa592SLiming Wang 	.iir			= UART_IIR_NO_INT, \
37f3efa592SLiming Wang 	.lsr			= UART_LSR_TEMT | UART_LSR_THRE, \
38f3efa592SLiming Wang 	.msr			= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS, \
39f3efa592SLiming Wang 	.mcr			= UART_MCR_OUT2,
40f3efa592SLiming Wang 
41e62c18deSPekka Enberg static struct serial8250_device devices[] = {
42c6a69c61SPekka Enberg 	/* ttyS0 */
43c6a69c61SPekka Enberg 	[0]	= {
442932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
452932c9ebSPekka Enberg 
461add4b76SSasha Levin 		.id			= 0,
47c6a69c61SPekka Enberg 		.iobase			= 0x3f8,
48e557eef9SPekka Enberg 		.irq			= 4,
49e557eef9SPekka Enberg 
50f3efa592SLiming Wang 		SERIAL_REGS_SETTING
51c6a69c61SPekka Enberg 	},
52e62c18deSPekka Enberg 	/* ttyS1 */
53e62c18deSPekka Enberg 	[1]	= {
542932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
552932c9ebSPekka Enberg 
561add4b76SSasha Levin 		.id			= 1,
57e62c18deSPekka Enberg 		.iobase			= 0x2f8,
58e62c18deSPekka Enberg 		.irq			= 3,
59133bedc1SPekka Enberg 
60f3efa592SLiming Wang 		SERIAL_REGS_SETTING
61e62c18deSPekka Enberg 	},
62e62c18deSPekka Enberg 	/* ttyS2 */
63e62c18deSPekka Enberg 	[2]	= {
642932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
652932c9ebSPekka Enberg 
661add4b76SSasha Levin 		.id			= 2,
67e62c18deSPekka Enberg 		.iobase			= 0x3e8,
68e62c18deSPekka Enberg 		.irq			= 4,
69133bedc1SPekka Enberg 
70f3efa592SLiming Wang 		SERIAL_REGS_SETTING
71e62c18deSPekka Enberg 	},
72bf459c83SPekka Enberg 	/* ttyS3 */
73bf459c83SPekka Enberg 	[3]	= {
74bf459c83SPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
75bf459c83SPekka Enberg 
761add4b76SSasha Levin 		.id			= 3,
77bf459c83SPekka Enberg 		.iobase			= 0x2e8,
78bf459c83SPekka Enberg 		.irq			= 3,
79bf459c83SPekka Enberg 
80f3efa592SLiming Wang 		SERIAL_REGS_SETTING
81bf459c83SPekka Enberg 	},
8246aa8d69SPekka Enberg };
8346aa8d69SPekka Enberg 
84a428f72eSPekka Enberg #define SYSRQ_PENDING_NONE		0
85a428f72eSPekka Enberg #define SYSRQ_PENDING_BREAK		1
86a428f72eSPekka Enberg #define SYSRQ_PENDING_CMD		2
87a428f72eSPekka Enberg 
88a428f72eSPekka Enberg static int sysrq_pending;
89a428f72eSPekka Enberg 
9043835ac9SSasha Levin static void serial8250__sysrq(struct kvm *kvm, struct serial8250_device *dev)
91a428f72eSPekka Enberg {
92a428f72eSPekka Enberg 	switch (sysrq_pending) {
93a428f72eSPekka Enberg 	case SYSRQ_PENDING_BREAK:
94a428f72eSPekka Enberg 		dev->lsr	|= UART_LSR_DR | UART_LSR_BI;
95a428f72eSPekka Enberg 
96a428f72eSPekka Enberg 		sysrq_pending	= SYSRQ_PENDING_CMD;
97a428f72eSPekka Enberg 		break;
98a428f72eSPekka Enberg 	case SYSRQ_PENDING_CMD:
99a428f72eSPekka Enberg 		dev->rbr	= 'p';
100a428f72eSPekka Enberg 		dev->lsr	|= UART_LSR_DR;
101a428f72eSPekka Enberg 
102a428f72eSPekka Enberg 		sysrq_pending	= SYSRQ_PENDING_NONE;
103a428f72eSPekka Enberg 		break;
104a428f72eSPekka Enberg 	}
105a428f72eSPekka Enberg }
106a428f72eSPekka Enberg 
10743835ac9SSasha Levin static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev)
108251cf9a6SPekka Enberg {
109251cf9a6SPekka Enberg 	int c;
110251cf9a6SPekka Enberg 
111db34045cSPekka Enberg 	if (dev->lsr & UART_LSR_DR)
112db34045cSPekka Enberg 		return;
113db34045cSPekka Enberg 
114a428f72eSPekka Enberg 	if (sysrq_pending) {
11543835ac9SSasha Levin 		serial8250__sysrq(kvm, dev);
116a428f72eSPekka Enberg 		return;
117a428f72eSPekka Enberg 	}
118a428f72eSPekka Enberg 
1191add4b76SSasha Levin 	if (!term_readable(CONSOLE_8250, dev->id))
120251cf9a6SPekka Enberg 		return;
121251cf9a6SPekka Enberg 
1221add4b76SSasha Levin 	c = term_getc(CONSOLE_8250, dev->id);
12305d1a2a6SAsias He 
124251cf9a6SPekka Enberg 	if (c < 0)
125251cf9a6SPekka Enberg 		return;
126251cf9a6SPekka Enberg 
127251cf9a6SPekka Enberg 	dev->rbr	= c;
128251cf9a6SPekka Enberg 	dev->lsr	|= UART_LSR_DR;
129251cf9a6SPekka Enberg }
130251cf9a6SPekka Enberg 
13143835ac9SSasha Levin void serial8250__inject_interrupt(struct kvm *kvm)
1328bb34e0dSPekka Enberg {
133479de16fSCyrill Gorcunov 	unsigned int i;
1341add4b76SSasha Levin 
135479de16fSCyrill Gorcunov 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
1361add4b76SSasha Levin 		struct serial8250_device *dev = &devices[i];
137934c193bSPekka Enberg 
1384ef0f4d6SPekka Enberg 		mutex_lock(&dev->mutex);
1392932c9ebSPekka Enberg 
14043835ac9SSasha Levin 		serial8250__receive(kvm, dev);
141251cf9a6SPekka Enberg 
142369c01c0SPekka Enberg 		if (dev->ier & UART_IER_RDI && dev->lsr & UART_LSR_DR)
143369c01c0SPekka Enberg 			dev->iir		= UART_IIR_RDI;
144251cf9a6SPekka Enberg 		else if (dev->ier & UART_IER_THRI)
145369c01c0SPekka Enberg 			dev->iir		= UART_IIR_THRI;
146369c01c0SPekka Enberg 		else
147369c01c0SPekka Enberg 			dev->iir		= UART_IIR_NO_INT;
14876b4a122SPekka Enberg 
149369c01c0SPekka Enberg 		if (dev->iir != UART_IIR_NO_INT) {
15043835ac9SSasha Levin 			kvm__irq_line(kvm, dev->irq, 0);
15143835ac9SSasha Levin 			kvm__irq_line(kvm, dev->irq, 1);
152e557eef9SPekka Enberg 		}
1532932c9ebSPekka Enberg 
1544ef0f4d6SPekka Enberg 		mutex_unlock(&dev->mutex);
1558bb34e0dSPekka Enberg 	}
1561add4b76SSasha Levin }
1578bb34e0dSPekka Enberg 
15843835ac9SSasha Levin void serial8250__inject_sysrq(struct kvm *kvm)
159a428f72eSPekka Enberg {
160a428f72eSPekka Enberg 	sysrq_pending	= SYSRQ_PENDING_BREAK;
161a428f72eSPekka Enberg }
162a428f72eSPekka Enberg 
1633fdf659dSSasha Levin static struct serial8250_device *find_device(u16 port)
164c6a69c61SPekka Enberg {
165c6a69c61SPekka Enberg 	unsigned int i;
166c6a69c61SPekka Enberg 
167c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
168c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
169c6a69c61SPekka Enberg 
170c6a69c61SPekka Enberg 		if (dev->iobase == (port & ~0x7))
171c6a69c61SPekka Enberg 			return dev;
172c6a69c61SPekka Enberg 	}
173c6a69c61SPekka Enberg 	return NULL;
174c6a69c61SPekka Enberg }
175c6a69c61SPekka Enberg 
176c9f6a037SXiao Guangrong static bool serial8250_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
17713a7760fSPekka Enberg {
178c6a69c61SPekka Enberg 	struct serial8250_device *dev;
1793fdf659dSSasha Levin 	u16 offset;
1802932c9ebSPekka Enberg 	bool ret = true;
18146aa8d69SPekka Enberg 
182c6a69c61SPekka Enberg 	dev		= find_device(port);
183c6a69c61SPekka Enberg 	if (!dev)
184c6a69c61SPekka Enberg 		return false;
185c6a69c61SPekka Enberg 
1864ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
1872932c9ebSPekka Enberg 
188c6a69c61SPekka Enberg 	offset		= port - dev->iobase;
189c6a69c61SPekka Enberg 
190c6a69c61SPekka Enberg 	if (dev->lcr & UART_LCR_DLAB) {
19146aa8d69SPekka Enberg 		switch (offset) {
1924e49b05bSCyrill Gorcunov 		case UART_DLL:
193c6a69c61SPekka Enberg 			dev->dll	= ioport__read8(data);
19446aa8d69SPekka Enberg 			break;
1954e49b05bSCyrill Gorcunov 		case UART_DLM:
196c6a69c61SPekka Enberg 			dev->dlm	= ioport__read8(data);
19746aa8d69SPekka Enberg 			break;
198369c01c0SPekka Enberg 		case UART_FCR:
199369c01c0SPekka Enberg 			dev->fcr	= ioport__read8(data);
200369c01c0SPekka Enberg 			break;
201369c01c0SPekka Enberg 		case UART_LCR:
202369c01c0SPekka Enberg 			dev->lcr	= ioport__read8(data);
203369c01c0SPekka Enberg 			break;
204369c01c0SPekka Enberg 		case UART_MCR:
205369c01c0SPekka Enberg 			dev->mcr	= ioport__read8(data);
206369c01c0SPekka Enberg 			break;
207369c01c0SPekka Enberg 		case UART_LSR:
208369c01c0SPekka Enberg 			/* Factory test */
209369c01c0SPekka Enberg 			break;
210369c01c0SPekka Enberg 		case UART_MSR:
211369c01c0SPekka Enberg 			/* Not used */
212369c01c0SPekka Enberg 			break;
213369c01c0SPekka Enberg 		case UART_SCR:
214369c01c0SPekka Enberg 			dev->scr	= ioport__read8(data);
215369c01c0SPekka Enberg 			break;
216369c01c0SPekka Enberg 		default:
2172932c9ebSPekka Enberg 			ret		= false;
2182932c9ebSPekka Enberg 			goto out_unlock;
21946aa8d69SPekka Enberg 		}
22046aa8d69SPekka Enberg 	} else {
22146aa8d69SPekka Enberg 		switch (offset) {
2224e49b05bSCyrill Gorcunov 		case UART_TX: {
22305d1a2a6SAsias He 			char *addr = data;
2240ea58e5bSPekka Enberg 
2250ea58e5bSPekka Enberg 			if (!(dev->mcr & UART_MCR_LOOP))
2261add4b76SSasha Levin 				term_putc(CONSOLE_8250, addr, size, dev->id);
2270ea58e5bSPekka Enberg 
228133bedc1SPekka Enberg 			dev->iir		= UART_IIR_NO_INT;
22946aa8d69SPekka Enberg 			break;
23046aa8d69SPekka Enberg 		}
231369c01c0SPekka Enberg 		case UART_FCR:
232369c01c0SPekka Enberg 			dev->fcr	= ioport__read8(data);
233369c01c0SPekka Enberg 			break;
2344e49b05bSCyrill Gorcunov 		case UART_IER:
235369c01c0SPekka Enberg 			dev->ier	= ioport__read8(data) & 0x3f;
236*610a82c4SSasha Levin 			kvm__irq_line(kvm, dev->irq, dev->ier ? 1 : 0);
23746aa8d69SPekka Enberg 			break;
238369c01c0SPekka Enberg 		case UART_LCR:
239369c01c0SPekka Enberg 			dev->lcr	= ioport__read8(data);
240369c01c0SPekka Enberg 			break;
241369c01c0SPekka Enberg 		case UART_MCR:
242369c01c0SPekka Enberg 			dev->mcr	= ioport__read8(data);
243369c01c0SPekka Enberg 			break;
244369c01c0SPekka Enberg 		case UART_LSR:
245369c01c0SPekka Enberg 			/* Factory test */
246369c01c0SPekka Enberg 			break;
247369c01c0SPekka Enberg 		case UART_MSR:
248369c01c0SPekka Enberg 			/* Not used */
249369c01c0SPekka Enberg 			break;
250369c01c0SPekka Enberg 		case UART_SCR:
251369c01c0SPekka Enberg 			dev->scr	= ioport__read8(data);
252369c01c0SPekka Enberg 			break;
253369c01c0SPekka Enberg 		default:
2542932c9ebSPekka Enberg 			ret		= false;
2552932c9ebSPekka Enberg 			goto out_unlock;
25646aa8d69SPekka Enberg 		}
25746aa8d69SPekka Enberg 	}
2582932c9ebSPekka Enberg 
2592932c9ebSPekka Enberg out_unlock:
2604ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
2612932c9ebSPekka Enberg 
2622932c9ebSPekka Enberg 	return ret;
26313a7760fSPekka Enberg }
26413a7760fSPekka Enberg 
265c9f6a037SXiao Guangrong static bool serial8250_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
26625af6674SPekka Enberg {
267c6a69c61SPekka Enberg 	struct serial8250_device *dev;
2683fdf659dSSasha Levin 	u16 offset;
2692932c9ebSPekka Enberg 	bool ret = true;
27046aa8d69SPekka Enberg 
271c6a69c61SPekka Enberg 	dev		= find_device(port);
272c6a69c61SPekka Enberg 	if (!dev)
273c6a69c61SPekka Enberg 		return false;
274c6a69c61SPekka Enberg 
2754ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
2762932c9ebSPekka Enberg 
277c6a69c61SPekka Enberg 	offset		= port - dev->iobase;
278c6a69c61SPekka Enberg 
27919a2bb7dSPekka Enberg 	if (dev->lcr & UART_LCR_DLAB) {
28019a2bb7dSPekka Enberg 		switch (offset) {
28119a2bb7dSPekka Enberg 		case UART_DLL:
28219a2bb7dSPekka Enberg 			ioport__write8(data, dev->dll);
2832932c9ebSPekka Enberg 			goto out_unlock;
2842932c9ebSPekka Enberg 
28519a2bb7dSPekka Enberg 		case UART_DLM:
28619a2bb7dSPekka Enberg 			ioport__write8(data, dev->dlm);
2872932c9ebSPekka Enberg 			goto out_unlock;
2882932c9ebSPekka Enberg 
289369c01c0SPekka Enberg 		default:
290369c01c0SPekka Enberg 			break;
29119a2bb7dSPekka Enberg 		}
29219a2bb7dSPekka Enberg 	} else {
29346aa8d69SPekka Enberg 		switch (offset) {
294251cf9a6SPekka Enberg 		case UART_RX:
295251cf9a6SPekka Enberg 			ioport__write8(data, dev->rbr);
296369c01c0SPekka Enberg 			dev->lsr		&= ~UART_LSR_DR;
297369c01c0SPekka Enberg 			dev->iir		= UART_IIR_NO_INT;
2982932c9ebSPekka Enberg 			goto out_unlock;
2992932c9ebSPekka Enberg 
3004e49b05bSCyrill Gorcunov 		case UART_IER:
301c6a69c61SPekka Enberg 			ioport__write8(data, dev->ier);
3022932c9ebSPekka Enberg 			goto out_unlock;
3032932c9ebSPekka Enberg 
304369c01c0SPekka Enberg 		default:
305369c01c0SPekka Enberg 			break;
30619a2bb7dSPekka Enberg 		}
30719a2bb7dSPekka Enberg 	}
30819a2bb7dSPekka Enberg 
30919a2bb7dSPekka Enberg 	switch (offset) {
310369c01c0SPekka Enberg 	case UART_IIR: {
3113fdf659dSSasha Levin 		u8 iir = dev->iir;
312369c01c0SPekka Enberg 
313369c01c0SPekka Enberg 		if (dev->fcr & UART_FCR_ENABLE_FIFO)
314369c01c0SPekka Enberg 			iir		|= 0xc0;
315369c01c0SPekka Enberg 
316369c01c0SPekka Enberg 		ioport__write8(data, iir);
31746aa8d69SPekka Enberg 		break;
318369c01c0SPekka Enberg 	}
3194e49b05bSCyrill Gorcunov 	case UART_LCR:
320c6a69c61SPekka Enberg 		ioport__write8(data, dev->lcr);
32146aa8d69SPekka Enberg 		break;
3224e49b05bSCyrill Gorcunov 	case UART_MCR:
323c6a69c61SPekka Enberg 		ioport__write8(data, dev->mcr);
32446aa8d69SPekka Enberg 		break;
3254e49b05bSCyrill Gorcunov 	case UART_LSR:
326c6a69c61SPekka Enberg 		ioport__write8(data, dev->lsr);
327369c01c0SPekka Enberg 		dev->lsr		&= ~(UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI);
32846aa8d69SPekka Enberg 		break;
3294e49b05bSCyrill Gorcunov 	case UART_MSR:
330369c01c0SPekka Enberg 		ioport__write8(data, dev->msr);
33146aa8d69SPekka Enberg 		break;
3324e49b05bSCyrill Gorcunov 	case UART_SCR:
333369c01c0SPekka Enberg 		ioport__write8(data, dev->scr);
33446aa8d69SPekka Enberg 		break;
335369c01c0SPekka Enberg 	default:
3362932c9ebSPekka Enberg 		ret		= false;
3372932c9ebSPekka Enberg 		goto out_unlock;
33825af6674SPekka Enberg 	}
3392932c9ebSPekka Enberg out_unlock:
3404ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
3412932c9ebSPekka Enberg 
3422932c9ebSPekka Enberg 	return ret;
34313a7760fSPekka Enberg }
34413a7760fSPekka Enberg 
34546aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
34646aa8d69SPekka Enberg 	.io_in		= serial8250_in,
34746aa8d69SPekka Enberg 	.io_out		= serial8250_out,
348a93ec68bSPekka Enberg };
349a93ec68bSPekka Enberg 
350bc4b0ffeSPekka Enberg static void serial8250__device_init(struct kvm *kvm, struct serial8250_device *dev)
351bc4b0ffeSPekka Enberg {
3523d62dea6SSasha Levin 	ioport__register(dev->iobase, &serial8250_ops, 8, NULL);
353bc4b0ffeSPekka Enberg 	kvm__irq_line(kvm, dev->irq, 0);
354bc4b0ffeSPekka Enberg }
355bc4b0ffeSPekka Enberg 
356bc4b0ffeSPekka Enberg void serial8250__init(struct kvm *kvm)
35713a7760fSPekka Enberg {
358c6a69c61SPekka Enberg 	unsigned int i;
359c6a69c61SPekka Enberg 
360c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
361c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
362c6a69c61SPekka Enberg 
363bc4b0ffeSPekka Enberg 		serial8250__device_init(kvm, dev);
364c6a69c61SPekka Enberg 	}
36513a7760fSPekka Enberg }
366