qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC v2 2/6] Add the libnative library


From: Alex Bennée
Subject: Re: [RFC v2 2/6] Add the libnative library
Date: Thu, 15 Jun 2023 08:59:39 +0100
User-agent: mu4e 1.11.6; emacs 29.0.91

Yeqi Fu <fufuyqqqqqq@gmail.com> writes:

> Signed-off-by: Yeqi Fu <fufuyqqqqqq@gmail.com>
> ---
>  common-user/native/libnative.c | 65 ++++++++++++++++++++++++++++++++++
>  include/native/libnative.h     | 11 ++++++
>  include/native/native-func.h   | 11 ++++++
>  3 files changed, 87 insertions(+)
>  create mode 100644 common-user/native/libnative.c
>  create mode 100644 include/native/libnative.h
>  create mode 100644 include/native/native-func.h
>
> diff --git a/common-user/native/libnative.c b/common-user/native/libnative.c
> new file mode 100644
> index 0000000000..d40e43c6fe
> --- /dev/null
> +++ b/common-user/native/libnative.c
> @@ -0,0 +1,65 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#include "native/libnative.h"
> +#include "native/native-func.h"
> +
> +#define STR_MACRO(str) #str
> +#define STR(num) STR_MACRO(num)
> +
> +#if defined(TARGET_I386) || defined(TARGET_X86_64)
> +
> +/* 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) ";" : ::

I think we can do a little better and encode the ABI parameters in the
expression. I hacked up an example for Aarch64 which:

  - uses HLT instead of SVC (as user-space should never see a halt)
  - encodes the size of in and out args
  - terminates the sequence with HLT 0

Using HLT gives us 16 bits of encoding informaion per instruction while
still looking vaguely sane in debuggers/in_asm dumps. Decoding this in
the translator is left as an exercise for the reader:

--8<---------------cut here---------------start------------->8---
experiment: encode ABI parameters in libnative

3 files changed, 85 insertions(+), 13 deletions(-)
configure                      |  2 +-
include/native/native-func.h   | 21 ++++++++++++
common-user/native/libnative.c | 75 +++++++++++++++++++++++++++++++++++-------

modified   configure
@@ -1821,7 +1821,7 @@ native_flag_i386="-DTARGET_I386"
 native_flag_x86_64="-DTARGET_X86_64"
 native_flag_mips="-DTARGET_MIPS"
 native_flag_mips64="-DTARGET_MIPS64"
-native_flag_arm="-DTARGET_ARM"
+native_flag_arm="-DTARGET_ARM -marm"
 native_flag_aarch64="-DTARGET_AARCH64"
 
 (config_host_mak=common-user/native/config-host.mak
modified   include/native/native-func.h
@@ -8,4 +8,25 @@
 #define NATIVE_STRCMP 0x1005
 #define NATIVE_STRCAT 0x1006
 
+/*
+ * Argument encoding. We only really care about 3 types. The two base
+ * register sizes (32 and 64) and if the value is a pointer (in which
+ * case we need to adjust it g2h before passing to the native
+ * function).
+ */
+#define TYPE_NO_ARG  0x0
+#define TYPE_I32_ARG 0x1
+#define TYPE_I64_ARG 0x2
+#define TYPE_PTR_ARG 0x3
+
+/* Add an alias for the natural register size, it might be easier to
+ * pass this in */
+#if UINTPTR_MAX == 0xFFFFFFFF
+  #define TYPE_INT_ARG TYPE_I32_ARG
+#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu
+  #define TYPE_INT_ARG TYPE_I64_ARG
+#else
+  #error TBD pointer size
 #endif
+
+#endif /* __NATIVE_FUNC__ */
modified   common-user/native/libnative.c
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
 
 #include "native/libnative.h"
 #include "native/native-func.h"
@@ -20,15 +21,27 @@
 
 #if defined(TARGET_ARM) || defined(TARGET_AARCH64)
 
-/* unused syscall number */
+/*
+ * HLT is an invalid instruction for userspace and usefully has 16
+ * bits of spare immeadiate data which we can stuff data in.
+ */
+
 #define __PREFIX_INSTR \
-    "svc 0xff;"
+    "hlt 0xffff;"
 
