Home > Software engineering >  Best way to implement a callgate?
Best way to implement a callgate?

Time:06-11

I'm programming for an stm32 with firewall. To open the firewall a callgate must be used. The more or less usual way is to write a function with variable arguments and use the first argument in a switch statement to switch between different functions.

int function1_outside(int arg1)
{
    int status;
    // disable interrupts
    status = CallGate(ID1, arg1);
    // enable interrupts
    return status;
}

int CallGate(FunctionID eID, ...)
{
    va_list arguments;
    int status;

    /* Initializing arguments to store all values */
    va_start(arguments, PrimaskParam);

    switch (eID)
    {
        case ID1:
        {
            int arg1;
            /* Retrieve argument(s) */
            arg1 = va_arg(arguments, int);
            // Call the real function
            status = function1_inside(arg1);
            break;
        }
        case ID2:
        {
            // do more ...
            break;
        }
    }

    /* Clean up arguments list*/
    va_end(arguments);

    return status;
}

int function1_inside(int arg1)
{
    int result;
    // do stuff
    result = arg1   1;
    return result;
}

The biggest problem with this approach is keeping all parts in sync.
Is there a good way to reduce the parts that need to be kept in sync?

CodePudding user response:

I would do an array of function pointers, and would pass any argument via a void* argument. Using stdarg is confusing, it's better to just do a pointer from the start.

#include <stdio.h>
#include <string.h>
#include <assert.h>

enum callgate_func_u {
    CALLGATE_FUNC_THAT_PRINTS_INT,
    CALLGATE_OTHER_FUNC,
    CALLGATE_FUNC_LEN,
};

int function1_inside(int id, void *arg) {
    return printf("%d", *(int*)arg);
}
int function2_inside(int id, void *arg) {
    return printf("%lf", *(float*)arg);
}

/// Represents a function to be called.
/// @param id The ID used to call the function.
/// @param arg A pointer to __any__ user context.
typedef int callgate_func_t(int id, void *arg);
static callgate_func_t *const callgate_functions[CALLGATE_FUNC_LEN] = {
    [CALLGATE_FUNC_THAT_PRINTS_INT] = function1_inside,
    [CALLGATE_OTHER_FUNC] = function2_inside,
};

int callgate(enum callgate_func_u id, void *param) {
    static_assert(sizeof(callgate_functions)/sizeof(*callgate_functions) == CALLGATE_FUNC_LEN, "");
    assert(id < CALLGATE_FUNC_LEN);
    callgate_func_t *const func = callgate_functions[id];
    assert(func != NULL);
    return func(id, param);
}

int function1_outside(int arg1) {
    int status;
    // disable interrupts
    status = callgate(CALLGATE_FUNC_THAT_PRINTS_INT, &arg1);
    // enable interrupts
    return status;
}

int main() {
    printf("5 = ");
    function1_outside(5);
    printf("\n");
}

Overall, you might be interested in system calls like ioctl and fcntl. They are basically doing exactly the same thing, with a big switch on the kernel side that chooses proper action depending on id.

CodePudding user response:

The code is not as important as the correct placement of the callgate code.

It has to start execution from the 2-nd word in the protected memory segment. You need to define it in the linker script and then place the function in the correct segment (at position 4 in bytes). All functions called from there have to located in this memory segment (ie to be placed in the protected area address space). So basically - no libraries (unless you modify the linker script to put the code from particular object files into protected address space).

  • Related