diff --git a/README.md b/README.md index f38510f..5a20b66 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ died. The following sources were used as reference: - [https://android.googlesource.com/platform/system/chre](https://android.googlesource.com/platform/system/chre) + - [https://android.googlesource.com/platform/external/fastrpc](https://android.googlesource.com/platform/external/fastrpc) - [https://github.com/calebccff/sensordaemon-re](https://github.com/calebccff/sensordaemon-re) - [https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/merge\_requests/346](https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/merge_requests/346) - [https://git.linaro.org/landing-teams/working/qualcomm/fastrpc.git](https://git.linaro.org/landing-teams/working/qualcomm/fastrpc.git) diff --git a/fastrpc/fastrpc-ctx.c b/fastrpc/fastrpc-ctx.c new file mode 100644 index 0000000..97d8cbe --- /dev/null +++ b/fastrpc/fastrpc-ctx.c @@ -0,0 +1,54 @@ +/* + * FastRPC API Replacement - context-based interface + * + * Copyright (C) 2022 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "fastrpc.h" + +struct fastrpc_context *fastrpc_create_context(int fd, uint32_t handle) +{ + struct fastrpc_context *ctx; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->fd = fd; + ctx->handle = handle; +} + +int vfastrpc(const struct fastrpc_function_def_interp1 *def, + const struct fastrpc_context *ctx, va_list arg_list) +{ + return vfastrpc2(def, ctx->fd, ctx->handle, arg_list); +} + +int fastrpc(const struct fastrpc_function_def_interp1 *def, + const struct fastrpc_context *ctx, ...) +{ + va_list arg_list; + int ret; + + va_start(arg_list, ctx); + ret = vfastrpc(def, ctx, arg_list); + va_end(arg_list); + + return ret; +} + diff --git a/fastrpc/fastrpc.c b/fastrpc/fastrpc.c new file mode 100644 index 0000000..14c36bf --- /dev/null +++ b/fastrpc/fastrpc.c @@ -0,0 +1,229 @@ +/* + * FastRPC API Replacement + * + * Copyright (C) 2022 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include + +#include "fastrpc.h" + +/* + * This returns the amount of ioctl-level input buffers needed to pass a given + * amount of 32-bit numbers and variable-size function-level buffers passed to + * fastrpc(). + * + * This doesn't just return the number of buffers because fastrpc() generates + * the first ioctl-level buffer by itself. It should only provide it if needed, + * otherwise the remote processor will get an unexpected argument. + * + * So, this inline function returns the number of function-level buffers, added + * to the amount of first buffers (1), if any. + */ +static inline uint8_t io_side_count(uint8_t nums, uint8_t bufs) +{ + return bufs + ((nums || bufs) && 1); +} + +static void allocate_first_inbuf(const struct fastrpc_function_def_interp1 *def, + struct fastrpc_invoke_args *arg, + uint32_t **inbuf) +{ + arg->length = sizeof(uint32_t) * (def->in_nums + + def->in_bufs + + def->out_bufs); + arg->ptr = (__u64) (*inbuf = malloc(arg->length)); + arg->fd = -1; +} + +static void allocate_first_outbuf(const struct fastrpc_function_def_interp1 *def, + struct fastrpc_invoke_args *arg, + uint32_t **outbuf) +{ + arg->length = sizeof(uint32_t) * (def->out_nums + def->out_bufs); + arg->ptr = (__u64) (*outbuf = malloc(arg->length)); + arg->fd = -1; +} + +/* + * This populates relevant inputs (in general) 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 and returned function-level buffer sizes. 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 buffer to the kernel, and adds an entry + * to the first input buffer to tell the remote processor how large the + * function-level output buffer can be. + * + * This is the only part of the argument processing that can be split off into + * a different function because it is operating on a copy of the va_list. + * Calling va_arg() on a va_list after the return of a function that already + * used it causes undefined behavior. + */ +static void prepare_outbufs(const struct fastrpc_function_def_interp1 *def, + struct fastrpc_invoke_args *args, + uint8_t out_count, + uint32_t *inbuf, + uint32_t **outbuf, + va_list peek) +{ + int i; + + allocate_first_outbuf(def, args, outbuf); + + for (i = 0; i < def->out_nums; i++) + va_arg(peek, uint32_t *); + + for (i = 0; i < def->out_bufs; i++) { + inbuf[i] = args[i + 1].length = va_arg(peek, uint32_t); + va_arg(peek, uint32_t *); + args[i + 1].ptr = (__u64) va_arg(peek, void *); + args[i + 1].fd = -1; + } +} + +/* + * 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. + * + * The argument list has, in order: + * - a (uint32_t val) for each input number + * - a (uint32_t len, void *buf) for each input buffer + * - a (uint32_t *val) for each output number + * - a (uint32_t max_size, uint32_t *len, void *buf) for each output buffer + * + * 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_size, + * uint32_t *nested_inbufs_len, + * void *nested_inbufs) + * { + * struct fastrpc_function_def_interp1 def = { + * .in_nums = 2, + * .in_bufs = 1, + * .out_nums = 3, + * .out_bufs = 1, + * .mid = 4, + * }; + * return fastrpc(def, fd, handle, + * prev_ctx, + * prev_result, + * nested_outbufs_len, + * nested_outbufs, + * ctx, + * nested_handle, + * nested_sc, + * nested_inbufs_size, + * nested_inbufs_len, + * nested_inbufs); + * } + */ +int vfastrpc2(const struct fastrpc_function_def_interp1 *def, + int fd, uint32_t handle, va_list arg_list) +{ + va_list peek; + struct fastrpc_invoke invoke; + struct fastrpc_invoke_args *args; + uint32_t *inbuf; + uint32_t *outbuf; + uint8_t in_count; + uint8_t out_count; + uint8_t i; + int ret; + + /* + * Calculate the amount of needed buffers, accounting for the need for + * the maximum size of the output buffer. + */ + in_count = io_side_count(def->in_nums + def->out_bufs, def->in_bufs); + out_count = io_side_count(def->out_nums, def->out_bufs); + + invoke.handle = handle; + invoke.sc = REMOTE_SCALARS_MAKE(def->msg_id, in_count, out_count); + invoke.args = (__u64) (args = malloc(sizeof(*args) * (in_count + out_count))); + + allocate_first_inbuf(def, args, &inbuf); + + for (i = 0; i < def->in_nums; i++) + inbuf[i] = va_arg(arg_list, uint32_t); + + for (i = 1; i < in_count; i++) { + args[i].length = inbuf[def->in_nums + i] + = va_arg(arg_list, uint32_t); + args[i].ptr = (__u64) va_arg(arg_list, void *); + args[i].fd = -1; + } + + va_copy(peek, arg_list); + prepare_outbufs(def, + &args[in_count], + out_count, + &inbuf[def->in_nums + def->in_bufs], + &outbuf, + peek); + va_end(peek); + + ret = ioctl(fd, FASTRPC_IOCTL_INVOKE, (__u64) &invoke); + + for (i = 0; i < def->out_nums; i++) + *va_arg(arg_list, uint32_t *) = outbuf[i]; + + for (i = 1; i < def->out_bufs; i++) { + va_arg(arg_list, uint32_t); + *va_arg(arg_list, uint32_t *) = outbuf[def->out_nums + i]; + va_arg(arg_list, void *); + } + + free(args); + free(inbuf); + free(outbuf); + + return ret; +} + +int fastrpc2(const struct fastrpc_function_def_interp1 *def, + int fd, uint32_t handle, ...) +{ + va_list arg_list; + int ret; + + va_start(arg_list, handle); + ret = vfastrpc2(def, fd, handle, arg_list); + va_end(arg_list); + + return ret; +} diff --git a/fastrpc/fastrpc.h b/fastrpc/fastrpc.h new file mode 100644 index 0000000..5fc3e00 --- /dev/null +++ b/fastrpc/fastrpc.h @@ -0,0 +1,69 @@ +/* + * FastRPC API Replacement - header files + * + * Copyright (C) 2022 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FASTRPC_H +#define FASTRPC_H + +#include +#include +#include + +// See fastrpc.git/src/remotectl_stub.c +#define REMOTE_SCALARS_MAKEX(nAttr,nMethod,nIn,nOut,noIn,noOut) \ + ((((uint32_t) (nAttr) & 0x7) << 29) | \ + (((uint32_t) (nMethod) & 0x1f) << 24) | \ + (((uint32_t) (nIn) & 0xff) << 16) | \ + (((uint32_t) (nOut) & 0xff) << 8) | \ + (((uint32_t) (noIn) & 0x0f) << 4) | \ + ((uint32_t) (noOut) & 0x0f)) + +#define REMOTE_SCALARS_MAKE(nMethod,nIn,nOut) REMOTE_SCALARS_MAKEX(0,nMethod,nIn,nOut,0,0) + +struct fastrpc_context { + int fd; + uint32_t handle; +}; + +struct fastrpc_function_def_interp1 { + uint32_t msg_id; + uint8_t in_nums; + uint8_t in_bufs; + uint8_t out_nums; + uint8_t out_bufs; +}; + +struct fastrpc_context *fastrpc_create_context(int fd, uint32_t handle); + +static inline void fastrpc_destroy_context(struct fastrpc_context *ctx) +{ + free(ctx); +} + +int vfastrpc2(const struct fastrpc_function_def_interp1 *def, + int fd, uint32_t handle, va_list arg_list); +int vfastrpc(const struct fastrpc_function_def_interp1 *def, + const struct fastrpc_context *ctx, va_list arg_list); +int fastrpc2(const struct fastrpc_function_def_interp1 *def, + int fd, uint32_t handle, ...); +int fastrpc(const struct fastrpc_function_def_interp1 *def, + const struct fastrpc_context *ctx, ...); + +#endif diff --git a/fastrpc/fastrpc_remotectl.h b/fastrpc/fastrpc_remotectl.h new file mode 100644 index 0000000..6103f96 --- /dev/null +++ b/fastrpc/fastrpc_remotectl.h @@ -0,0 +1,42 @@ +/* + * Remote processor control interface + * + * Copyright (C) 2022 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FASTRPC_REMOTECTL_H +#define FASTRPC_REMOTECTL_H + +#include "fastrpc.h" + +#define REMOTECTL_HANDLE 0 + +#define DEFINE_REMOTE_PROCEDURE(mid, name, innums, inbufs, outnums, outbufs) \ + struct fastrpc_function_def_interp1 name##_def = { \ + .msg_id = mid, \ + .in_nums = innums, \ + .in_bufs = inbufs, \ + .out_nums = outnums, \ + .out_bufs = outbufs, \ + } + +DEFINE_REMOTE_PROCEDURE(0, remotectl_open, 0, 1, 1, 1); + +#undef DEFINE_REMOTE_PROCEDURE + +#endif diff --git a/fastrpc/rpcd.c b/fastrpc/rpcd.c new file mode 100644 index 0000000..2b75657 --- /dev/null +++ b/fastrpc/rpcd.c @@ -0,0 +1,62 @@ +/* + * FastRPC Example + * + * Copyright (C) 2022 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "fastrpc.h" +#include "fastrpc_remotectl.h" + +static int remotectl_open(int fd, char *name, struct fastrpc_context **ctx, void (*err_cb)(const char *err, size_t err_len)) +{ + uint32_t handle; + uint32_t err_len; + char err[256]; + int ret; + + ret = fastrpc2(&remotectl_open_def, fd, REMOTECTL_HANDLE, + strlen(name) + 1, name, + &handle, + 256, &err_len, err); + if (ret) { + err_cb(err, err_len); + return ret; + } + + *ctx = fastrpc_create_context(fd, handle); + + return 0; +} + +static void remotectl_err(const char *err, size_t err_len) +{ + write(STDERR_FILENO, err, err_len); +} + +int main() +{ + struct fastrpc_context *ctx; + + remotectl_open(-1, "adsp_listener", &ctx, remotectl_err); + printf("{ .fd = %u, .handle = %u, }\n", ctx->fd, ctx->handle); + free(ctx); +}