root/src/system/kernel/platform/openfirmware/openfirmware.cpp
/*
 * Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
 * Copyright 2019, Adrien Destugues, pulkomandy@pulkomandy.tk
 * Distributed under the terms of the MIT License.
 */

#include <platform/openfirmware/openfirmware.h>

#include <stdarg.h>


// OpenFirmware entry function
static intptr_t (*gCallOpenFirmware)(void *) = 0;
intptr_t gChosen;


status_t
of_init(intptr_t (*openFirmwareEntry)(void *))
{
        gCallOpenFirmware = openFirmwareEntry;

        gChosen = of_finddevice("/chosen");
        if (gChosen == OF_FAILED)
                return B_ERROR;

        return B_OK;
}


intptr_t
of_call_client_function(const char *method, intptr_t numArgs,
        intptr_t numReturns, ...)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                void            *args[10];
        } args = {method, numArgs, numReturns};
        va_list list;
        int i;

        // iterate over all arguments and copy them into the
        // structure passed over to the OpenFirmware

        va_start(list, numReturns);
        for (i = 0; i < numArgs; i++) {
                // copy args
                args.args[i] = (void *)va_arg(list, void *);
        }
        for (i = numArgs; i < numArgs + numReturns; i++) {
                // clear return values
                args.args[i] = NULL;
        }

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        if (numReturns > 0) {
                // copy return values over to the provided location

                for (i = numArgs; i < numArgs + numReturns; i++) {
                        void **store = va_arg(list, void **);
                        if (store)
                                *store = args.args[i];
                }
        }
        va_end(list);

        return 0;
}


intptr_t
of_interpret(const char *command, intptr_t numArgs, intptr_t numReturns, ...)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                        // "IN: [string] cmd, stack_arg1, ..., stack_argP
                        // OUT: catch-result, stack_result1, ..., stack_resultQ
                        // [...]
                        // An implementation shall allow at least six stack_arg and six
                        // stack_result items."
                const char      *command;
                void            *args[13];
        } args = {"interpret", numArgs + 1, numReturns + 1, command};
        va_list list;
        int i;

        // iterate over all arguments and copy them into the
        // structure passed over to the OpenFirmware

        va_start(list, numReturns);
        for (i = 0; i < numArgs; i++) {
                // copy args
                args.args[i] = (void *)va_arg(list, void *);
        }
        for (i = numArgs; i < numArgs + numReturns + 1; i++) {
                // clear return values
                args.args[i] = NULL;
        }

        // args.args[numArgs] is the "catch-result" return value
        if (gCallOpenFirmware(&args) == OF_FAILED || args.args[numArgs])
                return OF_FAILED;

        if (numReturns > 0) {
                // copy return values over to the provided location

                for (i = numArgs + 1; i < numArgs + 1 + numReturns; i++) {
                        void **store = va_arg(list, void **);
                        if (store)
                                *store = args.args[i];
                }
        }
        va_end(list);

        return 0;
}


intptr_t
of_call_method(uint32_t handle, const char *method, intptr_t numArgs,
        intptr_t numReturns, ...)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                        // "IN: [string] method, ihandle, stack_arg1, ..., stack_argP
                        // OUT: catch-result, stack_result1, ..., stack_resultQ
                        // [...]
                        // An implementation shall allow at least six stack_arg and six
                        // stack_result items."
                const char      *method;
                intptr_t        handle;
                void            *args[13];
        } args = {"call-method", numArgs + 2, numReturns + 1, method, handle};
        va_list list;
        int i;

        // iterate over all arguments and copy them into the
        // structure passed over to the OpenFirmware

        va_start(list, numReturns);
        for (i = 0; i < numArgs; i++) {
                // copy args
                args.args[i] = (void *)va_arg(list, void *);
        }
        for (i = numArgs; i < numArgs + numReturns + 1; i++) {
                // clear return values
                args.args[i] = NULL;
        }

        // args.args[numArgs] is the "catch-result" return value
        if (gCallOpenFirmware(&args) == OF_FAILED || args.args[numArgs])
                return OF_FAILED;

        if (numReturns > 0) {
                // copy return values over to the provided location

                for (i = numArgs + 1; i < numArgs + 1 + numReturns; i++) {
                        void **store = va_arg(list, void **);
                        if (store)
                                *store = args.args[i];
                }
        }
        va_end(list);

        return 0;
}


intptr_t
of_finddevice(const char *device)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                const char      *device;
                intptr_t        handle;
        } args = {"finddevice", 1, 1, device, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.handle;
}


/** Returns the first child of the given node
 */

intptr_t
of_child(intptr_t node)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        node;
                intptr_t        child;
        } args = {"child", 1, 1, node, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.child;
}


/** Returns the next sibling of the given node
 */

intptr_t
of_peer(intptr_t node)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        node;
                intptr_t        next_sibling;
        } args = {"peer", 1, 1, node, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.next_sibling;
}


/** Returns the parent of the given node
 */

intptr_t
of_parent(intptr_t node)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        node;
                intptr_t        parent;
        } args = {"parent", 1, 1, node, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.parent;
}


