1
2
3
4
5
6
7
8
9
10
11
12#include "qemu/osdep.h"
13#include "qemu/timer.h"
14#include "qemu/range.h"
15#include "qemu/units.h"
16#include "qemu/log.h"
17#include "qapi/error.h"
18#include "exec/address-spaces.h"
19#include "hw/ppc/vof.h"
20#include "hw/ppc/fdt.h"
21#include "sysemu/runstate.h"
22#include "qom/qom-qobject.h"
23#include "trace.h"
24
25#include <libfdt.h>
26
27
28
29
30
31#define OF_PROPNAME_LEN_MAX 64
32
33#define VOF_MAX_PATH 256
34#define VOF_MAX_SETPROPLEN 2048
35#define VOF_MAX_METHODLEN 256
36#define VOF_MAX_FORTHCODE 256
37#define VOF_VTY_BUF_SIZE 256
38
39typedef struct {
40 uint64_t start;
41 uint64_t size;
42} OfClaimed;
43
44typedef struct {
45 char *path;
46 uint32_t phandle;
47} OfInstance;
48
49static int readstr(hwaddr pa, char *buf, int size)
50{
51 if (VOF_MEM_READ(pa, buf, size) != MEMTX_OK) {
52 return -1;
53 }
54 if (strnlen(buf, size) == size) {
55 buf[size - 1] = '\0';
56 trace_vof_error_str_truncated(buf, size);
57 return -1;
58 }
59 return 0;
60}
61
62static bool cmpservice(const char *s, unsigned nargs, unsigned nret,
63 const char *s1, unsigned nargscheck, unsigned nretcheck)
64{
65 if (strcmp(s, s1)) {
66 return false;
67 }
68 if ((nargscheck && (nargs != nargscheck)) ||
69 (nretcheck && (nret != nretcheck))) {
70 trace_vof_error_param(s, nargscheck, nretcheck, nargs, nret);
71 return false;
72 }
73
74 return true;
75}
76
77static void prop_format(char *tval, int tlen, const void *prop, int len)
78{
79 int i;
80 const unsigned char *c;
81 char *t;
82 const char bin[] = "...";
83
84 for (i = 0, c = prop; i < len; ++i, ++c) {
85 if (*c == '\0' && i == len - 1) {
86 strncpy(tval, prop, tlen - 1);
87 return;
88 }
89 if (*c < 0x20 || *c >= 0x80) {
90 break;
91 }
92 }
93
94 for (i = 0, c = prop, t = tval; i < len; ++i, ++c) {
95 if (t >= tval + tlen - sizeof(bin) - 1 - 2 - 1) {
96 strcpy(t, bin);
97 return;
98 }
99 if (i && i % 4 == 0 && i != len - 1) {
100 strcat(t, " ");
101 ++t;
102 }
103 t += sprintf(t, "%02X", *c & 0xFF);
104 }
105}
106
107static int get_path(const void *fdt, int offset, char *buf, int len)
108{
109 int ret;
110
111 ret = fdt_get_path(fdt, offset, buf, len - 1);
112 if (ret < 0) {
113 return ret;
114 }
115
116 buf[len - 1] = '\0';
117
118 return strlen(buf) + 1;
119}
120
121static int phandle_to_path(const void *fdt, uint32_t ph, char *buf, int len)
122{
123 int ret;
124
125 ret = fdt_node_offset_by_phandle(fdt, ph);
126 if (ret < 0) {
127 return ret;
128 }
129
130 return get_path(fdt, ret, buf, len);
131}
132
133static int path_offset(const void *fdt, const char *path)
134{
135 g_autofree char *p = NULL;
136 char *at;
137
138
139
140
141
142
143
144
145 p = g_strdup(path);
146 for (at = strchr(p, '@'); at && *at; ) {
147 if (*at == '/') {
148 at = strchr(at, '@');
149 } else {
150 *at = tolower(*at);
151 ++at;
152 }
153 }
154
155 return fdt_path_offset(fdt, p);
156}
157
158static uint32_t vof_finddevice(const void *fdt, uint32_t nodeaddr)
159{
160 char fullnode[VOF_MAX_PATH];
161 uint32_t ret = PROM_ERROR;
162 int offset;
163
164 if (readstr(nodeaddr, fullnode, sizeof(fullnode))) {
165 return (uint32_t) ret;
166 }
167
168 offset = path_offset(fdt, fullnode);
169 if (offset >= 0) {
170 ret = fdt_get_phandle(fdt, offset);
171 }
172 trace_vof_finddevice(fullnode, ret);
173 return ret;
174}
175
176static const void *getprop(const void *fdt, int nodeoff, const char *propname,
177 int *proplen, bool *write0)
178{
179 const char *unit, *prop;
180 const void *ret = fdt_getprop(fdt, nodeoff, propname, proplen);
181
182 if (ret) {
183 if (write0) {
184 *write0 = false;
185 }
186 return ret;
187 }
188
189 if (strcmp(propname, "name")) {
190 return NULL;
191 }
192
193
194
195
196 prop = fdt_get_name(fdt, nodeoff, proplen);
197 if (!prop) {
198 *proplen = 0;
199 return NULL;
200 }
201
202 unit = memchr(prop, '@', *proplen);
203 if (unit) {
204 *proplen = unit - prop;
205 }
206 *proplen += 1;
207
208
209
210
211
212 if (write0) {
213 *write0 = true;
214 }
215 return prop;
216}
217
218static uint32_t vof_getprop(const void *fdt, uint32_t nodeph, uint32_t pname,
219 uint32_t valaddr, uint32_t vallen)
220{
221 char propname[OF_PROPNAME_LEN_MAX + 1];
222 uint32_t ret = 0;
223 int proplen = 0;
224 const void *prop;
225 char trval[64] = "";
226 int nodeoff = fdt_node_offset_by_phandle(fdt, nodeph);
227 bool write0;
228
229 if (nodeoff < 0) {
230 return PROM_ERROR;
231 }
232 if (readstr(pname, propname, sizeof(propname))) {
233 return PROM_ERROR;
234 }
235 prop = getprop(fdt, nodeoff, propname, &proplen, &write0);
236 if (prop) {
237 const char zero = 0;
238 int cb = MIN(proplen, vallen);
239
240 if (VOF_MEM_WRITE(valaddr, prop, cb) != MEMTX_OK ||
241
242 (write0 &&
243 cb == proplen &&
244 VOF_MEM_WRITE(valaddr + cb - 1, &zero, 1) != MEMTX_OK)) {
245 ret = PROM_ERROR;
246 } else {
247
248
249
250
251
252 ret = proplen;
253
254 if (trace_event_get_state(TRACE_VOF_GETPROP) &&
255 qemu_loglevel_mask(LOG_TRACE)) {
256 prop_format(trval, sizeof(trval), prop, ret);
257 }
258 }
259 } else {
260 ret = PROM_ERROR;
261 }
262 trace_vof_getprop(nodeph, propname, ret, trval);
263
264 return ret;
265}
266
267static uint32_t vof_getproplen(const void *fdt, uint32_t nodeph, uint32_t pname)
268{
269 char propname[OF_PROPNAME_LEN_MAX + 1];
270 uint32_t ret = 0;
271 int proplen = 0;
272 const void *prop;
273 int nodeoff = fdt_node_offset_by_phandle(fdt, nodeph);
274
275 if (nodeoff < 0) {
276 return PROM_ERROR;
277 }
278 if (readstr(pname, propname, sizeof(propname))) {
279 return PROM_ERROR;
280 }
281 prop = getprop(fdt, nodeoff, propname, &proplen, NULL);
282 if (prop) {
283 ret = proplen;
284 } else {
285 ret = PROM_ERROR;
286 }
287 trace_vof_getproplen(nodeph, propname, ret);
288
289 return ret;
290}
291
292static uint32_t vof_setprop(MachineState *ms, void *fdt, Vof *vof,
293 uint32_t nodeph, uint32_t pname,
294 uint32_t valaddr, uint32_t vallen)
295{
296 char propname[OF_PROPNAME_LEN_MAX + 1] = "";
297 uint32_t ret = PROM_ERROR;
298 int offset, rc;
299 char trval[64] = "";
300 char nodepath[VOF_MAX_PATH] = "";
301 Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
302 VofMachineIfClass *vmc;
303 g_autofree char *val = NULL;
304
305 if (vallen > VOF_MAX_SETPROPLEN) {
306 goto trace_exit;
307 }
308 if (readstr(pname, propname, sizeof(propname))) {
309 goto trace_exit;
310 }
311 offset = fdt_node_offset_by_phandle(fdt, nodeph);
312 if (offset < 0) {
313 goto trace_exit;
314 }
315 rc = get_path(fdt, offset, nodepath, sizeof(nodepath));
316 if (rc <= 0) {
317 goto trace_exit;
318 }
319
320 val = g_malloc0(vallen);
321 if (VOF_MEM_READ(valaddr, val, vallen) != MEMTX_OK) {
322 goto trace_exit;
323 }
324
325 if (!vmo) {
326 goto trace_exit;
327 }
328
329 vmc = VOF_MACHINE_GET_CLASS(vmo);
330 if (!vmc->setprop || !vmc->setprop(ms, nodepath, propname, val, vallen)) {
331 goto trace_exit;
332 }
333
334 rc = fdt_setprop(fdt, offset, propname, val, vallen);
335 if (rc) {
336 goto trace_exit;
337 }
338
339 if (trace_event_get_state(TRACE_VOF_SETPROP) &&
340 qemu_loglevel_mask(LOG_TRACE)) {
341 prop_format(trval, sizeof(trval), val, vallen);
342 }
343 ret = vallen;
344
345trace_exit:
346 trace_vof_setprop(nodeph, propname, trval, vallen, ret);
347
348 return ret;
349}
350
351static uint32_t vof_nextprop(const void *fdt, uint32_t phandle,
352 uint32_t prevaddr, uint32_t nameaddr)
353{
354 int offset, nodeoff = fdt_node_offset_by_phandle(fdt, phandle);
355 char prev[OF_PROPNAME_LEN_MAX + 1];
356 const char *tmp;
357
358 if (readstr(prevaddr, prev, sizeof(prev))) {
359 return PROM_ERROR;
360 }
361
362 fdt_for_each_property_offset(offset, fdt, nodeoff) {
363 if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) {
364 return 0;
365 }
366 if (prev[0] == '\0' || strcmp(prev, tmp) == 0) {
367 if (prev[0] != '\0') {
368 offset = fdt_next_property_offset(fdt, offset);
369 if (offset < 0) {
370 return 0;
371 }
372 }
373 if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) {
374 return 0;
375 }
376
377 if (VOF_MEM_WRITE(nameaddr, tmp, strlen(tmp) + 1) != MEMTX_OK) {
378 return PROM_ERROR;
379 }
380 return 1;
381 }
382 }
383
384 return 0;
385}
386
387static uint32_t vof_peer(const void *fdt, uint32_t phandle)
388{
389 uint32_t ret = 0;
390 int rc;
391
392 if (phandle == 0) {
393 rc = fdt_path_offset(fdt, "/");
394 } else {
395 rc = fdt_next_subnode(fdt, fdt_node_offset_by_phandle(fdt, phandle));
396 }
397
398 if (rc >= 0) {
399 ret = fdt_get_phandle(fdt, rc);
400 }
401
402 return ret;
403}
404
405static uint32_t vof_child(const void *fdt, uint32_t phandle)
406{
407 uint32_t ret = 0;
408 int rc = fdt_first_subnode(fdt, fdt_node_offset_by_phandle(fdt, phandle));
409
410 if (rc >= 0) {
411 ret = fdt_get_phandle(fdt, rc);
412 }
413
414 return ret;
415}
416
417static uint32_t vof_parent(const void *fdt, uint32_t phandle)
418{
419 uint32_t ret = 0;
420 int rc = fdt_parent_offset(fdt, fdt_node_offset_by_phandle(fdt, phandle));
421
422 if (rc >= 0) {
423 ret = fdt_get_phandle(fdt, rc);
424 }
425
426 return ret;
427}
428
429static uint32_t vof_do_open(void *fdt, Vof *vof, int offset, const char *path)
430{
431 uint32_t ret = PROM_ERROR;
432 OfInstance *inst = NULL;
433
434 if (vof->of_instance_last == 0xFFFFFFFF) {
435
436 goto trace_exit;
437 }
438
439 inst = g_new0(OfInstance, 1);
440 inst->phandle = fdt_get_phandle(fdt, offset);
441 g_assert(inst->phandle);
442 ++vof->of_instance_last;
443
444 inst->path = g_strdup(path);
445 g_hash_table_insert(vof->of_instances,
446 GINT_TO_POINTER(vof->of_instance_last),
447 inst);
448 ret = vof->of_instance_last;
449
450trace_exit:
451 trace_vof_open(path, inst ? inst->phandle : 0, ret);
452
453 return ret;
454}
455
456uint32_t vof_client_open_store(void *fdt, Vof *vof, const char *nodename,
457 const char *prop, const char *path)
458{
459 int offset, node = fdt_path_offset(fdt, nodename);
460 uint32_t inst;
461
462 offset = fdt_path_offset(fdt, path);
463 if (offset < 0) {
464 trace_vof_error_unknown_path(path);
465 return PROM_ERROR;
466 }
467
468 inst = vof_do_open(fdt, vof, offset, path);
469
470 return fdt_setprop_cell(fdt, node, prop, inst) >= 0 ? 0 : PROM_ERROR;
471}
472
473static uint32_t vof_open(void *fdt, Vof *vof, uint32_t pathaddr)
474{
475 char path[VOF_MAX_PATH];
476 int offset;
477
478 if (readstr(pathaddr, path, sizeof(path))) {
479 return PROM_ERROR;
480 }
481
482 offset = path_offset(fdt, path);
483 if (offset < 0) {
484 trace_vof_error_unknown_path(path);
485 return PROM_ERROR;
486 }
487
488 return vof_do_open(fdt, vof, offset, path);
489}
490
491static void vof_close(Vof *vof, uint32_t ihandle)
492{
493 if (!g_hash_table_remove(vof->of_instances, GINT_TO_POINTER(ihandle))) {
494 trace_vof_error_unknown_ihandle_close(ihandle);
495 }
496}
497
498static uint32_t vof_instance_to_package(Vof *vof, uint32_t ihandle)
499{
500 gpointer instp = g_hash_table_lookup(vof->of_instances,
501 GINT_TO_POINTER(ihandle));
502 uint32_t ret = PROM_ERROR;
503
504 if (instp) {
505 ret = ((OfInstance *)instp)->phandle;
506 }
507 trace_vof_instance_to_package(ihandle, ret);
508
509 return ret;
510}
511
512static uint32_t vof_package_to_path(const void *fdt, uint32_t phandle,
513 uint32_t buf, uint32_t len)
514{
515 int rc;
516 char tmp[VOF_MAX_PATH] = "";
517
518 rc = phandle_to_path(fdt, phandle, tmp, sizeof(tmp));
519 if (rc > 0) {
520 if (VOF_MEM_WRITE(buf, tmp, rc) != MEMTX_OK) {
521 rc = -1;
522 }
523 }
524
525 trace_vof_package_to_path(phandle, tmp, rc);
526
527 return rc > 0 ? (uint32_t)rc : PROM_ERROR;
528}
529
530static uint32_t vof_instance_to_path(void *fdt, Vof *vof, uint32_t ihandle,
531 uint32_t buf, uint32_t len)
532{
533 int rc = -1;
534 uint32_t phandle = vof_instance_to_package(vof, ihandle);
535 char tmp[VOF_MAX_PATH] = "";
536
537 if (phandle != -1) {
538 rc = phandle_to_path(fdt, phandle, tmp, sizeof(tmp));
539 if (rc > 0) {
540 if (VOF_MEM_WRITE(buf, tmp, rc) != MEMTX_OK) {
541 rc = -1;
542 }
543 }
544 }
545 trace_vof_instance_to_path(ihandle, phandle, tmp, rc);
546
547 return rc > 0 ? (uint32_t)rc : PROM_ERROR;
548}
549
550static uint32_t vof_write(Vof *vof, uint32_t ihandle, uint32_t buf,
551 uint32_t len)
552{
553 char tmp[VOF_VTY_BUF_SIZE];
554 unsigned cb;
555 OfInstance *inst = (OfInstance *)
556 g_hash_table_lookup(vof->of_instances, GINT_TO_POINTER(ihandle));
557
558 if (!inst) {
559 trace_vof_error_write(ihandle);
560 return PROM_ERROR;
561 }
562
563 for ( ; len > 0; len -= cb) {
564 cb = MIN(len, sizeof(tmp) - 1);
565 if (VOF_MEM_READ(buf, tmp, cb) != MEMTX_OK) {
566 return PROM_ERROR;
567 }
568
569
570 if (trace_event_get_state(TRACE_VOF_WRITE) &&
571 qemu_loglevel_mask(LOG_TRACE)) {
572 tmp[cb] = '\0';
573 trace_vof_write(ihandle, cb, tmp);
574 }
575 }
576
577 return len;
578}
579
580static void vof_claimed_dump(GArray *claimed)
581{
582 int i;
583 OfClaimed c;
584
585 if (trace_event_get_state(TRACE_VOF_CLAIMED) &&
586 qemu_loglevel_mask(LOG_TRACE)) {
587
588 for (i = 0; i < claimed->len; ++i) {
589 c = g_array_index(claimed, OfClaimed, i);
590 trace_vof_claimed(c.start, c.start + c.size, c.size);
591 }
592 }
593}
594
595static bool vof_claim_avail(GArray *claimed, uint64_t virt, uint64_t size)
596{
597 int i;
598 OfClaimed c;
599
600 for (i = 0; i < claimed->len; ++i) {
601 c = g_array_index(claimed, OfClaimed, i);
602 if (ranges_overlap(c.start, c.size, virt, size)) {
603 return false;
604 }
605 }
606
607 return true;
608}
609
610static void vof_claim_add(GArray *claimed, uint64_t virt, uint64_t size)
611{
612 OfClaimed newclaim;
613
614 newclaim.start = virt;
615 newclaim.size = size;
616 g_array_append_val(claimed, newclaim);
617}
618
619static gint of_claimed_compare_func(gconstpointer a, gconstpointer b)
620{
621 return ((OfClaimed *)a)->start - ((OfClaimed *)b)->start;
622}
623
624static void vof_dt_memory_available(void *fdt, GArray *claimed, uint64_t base)
625{
626 int i, n, offset, proplen = 0, sc, ac;
627 target_ulong mem0_end;
628 const uint8_t *mem0_reg;
629 g_autofree uint8_t *avail = NULL;
630 uint8_t *availcur;
631
632 if (!fdt || !claimed) {
633 return;
634 }
635
636 offset = fdt_path_offset(fdt, "/");
637 _FDT(offset);
638 ac = fdt_address_cells(fdt, offset);
639 g_assert(ac == 1 || ac == 2);
640 sc = fdt_size_cells(fdt, offset);
641 g_assert(sc == 1 || sc == 2);
642
643 offset = fdt_path_offset(fdt, "/memory@0");
644 _FDT(offset);
645
646 mem0_reg = fdt_getprop(fdt, offset, "reg", &proplen);
647 g_assert(mem0_reg && proplen == sizeof(uint32_t) * (ac + sc));
648 if (sc == 2) {
649 mem0_end = be64_to_cpu(*(uint64_t *)(mem0_reg + sizeof(uint32_t) * ac));
650 } else {
651 mem0_end = be32_to_cpu(*(uint32_t *)(mem0_reg + sizeof(uint32_t) * ac));
652 }
653
654 g_array_sort(claimed, of_claimed_compare_func);
655 vof_claimed_dump(claimed);
656
657
658
659
660
661 g_assert(claimed->len && (g_array_index(claimed, OfClaimed, 0).start == 0));
662
663 avail = g_malloc0(sizeof(uint32_t) * (ac + sc) * claimed->len);
664 for (i = 0, n = 0, availcur = avail; i < claimed->len; ++i) {
665 OfClaimed c = g_array_index(claimed, OfClaimed, i);
666 uint64_t start, size;
667
668 start = c.start + c.size;
669 if (i < claimed->len - 1) {
670 OfClaimed cn = g_array_index(claimed, OfClaimed, i + 1);
671
672 size = cn.start - start;
673 } else {
674 size = mem0_end - start;
675 }
676
677 if (ac == 2) {
678 *(uint64_t *) availcur = cpu_to_be64(start);
679 } else {
680 *(uint32_t *) availcur = cpu_to_be32(start);
681 }
682 availcur += sizeof(uint32_t) * ac;
683 if (sc == 2) {
684 *(uint64_t *) availcur = cpu_to_be64(size);
685 } else {
686 *(uint32_t *) availcur = cpu_to_be32(size);
687 }
688 availcur += sizeof(uint32_t) * sc;
689
690 if (size) {
691 trace_vof_avail(c.start + c.size, c.start + c.size + size, size);
692 ++n;
693 }
694 }
695 _FDT((fdt_setprop(fdt, offset, "available", avail, availcur - avail)));
696}
697
698
699
700
701
702
703
704
705
706uint64_t vof_claim(Vof *vof, uint64_t virt, uint64_t size,
707 uint64_t align)
708{
709 uint64_t ret;
710
711 if (size == 0) {
712 ret = -1;
713 } else if (align == 0) {
714 if (!vof_claim_avail(vof->claimed, virt, size)) {
715 ret = -1;
716 } else {
717 ret = virt;
718 }
719 } else {
720 vof->claimed_base = QEMU_ALIGN_UP(vof->claimed_base, align);
721 while (1) {
722 if (vof->claimed_base >= vof->top_addr) {
723 error_report("Out of RMA memory for the OF client");
724 return -1;
725 }
726 if (vof_claim_avail(vof->claimed, vof->claimed_base, size)) {
727 break;
728 }
729 vof->claimed_base += size;
730 }
731 ret = vof->claimed_base;
732 }
733
734 if (ret != -1) {
735 vof->claimed_base = MAX(vof->claimed_base, ret + size);
736 vof_claim_add(vof->claimed, ret, size);
737 }
738 trace_vof_claim(virt, size, align, ret);
739
740 return ret;
741}
742
743static uint32_t vof_release(Vof *vof, uint64_t virt, uint64_t size)
744{
745 uint32_t ret = PROM_ERROR;
746 int i;
747 GArray *claimed = vof->claimed;
748 OfClaimed c;
749
750 for (i = 0; i < claimed->len; ++i) {
751 c = g_array_index(claimed, OfClaimed, i);
752 if (c.start == virt && c.size == size) {
753 g_array_remove_index(claimed, i);
754 ret = 0;
755 break;
756 }
757 }
758
759 trace_vof_release(virt, size, ret);
760
761 return ret;
762}
763
764static void vof_instantiate_rtas(Error **errp)
765{
766 error_setg(errp, "The firmware should have instantiated RTAS");
767}
768
769static uint32_t vof_call_method(MachineState *ms, Vof *vof, uint32_t methodaddr,
770 uint32_t ihandle, uint32_t param1,
771 uint32_t param2, uint32_t param3,
772 uint32_t param4, uint32_t *ret2)
773{
774 uint32_t ret = PROM_ERROR;
775 char method[VOF_MAX_METHODLEN] = "";
776 OfInstance *inst;
777
778 if (!ihandle) {
779 goto trace_exit;
780 }
781
782 inst = (OfInstance *)g_hash_table_lookup(vof->of_instances,
783 GINT_TO_POINTER(ihandle));
784 if (!inst) {
785 goto trace_exit;
786 }
787
788 if (readstr(methodaddr, method, sizeof(method))) {
789 goto trace_exit;
790 }
791
792 if (strcmp(inst->path, "/") == 0) {
793 if (strcmp(method, "ibm,client-architecture-support") == 0) {
794 Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
795
796 if (vmo) {
797 VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo);
798
799 g_assert(vmc->client_architecture_support);
800 ret = (uint32_t)vmc->client_architecture_support(ms, first_cpu,
801 param1);
802 }
803
804 *ret2 = 0;
805 }
806 } else if (strcmp(inst->path, "/rtas") == 0) {
807 if (strcmp(method, "instantiate-rtas") == 0) {
808 vof_instantiate_rtas(&error_fatal);
809 ret = 0;
810 *ret2 = param1;
811 }
812 } else {
813 trace_vof_error_unknown_method(method);
814 }
815
816trace_exit:
817 trace_vof_method(ihandle, method, param1, ret, *ret2);
818
819 return ret;
820}
821
822static uint32_t vof_call_interpret(uint32_t cmdaddr, uint32_t param1,
823 uint32_t param2, uint32_t *ret2)
824{
825 uint32_t ret = PROM_ERROR;
826 char cmd[VOF_MAX_FORTHCODE] = "";
827
828
829 readstr(cmdaddr, cmd, sizeof(cmd));
830 trace_vof_interpret(cmd, param1, param2, ret, *ret2);
831
832 return ret;
833}
834
835static void vof_quiesce(MachineState *ms, void *fdt, Vof *vof)
836{
837 Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
838
839 int rc = fdt_pack(fdt);
840
841 assert(rc == 0);
842
843 if (vmo) {
844 VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo);
845
846 if (vmc->quiesce) {
847 vmc->quiesce(ms);
848 }
849 }
850
851 vof_claimed_dump(vof->claimed);
852}
853
854static uint32_t vof_client_handle(MachineState *ms, void *fdt, Vof *vof,
855 const char *service,
856 uint32_t *args, unsigned nargs,
857 uint32_t *rets, unsigned nrets)
858{
859 uint32_t ret = 0;
860
861
862#define cmpserv(s, a, r) \
863 cmpservice(service, nargs, nrets, (s), (a), (r))
864
865 if (cmpserv("finddevice", 1, 1)) {
866 ret = vof_finddevice(fdt, args[0]);
867 } else if (cmpserv("getprop", 4, 1)) {
868 ret = vof_getprop(fdt, args[0], args[1], args[2], args[3]);
869 } else if (cmpserv("getproplen", 2, 1)) {
870 ret = vof_getproplen(fdt, args[0], args[1]);
871 } else if (cmpserv("setprop", 4, 1)) {
872 ret = vof_setprop(ms, fdt, vof, args[0], args[1], args[2], args[3]);
873 } else if (cmpserv("nextprop", 3, 1)) {
874 ret = vof_nextprop(fdt, args[0], args[1], args[2]);
875 } else if (cmpserv("peer", 1, 1)) {
876 ret = vof_peer(fdt, args[0]);
877 } else if (cmpserv("child", 1, 1)) {
878 ret = vof_child(fdt, args[0]);
879 } else if (cmpserv("parent", 1, 1)) {
880 ret = vof_parent(fdt, args[0]);
881 } else if (cmpserv("open", 1, 1)) {
882 ret = vof_open(fdt, vof, args[0]);
883 } else if (cmpserv("close", 1, 0)) {
884 vof_close(vof, args[0]);
885 } else if (cmpserv("instance-to-package", 1, 1)) {
886 ret = vof_instance_to_package(vof, args[0]);
887 } else if (cmpserv("package-to-path", 3, 1)) {
888 ret = vof_package_to_path(fdt, args[0], args[1], args[2]);
889 } else if (cmpserv("instance-to-path", 3, 1)) {
890 ret = vof_instance_to_path(fdt, vof, args[0], args[1], args[2]);
891 } else if (cmpserv("write", 3, 1)) {
892 ret = vof_write(vof, args[0], args[1], args[2]);
893 } else if (cmpserv("claim", 3, 1)) {
894 uint64_t ret64 = vof_claim(vof, args[0], args[1], args[2]);
895
896 if (ret64 < 0x100000000UL) {
897 vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base);
898 ret = (uint32_t)ret64;
899 } else {
900 if (ret64 != -1) {
901 vof_release(vof, ret, args[1]);
902 }
903 ret = PROM_ERROR;
904 }
905 } else if (cmpserv("release", 2, 0)) {
906 ret = vof_release(vof, args[0], args[1]);
907 if (ret != PROM_ERROR) {
908 vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base);
909 }
910 } else if (cmpserv("call-method", 0, 0)) {
911 ret = vof_call_method(ms, vof, args[0], args[1], args[2], args[3],
912 args[4], args[5], rets);
913 } else if (cmpserv("interpret", 0, 0)) {
914 ret = vof_call_interpret(args[0], args[1], args[2], rets);
915 } else if (cmpserv("milliseconds", 0, 1)) {
916 ret = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
917 } else if (cmpserv("quiesce", 0, 0)) {
918 vof_quiesce(ms, fdt, vof);
919 } else if (cmpserv("exit", 0, 0)) {
920 error_report("Stopped as the VM requested \"exit\"");
921 vm_stop(RUN_STATE_PAUSED);
922 } else {
923 trace_vof_error_unknown_service(service, nargs, nrets);
924 ret = -1;
925 }
926
927#undef cmpserv
928
929 return ret;
930}
931
932
933struct prom_args {
934 uint32_t service;
935 uint32_t nargs;
936 uint32_t nret;
937 uint32_t args[10];
938} QEMU_PACKED;
939
940int vof_client_call(MachineState *ms, Vof *vof, void *fdt,
941 target_ulong args_real)
942{
943 struct prom_args args_be;
944 uint32_t args[ARRAY_SIZE(args_be.args)];
945 uint32_t rets[ARRAY_SIZE(args_be.args)] = { 0 }, ret;
946 char service[64];
947 unsigned nargs, nret, i;
948
949 if (VOF_MEM_READ(args_real, &args_be, sizeof(args_be)) != MEMTX_OK) {
950 return -EINVAL;
951 }
952 nargs = be32_to_cpu(args_be.nargs);
953 if (nargs >= ARRAY_SIZE(args_be.args)) {
954 return -EINVAL;
955 }
956
957 if (VOF_MEM_READ(be32_to_cpu(args_be.service), service, sizeof(service)) !=
958 MEMTX_OK) {
959 return -EINVAL;
960 }
961 if (strnlen(service, sizeof(service)) == sizeof(service)) {
962
963 return -EINVAL;
964 }
965
966 for (i = 0; i < nargs; ++i) {
967 args[i] = be32_to_cpu(args_be.args[i]);
968 }
969
970 nret = be32_to_cpu(args_be.nret);
971 if (nret > ARRAY_SIZE(args_be.args) - nargs) {
972 return -EINVAL;
973 }
974 ret = vof_client_handle(ms, fdt, vof, service, args, nargs, rets, nret);
975 if (!nret) {
976 return 0;
977 }
978
979
980 args_be.args[nargs] = cpu_to_be32(ret);
981 for (i = 1; i < nret; ++i) {
982 args_be.args[nargs + i] = cpu_to_be32(rets[i - 1]);
983 }
984
985 if (VOF_MEM_WRITE(args_real + offsetof(struct prom_args, args[nargs]),
986 args_be.args + nargs, sizeof(args_be.args[0]) * nret) !=
987 MEMTX_OK) {
988 return -EINVAL;
989 }
990
991 return 0;
992}
993
994static void vof_instance_free(gpointer data)
995{
996 OfInstance *inst = (OfInstance *)data;
997
998 g_free(inst->path);
999 g_free(inst);
1000}
1001
1002void vof_init(Vof *vof, uint64_t top_addr, Error **errp)
1003{
1004 vof_cleanup(vof);
1005
1006 vof->of_instances = g_hash_table_new_full(g_direct_hash, g_direct_equal,
1007 NULL, vof_instance_free);
1008 vof->claimed = g_array_new(false, false, sizeof(OfClaimed));
1009
1010
1011 vof->top_addr = MIN(top_addr, 4 * GiB);
1012 if (vof_claim(vof, 0, vof->fw_size, 0) == -1) {
1013 error_setg(errp, "Memory for firmware is in use");
1014 }
1015}
1016
1017void vof_cleanup(Vof *vof)
1018{
1019 if (vof->claimed) {
1020 g_array_unref(vof->claimed);
1021 }
1022 if (vof->of_instances) {
1023 g_hash_table_unref(vof->of_instances);
1024 }
1025 vof->claimed = NULL;
1026 vof->of_instances = NULL;
1027}
1028
1029void vof_build_dt(void *fdt, Vof *vof)
1030{
1031 uint32_t phandle = fdt_get_max_phandle(fdt);
1032 int offset, proplen = 0;
1033 const void *prop;
1034
1035
1036 for (offset = fdt_next_node(fdt, -1, NULL);
1037 offset >= 0;
1038 offset = fdt_next_node(fdt, offset, NULL)) {
1039 prop = fdt_getprop(fdt, offset, "phandle", &proplen);
1040 if (prop) {
1041 continue;
1042 }
1043 ++phandle;
1044 _FDT(fdt_setprop_cell(fdt, offset, "phandle", phandle));
1045 }
1046
1047 vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base);
1048}
1049
1050static const TypeInfo vof_machine_if_info = {
1051 .name = TYPE_VOF_MACHINE_IF,
1052 .parent = TYPE_INTERFACE,
1053 .class_size = sizeof(VofMachineIfClass),
1054};
1055
1056static void vof_machine_if_register_types(void)
1057{
1058 type_register_static(&vof_machine_if_info);
1059}
1060type_init(vof_machine_if_register_types)
1061