diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c
index e4e030043f..3f173f2a53 100644
--- a/accel/tcg/tcg-runtime.c
+++ b/accel/tcg/tcg-runtime.c
@@ -148,3 +148,40 @@ void HELPER(exit_atomic)(CPUArchState *env)
 {
     cpu_loop_exit_atomic(env_cpu(env), GETPC());
 }
+
+target_ulong helper_native_call(CPUArchState *env, target_ulong arg1,
+                                target_ulong arg2, target_ulong arg3,
+                                uint32_t abi_map, uint32_t func_id, uint32_t mmu_idx)
+{
+    if (GET_ARG3(abi_map)) {
+        return do_native_3in1out(env, arg1, arg2, arg3, abi_map, func_id,
+                                 mmu_idx);
+    } else {
+        return do_native_2in1out(env, arg1, arg2, abi_map, func_id, mmu_idx);
+    }
+}
+
+
+uint32_t helper_native_call_i32(CPUArchState *env, uint32_t arg1, uint32_t arg2,
+                                uint32_t arg3, uint32_t abi_map,
+                                uint32_t func_id, uint32_t mmu_idx)
+{
+    if (GET_ARG3(abi_map)) {
+        return do_native_3in1out(env, arg1, arg2, arg3, abi_map, func_id,
+                                 mmu_idx);
+    } else {
+        return do_native_2in1out(env, arg1, arg2, abi_map, func_id, mmu_idx);
+    }
+}
+
+uint64_t helper_native_call_i64(CPUArchState *env, uint64_t arg1, uint64_t arg2,
+                                uint64_t arg3, uint32_t abi_map,
+                                uint32_t func_id, uint32_t mmu_idx)
+{
+    if (GET_ARG3(abi_map)) {
+        return do_native_3in1out(env, arg1, arg2, arg3, abi_map, func_id,
+                                 mmu_idx);
+    } else {
+        return do_native_2in1out(env, arg1, arg2, abi_map, func_id, mmu_idx);
+    }
+}
diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
index 6f8c2061d0..3b1fe606e5 100644
--- a/accel/tcg/tcg-runtime.h
+++ b/accel/tcg/tcg-runtime.h
@@ -300,3 +300,7 @@ DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
 
 DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_7(native_call, tl, env, tl, tl, tl, i32, i32, i32)
+DEF_HELPER_7(native_call_i32, i32, env, i32, i32, i32, i32, i32, i32)
+DEF_HELPER_7(native_call_i64, i64, env, i64, i64, i64, i32, i32, i32)
diff --git a/include/native/native-calls.h b/include/native/native-calls.h
new file mode 100644
index 0000000000..5c99344a65
--- /dev/null
+++ b/include/native/native-calls.h
@@ -0,0 +1,75 @@
+#include "exec/cpu_ldst.h"
+#include "cpu.h"
+#include "native-defs.h"
+
+typedef uintptr_t (*nfunc_2in1out)(uintptr_t, uintptr_t);
+typedef uintptr_t (*nfunc_3in1out)(uintptr_t, uintptr_t, uintptr_t);
+
+static inline uintptr_t decode_arg(CPUArchState *env, abi_ptr arg,
+                                   uintptr_t size, uint8_t type,
+                                   uint32_t mmu_idx);
+abi_ptr do_native_2in1out(CPUArchState *env, abi_ptr arg1, abi_ptr arg2,
+                          uint32_t abi_map, uint32_t func_id, uint32_t mmu_idx);
+abi_ptr do_native_3in1out(CPUArchState *env, abi_ptr arg1, abi_ptr arg2,
+                          abi_ptr arg3, uint32_t abi_map, uint32_t func_id,
+                          uint32_t mmu_idx);
+
+nfunc_3in1out func3in1out_array[] = {
+    (nfunc_3in1out)NULL,   (nfunc_3in1out)memcpy,  (nfunc_3in1out)memcmp,
+    (nfunc_3in1out)memset, (nfunc_3in1out)strncpy, (nfunc_3in1out)strncmp,
+};
+
+nfunc_2in1out func2in1out_array[] = {
+    (nfunc_2in1out)NULL,
+    (nfunc_2in1out)strcpy,
+    (nfunc_2in1out)strcmp,
+};
+
+static inline uintptr_t decode_arg(CPUArchState *env, abi_ptr arg,
+                                   uintptr_t size, uint8_t type,
+                                   uint32_t mmu_idx)
+{
+    if (IS_ARG_PTR(type)) {
+        uintptr_t ra = GETPC();
+        void *host;
+        if (IS_ARG_WRITE(type)) {
+            host = probe_write(env, arg, size, mmu_idx, ra);
+            return (uintptr_t)host;
+        }
+        if (IS_ARG_READ(type)) {
+            host = probe_read(env, arg, size, mmu_idx, ra);
+            return (uintptr_t)host;
+        }
+        CPUState *cs = env_cpu(env);
+        return (uintptr_t)g2h(cs, arg);
+    }
+    return (uintptr_t)arg;
+}
+
+/* Currently, there is no distinction between int32, int64, and int. Their type
+ * conversions should be implemented. */
+abi_ptr do_native_3in1out(CPUArchState *env, abi_ptr arg1, abi_ptr arg2,
+                          abi_ptr arg3, uint32_t abi_map, uint32_t func_id,
+                          uint32_t mmu_idx)
+{
+    uintptr_t n = (uintptr_t)arg3;
+    uintptr_t n1 = decode_arg(env, arg1, n, GET_ARG1(abi_map), mmu_idx);
+    uintptr_t n2 = decode_arg(env, arg2, n, GET_ARG2(abi_map), mmu_idx);
+    nfunc_3in1out fn = func3in1out_array[func_id - 0x1000];
+    uintptr_t r = fn(n1, n2, n);
+    return IS_PTR_RVALUE(abi_map) ? h2g(r) : (target_ulong)r;
+}
+
+
+abi_ptr do_native_2in1out(CPUArchState *env, abi_ptr arg1, abi_ptr arg2,
+                          uint32_t abi_map, uint32_t func_id, uint32_t mmu_idx)
+{
+    CPUState *cs = env_cpu(env);
+    uintptr_t n1 =
+        IS_PTR_ARG1(abi_map) ? (uintptr_t)g2h(cs, arg1) : (uintptr_t)arg1;
+    uintptr_t n2 =
+        IS_PTR_ARG2(abi_map) ? (uintptr_t)g2h(cs, arg2) : (uintptr_t)arg2;
+    nfunc_2in1out fn = func2in1out_array[func_id - 0x2000];
+    uintptr_t r = fn(n1, n2);
+    return IS_PTR_RVALUE(abi_map) ? h2g(r) : (target_ulong)r;
+}
