xref: /qemu/hw/xen/xen_pvdev.c (revision 29b62a10)
1 /*
2  * Xen para-virtualization device
3  *
4  *  (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>
18  */
19 
20 #include "qemu/osdep.h"
21 #include "qemu/log.h"
22 #include "qemu/main-loop.h"
23 #include "hw/qdev-core.h"
24 #include "hw/xen/xen-legacy-backend.h"
25 #include "hw/xen/xen_pvdev.h"
26 
27 /* private */
28 static int debug;
29 
30 struct xs_dirs {
31     char *xs_dir;
32     QTAILQ_ENTRY(xs_dirs) list;
33 };
34 
35 static QTAILQ_HEAD(, xs_dirs) xs_cleanup =
36     QTAILQ_HEAD_INITIALIZER(xs_cleanup);
37 
38 static QTAILQ_HEAD(, XenLegacyDevice) xendevs =
39     QTAILQ_HEAD_INITIALIZER(xendevs);
40 
41 /* ------------------------------------------------------------- */
42 
43 static void xenstore_cleanup_dir(char *dir)
44 {
45     struct xs_dirs *d;
46 
47     d = g_malloc(sizeof(*d));
48     d->xs_dir = dir;
49     QTAILQ_INSERT_TAIL(&xs_cleanup, d, list);
50 }
51 
52 void xen_config_cleanup(void)
53 {
54     struct xs_dirs *d;
55 
56     QTAILQ_FOREACH(d, &xs_cleanup, list) {
57         xs_rm(xenstore, 0, d->xs_dir);
58     }
59 }
60 
61 int xenstore_mkdir(char *path, int p)
62 {
63     struct xs_permissions perms[2] = {
64         {
65             .id    = 0, /* set owner: dom0 */
66         }, {
67             .id    = xen_domid,
68             .perms = p,
69         }
70     };
71 
72     if (!xs_mkdir(xenstore, 0, path)) {
73         xen_pv_printf(NULL, 0, "xs_mkdir %s: failed\n", path);
74         return -1;
75     }
76     xenstore_cleanup_dir(g_strdup(path));
77 
78     if (!xs_set_permissions(xenstore, 0, path, perms, 2)) {
79         xen_pv_printf(NULL, 0, "xs_set_permissions %s: failed\n", path);
80         return -1;
81     }
82     return 0;
83 }
84 
85 int xenstore_write_str(const char *base, const char *node, const char *val)
86 {
87     char abspath[XEN_BUFSIZE];
88 
89     snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
90     if (!xs_write(xenstore, 0, abspath, val, strlen(val))) {
91         return -1;
92     }
93     return 0;
94 }
95 
96 char *xenstore_read_str(const char *base, const char *node)
97 {
98     char abspath[XEN_BUFSIZE];
99     unsigned int len;
100     char *str, *ret = NULL;
101 
102     snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
103     str = xs_read(xenstore, 0, abspath, &len);
104     if (str != NULL) {
105         /* move to qemu-allocated memory to make sure
106          * callers can savely g_free() stuff. */
107         ret = g_strdup(str);
108         free(str);
109     }
110     return ret;
111 }
112 
113 int xenstore_write_int(const char *base, const char *node, int ival)
114 {
115     char val[12];
116 
117     snprintf(val, sizeof(val), "%d", ival);
118     return xenstore_write_str(base, node, val);
119 }
120 
121 int xenstore_write_int64(const char *base, const char *node, int64_t ival)
122 {
123     char val[21];
124 
125     snprintf(val, sizeof(val), "%"PRId64, ival);
126     return xenstore_write_str(base, node, val);
127 }
128 
129 int xenstore_read_int(const char *base, const char *node, int *ival)
130 {
131     char *val;
132     int rc = -1;
133 
134     val = xenstore_read_str(base, node);
135     if (val && 1 == sscanf(val, "%d", ival)) {
136         rc = 0;
137     }
138     g_free(val);
139     return rc;
140 }
141 
142 int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval)
143 {
144     char *val;
145     int rc = -1;
146 
147     val = xenstore_read_str(base, node);
148     if (val && 1 == sscanf(val, "%"SCNu64, uval)) {
149         rc = 0;
150     }
151     g_free(val);
152     return rc;
153 }
154 
155 void xenstore_update(void *unused)
156 {
157     char **vec = NULL;
158     intptr_t type, ops, ptr;
159     unsigned int dom, count;
160 
161     vec = xs_read_watch(xenstore, &count);
162     if (vec == NULL) {
163         goto cleanup;
164     }
165 
166     if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR,
167                &type, &dom, &ops) == 3) {
168         xenstore_update_be(vec[XS_WATCH_PATH], (void *)type, dom, (void*)ops);
169     }
170     if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) {
171         xenstore_update_fe(vec[XS_WATCH_PATH], (void *)ptr);
172     }
173 
174 cleanup:
175     free(vec);
176 }
177 
178 const char *xenbus_strstate(enum xenbus_state state)
179 {
180     static const char *const name[] = {
181         [XenbusStateUnknown]       = "Unknown",
182         [XenbusStateInitialising]  = "Initialising",
183         [XenbusStateInitWait]      = "InitWait",
184         [XenbusStateInitialised]   = "Initialised",
185         [XenbusStateConnected]     = "Connected",
186         [XenbusStateClosing]       = "Closing",
187         [XenbusStateClosed]        = "Closed",
188     };
189     return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID";
190 }
191 
192 /*
193  * msg_level:
194  *  0 == errors (stderr + logfile).
195  *  1 == informative debug messages (logfile only).
196  *  2 == noisy debug messages (logfile only).
197  *  3 == will flood your log (logfile only).
198  */
199 G_GNUC_PRINTF(3, 0)
200 static void xen_pv_output_msg(struct XenLegacyDevice *xendev,
201                               FILE *f, const char *fmt, va_list args)
202 {
203     if (xendev) {
204         fprintf(f, "xen be: %s: ", xendev->name);
205     } else {
206         fprintf(f, "xen be core: ");
207     }
208     vfprintf(f, fmt, args);
209 }
210 
211 void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level,
212                    const char *fmt, ...)
213 {
214     FILE *logfile;
215     va_list args;
216 
217     if (msg_level > (xendev ? xendev->debug : debug)) {
218         return;
219     }
220 
221     logfile = qemu_log_trylock();
222     if (logfile) {
223         va_start(args, fmt);
224         xen_pv_output_msg(xendev, logfile, fmt, args);
225         va_end(args);
226         qemu_log_unlock(logfile);
227     }
228 
229     if (msg_level == 0) {
230         va_start(args, fmt);
231         xen_pv_output_msg(xendev, stderr, fmt, args);
232         va_end(args);
233     }
234 }
235 
236 void xen_pv_evtchn_event(void *opaque)
237 {
238     struct XenLegacyDevice *xendev = opaque;
239     evtchn_port_t port;
240 
241     port = xenevtchn_pending(xendev->evtchndev);
242     if (port != xendev->local_port) {
243         xen_pv_printf(xendev, 0,
244                       "xenevtchn_pending returned %d (expected %d)\n",
245                       port, xendev->local_port);
246         return;
247     }
248     xenevtchn_unmask(xendev->evtchndev, port);
249 
250     if (xendev->ops->event) {
251         xendev->ops->event(xendev);
252     }
253 }
254 
255 void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev)
256 {
257     if (xendev->local_port == -1) {
258         return;
259     }
260     qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
261     xenevtchn_unbind(xendev->evtchndev, xendev->local_port);
262     xen_pv_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port);
263     xendev->local_port = -1;
264 }
265 
266 int xen_pv_send_notify(struct XenLegacyDevice *xendev)
267 {
268     return xenevtchn_notify(xendev->evtchndev, xendev->local_port);
269 }
270 
271 /* ------------------------------------------------------------- */
272 
273 struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev)
274 {
275     struct XenLegacyDevice *xendev;
276 
277     QTAILQ_FOREACH(xendev, &xendevs, next) {
278         if (xendev->dom != dom) {
279             continue;
280         }
281         if (xendev->dev != dev) {
282             continue;
283         }
284         if (strcmp(xendev->type, type) != 0) {
285             continue;
286         }
287         return xendev;
288     }
289     return NULL;
290 }
291 
292 /*
293  * release xen backend device.
294  */
295 void xen_pv_del_xendev(struct XenLegacyDevice *xendev)
296 {
297     if (xendev->ops->free) {
298         xendev->ops->free(xendev);
299     }
300 
301     if (xendev->fe) {
302         char token[XEN_BUFSIZE];
303         snprintf(token, sizeof(token), "fe:%p", xendev);
304         xs_unwatch(xenstore, xendev->fe, token);
305         g_free(xendev->fe);
306     }
307 
308     if (xendev->evtchndev != NULL) {
309         xenevtchn_close(xendev->evtchndev);
310     }
311     if (xendev->gnttabdev != NULL) {
312         xengnttab_close(xendev->gnttabdev);
313     }
314 
315     QTAILQ_REMOVE(&xendevs, xendev, next);
316 
317     qdev_unplug(&xendev->qdev, NULL);
318 }
319 
320 void xen_pv_insert_xendev(struct XenLegacyDevice *xendev)
321 {
322     QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
323 }
324