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).