mirror of
https://github.com/linux-msm/hexagonrpc.git
synced 2026-10-07 15:29:50 -07:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
435ed1cd68 | ||
|
|
dd9ac70c02 | ||
|
|
ffd300c27e | ||
|
|
5c4e11360d | ||
|
|
eec389f322 | ||
|
|
196c72e80f | ||
|
|
cfc745ce09 | ||
|
|
93f3467dcf | ||
|
|
c4109b4502 | ||
|
|
5d3cef4e5b | ||
|
|
23a69640bf | ||
|
|
18e2ca3178 | ||
|
|
ffca60917c | ||
|
|
6e1281dc8d | ||
|
|
948c5d0d37 | ||
|
|
e52da43a63 | ||
|
|
6e5f7d9d54 | ||
|
|
9a68ca3abf | ||
|
|
146d0e1dab | ||
|
|
2377fd46f0 | ||
|
|
372ca67d7a | ||
|
|
4b1ecd8505 | ||
|
|
53e347f39d | ||
|
|
75150d4860 | ||
|
|
5620db43a7 | ||
|
|
0e9e03b37b | ||
|
|
61bb51bbc9 | ||
|
|
ffbc4da830 |
No files matched your search
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
root = true
|
||||
|
||||
[*.{c,h}]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Build+test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: ubuntu-24.04
|
||||
# 22.04 has old meson
|
||||
# - runner: ubuntu-22.04
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y meson ninja-build libjson-c-dev
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
meson setup build
|
||||
ninja -C build
|
||||
|
||||
- name: run tests
|
||||
run: |
|
||||
ninja -C build test
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
cc_defaults {
|
||||
name: "hexagonrpc_defaults",
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
header_libs: [
|
||||
"device_kernel_headers",
|
||||
"hexagonrpc_headers",
|
||||
],
|
||||
}
|
||||
@@ -125,19 +125,26 @@ Interfaces are initialized in the `start_reverse_tunnel` function, in hexagonrpc
|
||||
## HexagonFS
|
||||
|
||||
The reverse tunnel's `apps_std` interface serves files to the remote processor.
|
||||
These files are searched for in:
|
||||
These files are searched for in a directory tree, starting at the location
|
||||
defined by the `-R` option, or in `/usr/share/qcom` by default:
|
||||
|
||||
Physical file/dir Android file/dir
|
||||
/usr/share/qcom/acdb /vendor/etc/acdbdata
|
||||
/usr/share/qcom/dsp /vendor/dsp
|
||||
/usr/share/qcom/sensors/config /vendor/etc/sensors/config
|
||||
/usr/share/qcom/sensors/registry /mnt/vendor/persist/sensors/registry/registry
|
||||
/usr/share/qcom/sensors/sns_reg.conf /vendor/etc/sensors/sns_reg_config
|
||||
/usr/share/qcom/socinfo /sys/devices/soc0
|
||||
HexagonRPCD file/dir Android file/dir
|
||||
acdb /vendor/etc/acdbdata
|
||||
dsp /vendor/dsp
|
||||
sensors/config /vendor/etc/sensors/config
|
||||
sensors/registry /mnt/vendor/persist/sensors/registry/registry
|
||||
sensors/sns_reg.conf /vendor/etc/sensors/sns_reg_config
|
||||
socinfo /sys/devices/soc0
|
||||
|
||||
These files and directories should be populated with files from your device's
|
||||
Android firmware.
|
||||
|
||||
If you can pass the `-R` option, the convention is to pass `-R
|
||||
/usr/share/qcom/SOC/VENDOR/DEVICE`, where SOC is the SoC name in lowercase,
|
||||
VENDOR is the capitalized name of the device vendor, and DEVICE is the device
|
||||
name (e.g. `-R /usr/share/qcom/sdm845/SHIFT/axolotl` or `-R
|
||||
/usr/share/qcom/sdm845/Thundercomm/db845c`).
|
||||
|
||||
# Future plans
|
||||
|
||||
Reverse tunnels are separated by the process that opens the file descriptor and
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
cc_binary {
|
||||
name: "chrecd",
|
||||
defaults: ["hexagonrpc_defaults"],
|
||||
srcs: [
|
||||
"interfaces.c",
|
||||
"main.c",
|
||||
],
|
||||
shared_libs: [
|
||||
"libhexagonrpc",
|
||||
],
|
||||
vendor: true,
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
prebuilt_etc {
|
||||
name: "hexagonrpcd-adsp-rootpd.rc",
|
||||
src: "hexagonrpcd-adsp-rootpd.rc",
|
||||
sub_dir: "init",
|
||||
vendor: true,
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "hexagonrpcd-adsp-sensorspd.rc",
|
||||
src: "hexagonrpcd-adsp-sensorspd.rc",
|
||||
sub_dir: "init",
|
||||
vendor: true,
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "hexagonrpcd-sdsp.rc",
|
||||
src: "hexagonrpcd-sdsp.rc",
|
||||
sub_dir: "init",
|
||||
vendor: true,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
service vendor.hexagonrpcd-adsp-rootpd /vendor/bin/hexagonrpcd -f /dev/fastrpc-adsp -d adsp -R /vendor/etc/hexagonrpcd-root
|
||||
class core
|
||||
user system
|
||||
group system
|
||||
stdio_to_kmsg
|
||||
@@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=Server for FastRPC remote procedure calls from Qualcomm ADSP - RootPD
|
||||
Conflicts=suspend.target
|
||||
Before=suspend.target
|
||||
# FIXME: Remove this once hexagonrpcd can serve rootpd on the ADSP
|
||||
# of the SDM845 devices
|
||||
ConditionPathExists=!/dev/fastrpc-sdsp
|
||||
ConditionPathExists=/dev/fastrpc-adsp
|
||||
|
||||
[Service]
|
||||
ExecStart=@bindir@/hexagonrpcd -f /dev/fastrpc-adsp -d adsp
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
User=fastrpc
|
||||
Group=fastrpc
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,4 @@
|
||||
service vendor.hexagonrpcd-adsp-sensorspd /vendor/bin/hexagonrpcd -f /dev/fastrpc-adsp -d adsp -s -R /vendor/etc/hexagonrpcd-root
|
||||
class core
|
||||
user system
|
||||
group system
|
||||
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Server for FastRPC remote procedure calls from Qualcomm ADSP - sensorspd
|
||||
Conflicts=suspend.target
|
||||
Before=suspend.target
|
||||
# This service shouldn't be run on devices with an SDSP
|
||||
ConditionPathExists=!/dev/fastrpc-sdsp
|
||||
ConditionPathExists=/dev/fastrpc-adsp
|
||||
|
||||
[Service]
|
||||
ExecStart=@bindir@/hexagonrpcd -f /dev/fastrpc-adsp -d adsp -s
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
User=fastrpc
|
||||
Group=fastrpc
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,4 @@
|
||||
service vendor.hexagonrpcd-sdsp /vendor/bin/hexagonrpcd -f /dev/fastrpc-sdsp -d sdsp -s -R /vendor/etc/hexagonrpcd-root
|
||||
class core
|
||||
user system
|
||||
group system
|
||||
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Server for FastRPC remote procedure calls from Qualcomm SDSP
|
||||
ConditionPathExists=/dev/fastrpc-sdsp
|
||||
|
||||
[Service]
|
||||
ExecStart=@bindir@/hexagonrpcd -f /dev/fastrpc-sdsp -d sdsp -s
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
User=fastrpc
|
||||
Group=fastrpc
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,15 @@
|
||||
systemd_units = [
|
||||
'hexagonrpcd-adsp-rootpd.service',
|
||||
'hexagonrpcd-adsp-sensorspd.service',
|
||||
'hexagonrpcd-sdsp.service',
|
||||
]
|
||||
|
||||
foreach unit : systemd_units
|
||||
configure_file(
|
||||
input: unit + '.in',
|
||||
output: unit,
|
||||
configuration: { 'bindir': get_option('prefix') / get_option('bindir') },
|
||||
install: true,
|
||||
install_dir: get_option('libdir') / 'systemd/system',
|
||||
)
|
||||
endforeach
|
||||
@@ -0,0 +1,23 @@
|
||||
cc_binary {
|
||||
name: "hexagonrpcd",
|
||||
defaults: ["hexagonrpc_defaults"],
|
||||
srcs: [
|
||||
"aee_error.c",
|
||||
"apps_mem.c",
|
||||
"apps_std.c",
|
||||
"hexagonfs.c",
|
||||
"hexagonfs_mapped.c",
|
||||
"hexagonfs_plat_subtype_name.c",
|
||||
"hexagonfs_virt_dir.c",
|
||||
"interfaces.c",
|
||||
"iobuffer.c",
|
||||
"listener.c",
|
||||
"localctl.c",
|
||||
"rpcd.c",
|
||||
"rpcd_builder.c",
|
||||
],
|
||||
shared_libs: [
|
||||
"libhexagonrpc",
|
||||
],
|
||||
vendor: true,
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* FastRPC memory mapping interface implementation
|
||||
*
|
||||
* Copyright (C) 2024 The Sensor Shell Contributors
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <misc/fastrpc.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "aee_error.h"
|
||||
#include "apps_mem.h"
|
||||
#include "interfaces/apps_mem.def"
|
||||
#include "listener.h"
|
||||
|
||||
// See fastrpc.git/src/apps_mem_imp.c
|
||||
#define ADSP_MMAP_HEAP_ADDR 4
|
||||
#define ADSP_MMAP_REMOTE_HEAP_ADDR 8
|
||||
#define ADSP_MMAP_ADD_PAGES 0x1000
|
||||
|
||||
struct apps_mem_ctx {
|
||||
int fd;
|
||||
};
|
||||
|
||||
static uint32_t apps_mem_request_map64(void *data,
|
||||
const struct fastrpc_io_buffer *inbufs,
|
||||
struct fastrpc_io_buffer *outbufs)
|
||||
{
|
||||
struct apps_mem_ctx *ctx = data;
|
||||
const struct {
|
||||
uint32_t heap_id;
|
||||
uint32_t lflags;
|
||||
uint32_t rflags;
|
||||
uint32_t padding;
|
||||
uint64_t vin;
|
||||
uint64_t len;
|
||||
} *first_in = inbufs[0].p;
|
||||
struct {
|
||||
uint64_t vapps;
|
||||
uint64_t vadsp;
|
||||
} *first_out = outbufs[0].p;
|
||||
struct fastrpc_req_mmap mmap;
|
||||
int ret;
|
||||
|
||||
if (!(first_in->rflags & ADSP_MMAP_ADD_PAGES)) {
|
||||
fprintf(stderr, "Unsupported mmap operation: DMA buffer request\n");
|
||||
return AEE_EUNSUPPORTED;
|
||||
}
|
||||
|
||||
mmap.fd = -1;
|
||||
mmap.flags = first_in->rflags;
|
||||
mmap.vaddrin = 0;
|
||||
mmap.size = first_in->len;
|
||||
|
||||
ret = ioctl(ctx->fd, FASTRPC_IOCTL_MMAP, &mmap);
|
||||
if (ret == -1) {
|
||||
perror("Memory map failed");
|
||||
return AEE_EFAILED;
|
||||
} else if (ret) {
|
||||
fprintf(stderr, "Memory map failed: %s\n", aee_strerror[ret]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if HEXAGONRPC_VERBOSE
|
||||
printf("mmap(%d, %x, %x, %lu, %lu) -> %" PRIx64 "\n",
|
||||
first_in->heap_id,
|
||||
first_in->lflags,
|
||||
first_in->rflags,
|
||||
first_in->vin,
|
||||
first_in->len,
|
||||
mmap.vaddrout);
|
||||
#endif
|
||||
|
||||
first_out->vapps = 0;
|
||||
first_out->vadsp = mmap.vaddrout & 0xFFFFFFFF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct fastrpc_interface *fastrpc_apps_mem_init(int fd)
|
||||
{
|
||||
struct fastrpc_interface *iface;
|
||||
struct apps_mem_ctx *ctx;
|
||||
|
||||
iface = malloc(sizeof(*iface));
|
||||
if (iface == NULL)
|
||||
return NULL;
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
if (ctx == NULL)
|
||||
goto err_free_iface;
|
||||
|
||||
memcpy(iface, &apps_mem_interface, sizeof(*iface));
|
||||
|
||||
ctx->fd = fd;
|
||||
|
||||
iface->data = ctx;
|
||||
|
||||
return iface;
|
||||
|
||||
err_free_iface:
|
||||
free(iface);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fastrpc_apps_mem_deinit(struct fastrpc_interface *iface)
|
||||
{
|
||||
free(iface->data);
|
||||
free(iface);
|
||||
}
|
||||
|
||||
static const struct fastrpc_function_impl apps_mem_procs[] = {
|
||||
{ .def = NULL, .impl = NULL, },
|
||||
{ .def = NULL, .impl = NULL, },
|
||||
{ .def = &apps_mem_request_map64_def, .impl = apps_mem_request_map64, },
|
||||
{ .def = NULL, .impl = NULL, },
|
||||
{ .def = NULL, .impl = NULL, },
|
||||
{ .def = NULL, .impl = NULL, },
|
||||
};
|
||||
|
||||
const struct fastrpc_interface apps_mem_interface = {
|
||||
.name = "apps_mem",
|
||||
.n_procs = 6,
|
||||
.procs = apps_mem_procs,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* FastRPC operating system interface - context initialization
|
||||
*
|
||||
* Copyright (C) 2024 The Sensor Shell Contributors
|
||||
*
|
||||
* This file is part of HexagonRPC.
|
||||
*
|
||||
* HexagonRPC 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef APPS_MEM_H
|
||||
#define APPS_MEM_H
|
||||
|
||||
#include "listener.h"
|
||||
|
||||
struct fastrpc_interface *fastrpc_apps_mem_init(int fd);
|
||||
void fastrpc_apps_mem_deinit(struct fastrpc_interface *iface);
|
||||
|
||||
#endif
|
||||
@@ -197,9 +197,10 @@ static uint32_t apps_std_fopen_with_env(void *data,
|
||||
}
|
||||
|
||||
#ifdef HEXAGONRPC_VERBOSE
|
||||
printf("openat($%s, %s) -> %d\n", (const char *) inbufs[1].p,
|
||||
(const char *) inbufs[3].p,
|
||||
fd);
|
||||
printf("openat($%s, %s, %c) -> %d\n", (const char *) inbufs[1].p,
|
||||
(const char *) inbufs[3].p,
|
||||
rw_mode,
|
||||
fd);
|
||||
#endif
|
||||
|
||||
*out = fd;
|
||||
@@ -312,7 +313,7 @@ static uint32_t apps_std_stat(void *data,
|
||||
if (((const char *) inbufs[1].p)[inbufs[1].s - 1] != 0)
|
||||
return AEE_EBADPARM;
|
||||
|
||||
fd = hexagonfs_openat(ctx->fds, ctx->rootfd, ctx->rootfd, pathname);
|
||||
fd = hexagonfs_openat(ctx->fds, ctx->rootfd, ctx->adsp_library_dirfd, pathname);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Could not open %s: %s\n",
|
||||
pathname, strerror(-fd));
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
'\" t
|
||||
.TH HEXAGONRPCD 1 "2024-09-28" "hexagonrpcd" "Server for FastRPC remote procedure calls from Qualcomm DSPs"
|
||||
.SH NAME
|
||||
hexagonrpcd - Server for FastRPC remote procedure calls from Qualcomm DSPs
|
||||
.SH SYNOPSIS
|
||||
\fBhexagonrpcd\fP [\fIoptions\&.\&.\&.\fP] -f \fIDEVICE
|
||||
.SH DESCRIPTION
|
||||
Server for FastRPC remote procedure calls from Qualcomm DSPs\&.
|
||||
.PP
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-c \fISHELL\fP
|
||||
Create a new pd running the specified ELF
|
||||
\fB\-d \fIDSP\fP
|
||||
DSP name (default: )
|
||||
.TP
|
||||
\fB\-f \fIDEVICE\fP
|
||||
FastRPC device node to attach to
|
||||
.TP
|
||||
\fB\-p \fIPROGRAM\fP
|
||||
Run client program with shared file descriptor
|
||||
.TP
|
||||
\fB\-R \fIDIR\fP
|
||||
Root directory of served files (default: /usr/share/qcom/)
|
||||
.TP
|
||||
\fB\-s\fP
|
||||
Attach to sensorspd
|
||||
.PP
|
||||
.SH NOTES
|
||||
|
||||
Hexagonrpcd serves files to the remote processor via a reverse tunnel interface
|
||||
to the remote processor. These files are by default searched for in:
|
||||
|
||||
These files are searched for in a directory tree, starting at the location
|
||||
defined by the -R option, or in /usr/share/qcom by default:
|
||||
|
||||
.TS
|
||||
l l
|
||||
---
|
||||
l l.
|
||||
HexagonRPCD file/dir Android file/dir
|
||||
acdb /vendor/etc/acdbdata
|
||||
dsp /vendor/dsp
|
||||
sensors/config /vendor/etc/sensors/config
|
||||
sensors/registry /mnt/vendor/persist/sensors/registry/registry
|
||||
sensors/sns_reg.conf /vendor/etc/sensors/sns_reg_config
|
||||
socinfo /sys/devices/soc0
|
||||
.TE
|
||||
|
||||
These files and directories should be populated with files from your device's
|
||||
Android firmware.
|
||||
|
||||
If you can pass the -R option, the convention is to pass -R
|
||||
/usr/share/qcom/SOC/VENDOR/DEVICE, where SOC is the SoC name in lowercase,
|
||||
VENDOR is the capitalized name of the device vendor, and DEVICE is the device
|
||||
name (e.g. -R /usr/share/qcom/sdm845/SHIFT/axolotl or -R
|
||||
/usr/share/qcom/sdm845/Thundercomm/db845c).
|
||||
|
||||
.SH AUTHORS
|
||||
hexagonrpcd was written by The HexagonRPC Contributors <https://github.com/linux-msm/hexagonrpc>
|
||||
.SH COPYRIGHT
|
||||
Copyright (C) 2024 The HexagonRPC Contributors <https://github.com/linux-msm/hexagonrpc>
|
||||
Copyright (C) 2023 The Sensor Shell Contributors <https://github.com/linux-msm/hexagonrpc>
|
||||
.PP
|
||||
Released under GPL-3+ license\&.
|
||||
|
||||
@@ -22,5 +22,6 @@
|
||||
#define HEXAGONRPC_BUILD_METHOD_DEFINITIONS 1
|
||||
|
||||
#include "interfaces/adsp_default_listener.def"
|
||||
#include "interfaces/apps_mem.def"
|
||||
#include "interfaces/apps_std.def"
|
||||
#include "interfaces/adsp_listener.def"
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* FastRPC operating system interface
|
||||
*
|
||||
* Copyright (C) 2024 The Sensor Shell Contributors
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERFACE_APPS_MEM_DEF
|
||||
#define INTERFACE_APPS_MEM_DEF
|
||||
|
||||
#include <libhexagonrpc/interface.h>
|
||||
|
||||
HEXAGONRPC_DEFINE_REMOTE_METHOD(2, apps_mem_request_map64, 8, 0, 4, 0)
|
||||
|
||||
#endif /* INTERFACE_APPS_MEM_DEF */
|
||||
Loaded 20 of 31 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user