From 9adc9ea326d6ad057adef6f3bfd9be34751e2a07 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Wed, 4 Jan 2023 00:38:15 -0500 Subject: [PATCH] add fastrpc FastRPC is a communication protocol used to communicate with some remote processors on Qualcomm SoC's. It might be needed to configure sensors and to prevent an ADSP crash on the Pixel 3a. The programs provided by Google and Linaro don't work out of the box to set up an ADSP listener (even with kernel patches), and is a bit too unpleasant to read for my tastes. Add a new FastRPC implementation that will hopefully be more readable for everyone. --- README.md | 1 + fastrpc/fastrpc-ctx.c | 54 +++++++++ fastrpc/fastrpc.c | 229 ++++++++++++++++++++++++++++++++++++ fastrpc/fastrpc.h | 69 +++++++++++ fastrpc/fastrpc_remotectl.h | 42 +++++++ fastrpc/rpcd.c | 62 ++++++++++ 6 files changed, 457 insertions(+) create mode 100644 fastrpc/fastrpc-ctx.c create mode 100644 fastrpc/fastrpc.c create mode 100644 fastrpc/fastrpc.h create mode 100644 fastrpc/fastrpc_remotectl.h create mode 100644 fastrpc/rpcd.c 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); +}