There are error paths which do not initialize propname but the trace_exit label prints it anyway. This initializes the problem string. Spotted by Coverity CID 1487241. Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com> Message-Id: <20220406045013.3610172-1-aik@ozlabs.ru> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
		
			
				
	
	
		
			1061 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1061 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * QEMU PowerPC Virtual Open Firmware.
 | 
						|
 *
 | 
						|
 * This implements client interface from OpenFirmware IEEE1275 on the QEMU
 | 
						|
 * side to leave only a very basic firmware in the VM.
 | 
						|
 *
 | 
						|
 * Copyright (c) 2021 IBM Corporation.
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
 */
 | 
						|
 | 
						|
#include "qemu/osdep.h"
 | 
						|
#include "qemu/timer.h"
 | 
						|
#include "qemu/range.h"
 | 
						|
#include "qemu/units.h"
 | 
						|
#include "qemu/log.h"
 | 
						|
#include "qapi/error.h"
 | 
						|
#include "exec/address-spaces.h"
 | 
						|
#include "hw/ppc/vof.h"
 | 
						|
#include "hw/ppc/fdt.h"
 | 
						|
#include "sysemu/runstate.h"
 | 
						|
#include "qom/qom-qobject.h"
 | 
						|
#include "trace.h"
 | 
						|
 | 
						|
#include <libfdt.h>
 | 
						|
 | 
						|
/*
 | 
						|
 * OF 1275 "nextprop" description suggests is it 32 bytes max but
 | 
						|
 * LoPAPR defines "ibm,query-interrupt-source-number" which is 33 chars long.
 | 
						|
 */
 | 
						|
#define OF_PROPNAME_LEN_MAX 64
 | 
						|
 | 
						|
#define VOF_MAX_PATH        256
 | 
						|
#define VOF_MAX_SETPROPLEN  2048
 | 
						|
#define VOF_MAX_METHODLEN   256
 | 
						|
#define VOF_MAX_FORTHCODE   256
 | 
						|
#define VOF_VTY_BUF_SIZE    256
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    uint64_t start;
 | 
						|
    uint64_t size;
 | 
						|
} OfClaimed;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    char *path; /* the path used to open the instance */
 | 
						|
    uint32_t phandle;
 | 
						|
} OfInstance;
 | 
						|
 | 
						|
