]>
Commit | Line | Data |
---|---|---|
60e49ff1 OA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * (C) 2006 - Cambridge University | |
4 | * (C) 2020 - EPAM Systems Inc. | |
5 | * | |
6 | * File: xenbus.c [1] | |
7 | * Author: Steven Smith ([email protected]) | |
8 | * Changes: Grzegorz Milos ([email protected]) | |
9 | * Changes: John D. Ramsdell | |
10 | * | |
11 | * Date: Jun 2006, changes Aug 2006 | |
12 | * | |
13 | * Description: Minimal implementation of xenbus | |
14 | * | |
15 | * [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary | |
16 | */ | |
17 | ||
18 | #include <common.h> | |
19 | #include <log.h> | |
20 | ||
21 | #include <asm/armv8/mmu.h> | |
22 | #include <asm/io.h> | |
23 | #include <asm/xen/system.h> | |
24 | ||
25 | #include <linux/bug.h> | |
26 | #include <linux/compat.h> | |
27 | ||
28 | #include <xen/events.h> | |
29 | #include <xen/hvm.h> | |
30 | #include <xen/xenbus.h> | |
31 | ||
32 | #include <xen/interface/io/xs_wire.h> | |
33 | ||
34 | #define map_frame_virt(v) (v << PAGE_SHIFT) | |
35 | ||
36 | #define SCNd16 "d" | |
37 | ||
38 | /* Wait for reply time out, ms */ | |
39 | #define WAIT_XENBUS_TO_MS 5000 | |
40 | /* Polling time out, ms */ | |
41 | #define WAIT_XENBUS_POLL_TO_MS 1 | |
42 | ||
43 | static struct xenstore_domain_interface *xenstore_buf; | |
44 | ||
45 | static char *errmsg(struct xsd_sockmsg *rep); | |
46 | ||
47 | u32 xenbus_evtchn; | |
48 | ||
49 | struct write_req { | |
50 | const void *data; | |
51 | unsigned int len; | |
52 | }; | |
53 | ||
54 | static void memcpy_from_ring(const void *r, void *d, int off, int len) | |
55 | { | |
56 | int c1, c2; | |
57 | const char *ring = r; | |
58 | char *dest = d; | |
59 | ||
60 | c1 = min(len, XENSTORE_RING_SIZE - off); | |
61 | c2 = len - c1; | |
62 | memcpy(dest, ring + off, c1); | |
63 | memcpy(dest + c1, ring, c2); | |
64 | } | |
65 | ||
66 | /** | |
67 | * xenbus_get_reply() - Receive reply from xenbus | |
68 | * @req_reply: reply message structure | |
69 | * | |
70 | * Wait for reply message event from the ring and copy received message | |
71 | * to input xsd_sockmsg structure. Repeat until full reply is | |
72 | * proceeded. | |
73 | * | |
74 | * Return: false - timeout | |
75 | * true - reply is received | |
76 | */ | |
77 | static bool xenbus_get_reply(struct xsd_sockmsg **req_reply) | |
78 | { | |
79 | struct xsd_sockmsg msg; | |
80 | unsigned int prod = xenstore_buf->rsp_prod; | |
81 | ||
82 | again: | |
83 | if (!wait_event_timeout(NULL, prod != xenstore_buf->rsp_prod, | |
84 | WAIT_XENBUS_TO_MS)) { | |
85 | printk("%s: wait_event timeout\n", __func__); | |
86 | return false; | |
87 | } | |
88 | ||
89 | prod = xenstore_buf->rsp_prod; | |
90 | if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg)) | |
91 | goto again; | |
92 | ||
93 | rmb(); | |
94 | memcpy_from_ring(xenstore_buf->rsp, &msg, | |
95 | MASK_XENSTORE_IDX(xenstore_buf->rsp_cons), | |
96 | sizeof(msg)); | |
97 | ||
98 | if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg) + msg.len) | |
99 | goto again; | |
100 | ||
101 | /* We do not support and expect any Xen bus wathes. */ | |
102 | BUG_ON(msg.type == XS_WATCH_EVENT); | |
103 | ||
104 | *req_reply = malloc(sizeof(msg) + msg.len); | |
105 | memcpy_from_ring(xenstore_buf->rsp, *req_reply, | |
106 | MASK_XENSTORE_IDX(xenstore_buf->rsp_cons), | |
107 | msg.len + sizeof(msg)); | |
108 | mb(); | |
109 | xenstore_buf->rsp_cons += msg.len + sizeof(msg); | |
110 | ||
111 | wmb(); | |
112 | notify_remote_via_evtchn(xenbus_evtchn); | |
113 | return true; | |
114 | } | |
115 | ||
116 | char *xenbus_switch_state(xenbus_transaction_t xbt, const char *path, | |
117 | XenbusState state) | |
118 | { | |
119 | char *current_state; | |
120 | char *msg = NULL; | |
121 | char *msg2 = NULL; | |
122 | char value[2]; | |
123 | XenbusState rs; | |
124 | int xbt_flag = 0; | |
125 | int retry = 0; | |
126 | ||
127 | do { | |
128 | if (xbt == XBT_NIL) { | |
129 | msg = xenbus_transaction_start(&xbt); | |
130 | if (msg) | |
131 | goto exit; | |
132 | xbt_flag = 1; | |
133 | } | |
134 | ||
135 | msg = xenbus_read(xbt, path, ¤t_state); | |
136 | if (msg) | |
137 | goto exit; | |
138 | ||
139 | rs = (XenbusState)(current_state[0] - '0'); | |
140 | free(current_state); | |
141 | if (rs == state) { | |
142 | msg = NULL; | |
143 | goto exit; | |
144 | } | |
145 | ||
146 | snprintf(value, 2, "%d", state); | |
147 | msg = xenbus_write(xbt, path, value); | |
148 | ||
149 | exit: | |
150 | if (xbt_flag) { | |
151 | msg2 = xenbus_transaction_end(xbt, 0, &retry); | |
152 | xbt = XBT_NIL; | |
153 | } | |
154 | if (msg == NULL && msg2 != NULL) | |
155 | msg = msg2; | |
156 | else | |
157 | free(msg2); | |
158 | } while (retry); | |
159 | ||
160 | return msg; | |
161 | } | |
162 | ||
163 | char *xenbus_wait_for_state_change(const char *path, XenbusState *state) | |
164 | { | |
165 | for (;;) { | |
166 | char *res, *msg; | |
167 | XenbusState rs; | |
168 | ||
169 | msg = xenbus_read(XBT_NIL, path, &res); | |
170 | if (msg) | |
171 | return msg; | |
172 | ||
173 | rs = (XenbusState)(res[0] - 48); | |
174 | free(res); | |
175 | ||
176 | if (rs == *state) { | |
177 | wait_event_timeout(NULL, false, WAIT_XENBUS_POLL_TO_MS); | |
178 | } else { | |
179 | *state = rs; | |
180 | break; | |
181 | } | |
182 | } | |
183 | return NULL; | |
184 | } | |
185 | ||
186 | /* Send data to xenbus. This can block. All of the requests are seen | |
187 | * by xenbus as if sent atomically. The header is added | |
188 | * automatically, using type %type, req_id %req_id, and trans_id | |
189 | * %trans_id. | |
190 | */ | |
191 | static void xb_write(int type, int req_id, xenbus_transaction_t trans_id, | |
192 | const struct write_req *req, int nr_reqs) | |
193 | { | |
194 | XENSTORE_RING_IDX prod; | |
195 | int r; | |
196 | int len = 0; | |
197 | const struct write_req *cur_req; | |
198 | int req_off; | |
199 | int total_off; | |
200 | int this_chunk; | |
201 | struct xsd_sockmsg m = { | |
202 | .type = type, | |
203 | .req_id = req_id, | |
204 | .tx_id = trans_id | |
205 | }; | |
206 | struct write_req header_req = { | |
207 | &m, | |
208 | sizeof(m) | |
209 | }; | |
210 | ||
211 | for (r = 0; r < nr_reqs; r++) | |
212 | len += req[r].len; | |
213 | m.len = len; | |
214 | len += sizeof(m); | |
215 | ||
216 | cur_req = &header_req; | |
217 | ||
218 | BUG_ON(len > XENSTORE_RING_SIZE); | |
219 | prod = xenstore_buf->req_prod; | |
220 | /* We are running synchronously, so it is a bug if we do not | |
221 | * have enough room to send a message: please note that a message | |
222 | * can occupy multiple slots in the ring buffer. | |
223 | */ | |
224 | BUG_ON(prod + len - xenstore_buf->req_cons > XENSTORE_RING_SIZE); | |
225 | ||
226 | total_off = 0; | |
227 | req_off = 0; | |
228 | while (total_off < len) { | |
229 | this_chunk = min(cur_req->len - req_off, | |
230 | XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod)); | |
231 | memcpy((char *)xenstore_buf->req + MASK_XENSTORE_IDX(prod), | |
232 | (char *)cur_req->data + req_off, this_chunk); | |
233 | prod += this_chunk; | |
234 | req_off += this_chunk; | |
235 | total_off += this_chunk; | |
236 | if (req_off == cur_req->len) { | |
237 | req_off = 0; | |
238 | if (cur_req == &header_req) | |
239 | cur_req = req; | |
240 | else | |
241 | cur_req++; | |
242 | } | |
243 | } | |
244 | ||
245 | BUG_ON(req_off != 0); | |
246 | BUG_ON(total_off != len); | |
247 | BUG_ON(prod > xenstore_buf->req_cons + XENSTORE_RING_SIZE); | |
248 | ||
249 | /* Remote must see entire message before updating indexes */ | |
250 | wmb(); | |
251 | ||
252 | xenstore_buf->req_prod += len; | |
253 | ||
254 | /* Send evtchn to notify remote */ | |
255 | notify_remote_via_evtchn(xenbus_evtchn); | |
256 | } | |
257 | ||
258 | /* Send a message to xenbus, in the same fashion as xb_write, and | |
259 | * block waiting for a reply. The reply is malloced and should be | |
260 | * freed by the caller. | |
261 | */ | |
262 | struct xsd_sockmsg *xenbus_msg_reply(int type, | |
263 | xenbus_transaction_t trans, | |
264 | struct write_req *io, | |
265 | int nr_reqs) | |
266 | { | |
267 | struct xsd_sockmsg *rep; | |
268 | ||
269 | /* We do not use request identifier which is echoed in daemon's response. */ | |
270 | xb_write(type, 0, trans, io, nr_reqs); | |
271 | /* Now wait for the message to arrive. */ | |
272 | if (!xenbus_get_reply(&rep)) | |
273 | return NULL; | |
274 | return rep; | |
275 | } | |
276 | ||
277 | static char *errmsg(struct xsd_sockmsg *rep) | |
278 | { | |
279 | char *res; | |
280 | ||
281 | if (!rep) { | |
282 | char msg[] = "No reply"; | |
283 | size_t len = strlen(msg) + 1; | |
284 | ||
285 | return memcpy(malloc(len), msg, len); | |
286 | } | |
287 | if (rep->type != XS_ERROR) | |
288 | return NULL; | |
289 | res = malloc(rep->len + 1); | |
290 | memcpy(res, rep + 1, rep->len); | |
291 | res[rep->len] = 0; | |
292 | free(rep); | |
293 | return res; | |
294 | } | |
295 | ||
296 | /* List the contents of a directory. Returns a malloc()ed array of | |
297 | * pointers to malloc()ed strings. The array is NULL terminated. May | |
298 | * block. | |
299 | */ | |
300 | char *xenbus_ls(xenbus_transaction_t xbt, const char *pre, char ***contents) | |
301 | { | |
302 | struct xsd_sockmsg *reply, *repmsg; | |
303 | struct write_req req[] = { { pre, strlen(pre) + 1 } }; | |
304 | int nr_elems, x, i; | |
305 | char **res, *msg; | |
306 | ||
307 | repmsg = xenbus_msg_reply(XS_DIRECTORY, xbt, req, ARRAY_SIZE(req)); | |
308 | msg = errmsg(repmsg); | |
309 | if (msg) { | |
310 | *contents = NULL; | |
311 | return msg; | |
312 | } | |
313 | reply = repmsg + 1; | |
314 | for (x = nr_elems = 0; x < repmsg->len; x++) | |
315 | nr_elems += (((char *)reply)[x] == 0); | |
316 | res = malloc(sizeof(res[0]) * (nr_elems + 1)); | |
317 | for (x = i = 0; i < nr_elems; i++) { | |
318 | int l = strlen((char *)reply + x); | |
319 | ||
320 | res[i] = malloc(l + 1); | |
321 | memcpy(res[i], (char *)reply + x, l + 1); | |
322 | x += l + 1; | |
323 | } | |
324 | res[i] = NULL; | |
325 | free(repmsg); | |
326 | *contents = res; | |
327 | return NULL; | |
328 | } | |
329 | ||
330 | char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value) | |
331 | { | |
332 | struct write_req req[] = { {path, strlen(path) + 1} }; | |
333 | struct xsd_sockmsg *rep; | |
334 | char *res, *msg; | |
335 | ||
336 | rep = xenbus_msg_reply(XS_READ, xbt, req, ARRAY_SIZE(req)); | |
337 | msg = errmsg(rep); | |
338 | if (msg) { | |
339 | *value = NULL; | |
340 | return msg; | |
341 | } | |
342 | res = malloc(rep->len + 1); | |
343 | memcpy(res, rep + 1, rep->len); | |
344 | res[rep->len] = 0; | |
345 | free(rep); | |
346 | *value = res; | |
347 | return NULL; | |
348 | } | |
349 | ||
350 | char *xenbus_write(xenbus_transaction_t xbt, const char *path, | |
351 | const char *value) | |
352 | { | |
353 | struct write_req req[] = { | |
354 | {path, strlen(path) + 1}, | |
355 | {value, strlen(value)}, | |
356 | }; | |
357 | struct xsd_sockmsg *rep; | |
358 | char *msg; | |
359 | ||
360 | rep = xenbus_msg_reply(XS_WRITE, xbt, req, ARRAY_SIZE(req)); | |
361 | msg = errmsg(rep); | |
362 | if (msg) | |
363 | return msg; | |
364 | free(rep); | |
365 | return NULL; | |
366 | } | |
367 | ||
368 | char *xenbus_rm(xenbus_transaction_t xbt, const char *path) | |
369 | { | |
370 | struct write_req req[] = { {path, strlen(path) + 1} }; | |
371 | struct xsd_sockmsg *rep; | |
372 | char *msg; | |
373 | ||
374 | rep = xenbus_msg_reply(XS_RM, xbt, req, ARRAY_SIZE(req)); | |
375 | msg = errmsg(rep); | |
376 | if (msg) | |
377 | return msg; | |
378 | free(rep); | |
379 | return NULL; | |
380 | } | |
381 | ||
382 | char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value) | |
383 | { | |
384 | struct write_req req[] = { {path, strlen(path) + 1} }; | |
385 | struct xsd_sockmsg *rep; | |
386 | char *res, *msg; | |
387 | ||
388 | rep = xenbus_msg_reply(XS_GET_PERMS, xbt, req, ARRAY_SIZE(req)); | |
389 | msg = errmsg(rep); | |
390 | if (msg) { | |
391 | *value = NULL; | |
392 | return msg; | |
393 | } | |
394 | res = malloc(rep->len + 1); | |
395 | memcpy(res, rep + 1, rep->len); | |
396 | res[rep->len] = 0; | |
397 | free(rep); | |
398 | *value = res; | |
399 | return NULL; | |
400 | } | |
401 | ||
402 | #define PERM_MAX_SIZE 32 | |
403 | char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path, | |
404 | domid_t dom, char perm) | |
405 | { | |
406 | char value[PERM_MAX_SIZE]; | |
407 | struct write_req req[] = { | |
408 | {path, strlen(path) + 1}, | |
409 | {value, 0}, | |
410 | }; | |
411 | struct xsd_sockmsg *rep; | |
412 | char *msg; | |
413 | ||
414 | snprintf(value, PERM_MAX_SIZE, "%c%hu", perm, dom); | |
415 | req[1].len = strlen(value) + 1; | |
416 | rep = xenbus_msg_reply(XS_SET_PERMS, xbt, req, ARRAY_SIZE(req)); | |
417 | msg = errmsg(rep); | |
418 | if (msg) | |
419 | return msg; | |
420 | free(rep); | |
421 | return NULL; | |
422 | } | |
423 | ||
424 | char *xenbus_transaction_start(xenbus_transaction_t *xbt) | |
425 | { | |
426 | /* Xenstored becomes angry if you send a length 0 message, so just | |
427 | * shove a nul terminator on the end | |
428 | */ | |
429 | struct write_req req = { "", 1}; | |
430 | struct xsd_sockmsg *rep; | |
431 | char *err; | |
432 | ||
433 | rep = xenbus_msg_reply(XS_TRANSACTION_START, 0, &req, 1); | |
434 | err = errmsg(rep); | |
435 | if (err) | |
436 | return err; | |
437 | sscanf((char *)(rep + 1), "%lu", xbt); | |
438 | free(rep); | |
439 | return NULL; | |
440 | } | |
441 | ||
442 | char *xenbus_transaction_end(xenbus_transaction_t t, int abort, int *retry) | |
443 | { | |
444 | struct xsd_sockmsg *rep; | |
445 | struct write_req req; | |
446 | char *err; | |
447 | ||
448 | *retry = 0; | |
449 | ||
450 | req.data = abort ? "F" : "T"; | |
451 | req.len = 2; | |
452 | rep = xenbus_msg_reply(XS_TRANSACTION_END, t, &req, 1); | |
453 | err = errmsg(rep); | |
454 | if (err) { | |
455 | if (!strcmp(err, "EAGAIN")) { | |
456 | *retry = 1; | |
457 | free(err); | |
458 | return NULL; | |
459 | } else { | |
460 | return err; | |
461 | } | |
462 | } | |
463 | free(rep); | |
464 | return NULL; | |
465 | } | |
466 | ||
467 | int xenbus_read_integer(const char *path) | |
468 | { | |
469 | char *res, *buf; | |
470 | int t; | |
471 | ||
472 | res = xenbus_read(XBT_NIL, path, &buf); | |
473 | if (res) { | |
474 | printk("Failed to read %s.\n", path); | |
475 | free(res); | |
476 | return -1; | |
477 | } | |
478 | sscanf(buf, "%d", &t); | |
479 | free(buf); | |
480 | return t; | |
481 | } | |
482 | ||
483 | int xenbus_read_uuid(const char *path, unsigned char uuid[16]) | |
484 | { | |
485 | char *res, *buf; | |
486 | ||
487 | res = xenbus_read(XBT_NIL, path, &buf); | |
488 | if (res) { | |
489 | printk("Failed to read %s.\n", path); | |
490 | free(res); | |
491 | return 0; | |
492 | } | |
493 | if (strlen(buf) != ((2 * 16) + 4) /* 16 hex bytes and 4 hyphens */ | |
494 | || sscanf(buf, | |
495 | "%2hhx%2hhx%2hhx%2hhx-" | |
496 | "%2hhx%2hhx-" | |
497 | "%2hhx%2hhx-" | |
498 | "%2hhx%2hhx-" | |
499 | "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", | |
500 | uuid, uuid + 1, uuid + 2, uuid + 3, | |
501 | uuid + 4, uuid + 5, uuid + 6, uuid + 7, | |
502 | uuid + 8, uuid + 9, uuid + 10, uuid + 11, | |
503 | uuid + 12, uuid + 13, uuid + 14, uuid + 15) != 16) { | |
504 | printk("Xenbus path %s value %s is not a uuid!\n", path, buf); | |
505 | free(buf); | |
506 | return 0; | |
507 | } | |
508 | free(buf); | |
509 | return 1; | |
510 | } | |
511 | ||
512 | char *xenbus_printf(xenbus_transaction_t xbt, | |
513 | const char *node, const char *path, | |
514 | const char *fmt, ...) | |
515 | { | |
516 | #define BUFFER_SIZE 256 | |
517 | char fullpath[BUFFER_SIZE]; | |
518 | char val[BUFFER_SIZE]; | |
519 | va_list args; | |
520 | ||
521 | BUG_ON(strlen(node) + strlen(path) + 1 >= BUFFER_SIZE); | |
522 | sprintf(fullpath, "%s/%s", node, path); | |
523 | va_start(args, fmt); | |
524 | vsprintf(val, fmt, args); | |
525 | va_end(args); | |
526 | return xenbus_write(xbt, fullpath, val); | |
527 | } | |
528 | ||
529 | domid_t xenbus_get_self_id(void) | |
530 | { | |
531 | char *dom_id; | |
532 | domid_t ret; | |
533 | ||
534 | BUG_ON(xenbus_read(XBT_NIL, "domid", &dom_id)); | |
535 | sscanf(dom_id, "%"SCNd16, &ret); | |
536 | ||
537 | return ret; | |
538 | } | |
539 | ||
540 | void init_xenbus(void) | |
541 | { | |
542 | u64 v; | |
543 | ||
544 | debug("%s\n", __func__); | |
545 | if (hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v)) | |
546 | BUG(); | |
547 | xenbus_evtchn = v; | |
548 | ||
549 | if (hvm_get_parameter(HVM_PARAM_STORE_PFN, &v)) | |
550 | BUG(); | |
551 | xenstore_buf = (struct xenstore_domain_interface *)map_frame_virt(v); | |
552 | } | |
553 | ||
554 | void fini_xenbus(void) | |
555 | { | |
556 | debug("%s\n", __func__); | |
557 | } |