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