xref: /kvmtool/hw/serial.c (revision 8bb34e0dfa3b584e19ec6abb013c71e851e61c8d)
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
27900d32c1SPekka 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 
68*8bb34e0dSPekka Enberg void serial8250__interrupt(struct kvm *self)
69*8bb34e0dSPekka Enberg {
70*8bb34e0dSPekka Enberg 	/* TODO: inject 8250 interrupt for the guest */
71*8bb34e0dSPekka Enberg }
72*8bb34e0dSPekka Enberg 
7346aa8d69SPekka Enberg static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
7413a7760fSPekka Enberg {
7546aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
7646aa8d69SPekka Enberg 
779a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB) {
7846aa8d69SPekka Enberg 		switch (offset) {
7946aa8d69SPekka Enberg 		case DLL:
8046aa8d69SPekka Enberg 			device.dll		= ioport__read8(data);
8146aa8d69SPekka Enberg 			break;
8246aa8d69SPekka Enberg 		case DLM:
8346aa8d69SPekka Enberg 			device.dlm		= ioport__read8(data);
8446aa8d69SPekka Enberg 			break;
8546aa8d69SPekka Enberg 		case FCR:
8646aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
8746aa8d69SPekka Enberg 			break;
8846aa8d69SPekka Enberg 		case LCR:
8946aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
9046aa8d69SPekka Enberg 			break;
9146aa8d69SPekka Enberg 		default:
9246aa8d69SPekka Enberg 			return false;
9346aa8d69SPekka Enberg 		}
9446aa8d69SPekka Enberg 	} else {
9546aa8d69SPekka Enberg 		switch (offset) {
9646aa8d69SPekka Enberg 		case THR: {
9713a7760fSPekka Enberg 			char *p = data;
98f2d8dc88SCyrill Gorcunov 			int i;
9913a7760fSPekka Enberg 
100f2d8dc88SCyrill Gorcunov 			while (count--) {
101f2d8dc88SCyrill Gorcunov 				for (i = 0; i < size; i++)
102b7475544SPekka Enberg 					fprintf(stdout, "%c", *p++);
1035b9d0b58SAsias He 			}
104b7475544SPekka Enberg 			fflush(stdout);
10546aa8d69SPekka Enberg 			break;
10646aa8d69SPekka Enberg 		}
10746aa8d69SPekka Enberg 		case IER:
10846aa8d69SPekka Enberg 			device.ier		= ioport__read8(data);
10946aa8d69SPekka Enberg 			break;
11046aa8d69SPekka Enberg 		case FCR:
11146aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
11246aa8d69SPekka Enberg 			break;
11346aa8d69SPekka Enberg 		case LCR:
11446aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
11546aa8d69SPekka Enberg 			break;
11646aa8d69SPekka Enberg 		case MCR:
11746aa8d69SPekka Enberg 			device.mcr		= ioport__read8(data);
11846aa8d69SPekka Enberg 			break;
11946aa8d69SPekka Enberg 		case SCR:
12046aa8d69SPekka Enberg 			device.scr		= ioport__read8(data);
12146aa8d69SPekka Enberg 			break;
12246aa8d69SPekka Enberg 		default:
12346aa8d69SPekka Enberg 			return false;
12446aa8d69SPekka Enberg 		}
12546aa8d69SPekka Enberg 	}
12613a7760fSPekka Enberg 
12713a7760fSPekka Enberg 	return true;
12813a7760fSPekka Enberg }
12913a7760fSPekka Enberg 
13046aa8d69SPekka Enberg static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
13125af6674SPekka Enberg {
13246aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
13346aa8d69SPekka Enberg 
1349a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB)
13546aa8d69SPekka Enberg 		return false;
13646aa8d69SPekka Enberg 
13746aa8d69SPekka Enberg 	switch (offset) {
13846aa8d69SPekka Enberg 	case THR:
139900d32c1SPekka Enberg 		/* TODO: input support */
14046aa8d69SPekka Enberg 		break;
14146aa8d69SPekka Enberg 	case IER:
14246aa8d69SPekka Enberg 		ioport__write8(data, device.ier);
14346aa8d69SPekka Enberg 		break;
14446aa8d69SPekka Enberg 	case IIR:
145900d32c1SPekka Enberg 		ioport__write8(data, UART_IIR_NO_INT | UART_IIR_THRI);
14646aa8d69SPekka Enberg 		break;
14746aa8d69SPekka Enberg 	case LCR:
14846aa8d69SPekka Enberg 		ioport__write8(data, device.lcr);
14946aa8d69SPekka Enberg 		break;
15046aa8d69SPekka Enberg 	case MCR:
15146aa8d69SPekka Enberg 		ioport__write8(data, device.mcr);
15246aa8d69SPekka Enberg 		break;
15346aa8d69SPekka Enberg 	case LSR:
1549a7428ddSPekka Enberg 		ioport__write8(data, UART_LSR_THRE);
15546aa8d69SPekka Enberg 		break;
15646aa8d69SPekka Enberg 	case MSR:
1579a7428ddSPekka Enberg 		ioport__write8(data, UART_MSR_CTS);
15846aa8d69SPekka Enberg 		break;
15946aa8d69SPekka Enberg 	case SCR:
16046aa8d69SPekka Enberg 		ioport__write8(data, device.scr);
16146aa8d69SPekka Enberg 		break;
16246aa8d69SPekka Enberg 	default:
16346aa8d69SPekka Enberg 		return false;
16425af6674SPekka Enberg 	}
16525af6674SPekka Enberg 
16613a7760fSPekka Enberg 	return true;
16713a7760fSPekka Enberg }
16813a7760fSPekka Enberg 
16946aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
17046aa8d69SPekka Enberg 	.io_in		= serial8250_in,
17146aa8d69SPekka Enberg 	.io_out		= serial8250_out,
172a93ec68bSPekka Enberg };
173a93ec68bSPekka Enberg 
17413a7760fSPekka Enberg void early_printk__init(void)
17513a7760fSPekka Enberg {
17646aa8d69SPekka Enberg 	ioport__register(device.iobase, &serial8250_ops, 8);
17713a7760fSPekka Enberg }
178