xref: /kvmtool/hw/serial.c (revision 900d32c15c5851063a29f28e538d79c2a4800594)
1 #include "kvm/early_printk.h"
2 
3 #include "kvm/ioport.h"
4 #include "kvm/util.h"
5 
6 #include <stdbool.h>
7 
8 /* Transmitter holding register */
9 #define THR             0
10 
11 /* Receive buffer register */
12 #define RBR             0
13 
14 /* Divisor latch low byte */
15 #define DLL		0
16 
17 /* Divisor latch high byte */
18 #define DLM		1
19 
20 /* Interrupt enable register */
21 #define IER		1
22 
23 /* Interrupt identification register */
24 #define IIR		2
25 
26 #define UART_IIR_NO_INT		0x01
27 #define UART_IIR_THRI		0x02
28 
29 /* 16550 FIFO Control Register */
30 #define FCR		2
31 
32 /* Line control register */
33 #define LCR		3
34 
35 #define UART_LCR_DLAB		0x80
36 
37 /* Modem control register */
38 #define MCR		4
39 
40 /* Line status register */
41 #define LSR		5
42 
43 #define UART_LSR_THRE		0x20
44 
45 /* Modem status register */
46 #define MSR		6
47 
48 #define UART_MSR_CTS		0x10
49 
50 /* Scratch register */
51 #define SCR		7
52 
53 struct serial8250_device {
54 	uint16_t		iobase;
55 	uint8_t			dll;
56 	uint8_t			dlm;
57 	uint8_t			ier;
58 	uint8_t			fcr;
59 	uint8_t			lcr;
60 	uint8_t			mcr;
61 	uint8_t			scr;
62 };
63 
64 static struct serial8250_device device = {
65 	.iobase			= 0x3f8,	/* ttyS0 */
66 };
67 
68 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
69 {
70 	uint16_t offset = port - device.iobase;
71 
72 	if (device.lcr & UART_LCR_DLAB) {
73 		switch (offset) {
74 		case DLL:
75 			device.dll		= ioport__read8(data);
76 			break;
77 		case DLM:
78 			device.dlm		= ioport__read8(data);
79 			break;
80 		case FCR:
81 			device.fcr		= ioport__read8(data);
82 			break;
83 		case LCR:
84 			device.lcr		= ioport__read8(data);
85 			break;
86 		default:
87 			return false;
88 		}
89 	} else {
90 		switch (offset) {
91 		case THR: {
92 			char *p = data;
93 			int i;
94 
95 			while (count--) {
96 				for (i = 0; i < size; i++)
97 					fprintf(stdout, "%c", *p++);
98 			}
99 			fflush(stdout);
100 			break;
101 		}
102 		case IER:
103 			device.ier		= ioport__read8(data);
104 			break;
105 		case FCR:
106 			device.fcr		= ioport__read8(data);
107 			break;
108 		case LCR:
109 			device.lcr		= ioport__read8(data);
110 			break;
111 		case MCR:
112 			device.mcr		= ioport__read8(data);
113 			break;
114 		case SCR:
115 			device.scr		= ioport__read8(data);
116 			break;
117 		default:
118 			return false;
119 		}
120 	}
121 
122 	return true;
123 }
124 
125 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
126 {
127 	uint16_t offset = port - device.iobase;
128 
129 	if (device.lcr & UART_LCR_DLAB)
130 		return false;
131 
132 	switch (offset) {
133 	case THR:
134 		/* TODO: input support */
135 		break;
136 	case IER:
137 		ioport__write8(data, device.ier);
138 		break;
139 	case IIR:
140 		ioport__write8(data, UART_IIR_NO_INT | UART_IIR_THRI);
141 		break;
142 	case LCR:
143 		ioport__write8(data, device.lcr);
144 		break;
145 	case MCR:
146 		ioport__write8(data, device.mcr);
147 		break;
148 	case LSR:
149 		ioport__write8(data, UART_LSR_THRE);
150 		break;
151 	case MSR:
152 		ioport__write8(data, UART_MSR_CTS);
153 		break;
154 	case SCR:
155 		ioport__write8(data, device.scr);
156 		break;
157 	default:
158 		return false;
159 	}
160 
161 	return true;
162 }
163 
164 static struct ioport_operations serial8250_ops = {
165 	.io_in		= serial8250_in,
166 	.io_out		= serial8250_out,
167 };
168 
169 void early_printk__init(void)
170 {
171 	ioport__register(device.iobase, &serial8250_ops, 8);
172 }
173