intptr_t
of_instance_to_path(uint32_t instance, char *pathBuffer, intptr_t bufferSize)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        instance;
                char            *path_buffer;
                intptr_t        buffer_size;
                intptr_t        size;
        } args = {"instance-to-path", 3, 1, instance, pathBuffer, bufferSize, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.size;
}


intptr_t
of_instance_to_package(uint32_t instance)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        instance;
                intptr_t        package;
        } args = {"instance-to-package", 1, 1, instance, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.package;
}


intptr_t
of_getprop(intptr_t package, const char *property, void *buffer, intptr_t bufferSize)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        package;
                const char      *property;
                void            *buffer;
                intptr_t        buffer_size;
                intptr_t        size;
        } args = {"getprop", 4, 1, package, property, buffer, bufferSize, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.size;
}


intptr_t
of_setprop(intptr_t package, const char *property, const void *buffer,
        intptr_t bufferSize)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        package;
                const char      *property;
                const void      *buffer;
                intptr_t        buffer_size;
                intptr_t        size;
        } args = {"setprop", 4, 1, package, property, buffer, bufferSize, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.size;
}


intptr_t
of_getproplen(intptr_t package, const char *property)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        package;
                const char      *property;
                intptr_t        size;
        } args = {"getproplen", 2, 1, package, property, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.size;
}


intptr_t
of_nextprop(intptr_t package, const char *previousProperty, char *nextProperty)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        package;
                const char      *previous_property;
                char            *next_property;
                intptr_t        flag;
        } args = {"nextprop", 3, 1, package, previousProperty, nextProperty, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.flag;
}


intptr_t
of_package_to_path(intptr_t package, char *pathBuffer, intptr_t bufferSize)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        package;
                char            *path_buffer;
                intptr_t        buffer_size;
                intptr_t        size;
        } args = {"package-to-path", 3, 1, package, pathBuffer, bufferSize, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.size;
}


//      I/O functions


intptr_t
of_open(const char *nodeName)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                const char      *node_name;
                intptr_t        handle;
        } args = {"open", 1, 1, nodeName, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED || args.handle == 0)
                return OF_FAILED;

        return args.handle;
}


void
of_close(intptr_t handle)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        handle;
        } args = {"close", 1, 0, handle};

        gCallOpenFirmware(&args);
}


intptr_t
of_read(intptr_t handle, void *buffer, intptr_t bufferSize)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        handle;
                void            *buffer;
                intptr_t        buffer_size;
                intptr_t        size;
        } args = {"read", 3, 1, handle, buffer, bufferSize, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.size;
}


intptr_t
of_write(intptr_t handle, const void *buffer, intptr_t bufferSize)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        handle;
                const void      *buffer;
                intptr_t        buffer_size;
                intptr_t        size;
        } args = {"write", 3, 1, handle, buffer, bufferSize, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.size;
}


intptr_t
of_seek(intptr_t handle, off_t pos)
{
        intptr_t pos_hi = 0;
        if (sizeof(off_t) > sizeof(intptr_t))
                pos_hi = pos >> ((sizeof(off_t) - sizeof(intptr_t)) * CHAR_BIT);

        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        handle;
                intptr_t        pos_hi;
                intptr_t        pos;
                intptr_t        status;
        } args = {"seek", 3, 1, handle, pos_hi, pos, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.status;
}


intptr_t
of_blocks(intptr_t handle)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        handle;
                intptr_t        result;
                intptr_t        blocks;
        } args = {"#blocks", 2, 1, handle, 0, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;
        return args.blocks;
}


intptr_t
of_block_size(intptr_t handle)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        handle;
                intptr_t        result;
                intptr_t        size;
        } args = {"block-size", 2, 1, handle, 0, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;
        return args.size;
}


// memory functions


intptr_t
of_release(void *virtualAddress, intptr_t size)
{
        struct {
                const char *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                void            *virtualAddress;
                intptr_t        size;
        } args = {"release", 2, 0, virtualAddress, size};

        return gCallOpenFirmware(&args);
}


void *
of_claim(void *virtualAddress, intptr_t size, intptr_t align)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                void            *virtualAddress;
                intptr_t        size;
                intptr_t        align;
                void            *address;
        } args = {"claim", 3, 1, virtualAddress, size, align};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return NULL;

        return args.address;
}


// misc functions


/** tests if the given service is missing
 */

intptr_t
of_test(const char *service)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                const char      *service;
                intptr_t        missing;
        } args = {"test", 1, 1, service, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.missing;
}


/** Returns the millisecond counter
 */

intptr_t
of_milliseconds(void)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
                intptr_t        milliseconds;
        } args = {"milliseconds", 0, 1, 0};

        if (gCallOpenFirmware(&args) == OF_FAILED)
                return OF_FAILED;

        return args.milliseconds;
}


void
of_exit(void)
{
        struct {
                const char      *name;
                intptr_t        num_args;
                intptr_t        num_returns;
        } args = {"exit", 0, 0};

        gCallOpenFirmware(&args);
}