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