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