[3/3] arm64: Introduce arm64 support

Message ID 1383930736-20005-4-git-send-email-steve.capper@linaro.org
State New
Headers show

Commit Message

Steve Capper Nov. 8, 2013, 5:12 p.m.
Based on work by Neil Williams (codehelp@debian.org) and Anil Singhar
(anil.singhar@linaro.org), this patch introduces arm64 support.

Signed-off-by: Steve Capper <steve.capper@linaro.org>
---
 Makefile                                  |  3 +-
 usr/include/arch/arm64/klibc/archconfig.h | 17 ++++++++++
 usr/include/arch/arm64/klibc/archsetjmp.h | 20 ++++++++++++
 usr/include/arch/arm64/klibc/archsignal.h | 14 +++++++++
 usr/include/arch/arm64/klibc/archstat.h   | 29 +++++++++++++++++
 usr/include/arch/arm64/klibc/asmmacros.h  | 11 +++++++
 usr/klibc/README.klibc                    |  1 +
 usr/klibc/SYSCALLS.def                    |  8 ++---
 usr/klibc/arch/arm64/Kbuild               |  7 +++++
 usr/klibc/arch/arm64/MCONFIG              | 23 ++++++++++++++
 usr/klibc/arch/arm64/crt0.S               | 19 +++++++++++
 usr/klibc/arch/arm64/pipe.c               | 10 ++++++
 usr/klibc/arch/arm64/setjmp.S             | 52 +++++++++++++++++++++++++++++++
 usr/klibc/arch/arm64/syscall.S            | 25 +++++++++++++++
 usr/klibc/arch/arm64/sysstub.ph           | 25 +++++++++++++++
 usr/klibc/arch/arm64/vfork.S              | 34 ++++++++++++++++++++
 16 files changed, 293 insertions(+), 5 deletions(-)
 create mode 100644 usr/include/arch/arm64/klibc/archconfig.h
 create mode 100644 usr/include/arch/arm64/klibc/archsetjmp.h
 create mode 100644 usr/include/arch/arm64/klibc/archsignal.h
 create mode 100644 usr/include/arch/arm64/klibc/archstat.h
 create mode 100644 usr/include/arch/arm64/klibc/asmmacros.h
 create mode 100644 usr/klibc/arch/arm64/Kbuild
 create mode 100644 usr/klibc/arch/arm64/MCONFIG
 create mode 100644 usr/klibc/arch/arm64/crt0.S
 create mode 100644 usr/klibc/arch/arm64/pipe.c
 create mode 100644 usr/klibc/arch/arm64/setjmp.S
 create mode 100644 usr/klibc/arch/arm64/syscall.S
 create mode 100644 usr/klibc/arch/arm64/sysstub.ph
 create mode 100644 usr/klibc/arch/arm64/vfork.S

Comments

H. Peter Anvin Nov. 8, 2013, 5:24 p.m. | #1
On 11/08/2013 09:12 AM, Steve Capper wrote:
> +
> +/*
> + * x19-x28 are callee saved, also save fp, lr, sp.
> + * d8-d15 are also callee saved.
> + */
> +
> +struct __jmp_buf {
> +	uint64_t __gregs[13];
> +	uint64_t __fpregs[8];
> +};
> +

Since the index of these arrays have no connection with what is stored
in them, they should be named fields in the structure, not an array.

Do we need the fpregs saved even though klibc doesn't do fp?

> diff --git a/usr/klibc/arch/arm64/pipe.c b/usr/klibc/arch/arm64/pipe.c
> new file mode 100644
> index 0000000..f10a69e
> --- /dev/null
> +++ b/usr/klibc/arch/arm64/pipe.c
> @@ -0,0 +1,10 @@
> +#include <unistd.h>
> +
> +#ifndef __NR_pipe
> +
> +int pipe(int pipefd[2])
> +{
> +	return pipe2(pipefd, 0);
> +}
> +
> +#endif  /* __NR_pipe */

This is a generic routine, right?  It should be part of the generic
ersatz functions, no?

	-hpa
H. Peter Anvin Nov. 8, 2013, 7:34 p.m. | #2
On 11/08/2013 09:12 AM, Steve Capper wrote:
> -<!ppc64> int stat64,stat::stat(const char *, struct stat *);
> -<!ppc64> int lstat64,lstat::lstat(const char *, struct stat *);
> +<!arm64,ppc64> int stat64,stat::stat(const char *, struct stat *);
> +<!arm64,ppc64> int lstat64,lstat::lstat(const char *, struct stat *);

