1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * xen/arch/arm/vuart.c
4 *
5 * Virtual UART Emulator.
6 *
7 * This emulator uses the information from dtuart. This is not intended to be
8 * a full emulation of an UART device. Rather it is intended to provide a
9 * sufficient veneer of one that early code (such as Linux's boot time
10 * decompressor) which hardcodes output directly to such a device are able to
11 * make progress.
12 *
13 * The minimal register set to emulate an UART are:
14 * - Single byte transmit register
15 * - Single status register
16 *
17 * /!\ This device is not intended to be enumerable or exposed to the OS
18 * (e.g. via Device Tree).
19 *
20 * Julien Grall <julien.grall@linaro.org>
21 * Ian Campbell <ian.campbell@citrix.com>
22 * Copyright (c) 2012 Citrix Systems.
23 */
24 #include <xen/lib.h>
25 #include <xen/sched.h>
26 #include <xen/errno.h>
27 #include <xen/console.h>
28 #include <xen/serial.h>
29 #include <asm/mmio.h>
30 #include <xen/perfc.h>
31
32 #include "vuart.h"
33
34 #define domain_has_vuart(d) ((d)->arch.vuart.info != NULL)
35
36 static int vuart_mmio_read(struct vcpu *v, mmio_info_t *info,
37 register_t *r, void *priv);
38 static int vuart_mmio_write(struct vcpu *v, mmio_info_t *info,
39 register_t r, void *priv);
40
41 static const struct mmio_handler_ops vuart_mmio_handler = {
42 .read = vuart_mmio_read,
43 .write = vuart_mmio_write,
44 };
45
domain_vuart_init(struct domain * d)46 int domain_vuart_init(struct domain *d)
47 {
48 ASSERT( is_hardware_domain(d) );
49
50 d->arch.vuart.info = serial_vuart_info(SERHND_DTUART);
51 if ( !d->arch.vuart.info )
52 return 0;
53
54 spin_lock_init(&d->arch.vuart.lock);
55 d->arch.vuart.idx = 0;
56
57 d->arch.vuart.buf = xzalloc_array(char, VUART_BUF_SIZE);
58 if ( !d->arch.vuart.buf )
59 return -ENOMEM;
60
61 register_mmio_handler(d, &vuart_mmio_handler,
62 d->arch.vuart.info->base_addr,
63 d->arch.vuart.info->size,
64 NULL);
65
66 return 0;
67 }
68
domain_vuart_free(struct domain * d)69 void domain_vuart_free(struct domain *d)
70 {
71 if ( !domain_has_vuart(d) )
72 return;
73
74 xfree(d->arch.vuart.buf);
75 }
76
vuart_print_char(struct vcpu * v,char c)77 static void vuart_print_char(struct vcpu *v, char c)
78 {
79 struct domain *d = v->domain;
80 struct vuart *uart = &d->arch.vuart;
81
82 if ( !is_console_printable(c) )
83 return ;
84
85 spin_lock(&uart->lock);
86 uart->buf[uart->idx++] = c;
87 if ( (uart->idx == (VUART_BUF_SIZE - 2)) || (c == '\n') )
88 {
89 if ( c != '\n' )
90 uart->buf[uart->idx++] = '\n';
91 uart->buf[uart->idx] = '\0';
92 printk(XENLOG_G_DEBUG "DOM%u: %s", d->domain_id, uart->buf);
93 uart->idx = 0;
94 }
95 spin_unlock(&uart->lock);
96 }
97
vuart_mmio_read(struct vcpu * v,mmio_info_t * info,register_t * r,void * priv)98 static int vuart_mmio_read(struct vcpu *v, mmio_info_t *info,
99 register_t *r, void *priv)
100 {
101 struct domain *d = v->domain;
102 paddr_t offset = info->gpa - d->arch.vuart.info->base_addr;
103
104 perfc_incr(vuart_reads);
105
106 /* By default zeroed the register */
107 *r = 0;
108
109 if ( offset == d->arch.vuart.info->status_off )
110 /* All holding registers empty, ready to send etc */
111 *r = d->arch.vuart.info->status;
112
113 return 1;
114 }
115
vuart_mmio_write(struct vcpu * v,mmio_info_t * info,register_t r,void * priv)116 static int vuart_mmio_write(struct vcpu *v, mmio_info_t *info,
117 register_t r, void *priv)
118 {
119 struct domain *d = v->domain;
120 paddr_t offset = info->gpa - d->arch.vuart.info->base_addr;
121
122 perfc_incr(vuart_writes);
123
124 if ( offset == d->arch.vuart.info->data_off )
125 /* ignore any status bits */
126 vuart_print_char(v, r & 0xFF);
127
128 return 1;
129 }
130
131 /*
132 * Local variables:
133 * mode: C
134 * c-file-style: "BSD"
135 * c-basic-offset: 4
136 * indent-tabs-mode: nil
137 * End:
138 */
139
140