diff --git a/Makefile b/Makefile index 7548bf0..2dcf394 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,8 @@ ifndef ARCH ARCH = arm else ifeq ($(findstring aarch64, $(GCC_TARGET)), aarch64) ARCH = arm64 + else ifeq ($(findstring riscv64, $(GCC_TARGET)), riscv64) + ARCH = riscv64 else $(error unable to detect arch: $(GCC_TARGET)) endif @@ -94,6 +96,8 @@ else ifeq ($(ARCH), arm) PCFLAGS += -marm else ifeq ($(ARCH), arm64) # do nothing +else ifeq ($(ARCH), riscv64) + # do nothing else $(error unsupported arch: $(ARCH)) endif diff --git a/README.md b/README.md index f74852a..4a08138 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ make COMPRESS_LZ4=1 CHECKSUM_MD5=1 ENCRYPT=1 ``` ##### cross compilation -Currently, supported architectures are x86_64, arm and arm64. You can cross compile memcr by providing `CROSS_COMPILE` prefix. i.e.: +Currently, supported architectures are x86_64, arm, arm64 and riscv64. You can cross compile memcr by providing `CROSS_COMPILE` prefix. i.e.: ``` make CROSS_COMPILE=arm-linux-gnueabihf- make CROSS_COMPILE=aarch64-linux-gnu- diff --git a/arch/cpu.h b/arch/cpu.h index 0f9531a..be83e9c 100644 --- a/arch/cpu.h +++ b/arch/cpu.h @@ -25,6 +25,8 @@ #include "arm/cpu.h" #elif defined(__aarch64__) #include "arm64/cpu.h" +#elif defined(__riscv_xlen) +#include "riscv64/cpu.h" #else #error unsupported arch #endif diff --git a/arch/riscv64/cpu.c b/arch/riscv64/cpu.c new file mode 100644 index 0000000..86297f2 --- /dev/null +++ b/arch/riscv64/cpu.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 Mariusz Kozłowski + * + * This program 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, version 2 + * of the license. + * + * 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 library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "../cpu.h" + +void set_cpu_regs(struct registers *regs, unsigned long *pc, unsigned long arg0, unsigned long arg1) +{ + regs->pc = (unsigned long)pc; /* point to the injected blob */ + regs->s6 = arg0; /* s6 used as arg0 to blob */ + regs->s7 = arg1; /* s7 used as arg1 to blob */ +} + +void *get_cpu_regs_sp(struct registers *regs) +{ + return (void *)regs->sp; +} + +void *get_cpu_regs_pc(struct registers *regs) +{ + return (void *)regs->pc; +} + +unsigned long get_cpu_syscall_ret(struct registers *regs) +{ + return regs->a0; +} + +#if 0 +unsigned long get_cpu_syscall_arg0(struct registers *regs) +{ + return regs->x10; +} +#endif + +void print_cpu_regs(struct registers *regs) +{ + int idx; + const char *rg_names[] = { + "pc", "ra", "sp", "gp", "tp", "t0", "t1", "t2", + "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" + }; + + for (idx = 0; idx < sizeof(*regs)/sizeof(regs->pc); idx++) { + fprintf(stdout, "regs[%s]\t %0*lx\n", rg_names[idx], 2 * (int)sizeof(unsigned long), ((unsigned long *)regs)[idx]); + } +} + +static_assert(sizeof(struct registers) == sizeof(struct user_regs_struct), "struct registers size mismatch"); diff --git a/arch/riscv64/cpu.h b/arch/riscv64/cpu.h new file mode 100644 index 0000000..9a53baa --- /dev/null +++ b/arch/riscv64/cpu.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 Mariusz Kozłowski + * + * This program 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, version 2 + * of the license. + * + * 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 library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +struct registers { + unsigned long pc; + unsigned long ra; + unsigned long sp; + unsigned long gp; + unsigned long tp; + unsigned long t0; + unsigned long t1; + unsigned long t2; + unsigned long s0; + unsigned long s1; + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; + unsigned long s2; + unsigned long s3; + unsigned long s4; + unsigned long s5; + unsigned long s6; + unsigned long s7; + unsigned long s8; + unsigned long s9; + unsigned long s10; + unsigned long s11; + unsigned long t3; + unsigned long t4; + unsigned long t5; + unsigned long t6; +}; diff --git a/arch/riscv64/enter.c b/arch/riscv64/enter.c new file mode 100644 index 0000000..1aa5afc --- /dev/null +++ b/arch/riscv64/enter.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2023 Mariusz Kozłowski + * Copyright (C) 2024 Wojciech Łazarski + * + * This program 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, version 2 + * of the license. + * + * 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 library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include + + +static void __attribute__((used)) container(void) +{ + /* + * Upon completion, each blob triggers debug trap to pass the + * control back to the main program. + */ + + /* rt_sigprocmask(), expects pointer to area for masks in a6 */ + + asm volatile( + ".global sigprocmask_blob \n" + ".align 3 \n" + "sigprocmask_blob: \n" + "li a7, 135 \n" /* __NR_rt_sigprocmask */ + "li a0, %0 \n" /* @how */ + "mv a1, s6 \n" /* @nset */ + "mv a2, s6 \n" /* @oset */ + "li a3, 8 \n" /* @sigsetsize */ + "ecall \n" + "ebreak \n" /* SIGTRAP */ + ".global sigprocmask_blob_size \n" + ".align 3 \n" + "sigprocmask_blob_size: \n" + ".int sigprocmask_blob_size - sigprocmask_blob \n" + :: "i" (SIG_SETMASK) + ); + + /* mmaps anon area for parasite_blob */ + asm volatile( + ".global mmap_blob \n" + ".align 3 \n" + "mmap_blob: \n" + "li a7, 222 \n" /* __NR_mmap2 */ + "li a0, 0 \n" /* @addr */ + "mv a1, s6 \n" /* @len */ + "li a2, %0 \n" /* @prot */ + "li a3, %1 \n" /* @flags */ + "li a4, -1 \n" /* @fd */ + "li a5, 0 \n" /* @off */ + "ecall \n" + "ebreak \n" /* SIGTRAP */ + ".global mmap_blob_size \n" + + ".align 3 \n" + "mmap_blob_size: \n" + ".int mmap_blob_size - mmap_blob \n" + :: "i" (PROT_EXEC | PROT_READ | PROT_WRITE), + "i" (MAP_ANONYMOUS | MAP_PRIVATE) + ); + + /* clones parasite, expects parasite address in a6 */ + asm volatile( + ".global clone_blob \n" + ".align 3 \n" + "clone_blob: \n" + "li a7, 220 \n" /* __NR_clone */ + "ld a0, CLONE_FLAGS \n" /* @flags */ + "li a1, 0 \n" /* @newsp */ + "li a2, 0 \n" /* @parent_tid */ + "li a3, 0 \n" /* @child_tid */ + "ecall \n" + "li a1, 0 \n" + "beq a0, a1, .child \n" + "ebreak \n" /* SIGTRAP */ + ".child: \n" + "jr s6 \n" /* br parasite */ + "CLONE_FLAGS: \n" + ".quad (%0 & 0xffffffff) \n" /* zero high .word */ + ".global clone_blob_size \n" + "clone_blob_size: \n" + + ".align 3 \n" + ".int clone_blob_size - clone_blob \n" + :: "i" (CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_SIGHAND | CLONE_SYSVSEM | CLONE_THREAD | CLONE_VM) + ); + + /* munmap anon area for parasite_blob, expects addr in a6 and len in a7 */ + asm volatile( + ".global munmap_blob \n" + ".align 3 \n" + "munmap_blob: \n" + "li a7, 215 \n" /* __NR_munmap */ + "mv a0, s6 \n" /* @addr */ + "mv a1, s7 \n" /* @len */ + "ecall \n" + "ebreak \n" /* SIGTRAP */ + ".global munmap_blob_size \n" + + ".align 3 \n" + "munmap_blob_size: \n" + ".int munmap_blob_size - munmap_blob \n" + ); +} diff --git a/arch/riscv64/linux-abi.h b/arch/riscv64/linux-abi.h new file mode 100644 index 0000000..3453403 --- /dev/null +++ b/arch/riscv64/linux-abi.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 Mariusz Kozłowski + * + * This program 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, version 2 + * of the license. + * + * 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 library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __LINUX_ABI_RISCV64_H__ +#define __LINUX_ABI_RISCV64_H__ + +static long syscall0(int nr) +{ + register long a7 asm("a7") = nr; + register long a0 asm("a0"); + asm volatile("ecall" + : "=r" (a0) + : "r" (a7) + : "memory"); + return a0; +} + +static long syscall1(int nr, unsigned long arg0) +{ + register long a7 asm("a7") = nr; + register long a0 asm("a0") = arg0; + asm volatile("ecall" + : "=r" (a0) + : "r" (a7), "r" (a0) + : "memory"); + return a0; +} + +static long syscall2(int nr, unsigned long arg0, unsigned long arg1) +{ + register long a7 asm("a7") = nr; + register long a0 asm("a0") = arg0; + register long a1 asm("a1") = arg1; + asm volatile("ecall" + : "=r" (a0) + : "r" (a7), "r" (a0), "r" (a1) + : "memory"); + return a0; +} + +static long syscall3(int nr, unsigned long arg0, unsigned long arg1, unsigned long arg2) +{ + register long a7 asm("a7") = nr; + register long a0 asm("a0") = arg0; + register long a1 asm("a1") = arg1; + register long a2 asm("a2") = arg2; + asm volatile("ecall" + : "=r" (a0) + : "r" (a7), "r" (a0), "r" (a1), "r" (a2) + : "memory"); + return a0; +} + +#if 0 +static long syscall4(int nr, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3) +{ + register long a7 asm("a7") = nr; + register long a0 asm("a0") = arg0; + register long a1 asm("a1") = arg1; + register long a2 asm("a2") = arg2; + register long a3 asm("a3") = arg3; + asm volatile("ecall" + : "=r" (a0) + : "r" (a7), "r" (a0), "r" (a1), "r" (a2), "r" (a3) + : "memory"); + return a0; +} +#endif + +#if 0 +static long syscall5(int nr, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) +{ + register long a7 asm("a7") = nr; + register long a0 asm("a0") = arg0; + register long a1 asm("a1") = arg1; + register long a2 asm("a2") = arg2; + register long a3 asm("a3") = arg3; + register long a4 asm("a4") = arg4; + asm volatile("ecall" + : "=r" (a0) + : "r" (a7), "r" (a0), "r" (a1), "r" (a2), "r" (a3), "r" (a4) + : "memory"); + return a0; +} +#endif + +#if 0 +static long syscall6(int nr, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) +{ + register long a7 asm("a7") = nr; + register long a0 asm("a0") = arg0; + register long a1 asm("a1") = arg1; + register long a2 asm("a2") = arg2; + register long a3 asm("a3") = arg3; + register long a4 asm("a4") = arg4; + register long a5 asm("a5") = arg5; + asm volatile("ecall" + : "=r" (a0) + : "r" (a7), "r" (a0), "r" (a1), "r" (a2), "r" (a3), "r" (a4), "r" (a5) + : "memory"); + return a0; +} +#endif + +#endif diff --git a/arch/riscv64/parasite-head.S b/arch/riscv64/parasite-head.S new file mode 100644 index 0000000..28304f6 --- /dev/null +++ b/arch/riscv64/parasite-head.S @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Mariusz Kozłowski + * Copyright (C) 2023 Wojciech Łazarski + * + * This program 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, version 2 + * of the license. + * + * 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 library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define PARASITE_STACK_SIZE 16384 +#define PARASITE_ARG_SIZE 4096 + +.section .head.text, "ax" + .globl __parasite_head_start + .align 3 + +__parasite_head_start: + lla a0, __parasite_stack + mv sp, a0 + lla a0, __parasite_args + j service + ebreak /* SIGTRAP */ + + .align 3 +__parasite_args: + .space PARASITE_ARG_SIZE, 0 + +__parasite_stack: + .space PARASITE_STACK_SIZE, 0 diff --git a/arch/riscv64/parasite.lds.S b/arch/riscv64/parasite.lds.S new file mode 100644 index 0000000..e401938 --- /dev/null +++ b/arch/riscv64/parasite.lds.S @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Mariusz Kozłowski + * + * This program 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, version 2 + * of the license. + * + * 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 library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +OUTPUT_ARCH(riscv) + +#https://www.spinics.net/lists/kernel/msg3571205.html + +SECTIONS +{ + .blob 0x0 : { + *(.head.text) + *(.text*) + . = ALIGN(32); + *(.data*) + . = ALIGN(32); + *(.rodata*) + . = ALIGN(32); + *(.bss*) + . = ALIGN(32); + } =0x00000000 + + /DISCARD/ : { + *(.debug*) + *(.comment*) + *(.note*) + *(.group*) + *(.eh_frame*) + } + #*(*) +} diff --git a/arch/syscall.c b/arch/syscall.c index e5e33d9..035f9cc 100644 --- a/arch/syscall.c +++ b/arch/syscall.c @@ -26,6 +26,8 @@ #include "arm/linux-abi.h" #elif defined(__aarch64__) #include "arm64/linux-abi.h" +#elif defined(__riscv_xlen) +#include "riscv64/linux-abi.h" #else #error unsupported arch #endif