-#define NATIVE_CALL_EXPR(func) \
-    __PREFIX_INSTR             \
-    ".word " STR(func) ";" : ::
+#define WRAP_NATIVE_CALL(id, types)                          \
+    do {                                                     \
+        __asm__ volatile(                                    \
+            __PREFIX_INSTR "\n\t"                            \
+            "hlt  %c0\n\t"                                   \
+            "hlt  %c1\n\t"                                   \
+            "hlt  0\n\t"                                     \
+            : /* no outputs */                               \
+            : "i" (id), "i" (types)                          \
+            : "memory");                                     \
+    } while (0)
 
-#endif
+#endif /* TARGET_ARM || TARGET_AARCH64 */
 
 #if defined(TARGET_MIPS) || defined(TARGET_MIPS64)
 
@@ -38,28 +51,66 @@
 
 #endif
 
+static inline const uint32_t encode_1out_3in(int rtype, int arg1, int arg2, 
int arg3)
+{
+    return (rtype & 0xf) |
+        ((arg1 & 0xf) << 4) |
+        ((arg2 & 0xf) << 8) |
+        ((arg3 & 0xf) << 12);
+}
+
+static inline const uint32_t encode_0out_3in(int arg1, int arg2, int arg3)
+{
+    return encode_1out_3in(TYPE_NO_ARG, arg1, arg2, arg3);
+}
+
+static inline const uint32_t encode_1out_2in(int rtype, int arg1, int arg2)
+{
+    return encode_1out_3in(rtype, arg1, arg2, TYPE_NO_ARG);
+}
+
 void *memcpy(void *dest, const void *src, size_t n)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCPY));
+    const uint32_t args = encode_1out_3in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG, TYPE_PTR_ARG,
+                                          TYPE_INT_ARG);
+    WRAP_NATIVE_CALL(NATIVE_MEMCPY, args);
 }
 
 int memcmp(const void *s1, const void *s2, size_t n)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMCMP));
+    const uint32_t args = encode_1out_3in(TYPE_INT_ARG,
+                                          TYPE_PTR_ARG, TYPE_PTR_ARG,
+                                          TYPE_INT_ARG);
+    WRAP_NATIVE_CALL(NATIVE_MEMCMP, args);
 }
+
 void *memset(void *s, int c, size_t n)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_MEMSET));
+    const uint32_t args = encode_1out_3in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_INT_ARG,
+                                          TYPE_INT_ARG);
+    WRAP_NATIVE_CALL(NATIVE_MEMSET, args);
 }
 char *strcpy(char *dest, const char *src)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCPY));
+    const uint32_t args = encode_1out_2in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG);
+    WRAP_NATIVE_CALL(NATIVE_STRCPY, args);
 }
 int strcmp(const char *s1, const char *s2)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCMP));
+    const uint32_t args = encode_1out_2in(TYPE_INT_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG);
+    WRAP_NATIVE_CALL(NATIVE_STRCMP, args);
 }
 char *strcat(char *dest, const char *src)
 {
-    __asm__ volatile(NATIVE_CALL_EXPR(NATIVE_STRCAT));
+    const uint32_t args = encode_1out_2in(TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG,
+                                          TYPE_PTR_ARG);
+    WRAP_NATIVE_CALL(NATIVE_STRCAT, args);
 }

--8<---------------cut here---------------end--------------->8---

It would be nice if we could pass the string of the function name as
well but I wasn't able to find the best way to get the address of the
string into the inline assembler. However that could be added later if
we want to make the interface even more generic.



> +
> +#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/include/native/libnative.h b/include/native/libnative.h
> new file mode 100644
> index 0000000000..d3c24f89f4
> --- /dev/null
> +++ b/include/native/libnative.h
> @@ -0,0 +1,11 @@
> +#ifndef __LIBNATIVE_H__
> +#define __LIBNATIVE_H__
> +
> +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);
> +
> +#endif /* __LIBNATIVE_H__ */
> diff --git a/include/native/native-func.h b/include/native/native-func.h
> new file mode 100644
> index 0000000000..d48a8e547a
> --- /dev/null
> +++ b/include/native/native-func.h
> @@ -0,0 +1,11 @@
> +#ifndef __NATIVE_FUNC_H__
> +#define __NATIVE_FUNC_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
> +
> +#endif


-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro



reply via email to

[Prev in Thread] Current Thread [Next in Thread]