This should have been part of the previous patch but using <?!ppc64>.

	-hpa
Steve Capper Nov. 11, 2013, 2:50 p.m. | #3
On Fri, Nov 08, 2013 at 09:24:06AM -0800, H. Peter Anvin wrote:
> On 11/08/2013 09:12 AM, Steve Capper wrote:
> > +
> > +/*
> > + * x19-x28 are callee saved, also save fp, lr, sp.
> > + * d8-d15 are also callee saved.
> > + */
> > +
> > +struct __jmp_buf {
> > +	uint64_t __gregs[13];
> > +	uint64_t __fpregs[8];
> > +};
> > +
> 
> Since the index of these arrays have no connection with what is stored
> in them, they should be named fields in the structure, not an array.
> 
> Do we need the fpregs saved even though klibc doesn't do fp?
> 

I agree about the named struct, it looks a lot nicer, thanks.

For gcc targetting Aarch64, We can only guarantee that d8-d15 are
left alone when -mgeneral-regs-only is supplied for building klibc
and any software linked against klibc. I would much prefer to
save/restore d8-d15 to avoid potential future headaches.

> > diff --git a/usr/klibc/arch/arm64/pipe.c b/usr/klibc/arch/arm64/pipe.c
> > new file mode 100644
> > index 0000000..f10a69e
> > --- /dev/null
> > +++ b/usr/klibc/arch/arm64/pipe.c
> > @@ -0,0 +1,10 @@
> > +#include <unistd.h>
> > +
> > +#ifndef __NR_pipe
> > +
> > +int pipe(int pipefd[2])
> > +{
> > +	return pipe2(pipefd, 0);
> > +}
> > +
> > +#endif  /* __NR_pipe */
> 
> This is a generic routine, right?  It should be part of the generic
> ersatz functions, no?

Thanks, I've moved pipe to the generic set.

Cheers,
Steve Capper Nov. 11, 2013, 2:51 p.m. | #4
On Fri, Nov 08, 2013 at 11:34:41AM -0800, H. Peter Anvin wrote:
> On 11/08/2013 09:12 AM, Steve Capper wrote:
> > -<!ppc64> int stat64,stat::stat(const char *, struct stat *);
> > -<!ppc64> int lstat64,lstat::lstat(const char *, struct stat *);
> > +<!arm64,ppc64> int stat64,stat::stat(const char *, struct stat *);
> > +<!arm64,ppc64> int lstat64,lstat::lstat(const char *, struct stat *);
> 
> This should have been part of the previous patch but using <?!ppc64>.
> 
> 	-hpa
> 

Thanks, I've moved and corrected this change.
Thorsten Glaser Nov. 11, 2013, 3:01 p.m. | #5
Steve Capper dixit:

>> Do we need the fpregs saved even though klibc doesn't do fp?

>For gcc targetting Aarch64, We can only guarantee that d8-d15 are
>left alone when -mgeneral-regs-only is supplied for building klibc
>and any software linked against klibc. I would much prefer to

We can enforce this in klcc, just like -mregparm=3 is used by
the i386 target.

bye,
//mirabilos
H. Peter Anvin Nov. 11, 2013, 3:20 p.m. | #6
Yes, that is not an issue, but the question is if the relatively small cost of adding a bit too jmp_buf makes it worth it, as long as people understand that there is no fp support in klibc.

Thorsten Glaser <tg@mirbsd.de> wrote:
>Steve Capper dixit:
>
>>> Do we need the fpregs saved even though klibc doesn't do fp?
>
>>For gcc targetting Aarch64, We can only guarantee that d8-d15 are
>>left alone when -mgeneral-regs-only is supplied for building klibc
>>and any software linked against klibc. I would much prefer to
>
>We can enforce this in klcc, just like -mregparm=3 is used by
>the i386 target.
>
>bye,
>//mirabilos
Steve Capper Nov. 11, 2013, 3:54 p.m. | #7
On Mon, Nov 11, 2013 at 07:20:04AM -0800, H. Peter Anvin wrote:
> Yes, that is not an issue, but the question is if the relatively small cost of adding a bit too jmp_buf makes it worth it, as long as people understand that there is no fp support in klibc.
> 
> Thorsten Glaser <tg@mirbsd.de> wrote:
> >Steve Capper dixit:
> >
> >>> Do we need the fpregs saved even though klibc doesn't do fp?
> >
> >>For gcc targetting Aarch64, We can only guarantee that d8-d15 are
> >>left alone when -mgeneral-regs-only is supplied for building klibc
> >>and any software linked against klibc. I would much prefer to
> >
> >We can enforce this in klcc, just like -mregparm=3 is used by
> >the i386 target.
> >
> >bye,
> >//mirabilos

