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