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