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