xref: /kvmtool/hw/serial.c (revision 900d32c15c5851063a29f28e538d79c2a4800594)
113a7760fSPekka Enberg #include "kvm/early_printk.h"
213a7760fSPekka Enberg 
313a7760fSPekka Enberg #include "kvm/ioport.h"
446aa8d69SPekka Enberg #include "kvm/util.h"
513a7760fSPekka Enberg 
646aa8d69SPekka Enberg #include <stdbool.h>
713a7760fSPekka Enberg 
846aa8d69SPekka Enberg /* Transmitter holding register */
946aa8d69SPekka Enberg #define THR             0
103f55f3acSPekka Enberg 
1146aa8d69SPekka Enberg /* Receive buffer register */
1246aa8d69SPekka Enberg #define RBR             0
133f55f3acSPekka Enberg 
1446aa8d69SPekka Enberg /* Divisor latch low byte */
1546aa8d69SPekka Enberg #define DLL		0
163f55f3acSPekka Enberg 
1746aa8d69SPekka Enberg /* Divisor latch high byte */
1846aa8d69SPekka Enberg #define DLM		1
1946aa8d69SPekka Enberg 
2046aa8d69SPekka Enberg /* Interrupt enable register */
2146aa8d69SPekka Enberg #define IER		1
2246aa8d69SPekka Enberg 
2346aa8d69SPekka Enberg /* Interrupt identification register */
2446aa8d69SPekka Enberg #define IIR		2
2546aa8d69SPekka Enberg 
269a7428ddSPekka Enberg #define UART_IIR_NO_INT		0x01
27*900d32c1SPekka Enberg #define UART_IIR_THRI		0x02
289a7428ddSPekka Enberg 
2946aa8d69SPekka Enberg /* 16550 FIFO Control Register */
3046aa8d69SPekka Enberg #define FCR		2
3146aa8d69SPekka Enberg 
3246aa8d69SPekka Enberg /* Line control register */
3346aa8d69SPekka Enberg #define LCR		3
349a7428ddSPekka Enberg 
359a7428ddSPekka Enberg #define UART_LCR_DLAB		0x80
3646aa8d69SPekka Enberg 
3746aa8d69SPekka Enberg /* Modem control register */
3846aa8d69SPekka Enberg #define MCR		4
3946aa8d69SPekka Enberg 
4046aa8d69SPekka Enberg /* Line status register */
4146aa8d69SPekka Enberg #define LSR		5
4246aa8d69SPekka Enberg 
439a7428ddSPekka Enberg #define UART_LSR_THRE		0x20
449a7428ddSPekka Enberg 
4546aa8d69SPekka Enberg /* Modem status register */
4646aa8d69SPekka Enberg #define MSR		6
4746aa8d69SPekka Enberg 
489a7428ddSPekka Enberg #define UART_MSR_CTS		0x10
499a7428ddSPekka Enberg 
5046aa8d69SPekka Enberg /* Scratch register */
5146aa8d69SPekka Enberg #define SCR		7
5246aa8d69SPekka Enberg 
5346aa8d69SPekka Enberg struct serial8250_device {
5446aa8d69SPekka Enberg 	uint16_t		iobase;
5546aa8d69SPekka Enberg 	uint8_t			dll;
5646aa8d69SPekka Enberg 	uint8_t			dlm;
5746aa8d69SPekka Enberg 	uint8_t			ier;
5846aa8d69SPekka Enberg 	uint8_t			fcr;
5946aa8d69SPekka Enberg 	uint8_t			lcr;
6046aa8d69SPekka Enberg 	uint8_t			mcr;
6146aa8d69SPekka Enberg 	uint8_t			scr;
6246aa8d69SPekka Enberg };
6346aa8d69SPekka Enberg 
6446aa8d69SPekka Enberg static struct serial8250_device device = {
6546aa8d69SPekka Enberg 	.iobase			= 0x3f8,	/* ttyS0 */
6646aa8d69SPekka Enberg };
6746aa8d69SPekka Enberg 
6846aa8d69SPekka Enberg static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
6913a7760fSPekka Enberg {
7046aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
7146aa8d69SPekka Enberg 
729a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB) {
7346aa8d69SPekka Enberg 		switch (offset) {
7446aa8d69SPekka Enberg 		case DLL:
7546aa8d69SPekka Enberg 			device.dll		= ioport__read8(data);
7646aa8d69SPekka Enberg 			break;
7746aa8d69SPekka Enberg 		case DLM:
7846aa8d69SPekka Enberg 			device.dlm		= ioport__read8(data);
7946aa8d69SPekka Enberg 			break;
8046aa8d69SPekka Enberg 		case FCR:
8146aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
8246aa8d69SPekka Enberg 			break;
8346aa8d69SPekka Enberg 		case LCR:
8446aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
8546aa8d69SPekka Enberg 			break;
8646aa8d69SPekka Enberg 		default:
8746aa8d69SPekka Enberg 			return false;
8846aa8d69SPekka Enberg 		}
8946aa8d69SPekka Enberg 	} else {
9046aa8d69SPekka Enberg 		switch (offset) {
9146aa8d69SPekka Enberg 		case THR: {
9213a7760fSPekka Enberg 			char *p = data;
93f2d8dc88SCyrill Gorcunov 			int i;
9413a7760fSPekka Enberg 
95f2d8dc88SCyrill Gorcunov 			while (count--) {
96f2d8dc88SCyrill Gorcunov 				for (i = 0; i < size; i++)
97b7475544SPekka Enberg 					fprintf(stdout, "%c", *p++);
985b9d0b58SAsias He 			}
99b7475544SPekka Enberg 			fflush(stdout);
10046aa8d69SPekka Enberg 			break;
10146aa8d69SPekka Enberg 		}
10246aa8d69SPekka Enberg 		case IER:
10346aa8d69SPekka Enberg 			device.ier		= ioport__read8(data);
10446aa8d69SPekka Enberg 			break;
10546aa8d69SPekka Enberg 		case FCR:
10646aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
10746aa8d69SPekka Enberg 			break;
10846aa8d69SPekka Enberg 		case LCR:
10946aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
11046aa8d69SPekka Enberg 			break;
11146aa8d69SPekka Enberg 		case MCR:
11246aa8d69SPekka Enberg 			device.mcr		= ioport__read8(data);
11346aa8d69SPekka Enberg 			break;
11446aa8d69SPekka Enberg 		case SCR:
11546aa8d69SPekka Enberg 			device.scr		= ioport__read8(data);
11646aa8d69SPekka Enberg 			break;
11746aa8d69SPekka Enberg 		default:
11846aa8d69SPekka Enberg 			return false;
11946aa8d69SPekka Enberg 		}
12046aa8d69SPekka Enberg 	}
12113a7760fSPekka Enberg 
12213a7760fSPekka Enberg 	return true;
12313a7760fSPekka Enberg }
12413a7760fSPekka Enberg 
12546aa8d69SPekka Enberg static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
12625af6674SPekka Enberg {
12746aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
12846aa8d69SPekka Enberg 
1299a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB)
13046aa8d69SPekka Enberg 		return false;
13146aa8d69SPekka Enberg 
13246aa8d69SPekka Enberg 	switch (offset) {
13346aa8d69SPekka Enberg 	case THR:
134*900d32c1SPekka Enberg 		/* TODO: input support */
13546aa8d69SPekka Enberg 		break;
13646aa8d69SPekka Enberg 	case IER:
13746aa8d69SPekka Enberg 		ioport__write8(data, device.ier);
13846aa8d69SPekka Enberg 		break;
13946aa8d69SPekka Enberg 	case IIR:
140*900d32c1SPekka Enberg 		ioport__write8(data, UART_IIR_NO_INT | UART_IIR_THRI);
14146aa8d69SPekka Enberg 		break;
14246aa8d69SPekka Enberg 	case LCR:
14346aa8d69SPekka Enberg 		ioport__write8(data, device.lcr);
14446aa8d69SPekka Enberg 		break;
14546aa8d69SPekka Enberg 	case MCR:
14646aa8d69SPekka Enberg 		ioport__write8(data, device.mcr);
14746aa8d69SPekka Enberg 		break;
14846aa8d69SPekka Enberg 	case LSR:
1499a7428ddSPekka Enberg 		ioport__write8(data, UART_LSR_THRE);
15046aa8d69SPekka Enberg 		break;
15146aa8d69SPekka Enberg 	case MSR:
1529a7428ddSPekka Enberg 		ioport__write8(data, UART_MSR_CTS);
15346aa8d69SPekka Enberg 		break;
15446aa8d69SPekka Enberg 	case SCR:
15546aa8d69SPekka Enberg 		ioport__write8(data, device.scr);
15646aa8d69SPekka Enberg 		break;
15746aa8d69SPekka Enberg 	default:
15846aa8d69SPekka Enberg 		return false;
15925af6674SPekka Enberg 	}
16025af6674SPekka Enberg 
16113a7760fSPekka Enberg 	return true;
16213a7760fSPekka Enberg }
16313a7760fSPekka Enberg 
16446aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
16546aa8d69SPekka Enberg 	.io_in		= serial8250_in,
16646aa8d69SPekka Enberg 	.io_out		= serial8250_out,
167a93ec68bSPekka Enberg };
168a93ec68bSPekka Enberg 
16913a7760fSPekka Enberg void early_printk__init(void)
17013a7760fSPekka Enberg {
17146aa8d69SPekka Enberg 	ioport__register(device.iobase, &serial8250_ops, 8);
17213a7760fSPekka Enberg }
173