I've added -mgeneral-regs-only to KLIBCREQFLAGS (and some comments and
code changes in pertinent places), and this has made it into the klcc
wrapper.

Thanks,

Patch

diff --git a/Makefile b/Makefile
index 0a3ee69..a7da622 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,8 @@  export OBJDUMP  := $(KLIBCROSS)objdump
 
 NOSTDINC_FLAGS := -nostdlib -nostdinc -isystem $(shell $(CC) -print-file-name=include)
 
-ARCH	          := $(shell uname -m | sed -e s/i.86/i386/ -e s/parisc64/parisc/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/ -e s/sh.*/sh/)
+ARCH	          := $(shell uname -m | sed -e s/i.86/i386/ -e s/parisc64/parisc/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/ \
+			-e s/aarch64.*/arm64/ -e s/sh.*/sh/)
 export KLIBCARCH  ?= $(ARCH)
 export KLIBCARCHDIR := $(shell echo $(KLIBCARCH) | sed -e s/s390x/s390/)
 
diff --git a/usr/include/arch/arm64/klibc/archconfig.h b/usr/include/arch/arm64/klibc/archconfig.h
new file mode 100644
index 0000000..5e2004b
--- /dev/null
+++ b/usr/include/arch/arm64/klibc/archconfig.h
@@ -0,0 +1,17 @@ 
+/*
+ * include/arch/arm64/klibc/archconfig.h
+ *
+ * See include/klibc/sysconfig.h for the options that can be set in
+ * this file.
+ *
+ */
+
+#ifndef _KLIBC_ARCHCONFIG_H
+#define _KLIBC_ARCHCONFIG_H
+
+/* Use rt_* signals */
+#define _KLIBC_USE_RT_SIG 1
+#define _KLIBC_NO_MMU 0
+#define _KLIBC_REAL_VFORK 1
+
+#endif				/* _KLIBC_ARCHCONFIG_H */
diff --git a/usr/include/arch/arm64/klibc/archsetjmp.h b/usr/include/arch/arm64/klibc/archsetjmp.h
new file mode 100644
index 0000000..1738617
--- /dev/null
+++ b/usr/include/arch/arm64/klibc/archsetjmp.h
@@ -0,0 +1,20 @@ 
+/*
+ * arch/arm64/include/klibc/archsetjmp.h
+ */
+
+#ifndef _KLIBC_ARCHSETJMP_H
+#define _KLIBC_ARCHSETJMP_H
+
+/*
+ * x19-x28 are callee saved, also save fp, lr, sp.
+ * d8-d15 are also callee saved.
+ */
+
+struct __jmp_buf {
+	uint64_t __gregs[13];
+	uint64_t __fpregs[8];
+};
+
+typedef struct __jmp_buf jmp_buf[1];
+
+#endif				/* _SETJMP_H */
diff --git a/usr/include/arch/arm64/klibc/archsignal.h b/usr/include/arch/arm64/klibc/archsignal.h
new file mode 100644
index 0000000..94e6bc8
--- /dev/null
+++ b/usr/include/arch/arm64/klibc/archsignal.h
@@ -0,0 +1,14 @@ 
+/*
+ * arch/arm64/include/klibc/archsignal.h
+ *
+ * Architecture-specific signal definitions
+ *
+ */
+
+#ifndef _KLIBC_ARCHSIGNAL_H
+#define _KLIBC_ARCHSIGNAL_H
+
+#include <asm/signal.h>
+/* No special stuff for this architecture */
+
+#endif
diff --git a/usr/include/arch/arm64/klibc/archstat.h b/usr/include/arch/arm64/klibc/archstat.h
new file mode 100644
index 0000000..a1a3e79
--- /dev/null
+++ b/usr/include/arch/arm64/klibc/archstat.h
@@ -0,0 +1,29 @@ 
+#ifndef _KLIBC_ARCHSTAT_H
+#define _KLIBC_ARCHSTAT_H
+
+#include <klibc/stathelp.h>
+
+struct stat {
+          unsigned long   st_dev;         /* Device.  */
+          unsigned long   st_ino;         /* File serial number.  */
+          unsigned int    st_mode;        /* File mode.  */
+          unsigned int    st_nlink;       /* Link count.  */
+          unsigned int    st_uid;         /* User ID of the file's owner.  */
+          unsigned int    st_gid;         /* Group ID of the file's group. */
+          unsigned long   st_rdev;        /* Device number, if device.  */
+          unsigned long   __pad1;
+          long            st_size;        /* Size of file, in bytes.  */
+          int             st_blksize;     /* Optimal block size for I/O.  */
+          int             __pad2;
+          long            st_blocks;      /* Number 512-byte blocks allocated. */
+          long            st_atime;       /* Time of last access.  */
+          unsigned long   st_atime_nsec;
+          long            st_mtime;       /* Time of last modification.  */
+          unsigned long   st_mtime_nsec;
+          long            st_ctime;       /* Time of last status change.  */
+          unsigned long   st_ctime_nsec;
+          unsigned int    __unused4;
+          unsigned int    __unused5;
+  };
+
+#endif
diff --git a/usr/include/arch/arm64/klibc/asmmacros.h b/usr/include/arch/arm64/klibc/asmmacros.h
new file mode 100644
index 0000000..c298f66
--- /dev/null
+++ b/usr/include/arch/arm64/klibc/asmmacros.h
@@ -0,0 +1,11 @@ 
+/*
+ * usr/include/arch/arm64/klibc/asmmacros.h
+ *
+ * Assembly macros used by arm64 system call stubs
+ */
+
+#ifndef _KLIBC_ASMMACROS_H
+#define _KLIBC_ASMMACROS_H
+
+
+#endif /* _KLIBC_ASMMACROS_H */
diff --git a/usr/klibc/README.klibc b/usr/klibc/README.klibc
index 7de5fea..c72ae47 100644
--- a/usr/klibc/README.klibc
+++ b/usr/klibc/README.klibc
@@ -36,6 +36,7 @@  b) If you're cross-compiling, you need to set KLIBCARCH to the
    arm-thumb:	 Untested
    arm:		 Working
    arm26:	 Not yet ported
