xref: /kvmtool/hw/serial.c (revision 8bb34e0dfa3b584e19ec6abb013c71e851e61c8d)
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 void serial8250__interrupt(struct kvm *self)
69 {
70 	/* TODO: inject 8250 interrupt for the guest */
71 }
72 
73 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
74 {
75 	uint16_t offset = port - device.iobase;
76 
77 	if (device.lcr & UART_LCR_DLAB) {
78 		switch (offset) {
79 		case DLL:
80 			device.dll		= ioport__read8(data);
81 			break;
82 		case DLM:
83 			device.dlm		= ioport__read8(data);
84 			break;
85 		case FCR:
86 			device.fcr		= ioport__read8(data);
87 			break;
88 		case LCR:
89 			device.lcr		= ioport__read8(data);
90 			break;
91 		default:
92 			return false;
93 		}
94 	} else {
95 		switch (offset) {
96 		case THR: {
97 			char *p = data;
98 			int i;
99 
100 			while (count--) {
101 				for (i = 0; i < size; i++)
102 					fprintf(stdout, "%c", *p++);
103 			}
104 			fflush(stdout);
105 			break;
106 		}
107 		case IER:
108 			device.ier		= ioport__read8(data);
109 			break;
110 		case FCR:
111 			device.fcr		= ioport__read8(data);
112 			break;
113 		case LCR:
114 			device.lcr		= ioport__read8(data);
115 			break;
116 		case MCR:
117 			device.mcr		= ioport__read8(data);
118 			break;
119 		case SCR:
120 			device.scr		= ioport__read8(data);
121 			break;
122 		default:
123 			return false;
124 		}
125 	}
126 
127 	return true;
128 }
129 
130 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
131 {
132 	uint16_t offset = port - device.iobase;
133 
134 	if (device.lcr & UART_LCR_DLAB)
135 		return false;
136 
137 	switch (offset) {
138 	case THR:
139 		/* TODO: input support */
140 		break;
141 	case IER:
142 		ioport__write8(data, device.ier);
143 		break;
144 	case IIR:
145 		ioport__write8(data, UART_IIR_NO_INT | UART_IIR_THRI);
146 		break;
147 	case LCR:
148 		ioport__write8(data, device.lcr);
149 		break;
150 	case MCR:
151 		ioport__write8(data, device.mcr);
152 		break;
153 	case LSR:
154 		ioport__write8(data, UART_LSR_THRE);
155 		break;
156 	case MSR:
157 		ioport__write8(data, UART_MSR_CTS);
158 		break;
159 	case SCR:
160 		ioport__write8(data, device.scr);
161 		break;
162 	default:
163 		return false;
164 	}
165 
166 	return true;
167 }
168 
169 static struct ioport_operations serial8250_ops = {
170 	.io_in		= serial8250_in,
171 	.io_out		= serial8250_out,
172 };
173 
174 void early_printk__init(void)
175 {
176 	ioport__register(device.iobase, &serial8250_ops, 8);
177 }
178