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