+   arm64:	 Working
    avr32:	 Not yet ported
    cris:	 Working
    h8300:	 Not yet ported
diff --git a/usr/klibc/SYSCALLS.def b/usr/klibc/SYSCALLS.def
index c2f36e7..c1f5b70 100644
--- a/usr/klibc/SYSCALLS.def
+++ b/usr/klibc/SYSCALLS.def
@@ -21,7 +21,7 @@  void _exit,exit::_exit(int);
 <?!ia64> pid_t clone::__clone(unsigned long, void *);
 <?ia64> pid_t clone::__clone2(unsigned long, void *, void *);
 # if ! _KLIBC_NO_MMU
-<!sparc,sparc64,ia64> pid_t fork();
+<!sparc,sparc64,ia64,arm64> pid_t fork();
 <sparc,sparc64> pid_t fork@forkish();
 #endif
 #if _KLIBC_REAL_VFORK
@@ -124,7 +124,7 @@  int fchmod(int, mode_t);
 <?> int mkdir(const char *, mode_t);
 <?> int mkdirat(int, const char *, mode_t);
 <?> int rmdir(const char *);
-<!alpha,ia64,mips,mips64,sh,sparc,sparc64> int pipe(int *);
+<!alpha,arm64,ia64,mips,mips64,sh,sparc,sparc64> int pipe(int *);
 int pipe2(int *, int);
 mode_t umask(mode_t);
 int chroot(const char *);
@@ -132,8 +132,8 @@  int chroot(const char *);
 <?> int symlinkat(const char *, int, const char *);
 <?> int readlink(const char *, char *, size_t);
 <?> int readlinkat(int, const char *, char *, int);
