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