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