1 /*
2  * Copyright (c) 2021 EPAM Systems
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/arch/arm64/hypercall.h>
8 #include <zephyr/xen/console.h>
9 #include <zephyr/xen/events.h>
10 #include <zephyr/xen/generic.h>
11 #include <zephyr/xen/hvm.h>
12 #include <zephyr/xen/public/io/console.h>
13 #include <zephyr/xen/public/sched.h>
14 #include <zephyr/xen/public/xen.h>
15 
16 #include <zephyr/device.h>
17 #include <zephyr/init.h>
18 #include <zephyr/kernel.h>
19 #include <zephyr/sys/device_mmio.h>
20 
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(uart_hvc_xen, CONFIG_UART_LOG_LEVEL);
23 
24 static struct hvc_xen_data xen_hvc_data = {0};
25 
26 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
27 static void hvc_uart_evtchn_cb(void *priv);
28 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
29 
read_from_ring(const struct device * dev,char * str,int len)30 static int read_from_ring(const struct device *dev, char *str, int len)
31 {
32 	int recv = 0;
33 	struct hvc_xen_data *hvc_data = dev->data;
34 	XENCONS_RING_IDX cons = hvc_data->intf->in_cons;
35 	XENCONS_RING_IDX prod = hvc_data->intf->in_prod;
36 	XENCONS_RING_IDX in_idx = 0;
37 
38 	compiler_barrier();
39 	__ASSERT((prod - cons) <= sizeof(hvc_data->intf->in),
40 			"Invalid input ring buffer");
41 
42 	while (cons != prod && recv < len) {
43 		in_idx = MASK_XENCONS_IDX(cons, hvc_data->intf->in);
44 		str[recv] = hvc_data->intf->in[in_idx];
45 		recv++;
46 		cons++;
47 	}
48 
49 	compiler_barrier();
50 	hvc_data->intf->in_cons = cons;
51 
52 	notify_evtchn(hvc_data->evtchn);
53 	return recv;
54 }
55 
write_to_ring(const struct device * dev,const char * str,int len)56 static int write_to_ring(const struct device *dev, const char *str, int len)
57 {
58 	int sent = 0;
59 	struct hvc_xen_data *hvc_data = dev->data;
60 	XENCONS_RING_IDX cons = hvc_data->intf->out_cons;
61 	XENCONS_RING_IDX prod = hvc_data->intf->out_prod;
62 	XENCONS_RING_IDX out_idx = 0;
63 
64 	compiler_barrier();
65 	__ASSERT((prod - cons) <= sizeof(hvc_data->intf->out),
66 			"Invalid output ring buffer");
67 
68 	while ((sent < len) && ((prod - cons) < sizeof(hvc_data->intf->out))) {
69 		out_idx = MASK_XENCONS_IDX(prod, hvc_data->intf->out);
70 		hvc_data->intf->out[out_idx] = str[sent];
71 		prod++;
72 		sent++;
73 	}
74 
75 	compiler_barrier();
76 	hvc_data->intf->out_prod = prod;
77 
78 	if (sent) {
79 		notify_evtchn(hvc_data->evtchn);
80 	}
81 
82 	return sent;
83 }
84 
xen_hvc_poll_in(const struct device * dev,unsigned char * c)85 static int xen_hvc_poll_in(const struct device *dev,
86 			unsigned char *c)
87 {
88 	int ret = 0;
89 	char temp;
90 
91 	ret = read_from_ring(dev, &temp, sizeof(temp));
92 	if (!ret) {
93 		/* Char was not received */
94 		return -1;
95 	}
96 
97 	*c = temp;
98 	return 0;
99 }
100 
xen_hvc_poll_out(const struct device * dev,unsigned char c)101 static void xen_hvc_poll_out(const struct device *dev,
102 			unsigned char c)
103 {
104 	/* Not a good solution (notifying HV every time), but needed for poll_out */
105 	(void) write_to_ring(dev, &c, sizeof(c));
106 }
107 
108 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
xen_hvc_fifo_fill(const struct device * dev,const uint8_t * tx_data,int len)109 static int xen_hvc_fifo_fill(const struct device *dev, const uint8_t *tx_data,
110 			 int len)
111 {
112 	int ret = 0, sent = 0;
113 
114 	while (len) {
115 		sent = write_to_ring(dev, tx_data, len);
116 
117 		ret += sent;
118 		tx_data += sent;
119 		len -= sent;
120 
121 		if (len) {
122 			/* Need to be able to read it from another domain */
123 			HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
124 		}
125 	}
126 
127 	return ret;
128 }
129 
xen_hvc_fifo_read(const struct device * dev,uint8_t * rx_data,const int size)130 static int xen_hvc_fifo_read(const struct device *dev, uint8_t *rx_data,
131 			 const int size)
132 {
133 	return read_from_ring(dev, rx_data, size);
134 }
135 
xen_hvc_irq_tx_enable(const struct device * dev)136 static void xen_hvc_irq_tx_enable(const struct device *dev)
137 {
138 	/*
139 	 * Need to explicitly call UART callback on TX enabling to
140 	 * process available buffered TX actions, because no HV events
141 	 * will be generated on tx_enable.
142 	 */
143 	hvc_uart_evtchn_cb(dev->data);
144 }
145 
xen_hvc_irq_tx_ready(const struct device * dev)146 static int xen_hvc_irq_tx_ready(const struct device *dev)
147 {
148 	return 1;
149 }
150 
xen_hvc_irq_rx_enable(const struct device * dev)151 static void xen_hvc_irq_rx_enable(const struct device *dev)
152 {
153 	/*
154 	 * Need to explicitly call UART callback on RX enabling to
155 	 * process available buffered RX actions, because no HV events
156 	 * will be generated on rx_enable.
157 	 */
158 	hvc_uart_evtchn_cb(dev->data);
159 }
160 
xen_hvc_irq_tx_complete(const struct device * dev)161 static int xen_hvc_irq_tx_complete(const struct device *dev)
162 {
163 	/*
164 	 * TX is performed by copying in ring buffer by fifo_fill,
165 	 * so it will be always completed.
166 	 */
167 	return 1;
168 }
169 
xen_hvc_irq_rx_ready(const struct device * dev)170 static int xen_hvc_irq_rx_ready(const struct device *dev)
171 {
172 	struct hvc_xen_data *data = dev->data;
173 
174 	/* RX is ready only if data is available in ring buffer */
175 	return (data->intf->in_prod != data->intf->in_cons);
176 }
177 
xen_hvc_irq_is_pending(const struct device * dev)178 static int xen_hvc_irq_is_pending(const struct device *dev)
179 {
180 	return xen_hvc_irq_rx_ready(dev);
181 }
182 
xen_hvc_irq_update(const struct device * dev)183 static int xen_hvc_irq_update(const struct device *dev)
184 {
185 	/* Nothing needs to be updated before actual ISR */
186 	return 1;
187 }
188 
xen_hvc_irq_callback_set(const struct device * dev,uart_irq_callback_user_data_t cb,void * user_data)189 static void xen_hvc_irq_callback_set(const struct device *dev,
190 		 uart_irq_callback_user_data_t cb, void *user_data)
191 {
192 	struct hvc_xen_data *data = dev->data;
193 
194 	data->irq_cb = cb;
195 	data->irq_cb_data = user_data;
196 }
197 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
198 
199 static const struct uart_driver_api xen_hvc_api = {
200 	.poll_in = xen_hvc_poll_in,
201 	.poll_out = xen_hvc_poll_out,
202 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
203 	.fifo_fill = xen_hvc_fifo_fill,
204 	.fifo_read = xen_hvc_fifo_read,
205 	.irq_tx_enable = xen_hvc_irq_tx_enable,
206 	.irq_tx_ready = xen_hvc_irq_tx_ready,
207 	.irq_rx_enable = xen_hvc_irq_rx_enable,
208 	.irq_tx_complete = xen_hvc_irq_tx_complete,
209 	.irq_rx_ready = xen_hvc_irq_rx_ready,
210 	.irq_is_pending = xen_hvc_irq_is_pending,
211 	.irq_update = xen_hvc_irq_update,
212 	.irq_callback_set = xen_hvc_irq_callback_set,
213 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
214 };
215 
216 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
hvc_uart_evtchn_cb(void * priv)217 static void hvc_uart_evtchn_cb(void *priv)
218 {
219 	struct hvc_xen_data *data = priv;
220 
221 	if (data->irq_cb) {
222 		data->irq_cb(data->dev, data->irq_cb_data);
223 	}
224 }
225 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
226 
xen_console_init(const struct device * dev)227 int xen_console_init(const struct device *dev)
228 {
229 	int ret = 0;
230 	uint64_t console_pfn = 0;
231 	uintptr_t console_addr = 0;
232 	struct hvc_xen_data *data = dev->data;
233 
234 	data->dev = dev;
235 
236 	ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, DOMID_SELF, &data->evtchn);
237 	if (ret) {
238 		LOG_ERR("%s: failed to get Xen console evtchn, ret = %d\n",
239 				__func__, ret);
240 		return ret;
241 	}
242 
243 	ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, DOMID_SELF, &console_pfn);
244 	if (ret) {
245 		LOG_ERR("%s: failed to get Xen console PFN, ret = %d\n",
246 				__func__, ret);
247 		return ret;
248 	}
249 
250 	console_addr = (uintptr_t) (console_pfn << XEN_PAGE_SHIFT);
251 	device_map(DEVICE_MMIO_RAM_PTR(dev), console_addr, XEN_PAGE_SIZE,
252 		K_MEM_CACHE_WB);
253 
254 	data->intf = (struct xencons_interface *) DEVICE_MMIO_GET(dev);
255 
256 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
257 	bind_event_channel(data->evtchn, hvc_uart_evtchn_cb, data);
258 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
259 
260 	LOG_INF("Xen HVC inited successfully\n");
261 
262 	return 0;
263 }
264 
265 DEVICE_DT_DEFINE(DT_NODELABEL(xen_hvc), xen_console_init, NULL, &xen_hvc_data,
266 		NULL, PRE_KERNEL_1, CONFIG_XEN_HVC_INIT_PRIORITY,
267 		&xen_hvc_api);
268 
269 #ifdef CONFIG_XEN_EARLY_CONSOLEIO
270 extern void __printk_hook_install(int (*fn)(int));
271 extern void __stdout_hook_install(int (*fn)(int));
272 
xen_consoleio_putc(int c)273 int xen_consoleio_putc(int c)
274 {
275 	char symbol = (char) c;
276 
277 	HYPERVISOR_console_io(CONSOLEIO_write, sizeof(symbol), &symbol);
278 	return c;
279 }
280 
281 
282 
consoleio_hooks_set(void)283 int consoleio_hooks_set(void)
284 {
285 
286 	/* Will be replaced with poll_in/poll_out by uart_console.c later on boot */
287 	__stdout_hook_install(xen_consoleio_putc);
288 	__printk_hook_install(xen_consoleio_putc);
289 
290 	return 0;
291 }
292 
293 SYS_INIT(consoleio_hooks_set, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
294 #endif /* CONFIG_XEN_EARLY_CONSOLEIO */
295