xref: /openbmc/qemu/hw/ppc/vof.c (revision 93b799fafd9170da3a79a533ea6f73a18de82e22)
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/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   * OF 1275 "nextprop" description suggests is it 32 bytes max but
29   * LoPAPR defines "ibm,query-interrupt-source-number" which is 33 chars long.
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  
39  typedef struct {
40      uint64_t start;
41      uint64_t size;
42  } OfClaimed;
43  
44  typedef struct {
45      char *path; /* the path used to open the instance */
46      uint32_t phandle;
47  } OfInstance;
48  
readstr(hwaddr pa,char * buf,int size)49  static 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  
cmpservice(const char * s,unsigned nargs,unsigned nret,const char * s1,unsigned nargscheck,unsigned nretcheck)62  static 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  
prop_format(char * tval,int tlen,const void * prop,int len)77  static 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  
get_path(const void * fdt,int offset,char * buf,int len)107  static 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  
phandle_to_path(const void * fdt,uint32_t ph,char * buf,int len)121  static 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  
path_offset(const void * fdt,const char * path)133  static int path_offset(const void *fdt, const char *path)
134  {
135      g_autofree char *p = NULL;
136      char *at;
137  
138      /*
139       * https://www.devicetree.org/open-firmware/bindings/ppc/release/ppc-2_1.html#HDR16
140       *
141       * "Conversion from numeric representation to text representation shall use
142       * the lower case forms of the hexadecimal digits in the range a..f,
143       * suppressing leading zeros".
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  
vof_finddevice(const void * fdt,uint32_t nodeaddr)158  static 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  
getprop(const void * fdt,int nodeoff,const char * propname,int * proplen,bool * write0)176  static 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       * We return a value for "name" from path if queried but property does not
194       * exist. @proplen does not include the unit part in this case.
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       * Since it might be cut at "@" and there will be no trailing zero
210       * in the prop buffer, tell the caller to write zero at the end.
211       */
212      if (write0) {
213          *write0 = true;
214      }
215      return prop;
216  }
217  
vof_getprop(const void * fdt,uint32_t nodeph,uint32_t pname,uint32_t valaddr,uint32_t vallen)218  static 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              /* if that was "name" with a unit address, overwrite '@' with '0' */
242              (write0 &&
243               cb == proplen &&
244               VOF_MEM_WRITE(valaddr + cb - 1, &zero, 1) != MEMTX_OK)) {
245              ret = PROM_ERROR;
246          } else {
247              /*
248               * OF1275 says:
249               * "Size is either the actual size of the property, or -1 if name
250               * does not exist", hence returning proplen instead of cb.
251               */
252              ret = proplen;
253              /* Do not format a value if tracepoint is silent, for performance */
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  
vof_getproplen(const void * fdt,uint32_t nodeph,uint32_t pname)267  static 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  
vof_setprop(MachineState * ms,void * fdt,Vof * vof,uint32_t nodeph,uint32_t pname,uint32_t valaddr,uint32_t vallen)292  static 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  
345  trace_exit:
346      trace_vof_setprop(nodeph, propname, trval, vallen, ret);
347  
348      return ret;
349  }
350  
vof_nextprop(const void * fdt,uint32_t phandle,uint32_t prevaddr,uint32_t nameaddr)351  static 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  
vof_peer(const void * fdt,uint32_t phandle)387  static 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  
vof_child(const void * fdt,uint32_t phandle)405  static 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  
vof_parent(const void * fdt,uint32_t phandle)417  static 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  
vof_do_open(void * fdt,Vof * vof,int offset,const char * path)429  static 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          /* We do not recycle ihandles yet */
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  
450  trace_exit:
451      trace_vof_open(path, inst ? inst->phandle : 0, ret);
452  
453      return ret;
454  }
455  
vof_client_open_store(void * fdt,Vof * vof,const char * nodename,const char * prop,const char * path)456  uint32_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  
vof_open(void * fdt,Vof * vof,uint32_t pathaddr)473  static 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  
vof_close(Vof * vof,uint32_t ihandle)491  static 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  
vof_instance_to_package(Vof * vof,uint32_t ihandle)498  static 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  
vof_package_to_path(const void * fdt,uint32_t phandle,uint32_t buf,uint32_t len)512  static 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  
vof_instance_to_path(void * fdt,Vof * vof,uint32_t ihandle,uint32_t buf,uint32_t len)530  static 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  
vof_write(Vof * vof,uint32_t ihandle,uint32_t buf,uint32_t len)550  static 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          /* FIXME: there is no backend(s) yet so just call a trace */
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  
vof_claimed_dump(GArray * claimed)580  static 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  
vof_claim_avail(GArray * claimed,uint64_t virt,uint64_t size)595  static 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  
vof_claim_add(GArray * claimed,uint64_t virt,uint64_t size)610  static 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  
of_claimed_compare_func(gconstpointer a,gconstpointer b)619  static gint of_claimed_compare_func(gconstpointer a, gconstpointer b)
620  {
621      return ((OfClaimed *)a)->start - ((OfClaimed *)b)->start;
622  }
623  
vof_dt_memory_available(void * fdt,GArray * claimed,uint64_t base)624  static 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 = ldq_be_p(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       * VOF resides in the first page so we do not need to check if there is
659       * available memory before the first claimed block
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   * OF1275:
700   * "Allocates size bytes of memory. If align is zero, the allocated range
701   * begins at the virtual address virt. Otherwise, an aligned address is
702   * automatically chosen and the input argument virt is ignored".
703   *
704   * In other words, exactly one of @virt and @align is non-zero.
705   */
vof_claim(Vof * vof,uint64_t virt,uint64_t size,uint64_t align)706  uint64_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  
vof_release(Vof * vof,uint64_t virt,uint64_t size)743  static 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  
vof_instantiate_rtas(Error ** errp)764  static void vof_instantiate_rtas(Error **errp)
765  {
766      error_setg(errp, "The firmware should have instantiated RTAS");
767  }
768  
vof_call_method(MachineState * ms,Vof * vof,uint32_t methodaddr,uint32_t ihandle,uint32_t param1,uint32_t param2,uint32_t param3,uint32_t param4,uint32_t * ret2)769  static 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; /* rtas-base */
811          }
812      } else {
813          trace_vof_error_unknown_method(method);
814      }
815  
816  trace_exit:
817      trace_vof_method(ihandle, method, param1, ret, *ret2);
818  
819      return ret;
820  }
821  
vof_call_interpret(uint32_t cmdaddr,uint32_t param1,uint32_t param2,uint32_t * ret2)822  static 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      /* No interpret implemented so just call a trace */
829      readstr(cmdaddr, cmd, sizeof(cmd));
830      trace_vof_interpret(cmd, param1, param2, ret, *ret2);
831  
832      return ret;
833  }
834  
vof_quiesce(MachineState * ms,void * fdt,Vof * vof)835  static void vof_quiesce(MachineState *ms, void *fdt, Vof *vof)
836  {
837      Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
838      /* After "quiesce", no change is expected to the FDT, pack FDT to ensure */
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  
vof_client_handle(MachineState * ms,void * fdt,Vof * vof,const char * service,uint32_t * args,unsigned nargs,uint32_t * rets,unsigned nrets)854  static 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      /* @nrets includes the value which this function returns */
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  /* Defined as Big Endian */
933  struct prom_args {
934      uint32_t service;
935      uint32_t nargs;
936      uint32_t nret;
937      uint32_t args[10];
938  } QEMU_PACKED;
939  
vof_client_call(MachineState * ms,Vof * vof,void * fdt,target_ulong args_real)940  int 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          /* Too long service name */
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      /* @nrets includes the value which this function returns */
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  
vof_instance_free(gpointer data)994  static void vof_instance_free(gpointer data)
995  {
996      OfInstance *inst = (OfInstance *)data;
997  
998      g_free(inst->path);
999      g_free(inst);
1000  }
1001  
vof_init(Vof * vof,uint64_t top_addr,Error ** errp)1002  void 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      /* Keep allocations in 32bit as CLI ABI can only return cells==32bit */
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  
vof_cleanup(Vof * vof)1017  void 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      vof->of_instance_last = 0;
1028      vof->claimed_base = 0;
1029  }
1030  
vof_build_dt(void * fdt,Vof * vof)1031  void vof_build_dt(void *fdt, Vof *vof)
1032  {
1033      uint32_t phandle = fdt_get_max_phandle(fdt);
1034      int offset, proplen = 0;
1035      const void *prop;
1036  
1037      /* Assign phandles to nodes without predefined phandles (like XICS/XIVE) */
1038      for (offset = fdt_next_node(fdt, -1, NULL);
1039           offset >= 0;
1040           offset = fdt_next_node(fdt, offset, NULL)) {
1041          prop = fdt_getprop(fdt, offset, "phandle", &proplen);
1042          if (prop) {
1043              continue;
1044          }
1045          ++phandle;
1046          _FDT(fdt_setprop_cell(fdt, offset, "phandle", phandle));
1047      }
1048  
1049      vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base);
1050  }
1051  
1052  static const TypeInfo vof_machine_if_info = {
1053      .name = TYPE_VOF_MACHINE_IF,
1054      .parent = TYPE_INTERFACE,
1055      .class_size = sizeof(VofMachineIfClass),
1056  };
1057  
vof_machine_if_register_types(void)1058  static void vof_machine_if_register_types(void)
1059  {
1060      type_register_static(&vof_machine_if_info);
1061  }
1062  type_init(vof_machine_if_register_types)
1063