From 09add3eecce39ef4ba094c1eea4581c034eca669 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Wed, 16 Oct 2024 19:43:41 -0400 Subject: [PATCH] fastrpc: libhexagonrpc: add interpretation three of method call format The method call format as interpreted last time had some limitations. First, input words could not appear after the input buffers, making it impossible to represent the apps_std_mkdir method accurately. Second, it was assumed that the lengths in the primary input buffer represent the number of bytes. In reality, the lengths represent the number of elements, which just happened to be octets in the majority of remote methods found, and the adsp_perf_get_usecs remote method could not be represented. Update the format for the third time to remove these limitations. This format allows the mixing of words and lengths of other buffers in the primary buffer by using 0 to denote words in an array of arguments. It also allows a greater size of elements, denoted by any non-zero value. --- include/libhexagonrpc/fastrpc.h | 20 +++ include/libhexagonrpc/interface.h | 23 ++++ libhexagonrpc/context.c | 18 +++ libhexagonrpc/fastrpc.c | 218 ++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+) diff --git a/include/libhexagonrpc/fastrpc.h b/include/libhexagonrpc/fastrpc.h index 69f0d17..e581a91 100644 --- a/include/libhexagonrpc/fastrpc.h +++ b/include/libhexagonrpc/fastrpc.h @@ -23,6 +23,7 @@ #define FASTRPC_H #include +#include #include #include @@ -41,6 +42,8 @@ #define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0xff) #define REMOTE_SCALARS_OUTBUFS(sc) (((sc) >> 8) & 0xff) +#define HEXAGONRPC_DELIMITER 0xFFFFFFFF + struct fastrpc_context { int fd; uint32_t handle; @@ -54,6 +57,13 @@ struct fastrpc_function_def_interp2 { uint8_t out_bufs; }; +struct hrpc_method_def_interp3 { + uint32_t msg_id; + bool has_prim_out; + size_t n_args; + const uint32_t *args; +}; + struct fastrpc_context *fastrpc_create_context(int fd, uint32_t handle); static inline void fastrpc_destroy_context(struct fastrpc_context *ctx) @@ -70,4 +80,14 @@ int fastrpc2(const struct fastrpc_function_def_interp2 *def, int fastrpc(const struct fastrpc_function_def_interp2 *def, const struct fastrpc_context *ctx, ...); +int vhexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, va_list list); +int vhexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, va_list list); +int hexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, ...); +int hexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, ...); + + #endif diff --git a/include/libhexagonrpc/interface.h b/include/libhexagonrpc/interface.h index 0b6700f..c7a0af2 100644 --- a/include/libhexagonrpc/interface.h +++ b/include/libhexagonrpc/interface.h @@ -36,6 +36,12 @@ outnums, outbufs) \ extern const struct fastrpc_function_def_interp2 name##_def; +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3_EMPTY(mid, name) \ + extern const struct hrpc_method_def_interp3 name##_def; + +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3(mid, name, has_oprim, ...) \ + HEXAGONRPC_DEFINE_REMOTE_METHOD3_EMPTY(mid, name) + #else /* HEXAGONRPC_BUILD_METHOD_DEFINITIONS */ #define HEXAGONRPC_DEFINE_REMOTE_METHOD(mid, name, \ @@ -49,6 +55,23 @@ .out_bufs = outbufs, \ }; +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3_EMPTY(mid, name) \ + const struct hrpc_method_def_interp3 name##_def = { \ + .msg_id = mid, \ + .has_prim_out = false, \ + .n_args = 0, \ + .args = NULL, \ + }; + +#define HEXAGONRPC_DEFINE_REMOTE_METHOD3(mid, name, has_oprim, ...) \ + static const uint32_t name##_args[] = { __VA_ARGS__ }; \ + const struct hrpc_method_def_interp3 name##_def = { \ + .msg_id = mid, \ + .has_prim_out = has_oprim, \ + .n_args = sizeof(name##_args) / sizeof(*name##_args), \ + .args = name##_args, \ + }; + #endif /* HEXAGONRPC_BUILD_METHOD_DEFINITIONS */ #endif /* LIBHEXAGONRPC_INTERFACE_H */ diff --git a/libhexagonrpc/context.c b/libhexagonrpc/context.c index c3fa204..646ab4f 100644 --- a/libhexagonrpc/context.c +++ b/libhexagonrpc/context.c @@ -54,3 +54,21 @@ int fastrpc(const struct fastrpc_function_def_interp2 *def, return ret; } +int vhexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, va_list arg_list) +{ + return vhexagonrpc2(def, ctx->fd, ctx->handle, arg_list); +} + +int hexagonrpc(const struct hrpc_method_def_interp3 *def, + const struct fastrpc_context *ctx, ...) +{ + va_list arg_list; + int ret; + + va_start(arg_list, ctx); + ret = vhexagonrpc(def, ctx, arg_list); + va_end(arg_list); + + return ret; +} diff --git a/libhexagonrpc/fastrpc.c b/libhexagonrpc/fastrpc.c index cee9323..420543a 100644 --- a/libhexagonrpc/fastrpc.c +++ b/libhexagonrpc/fastrpc.c @@ -250,3 +250,221 @@ int fastrpc2(const struct fastrpc_function_def_interp2 *def, return ret; } + +/* + * This populates relevant arguments with information necessary to receive + * output from the remote processor. + * + * First, it allocates a new first output buffer to contain the returned 32-bit + * integers. This output buffer must be freed after use. + * + * With a peek at the output arguments, it populates the fastrpc_invoke_args + * struct to give information about the provided buffers to the kernel, and + * adds an entry to the first input buffer to tell the remote processor the + * size of the function-level output buffer. + */ +static void prepare_outbufs3(const struct hrpc_method_def_interp3 *def, + struct fastrpc_invoke_args *args, + size_t delim, + uint32_t *n_prim_in, uint8_t *n_outbufs, + uint32_t *prim, + va_list peek) +{ + uint32_t n_prim_out = 0; + size_t i; + + if (def->has_prim_out) + *n_outbufs = 1; + else + *n_outbufs = 0; + + for (i = delim; i < def->n_args; i++) { + if (def->args[i]) { + prim[*n_prim_in] = va_arg(peek, uint32_t); + + args[*n_outbufs].length = prim[*n_prim_in] * def->args[i]; + args[*n_outbufs].ptr = (__u64) va_arg(peek, void *); + args[*n_outbufs].fd = -1; + + (*n_prim_in)++; + (*n_outbufs)++; + } else { + va_arg(peek, uint32_t *); + n_prim_out++; + } + } + + if (def->has_prim_out) { + args[0].length = sizeof(uint32_t) * n_prim_out; + args[0].ptr = (__u64) &prim[*n_prim_in]; + args[0].fd = -1; + } +} + +static void return_prim_out(const struct hrpc_method_def_interp3 *def, + size_t delim, + const uint32_t *prim_out, + va_list list) +{ + uint32_t *ptr; + size_t i, j = 0; + + for (i = delim; i < def->n_args; i++) { + if (def->args[i]) { + va_arg(list, uint32_t); + va_arg(list, void *); + } else { + ptr = va_arg(list, uint32_t *); + *ptr = prim_out[j]; + j++; + } + } +} + +/* + * This is the main function to invoke a FastRPC procedure call. The first + * parameter specifies how to populate the ioctl-level buffers. The second and + * third parameters specify where to send the invocation to. The fourth is the + * list of arguments that the procedure call should interact with. + * + * A good example for this would be the adsp_listener_next2 call: + * + * static inline void adsp_listener_next2(int fd, uint32_t handle, + * uint32_t prev_ctx, + * uint32_t prev_result, + * uint32_t nested_outbufs_len, + * void *nested_outbufs, + * uint32_t *ctx, + * uint32_t *nested_handle, + * uint32_t *nested_sc, + * uint32_t *nested_inbufs_len, + * uint32_t nested_inbufs_size, + * void *nested_inbufs) + * { + * uint32_t args[] = { 0, 0, 1, HEXAGONRPC_DELIMITER, 0, 0, 0, 1 }; + * struct hrpc_method_def_interp3 def = { + * .mid = 4, + * .has_out_prim = true, + * .n_args = 8, + * .args = args, + * }; + * return vhexagonrpc2(def, fd, handle, + * prev_ctx, + * prev_result, + * nested_outbufs_len, + * nested_outbufs, + * ctx, + * nested_handle, + * nested_sc, + * nested_inbufs_len, + * nested_inbufs_size, + * nested_inbufs); + * } + */ +int vhexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, va_list list) +{ + va_list peek; + struct fastrpc_invoke invoke; + struct fastrpc_invoke_args *args; + uint32_t *prim; + uint32_t n_prim_in = 0; + uint32_t msg_id; + uint8_t n_inbufs = 1, n_outbufs; + size_t i; + int ret = -1; + + /* + * If there are only input buffers specified in the method definition, + * we need an extra primary input argument for their sizes. + */ + args = malloc(sizeof(struct fastrpc_invoke_args) * (def->n_args + 1)); + if (args == NULL) + return -1; + + /* + * The input and output buffers need a size in the primary input buffer + * so all arguments except the optional delimiter contribute to the + * primary buffers. + */ + prim = malloc(sizeof(uint32_t) * (def->n_args + 1)); + if (prim == NULL) + goto err_free_args; + + /* + * The apps_std interface demonstrates that there is support + * for more than 32 methods for a single interface. Support + * extended method IDs that go beyond 5 bits and the ID + * reserved for this purpose. + */ + if (def->msg_id >= 31) { + prim[n_prim_in] = def->msg_id; + n_prim_in++; + msg_id = 31; + } else { + msg_id = def->msg_id; + } + + for (i = 0; i < def->n_args; i++) { + if (def->args[i] == HEXAGONRPC_DELIMITER) { + i++; + break; + } + + prim[n_prim_in] = va_arg(list, uint32_t); + + if (def->args[i]) { + args[n_inbufs].length = prim[n_prim_in] * def->args[i]; + args[n_inbufs].ptr = (__u64) va_arg(list, const void *); + args[n_inbufs].fd = -1; + n_inbufs++; + } + + n_prim_in++; + } + + va_copy(peek, list); + prepare_outbufs3(def, &args[n_inbufs], i, &n_prim_in, &n_outbufs, prim, peek); + va_end(peek); + + args[0].length = sizeof(uint32_t) * n_prim_in; + args[0].ptr = (__u64) prim; + args[0].fd = -1; + + // Pass the primary input buffer if not empty, otherwise skip it. + if (n_prim_in != 0) { + invoke.args = (__u64) args; + } else { + invoke.args = (__u64) &args[1]; + n_inbufs--; + } + + invoke.handle = handle; + invoke.sc = REMOTE_SCALARS_MAKE(msg_id, n_inbufs, n_outbufs); + + ret = ioctl(fd, FASTRPC_IOCTL_INVOKE, (__u64) &invoke); + if (ret == -1) + goto err_free_prim; + + return_prim_out(def, i, &prim[n_prim_in], list); + +err_free_prim: + free(prim); +err_free_args: + free(args); + + return ret; +} + +int hexagonrpc2(const struct hrpc_method_def_interp3 *def, + int fd, uint32_t handle, ...) +{ + va_list list; + int ret; + + va_start(list, handle); + ret = vhexagonrpc2(def, fd, handle, list); + va_end(list); + + return ret; +}