-<!ppc64> int stat64,stat::stat(const char *, struct stat *);
-<!ppc64> int lstat64,lstat::lstat(const char *, struct stat *);
+<!arm64,ppc64> int stat64,stat::stat(const char *, struct stat *);
+<!arm64,ppc64> int lstat64,lstat::lstat(const char *, struct stat *);
 <!ppc64> int fstat64,fstat::fstat(int, struct stat *);
 <ppc64> int stat::stat(const char *, struct stat *);
 <ppc64> int lstat::lstat(const char *, struct stat *);
diff --git a/usr/klibc/arch/arm64/Kbuild b/usr/klibc/arch/arm64/Kbuild
new file mode 100644
index 0000000..c23bd69
--- /dev/null
+++ b/usr/klibc/arch/arm64/Kbuild
@@ -0,0 +1,7 @@ 
+
+# klibc files for arm64
+#
+
+klib-y := setjmp.o syscall.o vfork.o pipe.o
+always  := crt0.o
+targets := crt0.o
diff --git a/usr/klibc/arch/arm64/MCONFIG b/usr/klibc/arch/arm64/MCONFIG
new file mode 100644
index 0000000..dfebc5e
--- /dev/null
+++ b/usr/klibc/arch/arm64/MCONFIG
@@ -0,0 +1,23 @@ 
+# -*- makefile -*-
+#
+# arch/arm64/MCONFIG
+#
+# Special rules for this architecture.  Note that this is actually
+# included from the main Makefile, and that pathnames should be
+# accordingly.
+#
+
+CPU_ARCH ?= armv8-a
+CPU_TUNE ?= generic
+
+KLIBCOPTFLAGS += -g -Os -march=$(CPU_ARCH) -mtune=$(CPU_TUNE)
+KLIBCBITSIZE  = 64
+KLIBCREQFLAGS += -fno-exceptions
+
+# Extra linkflags when building the shared version of the library
+# This address needs to be reachable using normal inter-module
+# calls, and work on the memory models for this architecture
+
+# On arm64, binaries are normally loaded at 4MB. Place klibc.so
+# a little before that at 2MB to prevent overlap.
+KLIBCSHAREDFLAGS = -Ttext 0x0200000
diff --git a/usr/klibc/arch/arm64/crt0.S b/usr/klibc/arch/arm64/crt0.S
new file mode 100644
index 0000000..0b2dd32
--- /dev/null
+++ b/usr/klibc/arch/arm64/crt0.S
@@ -0,0 +1,19 @@ 
+#
+# arch/arm64/crt0.S
+#
+# void _start(void)
+# {
+#    __libc_init(elf_structure, atexit_ptr);
+# }
+#
+
+	.text
+	.balign 8
+	.type _start,#function
+	.globl _start
+
+_start:
+	mov	x0, sp
+	mov	x1, #0
+	bl	__libc_init
+	.size _start,.-_start
diff --git a/usr/klibc/arch/arm64/pipe.c b/usr/klibc/arch/arm64/pipe.c
new file mode 100644
index 0000000..f10a69e
--- /dev/null
+++ b/usr/klibc/arch/arm64/pipe.c
@@ -0,0 +1,10 @@ 
+#include <unistd.h>
+
+#ifndef __NR_pipe
+
+int pipe(int pipefd[2])
+{
+	return pipe2(pipefd, 0);
+}
+
+#endif  /* __NR_pipe */
diff --git a/usr/klibc/arch/arm64/setjmp.S b/usr/klibc/arch/arm64/setjmp.S
new file mode 100644
index 0000000..3d5c1c6
--- /dev/null
+++ b/usr/klibc/arch/arm64/setjmp.S
@@ -0,0 +1,52 @@ 
+#
+# arch/arm64/setjmp.S
+#
+# setjmp/longjmp for arm64
+#
+
+#include <klibc/asmmacros.h>
+
+	.text
+	.balign 8
+	.globl setjmp
+	.type setjmp, #function
+setjmp:
+	mov	x1, sp
+	stp	x19, x20, [x0, #0]
+	stp	x21, x22, [x0, #16]
+	stp	x23, x24, [x0, #32]
+	stp	x25, x26, [x0, #48]
+	stp	x27, x28, [x0, #64]
+	stp	x29, x30, [x0, #80]
+	str	x1,       [x0, #96]
+	stp	d8, d9,   [x0, #104]
+	stp	d10, d11, [x0, #120]
+	stp	d12, d13, [x0, #136]
+	stp	d14, d15, [x0, #152]
+	mov	x0, #0 			/* set the return value of setjmp */
+	br	x30
+	.size setjmp,.-setjmp
+
+	.text
+	.balign 8
+	.globl longjmp
+	.type longjmp, #function
+longjmp:
+	ldp	x19, x20, [x0, #0]
+	ldp	x21, x22, [x0, #16]
+	ldp	x23, x24, [x0, #32]
+	ldp	x25, x26, [x0, #48]
+	ldp	x27, x28, [x0, #64]
+	ldp	x29, x30, [x0, #80]
+	ldr	x2,	  [x0, #96]
+	ldp	d8, d9,   [x0, #104]
+	ldp	d10, d11, [x0, #120]
+	ldp	d12, d13, [x0, #136]
+	ldp	d14, d15, [x0, #152]
+	mov	sp, x2
+	mov	x0, x1
+	cbnz	x1, 1f
+	mov	x0, #1
+1:
+	br	x30
+	.size longjmp,.-longjmp
diff --git a/usr/klibc/arch/arm64/syscall.S b/usr/klibc/arch/arm64/syscall.S
new file mode 100644
index 0000000..3ce91fb
--- /dev/null
+++ b/usr/klibc/arch/arm64/syscall.S
@@ -0,0 +1,25 @@ 
+/*
+ * arch/arm64/syscall.S
+ *
+ * System call common handling - if the return
+ * value from the system call is negative, then
+ * extract the magnitude and return it as errno and
+ * return -1, if the return value is 0 that is
+ * success case.
+ */
+
+	.type	__syscall_common,#function
+	.globl  __syscall_common
+	.balign 8
+
+__syscall_common:
+	cmp	x0, #0x0
+	b.ge	2f
+	neg	x0, x0
+	ldr	x8, 1f
+	str	x0, [x8]
+	mov	x0, #-1
+2:
+	ret
+1:
+	.dword	errno
diff --git a/usr/klibc/arch/arm64/sysstub.ph b/usr/klibc/arch/arm64/sysstub.ph
new file mode 100644
index 0000000..47cbfd9
--- /dev/null
+++ b/usr/klibc/arch/arm64/sysstub.ph
@@ -0,0 +1,25 @@ 
+# -*- perl -*-
+#
+# arch/arm64/sysstub.ph
+#
+# Script to generate system call stubs
+#
+
+sub make_sysstub($$$$$@) {
+    my($outputdir, $fname, $type, $sname, $stype, @args) = @_;
+
+    open(OUT, '>', "${outputdir}/${fname}.S");
+    print  OUT "#include <asm/unistd.h>\n";
+    print  OUT "#include <klibc/asmmacros.h>\n";
+    print  OUT "	.text\n";
+    print  OUT "	.type	${fname}, #function\n";
+    print  OUT "	.globl	${fname}\n";
+    print  OUT "	.balign	8\n";
+    print  OUT "${fname}:\n";
+    print  OUT "	mov w8,__NR_${sname}\n";
+    print  OUT "	svc	0\n";
+    print  OUT "	b	__syscall_common\n";
+    print  OUT "	.size	${fname},.-${fname}\n";
+}
+
+1;
diff --git a/usr/klibc/arch/arm64/vfork.S b/usr/klibc/arch/arm64/vfork.S
new file mode 100644
index 0000000..494326c
--- /dev/null
+++ b/usr/klibc/arch/arm64/vfork.S
@@ -0,0 +1,34 @@ 
+/*
+ * arch/arm64/vfork.S
+ *
+ * vfork - a system call which must not use the stack.
+ */
+
+#include <klibc/asmmacros.h>
+#include <asm/unistd.h>
+
+	.type	vfork,#function
+	.globl	vfork
+	.balign	8
+
+vfork:
+	/* Prepare for the system call */
+        /* 1. Push the function pointer and argument location
+              on to the child process stack */
+        /* 2. Gather the Flags */
+        /* New sp is already in x1.  */
+        mov     x0, #0x4111     /* CLONE_VM | CLONE_VFORK | SIGCHLD */
+        mov     x1, sp
+        mov     w8,__NR_clone
+        svc     0
+        cmp     x0, #0x0
+        b.ge    2f
+        neg     x0, x0
+        ldr     x8, 1f
+        str     x0, [x8]
+        mov     x0, #-1
+2:
+        ret
+1:
+        .dword   errno
+        .size   vfork,.-vfork