and run the
'../configure --enable-user-native-call && make' command.
Signed-off-by: Yeqi Fu <fufuyqqq...@gmail.com>
---
include/exec/user/native-func.h | 8 +++
libnative.c | 76 ++++++++++++++++++++++++++++
meson.build | 8 +++
meson_options.txt | 2 +
scripts/make-config-poison.sh | 4 +-
scripts/meson-buildoptions.sh | 4 ++
target/arm/helper.c | 47 +++++++++++++++++
target/arm/helper.h | 6 +++
target/arm/tcg/translate-a64.c | 32 ++++++++++++
target/arm/tcg/translate.c | 30 ++++++++++-
target/arm/tcg/translate.h | 8 +++
target/i386/helper.h | 6 +++
target/i386/tcg/translate.c | 20 ++++++++
target/i386/tcg/user/meson.build | 1 +
target/i386/tcg/user/native_helper.c | 63 +++++++++++++++++++++++
target/mips/helper.h | 6 +++
target/mips/tcg/meson.build | 1 +
target/mips/tcg/native_helper.c | 55 ++++++++++++++++++++
target/mips/tcg/translate.c | 22 +++++++-
19 files changed, 396 insertions(+), 3 deletions(-)
create mode 100644 include/exec/user/native-func.h
create mode 100644 libnative.c
create mode 100644 target/i386/tcg/user/native_helper.c
create mode 100644 target/mips/tcg/native_helper.c
diff --git a/include/exec/user/native-func.h b/include/exec/user/native-func.h
new file mode 100644
index 0000000000..8eaac03299
--- /dev/null
+++ b/include/exec/user/native-func.h
@@ -0,0 +1,8 @@
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+#define NATIVE_MEMCPY 0x1001
+#define NATIVE_MEMCMP 0x1002
+#define NATIVE_MEMSET 0x1003
+#define NATIVE_STRCPY 0x1004
+#define NATIVE_STRCMP 0x1005
+#define NATIVE_STRCAT 0x1006
+#endif /* CONFIG_USER_NATIVE_CALL */
diff --git a/libnative.c b/libnative.c
new file mode 100644
index 0000000000..cc65c8270c
--- /dev/null
+++ b/libnative.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NATIVE_MEMCPY 0x1001
+#define NATIVE_MEMCMP 0x1002
+#define NATIVE_MEMSET 0x1003
+#define NATIVE_STRCPY 0x1004
+#define NATIVE_STRCMP 0x1005
+#define NATIVE_STRCAT 0x1006
+
+void *memcpy(void *dest, const void *src, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+void *memset(void *s, int c, size_t n);
+char *strcpy(char *dest, const char *src);
+int strcmp(const char *s1, const char *s2);
+char *strcat(char *dest, const char *src);
+
+#define STR_MACRO(str) #str
+#define STR(num) STR_MACRO(num)
+
+#if defined(TARGET_X86_64) || defined(TARGET_I386)
+
+/* unused opcode */
+#define __PREFIX_INSTR \
+ ".byte 0x0f,0xff;"
+
+#define NATIVE_CALL_EXPR(func) \
+ __PREFIX_INSTR \
+ ".word " STR(func) ";" : : :
+#endif
+
+#if defined(TARGET_ARM) || defined(TARGET_AARCH64)
+
+/* unused syscall number */
+#define __PREFIX_INSTR \
+ "svc 0xff;"
+
+#define NATIVE_CALL_EXPR(func) \
+ __PREFIX_INSTR \
+ ".word " STR(func) ";" : : :
+
+#endif
+
+#if defined(TARGET_MIPS) || defined(TARGET_MIPS64)
+
+/* unused bytes in syscall instructions */
+#define NATIVE_CALL_EXPR(func) \
+ ".long " STR((0x1 << 24) + (func << 8) + 0xC) ";" : : :
+
+#endif
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+ __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCPY));
+}
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCMP));
+}
+void *memset(void *s, int c, size_t n)
+{
+ __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMSET));
+}
+char *strcpy(char *dest, const char *src)
+{
+ __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCPY));
+}
+int strcmp(const char *s1, const char *s2)
+{
+ __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCMP));
+}
+char *strcat(char *dest, const char *src)
+{
+ __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCAT));
+}
diff --git a/meson.build b/meson.build
index 0a5cdefd4d..04e99a4f25 100644
--- a/meson.build
+++ b/meson.build
@@ -2012,6 +2012,11 @@ have_virtfs_proxy_helper =
get_option('virtfs_proxy_helper') \
.require(libcap_ng.found(), error_message: 'the virtfs proxy helper
requires libcap-ng') \
.allowed()
+have_user_native_call = get_option('user_native_call') \
+ .require(have_user, error_message: 'user_native_call requires user') \
+ .require(targetos == 'linux', error_message: 'user_native_call requires
Linux') \
+ .allowed()
+
if get_option('block_drv_ro_whitelist') == ''
config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '')
else
@@ -2853,6 +2858,9 @@ foreach target : target_dirs
error('Target @0@ is only available on a Linux host'.format(target))
endif
config_target += { 'CONFIG_LINUX_USER': 'y' }
+ if have_user_native_call
+ config_target += { 'CONFIG_USER_NATIVE_CALL': 'y' }
+ endif
elif target.endswith('bsd-user')
if 'CONFIG_BSD' not in config_host
if default_targets
diff --git a/meson_options.txt b/meson_options.txt
index 90237389e2..148dfc99d8 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -352,3 +352,5 @@ option('slirp_smbd', type : 'feature', value : 'auto',
option('hexagon_idef_parser', type : 'boolean', value : true,
description: 'use idef-parser to automatically generate TCG code for
the Hexagon frontend')
+option('user_native_call', type : 'feature', value : 'auto',
+ description: 'use native code for user mode emulation')
diff --git a/scripts/make-config-poison.sh b/scripts/make-config-poison.sh
index 1892854261..cf2ba69949 100755
--- a/scripts/make-config-poison.sh
+++ b/scripts/make-config-poison.sh
@@ -5,10 +5,12 @@ if test $# = 0; then
fi
# Create list of config switches that should be poisoned in common code...
-# but filter out CONFIG_TCG and CONFIG_USER_ONLY which are special.
+# but filter out CONFIG_TCG, CONFIG_USER_ONLY and CONFIG_USER_NATIVE_CALL
+# which are special.
exec sed -n \
-e' /CONFIG_TCG/d' \
-e '/CONFIG_USER_ONLY/d' \
+ -e '/CONFIG_USER_NATIVE_CALL/d' \
-e '/^#define / {' \
-e 's///' \
-e 's/ .*//' \
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 5714fd93d9..9e98db9eb7 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -173,6 +173,8 @@ meson_options_help() {
printf "%s\n" ' tpm TPM support'
printf "%s\n" ' u2f U2F emulation support'
printf "%s\n" ' usb-redir libusbredir support'
+ printf "%s\n" ' user-native-call'
+ printf "%s\n" ' use native code for user mode emulation'
printf "%s\n" ' vde vde network backend support'
printf "%s\n" ' vdi vdi image format support'
printf "%s\n" ' vduse-blk-export'
@@ -472,6 +474,8 @@ _meson_option_parse() {
--disable-u2f) printf "%s" -Du2f=disabled ;;
--enable-usb-redir) printf "%s" -Dusb_redir=enabled ;;
--disable-usb-redir) printf "%s" -Dusb_redir=disabled ;;
+ --enable-user-native-call) printf "%s" -Duser_native_call=enabled ;;
+ --disable-user-native-call) printf "%s" -Duser_native_call=disabled ;;
--enable-vde) printf "%s" -Dvde=enabled ;;
--disable-vde) printf "%s" -Dvde=disabled ;;
--enable-vdi) printf "%s" -Dvdi=enabled ;;
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 0b7fd2e7e6..03fbc3724b 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -25,6 +25,7 @@
#include "sysemu/tcg.h"
#include "qapi/error.h"
#include "qemu/guest-random.h"
+#include "exec/cpu_ldst.h"
#ifdef CONFIG_TCG
#include "semihosting/common-semi.h"
#endif
@@ -12045,3 +12046,49 @@ void aarch64_sve_change_el(CPUARMState *env, int
old_el,
}
}
#endif
+
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+
+#define NATIVE_FN_W_3W() \
+ target_ulong arg0, arg1, arg2; \
+ arg0 = env->regs[0]; \
+ arg1 = env->regs[1]; \
+ arg2 = env->regs[2];
+
+void helper_native_memcpy(CPUARMState *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ void *ret;
+ void *dest = g2h(cs, arg0);
+ void *src = g2h(cs, arg1);
+ size_t n = (size_t)arg2;
+ ret = memcpy(dest, src, n);
+ env->regs[0] = (target_ulong)h2g(ret);
+}
+
+void helper_native_memcmp(CPUARMState *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ int ret;
+ void *s1 = g2h(cs, arg0);
+ void *s2 = g2h(cs, arg1);
+ size_t n = (size_t)arg2;
+ ret = memcmp(s1, s2, n);
+ env->regs[0] = ret;
+}
+
+void helper_native_memset(CPUARMState *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ void *ret;
+ void *s = g2h(cs, arg0);
+ int c = (int)arg1;
+ size_t n = (size_t)arg2;
+ ret = memset(s, c, n);
+ env->regs[0] = (target_ulong)h2g(ret);
+}
+
+#endif
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 3335c2b10b..57144bf6fb 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -1038,6 +1038,12 @@ DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG,
DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, i32)
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+DEF_HELPER_1(native_memcpy, void, env)
+DEF_HELPER_1(native_memcmp, void, env)
+DEF_HELPER_1(native_memset, void, env)
+#endif
+
#ifdef TARGET_AARCH64
#include "tcg/helper-a64.h"
#include "tcg/helper-sve.h"
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 741a608739..a0ae96a2fd 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -35,6 +35,7 @@
#include "cpregs.h"
#include "translate-a64.h"
#include "qemu/atomic128.h"
+#include "exec/user/native-func.h"
static TCGv_i64 cpu_X[32];
static TCGv_i64 cpu_pc;
@@ -42,6 +43,11 @@ static TCGv_i64 cpu_pc;
/* Load/store exclusive handling */
static TCGv_i64 cpu_exclusive_high;
+#if defined(CONFIG_USER_ONLY) && defined(TARGET_AARCH64) && \
+ defined(CONFIG_USER_NATIVE_CALL)
+bool native_call_status;
+#endif
+
static const char *regnames[] = {
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
@@ -2292,6 +2298,12 @@ static void disas_exc(DisasContext *s, uint32_t insn)
gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2);
break;
}
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+ else if (imm16 == 0xff) {
+ native_call_status = true;
+ break;
+ }
+#endif
gen_ss_advance(s);
gen_exception_insn(s, 4, EXCP_SWI, syndrome);
break;
@@ -14203,6 +14215,26 @@ static void aarch64_tr_translate_insn(DisasContextBase
*dcbase, CPUState *cpu)
s->fp_access_checked = false;
s->sve_access_checked = false;
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+ if (native_call_status) {
+ switch (insn) {
+ case NATIVE_MEMCPY:
+ gen_helper_native_memcpy(cpu_env);
+ break;
+ case NATIVE_MEMCMP:
+ gen_helper_native_memcmp(cpu_env);
+ break;
+ case NATIVE_MEMSET:
+ gen_helper_native_memset(cpu_env);
+ break;
+ default:
+ unallocated_encoding(s);
+ }
+ native_call_status = false;
+ return;
+ }
+#endif
+
if (s->pstate_il) {
/*
* Illegal execution state. This has priority over BTI
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index 7468476724..9d84cad960 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -34,7 +34,7 @@
#include "exec/helper-gen.h"
#include "exec/log.h"
#include "cpregs.h"
-
+#include "exec/user/native-func.h"
#define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T)
#define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5)
@@ -58,6 +58,11 @@ TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF;
TCGv_i64 cpu_exclusive_addr;
TCGv_i64 cpu_exclusive_val;
+#if defined(CONFIG_USER_ONLY) && !defined(TARGET_AARCH64) \
+ && defined(CONFIG_USER_NATIVE_CALL)
+bool native_call_status;
+#endif
+
#include "exec/gen-icount.h"
static const char * const regnames[] =
@@ -8576,6 +8581,10 @@ static bool trans_SVC(DisasContext *s, arg_SVC *a)
if (s->fgt_svc) {
uint32_t syndrome = syn_aa32_svc(a->imm, s->thumb);
gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2);
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+ } else if (a->imm == 0xff) {
+ native_call_status = true;
+#endif
} else {
gen_update_pc(s, curr_insn_len(s));
s->svc_imm = a->imm;
@@ -9372,6 +9381,25 @@ static void arm_tr_translate_insn(DisasContextBase
*dcbase, CPUState *cpu)
insn = arm_ldl_code(env, &dc->base, pc, dc->sctlr_b);
dc->insn = insn;
dc->base.pc_next = pc + 4;
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+ if (native_call_status) {
+ switch (insn) {
+ case NATIVE_MEMCPY:
+ gen_helper_native_memcpy(cpu_env);
+ break;
+ case NATIVE_MEMCMP:
+ gen_helper_native_memcmp(cpu_env);
+ break;
+ case NATIVE_MEMSET:
+ gen_helper_native_memset(cpu_env);
+ break;
+ default:
+ unallocated_encoding(dc);
+ }
+ native_call_status = false;
+ return;
+ }
+#endif
disas_arm_insn(dc, insn);
arm_post_translate_insn(dc);
diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
index a9d1f4adc2..2291850f3c 100644
--- a/target/arm/tcg/translate.h
+++ b/target/arm/tcg/translate.h
@@ -161,6 +161,14 @@ extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF;
extern TCGv_i64 cpu_exclusive_addr;
extern TCGv_i64 cpu_exclusive_val;
+/*
+ * Indicate whether the next instruction is a native function call (true)
+ * or not (false).
+ */
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+extern bool native_call_status;
+#endif
+
/*
* Constant expanders for the decoders.
*/
diff --git a/target/i386/helper.h b/target/i386/helper.h
index e627a93107..6c91655887 100644
--- a/target/i386/helper.h
+++ b/target/i386/helper.h
@@ -221,3 +221,9 @@ DEF_HELPER_3(rcrq, tl, env, tl, tl)
#endif
DEF_HELPER_1(rdrand, tl, env)
+
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+DEF_HELPER_1(native_memcpy, void, env)
+DEF_HELPER_1(native_memcmp, void, env)
+DEF_HELPER_1(native_memset, void, env)
+#endif
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index 91c9c0c478..068f594532 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -33,6 +33,7 @@
#include "helper-tcg.h"
#include "exec/log.h"
+#include "exec/user/native-func.h"
#define PREFIX_REPZ 0x01
#define PREFIX_REPNZ 0x02
@@ -6806,6 +6807,25 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
case 0x1d0 ... 0x1fe:
disas_insn_new(s, cpu, b);
break;
+ /* One unknown opcode for native call */
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+ case 0x1ff:
+ uint16_t sig = x86_lduw_code(env, s);
+ switch (sig) {
+ case NATIVE_MEMCPY:
+ gen_helper_native_memcpy(cpu_env);
+ break;
+ case NATIVE_MEMSET:
+ gen_helper_native_memset(cpu_env);
+ break;
+ case NATIVE_MEMCMP:
+ gen_helper_native_memcmp(cpu_env);
+ break;
+ default:
+ goto unknown_op;
+ }
+ break;
+#endif
default:
goto unknown_op;
}
diff --git a/target/i386/tcg/user/meson.build b/target/i386/tcg/user/meson.build
index 1df6bc4343..490808bd65 100644
--- a/target/i386/tcg/user/meson.build
+++ b/target/i386/tcg/user/meson.build
@@ -1,4 +1,5 @@
i386_user_ss.add(when: ['CONFIG_TCG', 'CONFIG_USER_ONLY'], if_true: files(
'excp_helper.c',
'seg_helper.c',
+ 'native_helper.c',
))
diff --git a/target/i386/tcg/user/native_helper.c
b/target/i386/tcg/user/native_helper.c
new file mode 100644
index 0000000000..300e14d71a
--- /dev/null
+++ b/target/i386/tcg/user/native_helper.c
@@ -0,0 +1,63 @@
+/*
+ * native function call helpers
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+#include "tcg/helper-tcg.h"
+#include "tcg/seg_helper.h"
+
+#ifdef TARGET_X86_64
+#define NATIVE_FN_W_3W() \
+ target_ulong arg0, arg1, arg2; \
+ arg0 = env->regs[R_EDI]; \
+ arg1 = env->regs[R_ESI]; \
+ arg2 = env->regs[R_EDX];
+#else
+/* linux x86 has several calling conventions. The following implementation
+ is for the most commonly used cdecl calling convention. */
+#define NATIVE_FN_W_3W() \
+ target_ulong arg0, arg1, arg2; \
+ arg0 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 4); \
+ arg1 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 8); \
+ arg2 = *(target_ulong *)g2h(cs, env->regs[R_ESP] + 12);
+#endif
+
+void helper_native_memcpy(CPUX86State *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ void *ret;
+ void *dest = g2h(cs, arg0);
+ void *src = g2h(cs, arg1);
+ size_t n = (size_t)arg2;
+ ret = memcpy(dest, src, n);
+ env->regs[R_EAX] = (target_ulong)h2g(ret);
+}
+
+void helper_native_memcmp(CPUX86State *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ int ret;
+ void *s1 = g2h(cs, arg0);
+ void *s2 = g2h(cs, arg1);
+ size_t n = (size_t)arg2;
+ ret = memcmp(s1, s2, n);
+ env->regs[R_EAX] = ret;
+}
+
+void helper_native_memset(CPUX86State *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ void *ret;
+ void *s = g2h(cs, arg0);
+ int c = (int)arg1;
+ size_t n = (size_t)arg2;
+ ret = memset(s, c, n);
+ env->regs[R_EAX] = (target_ulong)h2g(ret);
+}
diff --git a/target/mips/helper.h b/target/mips/helper.h
index de32d82e98..9fa949d78c 100644
--- a/target/mips/helper.h
+++ b/target/mips/helper.h
@@ -589,6 +589,12 @@ DEF_HELPER_FLAGS_3(dmthlip, 0, void, tl, tl, env)
DEF_HELPER_FLAGS_3(wrdsp, 0, void, tl, tl, env)
DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env)
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+DEF_HELPER_1(native_memcpy, void, env)
+DEF_HELPER_1(native_memcmp, void, env)
+DEF_HELPER_1(native_memset, void, env)
+#endif
+
#ifndef CONFIG_USER_ONLY
#include "tcg/sysemu_helper.h.inc"
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build
index 7ee969ec8f..fb1ea64047 100644
--- a/target/mips/tcg/meson.build
+++ b/target/mips/tcg/meson.build
@@ -22,6 +22,7 @@ mips_ss.add(files(
'txx9_translate.c',
'vr54xx_helper.c',
'vr54xx_translate.c',
+ 'native_helper.c',
))
mips_ss.add(when: 'TARGET_MIPS64', if_true: files(
'tx79_translate.c',
diff --git a/target/mips/tcg/native_helper.c b/target/mips/tcg/native_helper.c
new file mode 100644
index 0000000000..bfd9c92e17
--- /dev/null
+++ b/target/mips/tcg/native_helper.c
@@ -0,0 +1,55 @@
+/*
+ * native function call helpers
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+
+#define NATIVE_FN_W_3W() \
+ target_ulong arg0, arg1, arg2; \
+ arg0 = env->active_tc.gpr[4]; /*"a0"*/ \
+ arg1 = env->active_tc.gpr[5]; /*"a1"*/ \
+ arg2 = env->active_tc.gpr[6]; /*"a2"*/
+
+void helper_native_memcpy(CPUMIPSState *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ void *ret;
+ void *dest = g2h(cs, arg0);
+ void *src = g2h(cs, arg1);
+ size_t n = (size_t)arg2;
+ ret = memcpy(dest, src, n);
+ env->active_tc.gpr[2] = (target_ulong)h2g(ret);
+}
+
+void helper_native_memcmp(CPUMIPSState *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ int ret;
+ void *s1 = g2h(cs, arg0);
+ void *s2 = g2h(cs, arg1);
+ size_t n = (size_t)arg2;
+ ret = memcmp(s1, s2, n);
+ env->active_tc.gpr[2] = ret;
+}
+
+void helper_native_memset(CPUMIPSState *env)
+{
+ CPUState *cs = env_cpu(env);
+ NATIVE_FN_W_3W();
+ void *ret;
+ void *s = g2h(cs, arg0);
+ int c = (int)arg1;
+ size_t n = (size_t)arg2;
+ ret = memset(s, c, n);
+ env->active_tc.gpr[2] = (target_ulong)h2g(ret);
+}
+
+#endif
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
index a6ca2e5a3b..8236dfba21 100644
--- a/target/mips/tcg/translate.c
+++ b/target/mips/tcg/translate.c
@@ -36,6 +36,7 @@
#include "qemu/qemu-print.h"
#include "fpu_helper.h"
#include "translate.h"
+#include "exec/user/native-func.h"
/*
* Many sysemu-only helpers are not reachable for user-only.
@@ -13591,7 +13592,26 @@ static void decode_opc_special(CPUMIPSState *env,
DisasContext *ctx)
gen_helper_pmon(cpu_env, tcg_constant_i32(sa));
#endif
break;
- case OPC_SYSCALL:
+ case OPC_SYSCALL: /* 00 00 00 0C */
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USER_NATIVE_CALL)
+ if ((((ctx->opcode) >> 24) & 0xff) == 0x1) {
+ uint16_t sig = (ctx->opcode) >> 8 & 0xffff;
+ switch (sig) {
+ case NATIVE_MEMCPY:
+ gen_helper_native_memcpy(cpu_env);
+ break;
+ case NATIVE_MEMSET:
+ gen_helper_native_memset(cpu_env);
+ break;
+ case NATIVE_MEMCMP:
+ gen_helper_native_memcmp(cpu_env);
+ break;
+ default:
+ gen_reserved_instruction(ctx);
+ }
+ break;
+ }
+#endif
generate_exception_end(ctx, EXCP_SYSCALL);
break;
case OPC_BREAK: