xref: /kvmtool/hw/serial.c (revision 934c193b93076a2aa43719914c1f33200ef940ad)
1 #include "kvm/8250-serial.h"
2 
3 #include "kvm/ioport.h"
4 #include "kvm/util.h"
5 #include "kvm/kvm.h"
6 
7 #include <linux/serial_reg.h>
8 
9 #include <stdbool.h>
10 #include <poll.h>
11 
12 struct serial8250_device {
13 	uint16_t		iobase;
14 	uint8_t			irq;
15 
16 	uint8_t			thr;
17 	uint8_t			dll;
18 	uint8_t			dlm;
19 	uint8_t			iir;
20 	uint8_t			ier;
21 	uint8_t			fcr;
22 	uint8_t			lcr;
23 	uint8_t			mcr;
24 	uint8_t			lsr;
25 	uint8_t			scr;
26 };
27 
28 static struct serial8250_device device = {
29 	.iobase			= 0x3f8,	/* ttyS0 */
30 	.irq			= 4,
31 
32 	.lsr			= UART_LSR_TEMT | UART_LSR_THRE,
33 };
34 
35 static int read_char(int fd)
36 {
37 	int c;
38 
39 	if (read(fd, &c, 1) < 0)
40 		return -1;
41 
42 	return c;
43 }
44 
45 static bool is_readable(int fd)
46 {
47 	struct pollfd pollfd = (struct pollfd) {
48 		.fd	= fd,
49 		.events	= POLLIN,
50 	};
51 
52 	return poll(&pollfd, 1, 0) > 0;
53 }
54 
55 void serial8250__interrupt(struct kvm *self)
56 {
57 	uint8_t new_iir;
58 
59 	device.iir	= UART_IIR_NO_INT;
60 
61 	/* No interrupts enabled. Exit... */
62 	if (!(device.ier & (UART_IER_THRI|UART_IER_RDI)))
63 		return;
64 
65 	new_iir		= 0;
66 
67 	/* We're always good for guest sending data. */
68 	if (device.ier & UART_IER_THRI)
69 		new_iir			|= UART_IIR_THRI;
70 
71 	/* Is there input in stdin to send to the guest? */
72 	if (!(device.lsr & UART_LSR_DR) && is_readable(fileno(stdin))) {
73 		int c;
74 
75 		c			= read_char(fileno(stdin));
76 		if (c >= 0) {
77 			device.thr		= c;
78 			device.lsr		|= UART_LSR_DR;
79 			new_iir			|= UART_IIR_RDI;
80 		}
81 	}
82 
83 	/* Only send an IRQ if there's work to do. */
84 	if (new_iir) {
85 		device.iir		= new_iir;
86 		kvm__irq_line(self, device.irq, 0);
87 		kvm__irq_line(self, device.irq, 1);
88 	}
89 }
90 
91 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
92 {
93 	uint16_t offset = port - device.iobase;
94 
95 	if (device.lcr & UART_LCR_DLAB) {
96 		switch (offset) {
97 		case UART_DLL:
98 			device.dll		= ioport__read8(data);
99 			break;
100 		case UART_DLM:
101 			device.dlm		= ioport__read8(data);
102 			break;
103 		case UART_FCR:
104 			device.fcr		= ioport__read8(data);
105 			break;
106 		case UART_LCR:
107 			device.lcr		= ioport__read8(data);
108 			break;
109 		default:
110 			return false;
111 		}
112 	} else {
113 		switch (offset) {
114 		case UART_TX: {
115 			char *p = data;
116 			int i;
117 
118 			while (count--) {
119 				for (i = 0; i < size; i++)
120 					fprintf(stdout, "%c", *p++);
121 			}
122 			fflush(stdout);
123 
124 			break;
125 		}
126 		case UART_IER:
127 			device.ier		= ioport__read8(data);
128 			break;
129 		case UART_FCR:
130 			device.fcr		= ioport__read8(data);
131 			break;
132 		case UART_LCR:
133 			device.lcr		= ioport__read8(data);
134 			break;
135 		case UART_MCR:
136 			device.mcr		= ioport__read8(data);
137 			break;
138 		case UART_SCR:
139 			device.scr		= ioport__read8(data);
140 			break;
141 		default:
142 			return false;
143 		}
144 	}
145 
146 	return true;
147 }
148 
149 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
150 {
151 	uint16_t offset = port - device.iobase;
152 
153 	if (device.lcr & UART_LCR_DLAB)
154 		return false;
155 
156 	switch (offset) {
157 	case UART_TX:
158 		if (device.lsr & UART_LSR_DR) {
159 			device.lsr		&= ~UART_LSR_DR;
160 			ioport__write8(data, device.thr);
161 		}
162 		break;
163 	case UART_IER:
164 		ioport__write8(data, device.ier);
165 		break;
166 	case UART_IIR:
167 		ioport__write8(data, device.iir);
168 		break;
169 	case UART_LCR:
170 		ioport__write8(data, device.lcr);
171 		break;
172 	case UART_MCR:
173 		ioport__write8(data, device.mcr);
174 		break;
175 	case UART_LSR:
176 		ioport__write8(data, device.lsr);
177 		break;
178 	case UART_MSR:
179 		ioport__write8(data, UART_MSR_CTS);
180 		break;
181 	case UART_SCR:
182 		ioport__write8(data, device.scr);
183 		break;
184 	default:
185 		return false;
186 	}
187 
188 	return true;
189 }
190 
191 static struct ioport_operations serial8250_ops = {
192 	.io_in		= serial8250_in,
193 	.io_out		= serial8250_out,
194 };
195 
196 void serial8250__init(void)
197 {
198 	ioport__register(device.iobase, &serial8250_ops, 8);
199 }
200