static int readstr(hwaddr pa, char *buf, int size)
 | 
						|
{
 | 
						|
    if (VOF_MEM_READ(pa, buf, size) != MEMTX_OK) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (strnlen(buf, size) == size) {
 | 
						|
        buf[size - 1] = '\0';
 | 
						|
        trace_vof_error_str_truncated(buf, size);
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static bool cmpservice(const char *s, unsigned nargs, unsigned nret,
 | 
						|
                       const char *s1, unsigned nargscheck, unsigned nretcheck)
 | 
						|
{
 | 
						|
    if (strcmp(s, s1)) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    if ((nargscheck && (nargs != nargscheck)) ||
 | 
						|
        (nretcheck && (nret != nretcheck))) {
 | 
						|
        trace_vof_error_param(s, nargscheck, nretcheck, nargs, nret);
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static void prop_format(char *tval, int tlen, const void *prop, int len)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    const unsigned char *c;
 | 
						|
    char *t;
 | 
						|
    const char bin[] = "...";
 | 
						|
 | 
						|
    for (i = 0, c = prop; i < len; ++i, ++c) {
 | 
						|
        if (*c == '\0' && i == len - 1) {
 | 
						|
            strncpy(tval, prop, tlen - 1);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (*c < 0x20 || *c >= 0x80) {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0, c = prop, t = tval; i < len; ++i, ++c) {
 | 
						|
        if (t >= tval + tlen - sizeof(bin) - 1 - 2 - 1) {
 | 
						|
            strcpy(t, bin);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (i && i % 4 == 0 && i != len - 1) {
 | 
						|
            strcat(t, " ");
 | 
						|
            ++t;
 | 
						|
        }
 | 
						|
        t += sprintf(t, "%02X", *c & 0xFF);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int get_path(const void *fdt, int offset, char *buf, int len)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = fdt_get_path(fdt, offset, buf, len - 1);
 | 
						|
    if (ret < 0) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    buf[len - 1] = '\0';
 | 
						|
 | 
						|
    return strlen(buf) + 1;
 | 
						|
}
 | 
						|
 | 
						|
static int phandle_to_path(const void *fdt, uint32_t ph, char *buf, int len)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = fdt_node_offset_by_phandle(fdt, ph);
 | 
						|
    if (ret < 0) {
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    return get_path(fdt, ret, buf, len);
 | 
						|
}
 | 
						|
 | 
						|
static int path_offset(const void *fdt, const char *path)
 | 
						|
{
 | 
						|
    g_autofree char *p = NULL;
 | 
						|
    char *at;
 | 
						|
 | 
						|
    /*
 | 
						|
     * https://www.devicetree.org/open-firmware/bindings/ppc/release/ppc-2_1.html#HDR16
 | 
						|
     *
 | 
						|
     * "Conversion from numeric representation to text representation shall use
 | 
						|
     * the lower case forms of the hexadecimal digits in the range a..f,
 | 
						|
     * suppressing leading zeros".
 | 
						|
     */
 | 
						|
    p = g_strdup(path);
 | 
						|
    for (at = strchr(p, '@'); at && *at; ) {
 | 
						|
            if (*at == '/') {
 | 
						|
                at = strchr(at, '@');
 | 
						|
            } else {
 | 
						|
                *at = tolower(*at);
 | 
						|
                ++at;
 | 
						|
            }
 | 
						|
    }
 | 
						|
 | 
						|
    return fdt_path_offset(fdt, p);
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_finddevice(const void *fdt, uint32_t nodeaddr)
 | 
						|
{
 | 
						|
    char fullnode[VOF_MAX_PATH];
 | 
						|
    uint32_t ret = PROM_ERROR;
 | 
						|
    int offset;
 | 
						|
 | 
						|
    if (readstr(nodeaddr, fullnode, sizeof(fullnode))) {
 | 
						|
        return (uint32_t) ret;
 | 
						|
    }
 | 
						|
 | 
						|
    offset = path_offset(fdt, fullnode);
 | 
						|
    if (offset >= 0) {
 | 
						|
        ret = fdt_get_phandle(fdt, offset);
 | 
						|
    }
 | 
						|
    trace_vof_finddevice(fullnode, ret);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static const void *getprop(const void *fdt, int nodeoff, const char *propname,
 | 
						|
                           int *proplen, bool *write0)
 | 
						|
{
 | 
						|
    const char *unit, *prop;
 | 
						|
    const void *ret = fdt_getprop(fdt, nodeoff, propname, proplen);
 | 
						|
 | 
						|
    if (ret) {
 | 
						|
        if (write0) {
 | 
						|
            *write0 = false;
 | 
						|
        }
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    if (strcmp(propname, "name")) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    /*
 | 
						|
     * We return a value for "name" from path if queried but property does not
 | 
						|
     * exist. @proplen does not include the unit part in this case.
 | 
						|
     */
 | 
						|
    prop = fdt_get_name(fdt, nodeoff, proplen);
 | 
						|
    if (!prop) {
 | 
						|
        *proplen = 0;
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    unit = memchr(prop, '@', *proplen);
 | 
						|
    if (unit) {
 | 
						|
        *proplen = unit - prop;
 | 
						|
    }
 | 
						|
    *proplen += 1;
 | 
						|
 | 
						|
    /*
 | 
						|
     * Since it might be cut at "@" and there will be no trailing zero
 | 
						|
     * in the prop buffer, tell the caller to write zero at the end.
 | 
						|
     */
 | 
						|
    if (write0) {
 | 
						|
        *write0 = true;
 | 
						|
    }
 | 
						|
    return prop;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_getprop(const void *fdt, uint32_t nodeph, uint32_t pname,
 | 
						|
                            uint32_t valaddr, uint32_t vallen)
 | 
						|
{
 | 
						|
    char propname[OF_PROPNAME_LEN_MAX + 1];
 | 
						|
    uint32_t ret = 0;
 | 
						|
    int proplen = 0;
 | 
						|
    const void *prop;
 | 
						|
    char trval[64] = "";
 | 
						|
    int nodeoff = fdt_node_offset_by_phandle(fdt, nodeph);
 | 
						|
    bool write0;
 | 
						|
 | 
						|
    if (nodeoff < 0) {
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
    if (readstr(pname, propname, sizeof(propname))) {
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
    prop = getprop(fdt, nodeoff, propname, &proplen, &write0);
 | 
						|
    if (prop) {
 | 
						|
        const char zero = 0;
 | 
						|
        int cb = MIN(proplen, vallen);
 | 
						|
 | 
						|
        if (VOF_MEM_WRITE(valaddr, prop, cb) != MEMTX_OK ||
 | 
						|
            /* if that was "name" with a unit address, overwrite '@' with '0' */
 | 
						|
            (write0 &&
 | 
						|
             cb == proplen &&
 | 
						|
             VOF_MEM_WRITE(valaddr + cb - 1, &zero, 1) != MEMTX_OK)) {
 | 
						|
            ret = PROM_ERROR;
 | 
						|
        } else {
 | 
						|
            /*
 | 
						|
             * OF1275 says:
 | 
						|
             * "Size is either the actual size of the property, or -1 if name
 | 
						|
             * does not exist", hence returning proplen instead of cb.
 | 
						|
             */
 | 
						|
            ret = proplen;
 | 
						|
            /* Do not format a value if tracepoint is silent, for performance */
 | 
						|
            if (trace_event_get_state(TRACE_VOF_GETPROP) &&
 | 
						|
                qemu_loglevel_mask(LOG_TRACE)) {
 | 
						|
                prop_format(trval, sizeof(trval), prop, ret);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        ret = PROM_ERROR;
 | 
						|
    }
 | 
						|
    trace_vof_getprop(nodeph, propname, ret, trval);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_getproplen(const void *fdt, uint32_t nodeph, uint32_t pname)
 | 
						|
{
 | 
						|
    char propname[OF_PROPNAME_LEN_MAX + 1];
 | 
						|
    uint32_t ret = 0;
 | 
						|
    int proplen = 0;
 | 
						|
    const void *prop;
 | 
						|
    int nodeoff = fdt_node_offset_by_phandle(fdt, nodeph);
 | 
						|
 | 
						|
    if (nodeoff < 0) {
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
    if (readstr(pname, propname, sizeof(propname))) {
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
    prop = getprop(fdt, nodeoff, propname, &proplen, NULL);
 | 
						|
    if (prop) {
 | 
						|
        ret = proplen;
 | 
						|
    } else {
 | 
						|
        ret = PROM_ERROR;
 | 
						|
    }
 | 
						|
    trace_vof_getproplen(nodeph, propname, ret);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_setprop(MachineState *ms, void *fdt, Vof *vof,
 | 
						|
                            uint32_t nodeph, uint32_t pname,
 | 
						|
                            uint32_t valaddr, uint32_t vallen)
 | 
						|
{
 | 
						|
    char propname[OF_PROPNAME_LEN_MAX + 1] = "";
 | 
						|
    uint32_t ret = PROM_ERROR;
 | 
						|
    int offset, rc;
 | 
						|
    char trval[64] = "";
 | 
						|
    char nodepath[VOF_MAX_PATH] = "";
 | 
						|
    Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
 | 
						|
    VofMachineIfClass *vmc;
 | 
						|
    g_autofree char *val = NULL;
 | 
						|
 | 
						|
    if (vallen > VOF_MAX_SETPROPLEN) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
    if (readstr(pname, propname, sizeof(propname))) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
    offset = fdt_node_offset_by_phandle(fdt, nodeph);
 | 
						|
    if (offset < 0) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
    rc = get_path(fdt, offset, nodepath, sizeof(nodepath));
 | 
						|
    if (rc <= 0) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    val = g_malloc0(vallen);
 | 
						|
    if (VOF_MEM_READ(valaddr, val, vallen) != MEMTX_OK) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!vmo) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    vmc = VOF_MACHINE_GET_CLASS(vmo);
 | 
						|
    if (!vmc->setprop || !vmc->setprop(ms, nodepath, propname, val, vallen)) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    rc = fdt_setprop(fdt, offset, propname, val, vallen);
 | 
						|
    if (rc) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (trace_event_get_state(TRACE_VOF_SETPROP) &&
 | 
						|
        qemu_loglevel_mask(LOG_TRACE)) {
 | 
						|
        prop_format(trval, sizeof(trval), val, vallen);
 | 
						|
    }
 | 
						|
    ret = vallen;
 | 
						|
 | 
						|
trace_exit:
 | 
						|
    trace_vof_setprop(nodeph, propname, trval, vallen, ret);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_nextprop(const void *fdt, uint32_t phandle,
 | 
						|
                             uint32_t prevaddr, uint32_t nameaddr)
 | 
						|
{
 | 
						|
    int offset, nodeoff = fdt_node_offset_by_phandle(fdt, phandle);
 | 
						|
    char prev[OF_PROPNAME_LEN_MAX + 1];
 | 
						|
    const char *tmp;
 | 
						|
 | 
						|
    if (readstr(prevaddr, prev, sizeof(prev))) {
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    fdt_for_each_property_offset(offset, fdt, nodeoff) {
 | 
						|
        if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        if (prev[0] == '\0' || strcmp(prev, tmp) == 0) {
 | 
						|
            if (prev[0] != '\0') {
 | 
						|
                offset = fdt_next_property_offset(fdt, offset);
 | 
						|
                if (offset < 0) {
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) {
 | 
						|
                return 0;
 | 
						|
            }
 | 
						|
 | 
						|
            if (VOF_MEM_WRITE(nameaddr, tmp, strlen(tmp) + 1) != MEMTX_OK) {
 | 
						|
                return PROM_ERROR;
 | 
						|
            }
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_peer(const void *fdt, uint32_t phandle)
 | 
						|
{
 | 
						|
    uint32_t ret = 0;
 | 
						|
    int rc;
 | 
						|
 | 
						|
    if (phandle == 0) {
 | 
						|
        rc = fdt_path_offset(fdt, "/");
 | 
						|
    } else {
 | 
						|
        rc = fdt_next_subnode(fdt, fdt_node_offset_by_phandle(fdt, phandle));
 | 
						|
    }
 | 
						|
 | 
						|
    if (rc >= 0) {
 | 
						|
        ret = fdt_get_phandle(fdt, rc);
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_child(const void *fdt, uint32_t phandle)
 | 
						|
{
 | 
						|
    uint32_t ret = 0;
 | 
						|
    int rc = fdt_first_subnode(fdt, fdt_node_offset_by_phandle(fdt, phandle));
 | 
						|
 | 
						|
    if (rc >= 0) {
 | 
						|
        ret = fdt_get_phandle(fdt, rc);
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_parent(const void *fdt, uint32_t phandle)
 | 
						|
{
 | 
						|
    uint32_t ret = 0;
 | 
						|
    int rc = fdt_parent_offset(fdt, fdt_node_offset_by_phandle(fdt, phandle));
 | 
						|
 | 
						|
    if (rc >= 0) {
 | 
						|
        ret = fdt_get_phandle(fdt, rc);
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_do_open(void *fdt, Vof *vof, int offset, const char *path)
 | 
						|
{
 | 
						|
    uint32_t ret = PROM_ERROR;
 | 
						|
    OfInstance *inst = NULL;
 | 
						|
 | 
						|
    if (vof->of_instance_last == 0xFFFFFFFF) {
 | 
						|
        /* We do not recycle ihandles yet */
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    inst = g_new0(OfInstance, 1);
 | 
						|
    inst->phandle = fdt_get_phandle(fdt, offset);
 | 
						|
    g_assert(inst->phandle);
 | 
						|
    ++vof->of_instance_last;
 | 
						|
 | 
						|
    inst->path = g_strdup(path);
 | 
						|
    g_hash_table_insert(vof->of_instances,
 | 
						|
                        GINT_TO_POINTER(vof->of_instance_last),
 | 
						|
                        inst);
 | 
						|
    ret = vof->of_instance_last;
 | 
						|
 | 
						|
trace_exit:
 | 
						|
    trace_vof_open(path, inst ? inst->phandle : 0, ret);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t vof_client_open_store(void *fdt, Vof *vof, const char *nodename,
 | 
						|
                               const char *prop, const char *path)
 | 
						|
{
 | 
						|
    int offset, node = fdt_path_offset(fdt, nodename);
 | 
						|
    uint32_t inst;
 | 
						|
 | 
						|
    offset = fdt_path_offset(fdt, path);
 | 
						|
    if (offset < 0) {
 | 
						|
        trace_vof_error_unknown_path(path);
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    inst = vof_do_open(fdt, vof, offset, path);
 | 
						|
 | 
						|
    return fdt_setprop_cell(fdt, node, prop, inst) >= 0 ? 0 : PROM_ERROR;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_open(void *fdt, Vof *vof, uint32_t pathaddr)
 | 
						|
{
 | 
						|
    char path[VOF_MAX_PATH];
 | 
						|
    int offset;
 | 
						|
 | 
						|
    if (readstr(pathaddr, path, sizeof(path))) {
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    offset = path_offset(fdt, path);
 | 
						|
    if (offset < 0) {
 | 
						|
        trace_vof_error_unknown_path(path);
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    return vof_do_open(fdt, vof, offset, path);
 | 
						|
}
 | 
						|
 | 
						|
static void vof_close(Vof *vof, uint32_t ihandle)
 | 
						|
{
 | 
						|
    if (!g_hash_table_remove(vof->of_instances, GINT_TO_POINTER(ihandle))) {
 | 
						|
        trace_vof_error_unknown_ihandle_close(ihandle);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_instance_to_package(Vof *vof, uint32_t ihandle)
 | 
						|
{
 | 
						|
    gpointer instp = g_hash_table_lookup(vof->of_instances,
 | 
						|
                                         GINT_TO_POINTER(ihandle));
 | 
						|
    uint32_t ret = PROM_ERROR;
 | 
						|
 | 
						|
    if (instp) {
 | 
						|
        ret = ((OfInstance *)instp)->phandle;
 | 
						|
    }
 | 
						|
    trace_vof_instance_to_package(ihandle, ret);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_package_to_path(const void *fdt, uint32_t phandle,
 | 
						|
                                    uint32_t buf, uint32_t len)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
    char tmp[VOF_MAX_PATH] = "";
 | 
						|
 | 
						|
    rc = phandle_to_path(fdt, phandle, tmp, sizeof(tmp));
 | 
						|
    if (rc > 0) {
 | 
						|
        if (VOF_MEM_WRITE(buf, tmp, rc) != MEMTX_OK) {
 | 
						|
            rc = -1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    trace_vof_package_to_path(phandle, tmp, rc);
 | 
						|
 | 
						|
    return rc > 0 ? (uint32_t)rc : PROM_ERROR;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_instance_to_path(void *fdt, Vof *vof, uint32_t ihandle,
 | 
						|
                                     uint32_t buf, uint32_t len)
 | 
						|
{
 | 
						|
    int rc = -1;
 | 
						|
    uint32_t phandle = vof_instance_to_package(vof, ihandle);
 | 
						|
    char tmp[VOF_MAX_PATH] = "";
 | 
						|
 | 
						|
    if (phandle != -1) {
 | 
						|
        rc = phandle_to_path(fdt, phandle, tmp, sizeof(tmp));
 | 
						|
        if (rc > 0) {
 | 
						|
            if (VOF_MEM_WRITE(buf, tmp, rc) != MEMTX_OK) {
 | 
						|
                rc = -1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    trace_vof_instance_to_path(ihandle, phandle, tmp, rc);
 | 
						|
 | 
						|
    return rc > 0 ? (uint32_t)rc : PROM_ERROR;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_write(Vof *vof, uint32_t ihandle, uint32_t buf,
 | 
						|
                          uint32_t len)
 | 
						|
{
 | 
						|
    char tmp[VOF_VTY_BUF_SIZE];
 | 
						|
    unsigned cb;
 | 
						|
    OfInstance *inst = (OfInstance *)
 | 
						|
        g_hash_table_lookup(vof->of_instances, GINT_TO_POINTER(ihandle));
 | 
						|
 | 
						|
    if (!inst) {
 | 
						|
        trace_vof_error_write(ihandle);
 | 
						|
        return PROM_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
    for ( ; len > 0; len -= cb) {
 | 
						|
        cb = MIN(len, sizeof(tmp) - 1);
 | 
						|
        if (VOF_MEM_READ(buf, tmp, cb) != MEMTX_OK) {
 | 
						|
            return PROM_ERROR;
 | 
						|
        }
 | 
						|
 | 
						|
        /* FIXME: there is no backend(s) yet so just call a trace */
 | 
						|
        if (trace_event_get_state(TRACE_VOF_WRITE) &&
 | 
						|
            qemu_loglevel_mask(LOG_TRACE)) {
 | 
						|
            tmp[cb] = '\0';
 | 
						|
            trace_vof_write(ihandle, cb, tmp);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return len;
 | 
						|
}
 | 
						|
 | 
						|
static void vof_claimed_dump(GArray *claimed)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    OfClaimed c;
 | 
						|
 | 
						|
    if (trace_event_get_state(TRACE_VOF_CLAIMED) &&
 | 
						|
        qemu_loglevel_mask(LOG_TRACE)) {
 | 
						|
 | 
						|
        for (i = 0; i < claimed->len; ++i) {
 | 
						|
            c = g_array_index(claimed, OfClaimed, i);
 | 
						|
            trace_vof_claimed(c.start, c.start + c.size, c.size);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static bool vof_claim_avail(GArray *claimed, uint64_t virt, uint64_t size)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    OfClaimed c;
 | 
						|
 | 
						|
    for (i = 0; i < claimed->len; ++i) {
 | 
						|
        c = g_array_index(claimed, OfClaimed, i);
 | 
						|
        if (ranges_overlap(c.start, c.size, virt, size)) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static void vof_claim_add(GArray *claimed, uint64_t virt, uint64_t size)
 | 
						|
{
 | 
						|
    OfClaimed newclaim;
 | 
						|
 | 
						|
    newclaim.start = virt;
 | 
						|
    newclaim.size = size;
 | 
						|
    g_array_append_val(claimed, newclaim);
 | 
						|
}
 | 
						|
 | 
						|
static gint of_claimed_compare_func(gconstpointer a, gconstpointer b)
 | 
						|
{
 | 
						|
    return ((OfClaimed *)a)->start - ((OfClaimed *)b)->start;
 | 
						|
}
 | 
						|
 | 
						|
static void vof_dt_memory_available(void *fdt, GArray *claimed, uint64_t base)
 | 
						|
{
 | 
						|
    int i, n, offset, proplen = 0, sc, ac;
 | 
						|
    target_ulong mem0_end;
 | 
						|
    const uint8_t *mem0_reg;
 | 
						|
    g_autofree uint8_t *avail = NULL;
 | 
						|
    uint8_t *availcur;
 | 
						|
 | 
						|
    if (!fdt || !claimed) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    offset = fdt_path_offset(fdt, "/");
 | 
						|
    _FDT(offset);
 | 
						|
    ac = fdt_address_cells(fdt, offset);
 | 
						|
    g_assert(ac == 1 || ac == 2);
 | 
						|
    sc = fdt_size_cells(fdt, offset);
 | 
						|
    g_assert(sc == 1 || sc == 2);
 | 
						|
 | 
						|
    offset = fdt_path_offset(fdt, "/memory@0");
 | 
						|
    _FDT(offset);
 | 
						|
 | 
						|
    mem0_reg = fdt_getprop(fdt, offset, "reg", &proplen);
 | 
						|
    g_assert(mem0_reg && proplen == sizeof(uint32_t) * (ac + sc));
 | 
						|
    if (sc == 2) {
 | 
						|
        mem0_end = be64_to_cpu(*(uint64_t *)(mem0_reg + sizeof(uint32_t) * ac));
 | 
						|
    } else {
 | 
						|
        mem0_end = be32_to_cpu(*(uint32_t *)(mem0_reg + sizeof(uint32_t) * ac));
 | 
						|
    }
 | 
						|
 | 
						|
    g_array_sort(claimed, of_claimed_compare_func);
 | 
						|
    vof_claimed_dump(claimed);
 | 
						|
 | 
						|
    /*
 | 
						|
     * VOF resides in the first page so we do not need to check if there is
 | 
						|
     * available memory before the first claimed block
 | 
						|
     */
 | 
						|
    g_assert(claimed->len && (g_array_index(claimed, OfClaimed, 0).start == 0));
 | 
						|
 | 
						|
    avail = g_malloc0(sizeof(uint32_t) * (ac + sc) * claimed->len);
 | 
						|
    for (i = 0, n = 0, availcur = avail; i < claimed->len; ++i) {
 | 
						|
        OfClaimed c = g_array_index(claimed, OfClaimed, i);
 | 
						|
        uint64_t start, size;
 | 
						|
 | 
						|
        start = c.start + c.size;
 | 
						|
        if (i < claimed->len - 1) {
 | 
						|
            OfClaimed cn = g_array_index(claimed, OfClaimed, i + 1);
 | 
						|
 | 
						|
            size = cn.start - start;
 | 
						|
        } else {
 | 
						|
            size = mem0_end - start;
 | 
						|
        }
 | 
						|
 | 
						|
        if (ac == 2) {
 | 
						|
            *(uint64_t *) availcur = cpu_to_be64(start);
 | 
						|
        } else {
 | 
						|
            *(uint32_t *) availcur = cpu_to_be32(start);
 | 
						|
        }
 | 
						|
        availcur += sizeof(uint32_t) * ac;
 | 
						|
        if (sc == 2) {
 | 
						|
            *(uint64_t *) availcur = cpu_to_be64(size);
 | 
						|
        } else {
 | 
						|
            *(uint32_t *) availcur = cpu_to_be32(size);
 | 
						|
        }
 | 
						|
        availcur += sizeof(uint32_t) * sc;
 | 
						|
 | 
						|
        if (size) {
 | 
						|
            trace_vof_avail(c.start + c.size, c.start + c.size + size, size);
 | 
						|
            ++n;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _FDT((fdt_setprop(fdt, offset, "available", avail, availcur - avail)));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * OF1275:
 | 
						|
 * "Allocates size bytes of memory. If align is zero, the allocated range
 | 
						|
 * begins at the virtual address virt. Otherwise, an aligned address is
 | 
						|
 * automatically chosen and the input argument virt is ignored".
 | 
						|
 *
 | 
						|
 * In other words, exactly one of @virt and @align is non-zero.
 | 
						|
 */
 | 
						|
uint64_t vof_claim(Vof *vof, uint64_t virt, uint64_t size,
 | 
						|
                   uint64_t align)
 | 
						|
{
 | 
						|
    uint64_t ret;
 | 
						|
 | 
						|
    if (size == 0) {
 | 
						|
        ret = -1;
 | 
						|
    } else if (align == 0) {
 | 
						|
        if (!vof_claim_avail(vof->claimed, virt, size)) {
 | 
						|
            ret = -1;
 | 
						|
        } else {
 | 
						|
            ret = virt;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        vof->claimed_base = QEMU_ALIGN_UP(vof->claimed_base, align);
 | 
						|
        while (1) {
 | 
						|
            if (vof->claimed_base >= vof->top_addr) {
 | 
						|
                error_report("Out of RMA memory for the OF client");
 | 
						|
                return -1;
 | 
						|
            }
 | 
						|
            if (vof_claim_avail(vof->claimed, vof->claimed_base, size)) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            vof->claimed_base += size;
 | 
						|
        }
 | 
						|
        ret = vof->claimed_base;
 | 
						|
    }
 | 
						|
 | 
						|
    if (ret != -1) {
 | 
						|
        vof->claimed_base = MAX(vof->claimed_base, ret + size);
 | 
						|
        vof_claim_add(vof->claimed, ret, size);
 | 
						|
    }
 | 
						|
    trace_vof_claim(virt, size, align, ret);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_release(Vof *vof, uint64_t virt, uint64_t size)
 | 
						|
{
 | 
						|
    uint32_t ret = PROM_ERROR;
 | 
						|
    int i;
 | 
						|
    GArray *claimed = vof->claimed;
 | 
						|
    OfClaimed c;
 | 
						|
 | 
						|
    for (i = 0; i < claimed->len; ++i) {
 | 
						|
        c = g_array_index(claimed, OfClaimed, i);
 | 
						|
        if (c.start == virt && c.size == size) {
 | 
						|
            g_array_remove_index(claimed, i);
 | 
						|
            ret = 0;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    trace_vof_release(virt, size, ret);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void vof_instantiate_rtas(Error **errp)
 | 
						|
{
 | 
						|
    error_setg(errp, "The firmware should have instantiated RTAS");
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t 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)
 | 
						|
{
 | 
						|
    uint32_t ret = PROM_ERROR;
 | 
						|
    char method[VOF_MAX_METHODLEN] = "";
 | 
						|
    OfInstance *inst;
 | 
						|
 | 
						|
    if (!ihandle) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    inst = (OfInstance *)g_hash_table_lookup(vof->of_instances,
 | 
						|
                                             GINT_TO_POINTER(ihandle));
 | 
						|
    if (!inst) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (readstr(methodaddr, method, sizeof(method))) {
 | 
						|
        goto trace_exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (strcmp(inst->path, "/") == 0) {
 | 
						|
        if (strcmp(method, "ibm,client-architecture-support") == 0) {
 | 
						|
            Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
 | 
						|
 | 
						|
            if (vmo) {
 | 
						|
                VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo);
 | 
						|
 | 
						|
                g_assert(vmc->client_architecture_support);
 | 
						|
                ret = (uint32_t)vmc->client_architecture_support(ms, first_cpu,
 | 
						|
                                                                 param1);
 | 
						|
            }
 | 
						|
 | 
						|
            *ret2 = 0;
 | 
						|
        }
 | 
						|
    } else if (strcmp(inst->path, "/rtas") == 0) {
 | 
						|
        if (strcmp(method, "instantiate-rtas") == 0) {
 | 
						|
            vof_instantiate_rtas(&error_fatal);
 | 
						|
            ret = 0;
 | 
						|
            *ret2 = param1; /* rtas-base */
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        trace_vof_error_unknown_method(method);
 | 
						|
    }
 | 
						|
 | 
						|
trace_exit:
 | 
						|
    trace_vof_method(ihandle, method, param1, ret, *ret2);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_call_interpret(uint32_t cmdaddr, uint32_t param1,
 | 
						|
                                   uint32_t param2, uint32_t *ret2)
 | 
						|
{
 | 
						|
    uint32_t ret = PROM_ERROR;
 | 
						|
    char cmd[VOF_MAX_FORTHCODE] = "";
 | 
						|
 | 
						|
    /* No interpret implemented so just call a trace */
 | 
						|
    readstr(cmdaddr, cmd, sizeof(cmd));
 | 
						|
    trace_vof_interpret(cmd, param1, param2, ret, *ret2);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void vof_quiesce(MachineState *ms, void *fdt, Vof *vof)
 | 
						|
{
 | 
						|
    Object *vmo = object_dynamic_cast(OBJECT(ms), TYPE_VOF_MACHINE_IF);
 | 
						|
    /* After "quiesce", no change is expected to the FDT, pack FDT to ensure */
 | 
						|
    int rc = fdt_pack(fdt);
 | 
						|
 | 
						|
    assert(rc == 0);
 | 
						|
 | 
						|
    if (vmo) {
 | 
						|
        VofMachineIfClass *vmc = VOF_MACHINE_GET_CLASS(vmo);
 | 
						|
 | 
						|
        if (vmc->quiesce) {
 | 
						|
            vmc->quiesce(ms);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    vof_claimed_dump(vof->claimed);
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t vof_client_handle(MachineState *ms, void *fdt, Vof *vof,
 | 
						|
                                  const char *service,
 | 
						|
                                  uint32_t *args, unsigned nargs,
 | 
						|
                                  uint32_t *rets, unsigned nrets)
 | 
						|
{
 | 
						|
    uint32_t ret = 0;
 | 
						|
 | 
						|
    /* @nrets includes the value which this function returns */
 | 
						|
#define cmpserv(s, a, r) \
 | 
						|
    cmpservice(service, nargs, nrets, (s), (a), (r))
 | 
						|
 | 
						|
    if (cmpserv("finddevice", 1, 1)) {
 | 
						|
        ret = vof_finddevice(fdt, args[0]);
 | 
						|
    } else if (cmpserv("getprop", 4, 1)) {
 | 
						|
        ret = vof_getprop(fdt, args[0], args[1], args[2], args[3]);
 | 
						|
    } else if (cmpserv("getproplen", 2, 1)) {
 | 
						|
        ret = vof_getproplen(fdt, args[0], args[1]);
 | 
						|
    } else if (cmpserv("setprop", 4, 1)) {
 | 
						|
        ret = vof_setprop(ms, fdt, vof, args[0], args[1], args[2], args[3]);
 | 
						|
    } else if (cmpserv("nextprop", 3, 1)) {
 | 
						|
        ret = vof_nextprop(fdt, args[0], args[1], args[2]);
 | 
						|
    } else if (cmpserv("peer", 1, 1)) {
 | 
						|
        ret = vof_peer(fdt, args[0]);
 | 
						|
    } else if (cmpserv("child", 1, 1)) {
 | 
						|
        ret = vof_child(fdt, args[0]);
 | 
						|
    } else if (cmpserv("parent", 1, 1)) {
 | 
						|
        ret = vof_parent(fdt, args[0]);
 | 
						|
    } else if (cmpserv("open", 1, 1)) {
 | 
						|
        ret = vof_open(fdt, vof, args[0]);
 | 
						|
    } else if (cmpserv("close", 1, 0)) {
 | 
						|
        vof_close(vof, args[0]);
 | 
						|
    } else if (cmpserv("instance-to-package", 1, 1)) {
 | 
						|
        ret = vof_instance_to_package(vof, args[0]);
 | 
						|
    } else if (cmpserv("package-to-path", 3, 1)) {
 | 
						|
        ret = vof_package_to_path(fdt, args[0], args[1], args[2]);
 | 
						|
    } else if (cmpserv("instance-to-path", 3, 1)) {
 | 
						|
        ret = vof_instance_to_path(fdt, vof, args[0], args[1], args[2]);
 | 
						|
    } else if (cmpserv("write", 3, 1)) {
 | 
						|
        ret = vof_write(vof, args[0], args[1], args[2]);
 | 
						|
    } else if (cmpserv("claim", 3, 1)) {
 | 
						|
        uint64_t ret64 = vof_claim(vof, args[0], args[1], args[2]);
 | 
						|
 | 
						|
        if (ret64 < 0x100000000UL) {
 | 
						|
            vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base);
 | 
						|
            ret = (uint32_t)ret64;
 | 
						|
        } else {
 | 
						|
            if (ret64 != -1) {
 | 
						|
                vof_release(vof, ret, args[1]);
 | 
						|
            }
 | 
						|
            ret = PROM_ERROR;
 | 
						|
        }
 | 
						|
    } else if (cmpserv("release", 2, 0)) {
 | 
						|
        ret = vof_release(vof, args[0], args[1]);
 | 
						|
        if (ret != PROM_ERROR) {
 | 
						|
            vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base);
 | 
						|
        }
 | 
						|
    } else if (cmpserv("call-method", 0, 0)) {
 | 
						|
        ret = vof_call_method(ms, vof, args[0], args[1], args[2], args[3],
 | 
						|
                              args[4], args[5], rets);
 | 
						|
    } else if (cmpserv("interpret", 0, 0)) {
 | 
						|
        ret = vof_call_interpret(args[0], args[1], args[2], rets);
 | 
						|
    } else if (cmpserv("milliseconds", 0, 1)) {
 | 
						|
        ret = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
 | 
						|
    } else if (cmpserv("quiesce", 0, 0)) {
 | 
						|
        vof_quiesce(ms, fdt, vof);
 | 
						|
    } else if (cmpserv("exit", 0, 0)) {
 | 
						|
        error_report("Stopped as the VM requested \"exit\"");
 | 
						|
        vm_stop(RUN_STATE_PAUSED);
 | 
						|
    } else {
 | 
						|
        trace_vof_error_unknown_service(service, nargs, nrets);
 | 
						|
        ret = -1;
 | 
						|
    }
 | 
						|
 | 
						|
#undef cmpserv
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* Defined as Big Endian */
 | 
						|
struct prom_args {
 | 
						|
    uint32_t service;
 | 
						|
    uint32_t nargs;
 | 
						|
    uint32_t nret;
 | 
						|
    uint32_t args[10];
 | 
						|
} QEMU_PACKED;
 | 
						|
 | 
						|
int vof_client_call(MachineState *ms, Vof *vof, void *fdt,
 | 
						|
                    target_ulong args_real)
 | 
						|
{
 | 
						|
    struct prom_args args_be;
 | 
						|
    uint32_t args[ARRAY_SIZE(args_be.args)];
 | 
						|
    uint32_t rets[ARRAY_SIZE(args_be.args)] = { 0 }, ret;
 | 
						|
    char service[64];
 | 
						|
    unsigned nargs, nret, i;
 | 
						|
 | 
						|
    if (VOF_MEM_READ(args_real, &args_be, sizeof(args_be)) != MEMTX_OK) {
 | 
						|
        return -EINVAL;
 | 
						|
    }
 | 
						|
    nargs = be32_to_cpu(args_be.nargs);
 | 
						|
    if (nargs >= ARRAY_SIZE(args_be.args)) {
 | 
						|
        return -EINVAL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (VOF_MEM_READ(be32_to_cpu(args_be.service), service, sizeof(service)) !=
 | 
						|
        MEMTX_OK) {
 | 
						|
        return -EINVAL;
 | 
						|
    }
 | 
						|
    if (strnlen(service, sizeof(service)) == sizeof(service)) {
 | 
						|
        /* Too long service name */
 | 
						|
        return -EINVAL;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < nargs; ++i) {
 | 
						|
        args[i] = be32_to_cpu(args_be.args[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    nret = be32_to_cpu(args_be.nret);
 | 
						|
    if (nret > ARRAY_SIZE(args_be.args) - nargs) {
 | 
						|
        return -EINVAL;
 | 
						|
    }
 | 
						|
    ret = vof_client_handle(ms, fdt, vof, service, args, nargs, rets, nret);
 | 
						|
    if (!nret) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    /* @nrets includes the value which this function returns */
 | 
						|
    args_be.args[nargs] = cpu_to_be32(ret);
 | 
						|
    for (i = 1; i < nret; ++i) {
 | 
						|
        args_be.args[nargs + i] = cpu_to_be32(rets[i - 1]);
 | 
						|
    }
 | 
						|
 | 
						|
    if (VOF_MEM_WRITE(args_real + offsetof(struct prom_args, args[nargs]),
 | 
						|
                      args_be.args + nargs, sizeof(args_be.args[0]) * nret) !=
 | 
						|
        MEMTX_OK) {
 | 
						|
        return -EINVAL;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void vof_instance_free(gpointer data)
 | 
						|
{
 | 
						|
    OfInstance *inst = (OfInstance *)data;
 | 
						|
 | 
						|
    g_free(inst->path);
 | 
						|
    g_free(inst);
 | 
						|
}
 | 
						|
 | 
						|
void vof_init(Vof *vof, uint64_t top_addr, Error **errp)
 | 
						|
{
 | 
						|
    vof_cleanup(vof);
 | 
						|
 | 
						|
    vof->of_instances = g_hash_table_new_full(g_direct_hash, g_direct_equal,
 | 
						|
                                              NULL, vof_instance_free);
 | 
						|
    vof->claimed = g_array_new(false, false, sizeof(OfClaimed));
 | 
						|
 | 
						|
    /* Keep allocations in 32bit as CLI ABI can only return cells==32bit */
 | 
						|
    vof->top_addr = MIN(top_addr, 4 * GiB);
 | 
						|
    if (vof_claim(vof, 0, vof->fw_size, 0) == -1) {
 | 
						|
        error_setg(errp, "Memory for firmware is in use");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void vof_cleanup(Vof *vof)
 | 
						|
{
 | 
						|
    if (vof->claimed) {
 | 
						|
        g_array_unref(vof->claimed);
 | 
						|
    }
 | 
						|
    if (vof->of_instances) {
 | 
						|
        g_hash_table_unref(vof->of_instances);
 | 
						|
    }
 | 
						|
    vof->claimed = NULL;
 | 
						|
    vof->of_instances = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void vof_build_dt(void *fdt, Vof *vof)
 | 
						|
{
 | 
						|
    uint32_t phandle = fdt_get_max_phandle(fdt);
 | 
						|
    int offset, proplen = 0;
 | 
						|
    const void *prop;
 | 
						|
 | 
						|
    /* Assign phandles to nodes without predefined phandles (like XICS/XIVE) */
 | 
						|
    for (offset = fdt_next_node(fdt, -1, NULL);
 | 
						|
         offset >= 0;
 | 
						|
         offset = fdt_next_node(fdt, offset, NULL)) {
 | 
						|
        prop = fdt_getprop(fdt, offset, "phandle", &proplen);
 | 
						|
        if (prop) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        ++phandle;
 | 
						|
        _FDT(fdt_setprop_cell(fdt, offset, "phandle", phandle));
 | 
						|
    }
 | 
						|
 | 
						|
    vof_dt_memory_available(fdt, vof->claimed, vof->claimed_base);
 | 
						|
}
 | 
						|
 | 
						|
static const TypeInfo vof_machine_if_info = {
 | 
						|
    .name = TYPE_VOF_MACHINE_IF,
 | 
						|
    .parent = TYPE_INTERFACE,
 | 
						|
    .class_size = sizeof(VofMachineIfClass),
 | 
						|
};
 | 
						|
 | 
						|
static void vof_machine_if_register_types(void)
 | 
						|
{
 | 
						|
    type_register_static(&vof_machine_if_info);
 | 
						|
}
 | 
						|
type_init(vof_machine_if_register_types)
 |