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/ctype.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     /* Accept only printable characters, newline, and horizontal tab. */
83     if ( !isprint(c) && (c != '\n') && (c != '\t') )
84         return ;
85 
86     spin_lock(&uart->lock);
87     uart->buf[uart->idx++] = c;
88     if ( (uart->idx == (VUART_BUF_SIZE - 2)) || (c == '\n') )
89     {
90         if ( c != '\n' )
91             uart->buf[uart->idx++] = '\n';
92         uart->buf[uart->idx] = '\0';
93         printk(XENLOG_G_DEBUG "DOM%u: %s", d->domain_id, uart->buf);
94         uart->idx = 0;
95     }
96     spin_unlock(&uart->lock);
97 }
98 
vuart_mmio_read(struct vcpu * v,mmio_info_t * info,register_t * r,void * priv)99 static int vuart_mmio_read(struct vcpu *v, mmio_info_t *info,
100                            register_t *r, void *priv)
101 {
102     struct domain *d = v->domain;
103     paddr_t offset = info->gpa - d->arch.vuart.info->base_addr;
104 
105     perfc_incr(vuart_reads);
106 
107     /* By default zeroed the register */
108     *r = 0;
109 
110     if ( offset == d->arch.vuart.info->status_off )
111         /* All holding registers empty, ready to send etc */
112         *r = d->arch.vuart.info->status;
113 
114     return 1;
115 }
116 
vuart_mmio_write(struct vcpu * v,mmio_info_t * info,register_t r,void * priv)117 static int vuart_mmio_write(struct vcpu *v, mmio_info_t *info,
118                             register_t r, void *priv)
119 {
120     struct domain *d = v->domain;
121     paddr_t offset = info->gpa - d->arch.vuart.info->base_addr;
122 
123     perfc_incr(vuart_writes);
124 
125     if ( offset == d->arch.vuart.info->data_off )
126         /* ignore any status bits */
127         vuart_print_char(v, r & 0xFF);
128 
129     return 1;
130 }
131 
132 /*
133  * Local variables:
134  * mode: C
135  * c-file-style: "BSD"
136  * c-basic-offset: 4
137  * indent-tabs-mode: nil
138  * End:
139  */
140 
141