xref: /kvmtool/hw/serial.c (revision 9a7428ddbb396364e8a70e4ee4f6c7546005dadf)
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 
26*9a7428ddSPekka Enberg #define UART_IIR_NO_INT		0x01
27*9a7428ddSPekka Enberg 
2846aa8d69SPekka Enberg /* 16550 FIFO Control Register */
2946aa8d69SPekka Enberg #define FCR		2
3046aa8d69SPekka Enberg 
3146aa8d69SPekka Enberg /* Line control register */
3246aa8d69SPekka Enberg #define LCR		3
33*9a7428ddSPekka Enberg 
34*9a7428ddSPekka Enberg #define UART_LCR_DLAB		0x80
3546aa8d69SPekka Enberg 
3646aa8d69SPekka Enberg /* Modem control register */
3746aa8d69SPekka Enberg #define MCR		4
3846aa8d69SPekka Enberg 
3946aa8d69SPekka Enberg /* Line status register */
4046aa8d69SPekka Enberg #define LSR		5
4146aa8d69SPekka Enberg 
42*9a7428ddSPekka Enberg #define UART_LSR_THRE		0x20
43*9a7428ddSPekka Enberg 
4446aa8d69SPekka Enberg /* Modem status register */
4546aa8d69SPekka Enberg #define MSR		6
4646aa8d69SPekka Enberg 
47*9a7428ddSPekka Enberg #define UART_MSR_CTS		0x10
48*9a7428ddSPekka Enberg 
4946aa8d69SPekka Enberg /* Scratch register */
5046aa8d69SPekka Enberg #define SCR		7
5146aa8d69SPekka Enberg 
5246aa8d69SPekka Enberg struct serial8250_device {
5346aa8d69SPekka Enberg 	uint16_t		iobase;
5446aa8d69SPekka Enberg 	uint8_t			dll;
5546aa8d69SPekka Enberg 	uint8_t			dlm;
5646aa8d69SPekka Enberg 	uint8_t			ier;
5746aa8d69SPekka Enberg 	uint8_t			fcr;
5846aa8d69SPekka Enberg 	uint8_t			lcr;
5946aa8d69SPekka Enberg 	uint8_t			mcr;
6046aa8d69SPekka Enberg 	uint8_t			scr;
6146aa8d69SPekka Enberg };
6246aa8d69SPekka Enberg 
6346aa8d69SPekka Enberg static struct serial8250_device device = {
6446aa8d69SPekka Enberg 	.iobase			= 0x3f8,	/* ttyS0 */
6546aa8d69SPekka Enberg };
6646aa8d69SPekka Enberg 
6746aa8d69SPekka Enberg static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
6813a7760fSPekka Enberg {
6946aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
7046aa8d69SPekka Enberg 
71*9a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB) {
7246aa8d69SPekka Enberg 		switch (offset) {
7346aa8d69SPekka Enberg 		case DLL:
7446aa8d69SPekka Enberg 			device.dll		= ioport__read8(data);
7546aa8d69SPekka Enberg 			break;
7646aa8d69SPekka Enberg 		case DLM:
7746aa8d69SPekka Enberg 			device.dlm		= ioport__read8(data);
7846aa8d69SPekka Enberg 			break;
7946aa8d69SPekka Enberg 		case FCR:
8046aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
8146aa8d69SPekka Enberg 			break;
8246aa8d69SPekka Enberg 		case LCR:
8346aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
8446aa8d69SPekka Enberg 			break;
8546aa8d69SPekka Enberg 		default:
8646aa8d69SPekka Enberg 			return false;
8746aa8d69SPekka Enberg 		}
8846aa8d69SPekka Enberg 	} else {
8946aa8d69SPekka Enberg 		switch (offset) {
9046aa8d69SPekka Enberg 		case THR: {
9113a7760fSPekka Enberg 			char *p = data;
92f2d8dc88SCyrill Gorcunov 			int i;
9313a7760fSPekka Enberg 
94f2d8dc88SCyrill Gorcunov 			while (count--) {
95f2d8dc88SCyrill Gorcunov 				for (i = 0; i < size; i++)
96b7475544SPekka Enberg 					fprintf(stdout, "%c", *p++);
975b9d0b58SAsias He 			}
98b7475544SPekka Enberg 			fflush(stdout);
9946aa8d69SPekka Enberg 			break;
10046aa8d69SPekka Enberg 		}
10146aa8d69SPekka Enberg 		case IER:
10246aa8d69SPekka Enberg 			device.ier		= ioport__read8(data);
10346aa8d69SPekka Enberg 			break;
10446aa8d69SPekka Enberg 		case FCR:
10546aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
10646aa8d69SPekka Enberg 			break;
10746aa8d69SPekka Enberg 		case LCR:
10846aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
10946aa8d69SPekka Enberg 			break;
11046aa8d69SPekka Enberg 		case MCR:
11146aa8d69SPekka Enberg 			device.mcr		= ioport__read8(data);
11246aa8d69SPekka Enberg 			break;
11346aa8d69SPekka Enberg 		case SCR:
11446aa8d69SPekka Enberg 			device.scr		= ioport__read8(data);
11546aa8d69SPekka Enberg 			break;
11646aa8d69SPekka Enberg 		default:
11746aa8d69SPekka Enberg 			return false;
11846aa8d69SPekka Enberg 		}
11946aa8d69SPekka Enberg 	}
12013a7760fSPekka Enberg 
12113a7760fSPekka Enberg 	return true;
12213a7760fSPekka Enberg }
12313a7760fSPekka Enberg 
12446aa8d69SPekka Enberg static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
12525af6674SPekka Enberg {
12646aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
12746aa8d69SPekka Enberg 
128*9a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB)
12946aa8d69SPekka Enberg 		return false;
13046aa8d69SPekka Enberg 
13146aa8d69SPekka Enberg 	switch (offset) {
13246aa8d69SPekka Enberg 	case THR:
13346aa8d69SPekka Enberg 		ioport__write8(data, 0x00);
13446aa8d69SPekka Enberg 		break;
13546aa8d69SPekka Enberg 	case IER:
13646aa8d69SPekka Enberg 		ioport__write8(data, device.ier);
13746aa8d69SPekka Enberg 		break;
13846aa8d69SPekka Enberg 	case IIR:
139*9a7428ddSPekka Enberg 		ioport__write8(data, UART_IIR_NO_INT);
14046aa8d69SPekka Enberg 		break;
14146aa8d69SPekka Enberg 	case LCR:
14246aa8d69SPekka Enberg 		ioport__write8(data, device.lcr);
14346aa8d69SPekka Enberg 		break;
14446aa8d69SPekka Enberg 	case MCR:
14546aa8d69SPekka Enberg 		ioport__write8(data, device.mcr);
14646aa8d69SPekka Enberg 		break;
14746aa8d69SPekka Enberg 	case LSR:
148*9a7428ddSPekka Enberg 		ioport__write8(data, UART_LSR_THRE);
14946aa8d69SPekka Enberg 		break;
15046aa8d69SPekka Enberg 	case MSR:
151*9a7428ddSPekka Enberg 		ioport__write8(data, UART_MSR_CTS);
15246aa8d69SPekka Enberg 		break;
15346aa8d69SPekka Enberg 	case SCR:
15446aa8d69SPekka Enberg 		ioport__write8(data, device.scr);
15546aa8d69SPekka Enberg 		break;
15646aa8d69SPekka Enberg 	default:
15746aa8d69SPekka Enberg 		return false;
15825af6674SPekka Enberg 	}
15925af6674SPekka Enberg 
16013a7760fSPekka Enberg 	return true;
16113a7760fSPekka Enberg }
16213a7760fSPekka Enberg 
16346aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
16446aa8d69SPekka Enberg 	.io_in		= serial8250_in,
16546aa8d69SPekka Enberg 	.io_out		= serial8250_out,
166a93ec68bSPekka Enberg };
167a93ec68bSPekka Enberg 
16813a7760fSPekka Enberg void early_printk__init(void)
16913a7760fSPekka Enberg {
17046aa8d69SPekka Enberg 	ioport__register(device.iobase, &serial8250_ops, 8);
17113a7760fSPekka Enberg }
172