xref: /kvmtool/hw/serial.c (revision e557eef9b67c7782a80a4639c637207d933495f3)
1899fe063SPekka Enberg #include "kvm/8250-serial.h"
213a7760fSPekka Enberg 
313a7760fSPekka Enberg #include "kvm/ioport.h"
446aa8d69SPekka Enberg #include "kvm/util.h"
5*e557eef9SPekka Enberg #include "kvm/kvm.h"
613a7760fSPekka Enberg 
746aa8d69SPekka Enberg #include <stdbool.h>
813a7760fSPekka Enberg 
946aa8d69SPekka Enberg /* Transmitter holding register */
1046aa8d69SPekka Enberg #define THR             0
113f55f3acSPekka Enberg 
1246aa8d69SPekka Enberg /* Receive buffer register */
1346aa8d69SPekka Enberg #define RBR             0
143f55f3acSPekka Enberg 
1546aa8d69SPekka Enberg /* Divisor latch low byte */
1646aa8d69SPekka Enberg #define DLL		0
173f55f3acSPekka Enberg 
1846aa8d69SPekka Enberg /* Divisor latch high byte */
1946aa8d69SPekka Enberg #define DLM		1
2046aa8d69SPekka Enberg 
2146aa8d69SPekka Enberg /* Interrupt enable register */
2246aa8d69SPekka Enberg #define IER		1
2346aa8d69SPekka Enberg 
24*e557eef9SPekka Enberg #define UART_IER_THRI		0x02
25*e557eef9SPekka Enberg 
2646aa8d69SPekka Enberg /* Interrupt identification register */
2746aa8d69SPekka Enberg #define IIR		2
2846aa8d69SPekka Enberg 
299a7428ddSPekka Enberg #define UART_IIR_NO_INT		0x01
30900d32c1SPekka Enberg #define UART_IIR_THRI		0x02
319a7428ddSPekka Enberg 
3246aa8d69SPekka Enberg /* 16550 FIFO Control Register */
3346aa8d69SPekka Enberg #define FCR		2
3446aa8d69SPekka Enberg 
3546aa8d69SPekka Enberg /* Line control register */
3646aa8d69SPekka Enberg #define LCR		3
379a7428ddSPekka Enberg 
389a7428ddSPekka Enberg #define UART_LCR_DLAB		0x80
3946aa8d69SPekka Enberg 
4046aa8d69SPekka Enberg /* Modem control register */
4146aa8d69SPekka Enberg #define MCR		4
4246aa8d69SPekka Enberg 
4346aa8d69SPekka Enberg /* Line status register */
4446aa8d69SPekka Enberg #define LSR		5
4546aa8d69SPekka Enberg 
469a7428ddSPekka Enberg #define UART_LSR_THRE		0x20
479a7428ddSPekka Enberg 
4846aa8d69SPekka Enberg /* Modem status register */
4946aa8d69SPekka Enberg #define MSR		6
5046aa8d69SPekka Enberg 
519a7428ddSPekka Enberg #define UART_MSR_CTS		0x10
529a7428ddSPekka Enberg 
5346aa8d69SPekka Enberg /* Scratch register */
5446aa8d69SPekka Enberg #define SCR		7
5546aa8d69SPekka Enberg 
5646aa8d69SPekka Enberg struct serial8250_device {
5746aa8d69SPekka Enberg 	uint16_t		iobase;
58*e557eef9SPekka Enberg 	uint8_t			irq;
5946aa8d69SPekka Enberg 	uint8_t			dll;
6046aa8d69SPekka Enberg 	uint8_t			dlm;
61*e557eef9SPekka Enberg 	uint8_t			iir;
6246aa8d69SPekka Enberg 	uint8_t			ier;
6346aa8d69SPekka Enberg 	uint8_t			fcr;
6446aa8d69SPekka Enberg 	uint8_t			lcr;
6546aa8d69SPekka Enberg 	uint8_t			mcr;
6646aa8d69SPekka Enberg 	uint8_t			scr;
6746aa8d69SPekka Enberg };
6846aa8d69SPekka Enberg 
6946aa8d69SPekka Enberg static struct serial8250_device device = {
7046aa8d69SPekka Enberg 	.iobase			= 0x3f8,	/* ttyS0 */
71*e557eef9SPekka Enberg 	.irq			= 4,
72*e557eef9SPekka Enberg 
73*e557eef9SPekka Enberg 	.iir			= UART_IIR_NO_INT | UART_IIR_THRI,
7446aa8d69SPekka Enberg };
7546aa8d69SPekka Enberg 
768bb34e0dSPekka Enberg void serial8250__interrupt(struct kvm *self)
778bb34e0dSPekka Enberg {
78*e557eef9SPekka Enberg 	if (device.ier & UART_IER_THRI) {
79*e557eef9SPekka Enberg 		device.iir		&= ~UART_IIR_NO_INT;
80*e557eef9SPekka Enberg 		kvm__irq_line(self, device.irq, 1);
81*e557eef9SPekka Enberg 	}
828bb34e0dSPekka Enberg }
838bb34e0dSPekka Enberg 
8446aa8d69SPekka Enberg static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
8513a7760fSPekka Enberg {
8646aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
8746aa8d69SPekka Enberg 
889a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB) {
8946aa8d69SPekka Enberg 		switch (offset) {
9046aa8d69SPekka Enberg 		case DLL:
9146aa8d69SPekka Enberg 			device.dll		= ioport__read8(data);
9246aa8d69SPekka Enberg 			break;
9346aa8d69SPekka Enberg 		case DLM:
9446aa8d69SPekka Enberg 			device.dlm		= ioport__read8(data);
9546aa8d69SPekka Enberg 			break;
9646aa8d69SPekka Enberg 		case FCR:
9746aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
9846aa8d69SPekka Enberg 			break;
9946aa8d69SPekka Enberg 		case LCR:
10046aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
10146aa8d69SPekka Enberg 			break;
10246aa8d69SPekka Enberg 		default:
10346aa8d69SPekka Enberg 			return false;
10446aa8d69SPekka Enberg 		}
10546aa8d69SPekka Enberg 	} else {
10646aa8d69SPekka Enberg 		switch (offset) {
10746aa8d69SPekka Enberg 		case THR: {
10813a7760fSPekka Enberg 			char *p = data;
109f2d8dc88SCyrill Gorcunov 			int i;
11013a7760fSPekka Enberg 
111f2d8dc88SCyrill Gorcunov 			while (count--) {
112f2d8dc88SCyrill Gorcunov 				for (i = 0; i < size; i++)
113b7475544SPekka Enberg 					fprintf(stdout, "%c", *p++);
1145b9d0b58SAsias He 			}
115b7475544SPekka Enberg 			fflush(stdout);
116*e557eef9SPekka Enberg 
117*e557eef9SPekka Enberg 			device.iir		|= UART_IIR_NO_INT;
118*e557eef9SPekka Enberg 			kvm__irq_line(self, device.irq, 0);
119*e557eef9SPekka Enberg 
12046aa8d69SPekka Enberg 			break;
12146aa8d69SPekka Enberg 		}
12246aa8d69SPekka Enberg 		case IER:
12346aa8d69SPekka Enberg 			device.ier		= ioport__read8(data);
12446aa8d69SPekka Enberg 			break;
12546aa8d69SPekka Enberg 		case FCR:
12646aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
12746aa8d69SPekka Enberg 			break;
12846aa8d69SPekka Enberg 		case LCR:
12946aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
13046aa8d69SPekka Enberg 			break;
13146aa8d69SPekka Enberg 		case MCR:
13246aa8d69SPekka Enberg 			device.mcr		= ioport__read8(data);
13346aa8d69SPekka Enberg 			break;
13446aa8d69SPekka Enberg 		case SCR:
13546aa8d69SPekka Enberg 			device.scr		= ioport__read8(data);
13646aa8d69SPekka Enberg 			break;
13746aa8d69SPekka Enberg 		default:
13846aa8d69SPekka Enberg 			return false;
13946aa8d69SPekka Enberg 		}
14046aa8d69SPekka Enberg 	}
14113a7760fSPekka Enberg 
14213a7760fSPekka Enberg 	return true;
14313a7760fSPekka Enberg }
14413a7760fSPekka Enberg 
14546aa8d69SPekka Enberg static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
14625af6674SPekka Enberg {
14746aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
14846aa8d69SPekka Enberg 
1499a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB)
15046aa8d69SPekka Enberg 		return false;
15146aa8d69SPekka Enberg 
15246aa8d69SPekka Enberg 	switch (offset) {
15346aa8d69SPekka Enberg 	case THR:
154900d32c1SPekka Enberg 		/* TODO: input support */
15546aa8d69SPekka Enberg 		break;
15646aa8d69SPekka Enberg 	case IER:
15746aa8d69SPekka Enberg 		ioport__write8(data, device.ier);
15846aa8d69SPekka Enberg 		break;
15946aa8d69SPekka Enberg 	case IIR:
160*e557eef9SPekka Enberg 		ioport__write8(data, device.iir);
16146aa8d69SPekka Enberg 		break;
16246aa8d69SPekka Enberg 	case LCR:
16346aa8d69SPekka Enberg 		ioport__write8(data, device.lcr);
16446aa8d69SPekka Enberg 		break;
16546aa8d69SPekka Enberg 	case MCR:
16646aa8d69SPekka Enberg 		ioport__write8(data, device.mcr);
16746aa8d69SPekka Enberg 		break;
16846aa8d69SPekka Enberg 	case LSR:
1699a7428ddSPekka Enberg 		ioport__write8(data, UART_LSR_THRE);
17046aa8d69SPekka Enberg 		break;
17146aa8d69SPekka Enberg 	case MSR:
1729a7428ddSPekka Enberg 		ioport__write8(data, UART_MSR_CTS);
17346aa8d69SPekka Enberg 		break;
17446aa8d69SPekka Enberg 	case SCR:
17546aa8d69SPekka Enberg 		ioport__write8(data, device.scr);
17646aa8d69SPekka Enberg 		break;
17746aa8d69SPekka Enberg 	default:
17846aa8d69SPekka Enberg 		return false;
17925af6674SPekka Enberg 	}
18025af6674SPekka Enberg 
18113a7760fSPekka Enberg 	return true;
18213a7760fSPekka Enberg }
18313a7760fSPekka Enberg 
18446aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
18546aa8d69SPekka Enberg 	.io_in		= serial8250_in,
18646aa8d69SPekka Enberg 	.io_out		= serial8250_out,
187a93ec68bSPekka Enberg };
188a93ec68bSPekka Enberg 
189899fe063SPekka Enberg void serial8250__init(void)
19013a7760fSPekka Enberg {
19146aa8d69SPekka Enberg 	ioport__register(device.iobase, &serial8250_ops, 8);
19213a7760fSPekka Enberg }
193