xref: /kvmtool/hw/serial.c (revision f3efa5929e49d3a7bd6ede02e38f9090f399bff2)
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;
172932c9ebSPekka Enberg 
183fdf659dSSasha Levin 	u16			iobase;
193fdf659dSSasha Levin 	u8			irq;
2076b4a122SPekka Enberg 
213fdf659dSSasha Levin 	u8			rbr;		/* receive buffer */
223fdf659dSSasha Levin 	u8			dll;
233fdf659dSSasha Levin 	u8			dlm;
243fdf659dSSasha Levin 	u8			iir;
253fdf659dSSasha Levin 	u8			ier;
263fdf659dSSasha Levin 	u8			fcr;
273fdf659dSSasha Levin 	u8			lcr;
283fdf659dSSasha Levin 	u8			mcr;
293fdf659dSSasha Levin 	u8			lsr;
303fdf659dSSasha Levin 	u8			msr;
313fdf659dSSasha Levin 	u8			scr;
3246aa8d69SPekka Enberg };
3346aa8d69SPekka Enberg 
34*f3efa592SLiming Wang #define SERIAL_REGS_SETTING \
35*f3efa592SLiming Wang 	.iir			= UART_IIR_NO_INT, \
36*f3efa592SLiming Wang 	.lsr			= UART_LSR_TEMT | UART_LSR_THRE, \
37*f3efa592SLiming Wang 	.msr			= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS, \
38*f3efa592SLiming Wang 	.mcr			= UART_MCR_OUT2,
39*f3efa592SLiming Wang 
40e62c18deSPekka Enberg static struct serial8250_device devices[] = {
41c6a69c61SPekka Enberg 	/* ttyS0 */
42c6a69c61SPekka Enberg 	[0]	= {
432932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
442932c9ebSPekka Enberg 
45c6a69c61SPekka Enberg 		.iobase			= 0x3f8,
46e557eef9SPekka Enberg 		.irq			= 4,
47e557eef9SPekka Enberg 
48*f3efa592SLiming Wang 		SERIAL_REGS_SETTING
49c6a69c61SPekka Enberg 	},
50e62c18deSPekka Enberg 	/* ttyS1 */
51e62c18deSPekka Enberg 	[1]	= {
522932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
532932c9ebSPekka Enberg 
54e62c18deSPekka Enberg 		.iobase			= 0x2f8,
55e62c18deSPekka Enberg 		.irq			= 3,
56133bedc1SPekka Enberg 
57*f3efa592SLiming Wang 		SERIAL_REGS_SETTING
58e62c18deSPekka Enberg 	},
59e62c18deSPekka Enberg 	/* ttyS2 */
60e62c18deSPekka Enberg 	[2]	= {
612932c9ebSPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
622932c9ebSPekka Enberg 
63e62c18deSPekka Enberg 		.iobase			= 0x3e8,
64e62c18deSPekka Enberg 		.irq			= 4,
65133bedc1SPekka Enberg 
66*f3efa592SLiming Wang 		SERIAL_REGS_SETTING
67e62c18deSPekka Enberg 	},
68bf459c83SPekka Enberg 	/* ttyS3 */
69bf459c83SPekka Enberg 	[3]	= {
70bf459c83SPekka Enberg 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
71bf459c83SPekka Enberg 
72bf459c83SPekka Enberg 		.iobase			= 0x2e8,
73bf459c83SPekka Enberg 		.irq			= 3,
74bf459c83SPekka Enberg 
75*f3efa592SLiming Wang 		SERIAL_REGS_SETTING
76bf459c83SPekka Enberg 	},
7746aa8d69SPekka Enberg };
7846aa8d69SPekka Enberg 
79a428f72eSPekka Enberg #define SYSRQ_PENDING_NONE		0
80a428f72eSPekka Enberg #define SYSRQ_PENDING_BREAK		1
81a428f72eSPekka Enberg #define SYSRQ_PENDING_CMD		2
82a428f72eSPekka Enberg 
83a428f72eSPekka Enberg static int sysrq_pending;
84a428f72eSPekka Enberg 
8543835ac9SSasha Levin static void serial8250__sysrq(struct kvm *kvm, struct serial8250_device *dev)
86a428f72eSPekka Enberg {
87a428f72eSPekka Enberg 	switch (sysrq_pending) {
88a428f72eSPekka Enberg 	case SYSRQ_PENDING_BREAK:
89a428f72eSPekka Enberg 		dev->lsr	|= UART_LSR_DR | UART_LSR_BI;
90a428f72eSPekka Enberg 
91a428f72eSPekka Enberg 		sysrq_pending	= SYSRQ_PENDING_CMD;
92a428f72eSPekka Enberg 		break;
93a428f72eSPekka Enberg 	case SYSRQ_PENDING_CMD:
94a428f72eSPekka Enberg 		dev->rbr	= 'p';
95a428f72eSPekka Enberg 		dev->lsr	|= UART_LSR_DR;
96a428f72eSPekka Enberg 
97a428f72eSPekka Enberg 		sysrq_pending	= SYSRQ_PENDING_NONE;
98a428f72eSPekka Enberg 		break;
99a428f72eSPekka Enberg 	}
100a428f72eSPekka Enberg }
101a428f72eSPekka Enberg 
10243835ac9SSasha Levin static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev)
103251cf9a6SPekka Enberg {
104251cf9a6SPekka Enberg 	int c;
105251cf9a6SPekka Enberg 
106db34045cSPekka Enberg 	if (dev->lsr & UART_LSR_DR)
107db34045cSPekka Enberg 		return;
108db34045cSPekka Enberg 
109a428f72eSPekka Enberg 	if (sysrq_pending) {
11043835ac9SSasha Levin 		serial8250__sysrq(kvm, dev);
111a428f72eSPekka Enberg 		return;
112a428f72eSPekka Enberg 	}
113a428f72eSPekka Enberg 
11405d1a2a6SAsias He 	if (!term_readable(CONSOLE_8250))
115251cf9a6SPekka Enberg 		return;
116251cf9a6SPekka Enberg 
11705d1a2a6SAsias He 	c		= term_getc(CONSOLE_8250);
11805d1a2a6SAsias He 
119251cf9a6SPekka Enberg 	if (c < 0)
120251cf9a6SPekka Enberg 		return;
121251cf9a6SPekka Enberg 
122251cf9a6SPekka Enberg 	dev->rbr	= c;
123251cf9a6SPekka Enberg 	dev->lsr	|= UART_LSR_DR;
124251cf9a6SPekka Enberg }
125251cf9a6SPekka Enberg 
126c6a69c61SPekka Enberg /*
127c6a69c61SPekka Enberg  * Interrupts are injected for ttyS0 only.
128c6a69c61SPekka Enberg  */
12943835ac9SSasha Levin void serial8250__inject_interrupt(struct kvm *kvm)
1308bb34e0dSPekka Enberg {
131c6a69c61SPekka Enberg 	struct serial8250_device *dev = &devices[0];
132934c193bSPekka Enberg 
1334ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
1342932c9ebSPekka Enberg 
13543835ac9SSasha Levin 	serial8250__receive(kvm, dev);
136251cf9a6SPekka Enberg 
137369c01c0SPekka Enberg 	if (dev->ier & UART_IER_RDI && dev->lsr & UART_LSR_DR)
138369c01c0SPekka Enberg 		dev->iir		= UART_IIR_RDI;
139251cf9a6SPekka Enberg 	else if (dev->ier & UART_IER_THRI)
140369c01c0SPekka Enberg 		dev->iir		= UART_IIR_THRI;
141369c01c0SPekka Enberg 	else
142369c01c0SPekka Enberg 		dev->iir		= UART_IIR_NO_INT;
14376b4a122SPekka Enberg 
144369c01c0SPekka Enberg 	if (dev->iir != UART_IIR_NO_INT) {
14543835ac9SSasha Levin 		kvm__irq_line(kvm, dev->irq, 0);
14643835ac9SSasha Levin 		kvm__irq_line(kvm, dev->irq, 1);
147e557eef9SPekka Enberg 	}
1482932c9ebSPekka Enberg 
1494ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
1508bb34e0dSPekka Enberg }
1518bb34e0dSPekka Enberg 
15243835ac9SSasha Levin void serial8250__inject_sysrq(struct kvm *kvm)
153a428f72eSPekka Enberg {
154a428f72eSPekka Enberg 	sysrq_pending	= SYSRQ_PENDING_BREAK;
155a428f72eSPekka Enberg }
156a428f72eSPekka Enberg 
1573fdf659dSSasha Levin static struct serial8250_device *find_device(u16 port)
158c6a69c61SPekka Enberg {
159c6a69c61SPekka Enberg 	unsigned int i;
160c6a69c61SPekka Enberg 
161c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
162c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
163c6a69c61SPekka Enberg 
164c6a69c61SPekka Enberg 		if (dev->iobase == (port & ~0x7))
165c6a69c61SPekka Enberg 			return dev;
166c6a69c61SPekka Enberg 	}
167c6a69c61SPekka Enberg 	return NULL;
168c6a69c61SPekka Enberg }
169c6a69c61SPekka Enberg 
1703d62dea6SSasha Levin static bool serial8250_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
17113a7760fSPekka Enberg {
172c6a69c61SPekka Enberg 	struct serial8250_device *dev;
1733fdf659dSSasha Levin 	u16 offset;
1742932c9ebSPekka Enberg 	bool ret = true;
17546aa8d69SPekka Enberg 
176c6a69c61SPekka Enberg 	dev		= find_device(port);
177c6a69c61SPekka Enberg 	if (!dev)
178c6a69c61SPekka Enberg 		return false;
179c6a69c61SPekka Enberg 
1804ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
1812932c9ebSPekka Enberg 
182c6a69c61SPekka Enberg 	offset		= port - dev->iobase;
183c6a69c61SPekka Enberg 
184c6a69c61SPekka Enberg 	if (dev->lcr & UART_LCR_DLAB) {
18546aa8d69SPekka Enberg 		switch (offset) {
1864e49b05bSCyrill Gorcunov 		case UART_DLL:
187c6a69c61SPekka Enberg 			dev->dll	= ioport__read8(data);
18846aa8d69SPekka Enberg 			break;
1894e49b05bSCyrill Gorcunov 		case UART_DLM:
190c6a69c61SPekka Enberg 			dev->dlm	= ioport__read8(data);
19146aa8d69SPekka Enberg 			break;
192369c01c0SPekka Enberg 		case UART_FCR:
193369c01c0SPekka Enberg 			dev->fcr	= ioport__read8(data);
194369c01c0SPekka Enberg 			break;
195369c01c0SPekka Enberg 		case UART_LCR:
196369c01c0SPekka Enberg 			dev->lcr	= ioport__read8(data);
197369c01c0SPekka Enberg 			break;
198369c01c0SPekka Enberg 		case UART_MCR:
199369c01c0SPekka Enberg 			dev->mcr	= ioport__read8(data);
200369c01c0SPekka Enberg 			break;
201369c01c0SPekka Enberg 		case UART_LSR:
202369c01c0SPekka Enberg 			/* Factory test */
203369c01c0SPekka Enberg 			break;
204369c01c0SPekka Enberg 		case UART_MSR:
205369c01c0SPekka Enberg 			/* Not used */
206369c01c0SPekka Enberg 			break;
207369c01c0SPekka Enberg 		case UART_SCR:
208369c01c0SPekka Enberg 			dev->scr	= ioport__read8(data);
209369c01c0SPekka Enberg 			break;
210369c01c0SPekka Enberg 		default:
2112932c9ebSPekka Enberg 			ret		= false;
2122932c9ebSPekka Enberg 			goto out_unlock;
21346aa8d69SPekka Enberg 		}
21446aa8d69SPekka Enberg 	} else {
21546aa8d69SPekka Enberg 		switch (offset) {
2164e49b05bSCyrill Gorcunov 		case UART_TX: {
21705d1a2a6SAsias He 			char *addr = data;
2180ea58e5bSPekka Enberg 
2190ea58e5bSPekka Enberg 			if (!(dev->mcr & UART_MCR_LOOP))
22005d1a2a6SAsias He 				term_putc(CONSOLE_8250, addr, size * count);
2210ea58e5bSPekka Enberg 
222133bedc1SPekka Enberg 			dev->iir		= UART_IIR_NO_INT;
22346aa8d69SPekka Enberg 			break;
22446aa8d69SPekka Enberg 		}
225369c01c0SPekka Enberg 		case UART_FCR:
226369c01c0SPekka Enberg 			dev->fcr	= ioport__read8(data);
227369c01c0SPekka Enberg 			break;
2284e49b05bSCyrill Gorcunov 		case UART_IER:
229369c01c0SPekka Enberg 			dev->ier	= ioport__read8(data) & 0x3f;
23046aa8d69SPekka Enberg 			break;
231369c01c0SPekka Enberg 		case UART_LCR:
232369c01c0SPekka Enberg 			dev->lcr	= ioport__read8(data);
233369c01c0SPekka Enberg 			break;
234369c01c0SPekka Enberg 		case UART_MCR:
235369c01c0SPekka Enberg 			dev->mcr	= ioport__read8(data);
236369c01c0SPekka Enberg 			break;
237369c01c0SPekka Enberg 		case UART_LSR:
238369c01c0SPekka Enberg 			/* Factory test */
239369c01c0SPekka Enberg 			break;
240369c01c0SPekka Enberg 		case UART_MSR:
241369c01c0SPekka Enberg 			/* Not used */
242369c01c0SPekka Enberg 			break;
243369c01c0SPekka Enberg 		case UART_SCR:
244369c01c0SPekka Enberg 			dev->scr	= ioport__read8(data);
245369c01c0SPekka Enberg 			break;
246369c01c0SPekka Enberg 		default:
2472932c9ebSPekka Enberg 			ret		= false;
2482932c9ebSPekka Enberg 			goto out_unlock;
24946aa8d69SPekka Enberg 		}
25046aa8d69SPekka Enberg 	}
2512932c9ebSPekka Enberg 
2522932c9ebSPekka Enberg out_unlock:
2534ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
2542932c9ebSPekka Enberg 
2552932c9ebSPekka Enberg 	return ret;
25613a7760fSPekka Enberg }
25713a7760fSPekka Enberg 
2583d62dea6SSasha Levin static bool serial8250_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
25925af6674SPekka Enberg {
260c6a69c61SPekka Enberg 	struct serial8250_device *dev;
2613fdf659dSSasha Levin 	u16 offset;
2622932c9ebSPekka Enberg 	bool ret = true;
26346aa8d69SPekka Enberg 
264c6a69c61SPekka Enberg 	dev		= find_device(port);
265c6a69c61SPekka Enberg 	if (!dev)
266c6a69c61SPekka Enberg 		return false;
267c6a69c61SPekka Enberg 
2684ef0f4d6SPekka Enberg 	mutex_lock(&dev->mutex);
2692932c9ebSPekka Enberg 
270c6a69c61SPekka Enberg 	offset		= port - dev->iobase;
271c6a69c61SPekka Enberg 
27219a2bb7dSPekka Enberg 	if (dev->lcr & UART_LCR_DLAB) {
27319a2bb7dSPekka Enberg 		switch (offset) {
27419a2bb7dSPekka Enberg 		case UART_DLL:
27519a2bb7dSPekka Enberg 			ioport__write8(data, dev->dll);
2762932c9ebSPekka Enberg 			goto out_unlock;
2772932c9ebSPekka Enberg 
27819a2bb7dSPekka Enberg 		case UART_DLM:
27919a2bb7dSPekka Enberg 			ioport__write8(data, dev->dlm);
2802932c9ebSPekka Enberg 			goto out_unlock;
2812932c9ebSPekka Enberg 
282369c01c0SPekka Enberg 		default:
283369c01c0SPekka Enberg 			break;
28419a2bb7dSPekka Enberg 		}
28519a2bb7dSPekka Enberg 	} else {
28646aa8d69SPekka Enberg 		switch (offset) {
287251cf9a6SPekka Enberg 		case UART_RX:
288251cf9a6SPekka Enberg 			ioport__write8(data, dev->rbr);
289369c01c0SPekka Enberg 			dev->lsr		&= ~UART_LSR_DR;
290369c01c0SPekka Enberg 			dev->iir		= UART_IIR_NO_INT;
2912932c9ebSPekka Enberg 			goto out_unlock;
2922932c9ebSPekka Enberg 
2934e49b05bSCyrill Gorcunov 		case UART_IER:
294c6a69c61SPekka Enberg 			ioport__write8(data, dev->ier);
2952932c9ebSPekka Enberg 			goto out_unlock;
2962932c9ebSPekka Enberg 
297369c01c0SPekka Enberg 		default:
298369c01c0SPekka Enberg 			break;
29919a2bb7dSPekka Enberg 		}
30019a2bb7dSPekka Enberg 	}
30119a2bb7dSPekka Enberg 
30219a2bb7dSPekka Enberg 	switch (offset) {
303369c01c0SPekka Enberg 	case UART_IIR: {
3043fdf659dSSasha Levin 		u8 iir = dev->iir;
305369c01c0SPekka Enberg 
306369c01c0SPekka Enberg 		if (dev->fcr & UART_FCR_ENABLE_FIFO)
307369c01c0SPekka Enberg 			iir		|= 0xc0;
308369c01c0SPekka Enberg 
309369c01c0SPekka Enberg 		ioport__write8(data, iir);
31046aa8d69SPekka Enberg 		break;
311369c01c0SPekka Enberg 	}
3124e49b05bSCyrill Gorcunov 	case UART_LCR:
313c6a69c61SPekka Enberg 		ioport__write8(data, dev->lcr);
31446aa8d69SPekka Enberg 		break;
3154e49b05bSCyrill Gorcunov 	case UART_MCR:
316c6a69c61SPekka Enberg 		ioport__write8(data, dev->mcr);
31746aa8d69SPekka Enberg 		break;
3184e49b05bSCyrill Gorcunov 	case UART_LSR:
319c6a69c61SPekka Enberg 		ioport__write8(data, dev->lsr);
320369c01c0SPekka Enberg 		dev->lsr		&= ~(UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI);
32146aa8d69SPekka Enberg 		break;
3224e49b05bSCyrill Gorcunov 	case UART_MSR:
323369c01c0SPekka Enberg 		ioport__write8(data, dev->msr);
32446aa8d69SPekka Enberg 		break;
3254e49b05bSCyrill Gorcunov 	case UART_SCR:
326369c01c0SPekka Enberg 		ioport__write8(data, dev->scr);
32746aa8d69SPekka Enberg 		break;
328369c01c0SPekka Enberg 	default:
3292932c9ebSPekka Enberg 		ret		= false;
3302932c9ebSPekka Enberg 		goto out_unlock;
33125af6674SPekka Enberg 	}
3322932c9ebSPekka Enberg out_unlock:
3334ef0f4d6SPekka Enberg 	mutex_unlock(&dev->mutex);
3342932c9ebSPekka Enberg 
3352932c9ebSPekka Enberg 	return ret;
33613a7760fSPekka Enberg }
33713a7760fSPekka Enberg 
33846aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
33946aa8d69SPekka Enberg 	.io_in		= serial8250_in,
34046aa8d69SPekka Enberg 	.io_out		= serial8250_out,
341a93ec68bSPekka Enberg };
342a93ec68bSPekka Enberg 
343bc4b0ffeSPekka Enberg static void serial8250__device_init(struct kvm *kvm, struct serial8250_device *dev)
344bc4b0ffeSPekka Enberg {
3453d62dea6SSasha Levin 	ioport__register(dev->iobase, &serial8250_ops, 8, NULL);
346bc4b0ffeSPekka Enberg 	kvm__irq_line(kvm, dev->irq, 0);
347bc4b0ffeSPekka Enberg }
348bc4b0ffeSPekka Enberg 
349bc4b0ffeSPekka Enberg void serial8250__init(struct kvm *kvm)
35013a7760fSPekka Enberg {
351c6a69c61SPekka Enberg 	unsigned int i;
352c6a69c61SPekka Enberg 
353c6a69c61SPekka Enberg 	for (i = 0; i < ARRAY_SIZE(devices); i++) {
354c6a69c61SPekka Enberg 		struct serial8250_device *dev = &devices[i];
355c6a69c61SPekka Enberg 
356bc4b0ffeSPekka Enberg 		serial8250__device_init(kvm, dev);
357c6a69c61SPekka Enberg 	}
35813a7760fSPekka Enberg }
359