xref: /openbmc/qemu/hw/char/xen_console.c (revision db725815985654007ade0fd53590d613fd657208)
1 /*
2  *  Copyright (C) International Business Machines  Corp., 2005
3  *  Author(s): Anthony Liguori <aliguori@us.ibm.com>
4  *
5  *  Copyright (C) Red Hat 2007
6  *
7  *  Xen Console
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; under version 2 of the License.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "qemu/osdep.h"
23 #include <sys/select.h>
24 #include <termios.h>
25 
26 #include "qapi/error.h"
27 #include "chardev/char-fe.h"
28 #include "hw/xen/xen-legacy-backend.h"
29 
30 #include "hw/xen/interface/io/console.h"
31 
32 struct buffer {
33     uint8_t *data;
34     size_t consumed;
35     size_t size;
36     size_t capacity;
37     size_t max_capacity;
38 };
39 
40 struct XenConsole {
41     struct XenLegacyDevice  xendev;  /* must be first */
42     struct buffer     buffer;
43     char              console[XEN_BUFSIZE];
44     int               ring_ref;
45     void              *sring;
46     CharBackend       chr;
47     int               backlog;
48 };
49 
50 static void buffer_append(struct XenConsole *con)
51 {
52     struct buffer *buffer = &con->buffer;
53     XENCONS_RING_IDX cons, prod, size;
54     struct xencons_interface *intf = con->sring;
55 
56     cons = intf->out_cons;
57     prod = intf->out_prod;
58     xen_mb();
59 
60     size = prod - cons;
61     if ((size == 0) || (size > sizeof(intf->out)))
62         return;
63 
64     if ((buffer->capacity - buffer->size) < size) {
65         buffer->capacity += (size + 1024);
66         buffer->data = g_realloc(buffer->data, buffer->capacity);
67     }
68 
69     while (cons != prod)
70         buffer->data[buffer->size++] = intf->out[
71             MASK_XENCONS_IDX(cons++, intf->out)];
72 
73     xen_mb();
74     intf->out_cons = cons;
75     xen_pv_send_notify(&con->xendev);
76 
77     if (buffer->max_capacity &&
78         buffer->size > buffer->max_capacity) {
79         /* Discard the middle of the data. */
80 
81         size_t over = buffer->size - buffer->max_capacity;
82         uint8_t *maxpos = buffer->data + buffer->max_capacity;
83 
84         memmove(maxpos - over, maxpos, over);
85         buffer->data = g_realloc(buffer->data, buffer->max_capacity);
86         buffer->size = buffer->capacity = buffer->max_capacity;
87 
88         if (buffer->consumed > buffer->max_capacity - over)
89             buffer->consumed = buffer->max_capacity - over;
90     }
91 }
92 
93 static void buffer_advance(struct buffer *buffer, size_t len)
94 {
95     buffer->consumed += len;
96     if (buffer->consumed == buffer->size) {
97         buffer->consumed = 0;
98         buffer->size = 0;
99     }
100 }
101 
102 static int ring_free_bytes(struct XenConsole *con)
103 {
104     struct xencons_interface *intf = con->sring;
105     XENCONS_RING_IDX cons, prod, space;
106 
107     cons = intf->in_cons;
108     prod = intf->in_prod;
109     xen_mb();
110 
111     space = prod - cons;
112     if (space > sizeof(intf->in))
113         return 0; /* ring is screwed: ignore it */
114 
115     return (sizeof(intf->in) - space);
116 }
117 
118 static int xencons_can_receive(void *opaque)
119 {
120     struct XenConsole *con = opaque;
121     return ring_free_bytes(con);
122 }
123 
124 static void xencons_receive(void *opaque, const uint8_t *buf, int len)
125 {
126     struct XenConsole *con = opaque;
127     struct xencons_interface *intf = con->sring;
128     XENCONS_RING_IDX prod;
129     int i, max;
130 
131     max = ring_free_bytes(con);
132     /* The can_receive() func limits this, but check again anyway */
133     if (max < len)
134         len = max;
135 
136     prod = intf->in_prod;
137     for (i = 0; i < len; i++) {
138         intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
139             buf[i];
140     }
141     xen_wmb();
142     intf->in_prod = prod;
143     xen_pv_send_notify(&con->xendev);
144 }
145 
146 static void xencons_send(struct XenConsole *con)
147 {
148     ssize_t len, size;
149 
150     size = con->buffer.size - con->buffer.consumed;
151     if (qemu_chr_fe_backend_connected(&con->chr)) {
152         len = qemu_chr_fe_write(&con->chr,
153                                 con->buffer.data + con->buffer.consumed,
154                                 size);
155     } else {
156         len = size;
157     }
158     if (len < 1) {
159         if (!con->backlog) {
160             con->backlog = 1;
161             xen_pv_printf(&con->xendev, 1,
162                           "backlog piling up, nobody listening?\n");
163         }
164     } else {
165         buffer_advance(&con->buffer, len);
166         if (con->backlog && len == size) {
167             con->backlog = 0;
168             xen_pv_printf(&con->xendev, 1, "backlog is gone\n");
169         }
170     }
171 }
172 
173 /* -------------------------------------------------------------------- */
174 
175 static int con_init(struct XenLegacyDevice *xendev)
176 {
177     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
178     char *type, *dom, label[32];
179     int ret = 0;
180     const char *output;
181 
182     /* setup */
183     dom = xs_get_domain_path(xenstore, con->xendev.dom);
184     if (!xendev->dev) {
185         snprintf(con->console, sizeof(con->console), "%s/console", dom);
186     } else {
187         snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev);
188     }
189     free(dom);
190 
191     type = xenstore_read_str(con->console, "type");
192     if (!type || strcmp(type, "ioemu") != 0) {
193         xen_pv_printf(xendev, 1, "not for me (type=%s)\n", type);
194         ret = -1;
195         goto out;
196     }
197 
198     output = xenstore_read_str(con->console, "output");
199 
200     /* no Xen override, use qemu output device */
201     if (output == NULL) {
202         if (con->xendev.dev) {
203             qemu_chr_fe_init(&con->chr, serial_hd(con->xendev.dev),
204                              &error_abort);
205         }
206     } else {
207         snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
208         qemu_chr_fe_init(&con->chr,
209                          /*
210                           * FIXME: sure we want to support implicit
211                           * muxed monitors here?
212                           */
213                          qemu_chr_new_mux_mon(label, output, NULL),
214                          &error_abort);
215     }
216 
217     xenstore_store_pv_console_info(con->xendev.dev,
218                                    qemu_chr_fe_get_driver(&con->chr));
219 
220 out:
221     g_free(type);
222     return ret;
223 }
224 
225 static int con_initialise(struct XenLegacyDevice *xendev)
226 {
227     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
228     int limit;
229 
230     if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1)
231         return -1;
232     if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1)
233         return -1;
234     if (xenstore_read_int(con->console, "limit", &limit) == 0)
235         con->buffer.max_capacity = limit;
236 
237     if (!xendev->dev) {
238         xen_pfn_t mfn = con->ring_ref;
239         con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom,
240                                           PROT_READ | PROT_WRITE,
241                                           1, &mfn, NULL);
242     } else {
243         con->sring = xen_be_map_grant_ref(xendev, con->ring_ref,
244                                           PROT_READ | PROT_WRITE);
245     }
246     if (!con->sring)
247         return -1;
248 
249     xen_be_bind_evtchn(&con->xendev);
250     qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
251                              xencons_receive, NULL, NULL, con, NULL, true);
252 
253     xen_pv_printf(xendev, 1,
254                   "ring mfn %d, remote port %d, local port %d, limit %zd\n",
255                   con->ring_ref,
256                   con->xendev.remote_port,
257                   con->xendev.local_port,
258                   con->buffer.max_capacity);
259     return 0;
260 }
261 
262 static void con_disconnect(struct XenLegacyDevice *xendev)
263 {
264     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
265 
266     qemu_chr_fe_deinit(&con->chr, false);
267     xen_pv_unbind_evtchn(&con->xendev);
268 
269     if (con->sring) {
270         if (!xendev->dev) {
271             xenforeignmemory_unmap(xen_fmem, con->sring, 1);
272         } else {
273             xen_be_unmap_grant_ref(xendev, con->sring);
274         }
275         con->sring = NULL;
276     }
277 }
278 
279 static void con_event(struct XenLegacyDevice *xendev)
280 {
281     struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
282 
283     buffer_append(con);
284     if (con->buffer.size - con->buffer.consumed)
285         xencons_send(con);
286 }
287 
288 /* -------------------------------------------------------------------- */
289 
290 struct XenDevOps xen_console_ops = {
291     .size       = sizeof(struct XenConsole),
292     .flags      = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV,
293     .init       = con_init,
294     .initialise = con_initialise,
295     .event      = con_event,
296     .disconnect = con_disconnect,
297 };
298