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