Skip to contentSkip to conversation

x86: Add 64-bit setjmp/longjmp implementation

PATCH Superseded Ivan Gorinov 4 participants 6 messages Jun 6, 2018 – Jun 19, 2018 x86

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 x86: Add 64-bit setjmp/longjmp implementation

Add setjmp/longjmp functions for x86_64.
The FPU control word and MXCSR control bits are preserved across calls.

---
 arch/x86/cpu/x86_64/setjmp.S  | 66 +++++++++++++++++++++++++++++++++++++++++++
 arch/x86/cpu/x86_64/setjmp.c  | 19 -------------
 arch/x86/include/asm/setjmp.h | 19 +++++++++++++
 3 files changed, 85 insertions(+), 19 deletions(-)
 create mode 100644 arch/x86/cpu/x86_64/setjmp.S
 delete mode 100644 arch/x86/cpu/x86_64/setjmp.c
arch/x86/cpu/x86_64/setjmp.S new file +66 -0
new file mode 100644
index 0000000..ef61a4a
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * See arch/x86/include/asm/setjmp.h for jmp_buf format
+ */
+
+#include <linux/linkage.h>
+
+.text
+.align 8
+
+ENTRY(setjmp)
+
+	pop	%rcx
+	movq	%rcx, (%rdi)	/* Return address */
+	movq	%rsp, 8 (%rdi)
+	movq	%rbp, 16 (%rdi)
+	movq	%rbx, 24 (%rdi)
+	movq	%r12, 32 (%rdi)
+	movq	%r13, 40 (%rdi)
+	movq	%r14, 48 (%rdi)
+	movq	%r15, 56 (%rdi)
+	fnstcw	64 (%rdi)
+	stmxcsr	68 (%rdi)
+	xorq	%rax, %rax	/* Direct invocation returns 0 */
+	jmpq	*%rcx
+
+ENDPROC(setjmp)
+
+.align 8
+
+ENTRY(longjmp)
+
+	subq	$8, %rsp
+
+/* Restore the control bits of MXCSR */
+
+	stmxcsr	(%rsp)
+	movl	$0x3f, %eax
+	andl	%eax, (%rsp)
+	notl	%eax
+	andl	68 (%rdi), %eax
+	orl	%eax, (%rsp)
+	ldmxcsr	(%rsp)
+
+	fldcw	64 (%rdi)
+
+	movq	(%rdi), %rcx	/* Return address */
+	movq	8 (%rdi), %rsp
+	movq	16 (%rdi), %rbp
+	movq	24 (%rdi), %rbx
+	movq	32 (%rdi), %r12
+	movq	40 (%rdi), %r13
+	movq	48 (%rdi), %r14
+	movq	56 (%rdi), %r15
+
+	movq	%rsi, %rax	/* Value to be returned by setjmp() */
+	testq	%rax, %rax	/* cannot be 0 in this case */
+	jnz	1f
+	incq	%rax		/* Return 1 instead */
+1:
+	jmpq	*%rcx
+
+ENDPROC(longjmp)
arch/x86/cpu/x86_64/setjmp.c deleted +0 -19
deleted file mode 100644
index 5d4a74a..0000000
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2016 Google, Inc
- */
-
-#include <common.h>
-#include <asm/setjmp.h>
-
-int setjmp(struct jmp_buf_data *jmp_buf)
-{
-	printf("WARNING: setjmp() is not supported\n");
-
-	return 0;
-}
-
-void longjmp(struct jmp_buf_data *jmp_buf, int val)
-{
-	printf("WARNING: longjmp() is not supported\n");
-}
arch/x86/include/asm/setjmp.h +19 -0
index f25975f..eae33fb 100644
@@ -8,6 +8,23 @@
 #ifndef __setjmp_h
 #define __setjmp_h
 
+#ifdef CONFIG_X86_64
+
+struct jmp_buf_data {
+	unsigned long __rip;
+	unsigned long __rsp;
+	unsigned long __rbp;
+	unsigned long __rbx;
+	unsigned long __r12;
+	unsigned long __r13;
+	unsigned long __r14;
+	unsigned long __r15;
+	unsigned int __fcw;
+	unsigned int __mxcsr;
+};
+
+#else
+
 struct jmp_buf_data {
 	unsigned int __ebx;
 	unsigned int __esp;
@@ -17,6 +34,8 @@ struct jmp_buf_data {
 	unsigned int __eip;
 };
 
+#endif
+
 int setjmp(struct jmp_buf_data *jmp_buf);
 void longjmp(struct jmp_buf_data *jmp_buf, int val);
 
Signature · 1 line
2.7.4
#1 lore raw

PATCH v2 1/1 x86: Add 64-bit setjmp/longjmp implementation

Quoted 2 lines · Ivan Gorinov

Add setjmp/longjmp functions for x86_64.
The FPU control word and MXCSR control bits are preserved across calls.

With this patch

make mrproper && make qemu-x86_64_defconfig && make -j6

produces

arch/x86/cpu/built-in.o: In function `car_init':
arch/x86/cpu/qemu/car.S:25: undefined reference to `car_init_ret'

The error does not occur without this patch.

The missing symbol is defined in
arch/x86/cpu/start.S:98:car_init_ret:
but it is not defined in
arch/x86/cpu/start64.S

The following patch helps:

[PATCH 1/1] x86: qemu: do not build car.o with start64.o
https://lists.denx.de/pipermail/u-boot/2018-June/331440.html

But bootefi selftest with your patch leads to an immediate reset of the qemu-x86_64 board.

Best regards

Heinrich

Quoted 145 lines
---
 arch/x86/cpu/x86_64/setjmp.S  | 66 +++++++++++++++++++++++++++++++++++++++++++
 arch/x86/cpu/x86_64/setjmp.c  | 19 -------------
 arch/x86/include/asm/setjmp.h | 19 +++++++++++++
 3 files changed, 85 insertions(+), 19 deletions(-)
 create mode 100644 arch/x86/cpu/x86_64/setjmp.S
 delete mode 100644 arch/x86/cpu/x86_64/setjmp.c
arch/x86/cpu/x86_64/setjmp.S new file +66 -0
new file mode 100644
index 0000000..ef61a4a
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * See arch/x86/include/asm/setjmp.h for jmp_buf format
+ */
+
+#include <linux/linkage.h>
+
+.text
+.align 8
+
+ENTRY(setjmp)
+
+	pop	%rcx
+	movq	%rcx, (%rdi)	/* Return address */
+	movq	%rsp, 8 (%rdi)
+	movq	%rbp, 16 (%rdi)
+	movq	%rbx, 24 (%rdi)
+	movq	%r12, 32 (%rdi)
+	movq	%r13, 40 (%rdi)
+	movq	%r14, 48 (%rdi)
+	movq	%r15, 56 (%rdi)
+	fnstcw	64 (%rdi)
+	stmxcsr	68 (%rdi)
+	xorq	%rax, %rax	/* Direct invocation returns 0 */
+	jmpq	*%rcx
+
+ENDPROC(setjmp)
+
+.align 8
+
+ENTRY(longjmp)
+
+	subq	$8, %rsp
+
+/* Restore the control bits of MXCSR */
+
+	stmxcsr	(%rsp)
+	movl	$0x3f, %eax
+	andl	%eax, (%rsp)
+	notl	%eax
+	andl	68 (%rdi), %eax
+	orl	%eax, (%rsp)
+	ldmxcsr	(%rsp)
+
+	fldcw	64 (%rdi)
+
+	movq	(%rdi), %rcx	/* Return address */
+	movq	8 (%rdi), %rsp
+	movq	16 (%rdi), %rbp
+	movq	24 (%rdi), %rbx
+	movq	32 (%rdi), %r12
+	movq	40 (%rdi), %r13
+	movq	48 (%rdi), %r14
+	movq	56 (%rdi), %r15
+
+	movq	%rsi, %rax	/* Value to be returned by setjmp() */
+	testq	%rax, %rax	/* cannot be 0 in this case */
+	jnz	1f
+	incq	%rax		/* Return 1 instead */
+1:
+	jmpq	*%rcx
+
+ENDPROC(longjmp)
arch/x86/cpu/x86_64/setjmp.c deleted +0 -19
deleted file mode 100644
index 5d4a74a..0000000
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2016 Google, Inc
- */
-
-#include <common.h>
-#include <asm/setjmp.h>
-
-int setjmp(struct jmp_buf_data *jmp_buf)
-{
-	printf("WARNING: setjmp() is not supported\n");
-
-	return 0;
-}
-
-void longjmp(struct jmp_buf_data *jmp_buf, int val)
-{
-	printf("WARNING: longjmp() is not supported\n");
-}
arch/x86/include/asm/setjmp.h +19 -0
index f25975f..eae33fb 100644
@@ -8,6 +8,23 @@
 #ifndef __setjmp_h
 #define __setjmp_h
 
+#ifdef CONFIG_X86_64
+
+struct jmp_buf_data {
+	unsigned long __rip;
+	unsigned long __rsp;
+	unsigned long __rbp;
+	unsigned long __rbx;
+	unsigned long __r12;
+	unsigned long __r13;
+	unsigned long __r14;
+	unsigned long __r15;
+	unsigned int __fcw;
+	unsigned int __mxcsr;
+};
+
+#else
+
 struct jmp_buf_data {
 	unsigned int __ebx;
 	unsigned int __esp;
@@ -17,6 +34,8 @@ struct jmp_buf_data {
 	unsigned int __eip;
 };
 
+#endif
+
 int setjmp(struct jmp_buf_data *jmp_buf);
 void longjmp(struct jmp_buf_data *jmp_buf, int val);
 
#2 lore raw

PATCH v2 1/1 x86: Add 64-bit setjmp/longjmp implementation

Quoted 20 lines · Heinrich Schuchardt
Quoted 2 lines · Ivan Gorinov

Add setjmp/longjmp functions for x86_64.
The FPU control word and MXCSR control bits are preserved across calls.

With this patch

make mrproper && make qemu-x86_64_defconfig && make -j6

produces

arch/x86/cpu/built-in.o: In function `car_init':
arch/x86/cpu/qemu/car.S:25: undefined reference to `car_init_ret'

The error does not occur without this patch.

The missing symbol is defined in
arch/x86/cpu/start.S:98:car_init_ret:
but it is not defined in
arch/x86/cpu/start64.S

Quoted 4 lines

The following patch helps:

[PATCH 1/1] x86: qemu: do not build car.o with start64.o
https://lists.denx.de/pipermail/u-boot/2018-June/331440.html

Thank you! Now it builds.

Quoted 2 lines

But bootefi selftest with your patch leads to an immediate reset of the qemu-x86_64 board.

Reproduced the qemu-x86_64 reset.
The "info registers" command shows CR0.MP = 0. Setting it in 64-bit startup code did not help. I will try to fix that.

On a 64-bit Minnowboard configuration, bootefi works without reset.

Quoted 150 lines

Best regards

Heinrich

Quoted 145 lines
---
 arch/x86/cpu/x86_64/setjmp.S  | 66 +++++++++++++++++++++++++++++++++++++++++++
 arch/x86/cpu/x86_64/setjmp.c  | 19 -------------
 arch/x86/include/asm/setjmp.h | 19 +++++++++++++
 3 files changed, 85 insertions(+), 19 deletions(-)
 create mode 100644 arch/x86/cpu/x86_64/setjmp.S
 delete mode 100644 arch/x86/cpu/x86_64/setjmp.c
arch/x86/cpu/x86_64/setjmp.S new file +66 -0
new file mode 100644
index 0000000..ef61a4a
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * See arch/x86/include/asm/setjmp.h for jmp_buf format
+ */
+
+#include <linux/linkage.h>
+
+.text
+.align 8
+
+ENTRY(setjmp)
+
+	pop	%rcx
+	movq	%rcx, (%rdi)	/* Return address */
+	movq	%rsp, 8 (%rdi)
+	movq	%rbp, 16 (%rdi)
+	movq	%rbx, 24 (%rdi)
+	movq	%r12, 32 (%rdi)
+	movq	%r13, 40 (%rdi)
+	movq	%r14, 48 (%rdi)
+	movq	%r15, 56 (%rdi)
+	fnstcw	64 (%rdi)
+	stmxcsr	68 (%rdi)
+	xorq	%rax, %rax	/* Direct invocation returns 0 */
+	jmpq	*%rcx
+
+ENDPROC(setjmp)
+
+.align 8
+
+ENTRY(longjmp)
+
+	subq	$8, %rsp
+
+/* Restore the control bits of MXCSR */
+
+	stmxcsr	(%rsp)
+	movl	$0x3f, %eax
+	andl	%eax, (%rsp)
+	notl	%eax
+	andl	68 (%rdi), %eax
+	orl	%eax, (%rsp)
+	ldmxcsr	(%rsp)
+
+	fldcw	64 (%rdi)
+
+	movq	(%rdi), %rcx	/* Return address */
+	movq	8 (%rdi), %rsp
+	movq	16 (%rdi), %rbp
+	movq	24 (%rdi), %rbx
+	movq	32 (%rdi), %r12
+	movq	40 (%rdi), %r13
+	movq	48 (%rdi), %r14
+	movq	56 (%rdi), %r15
+
+	movq	%rsi, %rax	/* Value to be returned by setjmp() */
+	testq	%rax, %rax	/* cannot be 0 in this case */
+	jnz	1f
+	incq	%rax		/* Return 1 instead */
+1:
+	jmpq	*%rcx
+
+ENDPROC(longjmp)
arch/x86/cpu/x86_64/setjmp.c deleted +0 -19
deleted file mode 100644
index 5d4a74a..0000000
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2016 Google, Inc
- */
-
-#include <common.h>
-#include <asm/setjmp.h>
-
-int setjmp(struct jmp_buf_data *jmp_buf)
-{
-	printf("WARNING: setjmp() is not supported\n");
-
-	return 0;
-}
-
-void longjmp(struct jmp_buf_data *jmp_buf, int val)
-{
-	printf("WARNING: longjmp() is not supported\n");
-}
arch/x86/include/asm/setjmp.h +19 -0
index f25975f..eae33fb 100644
@@ -8,6 +8,23 @@
 #ifndef __setjmp_h
 #define __setjmp_h
 
+#ifdef CONFIG_X86_64
+
+struct jmp_buf_data {
+	unsigned long __rip;
+	unsigned long __rsp;
+	unsigned long __rbp;
+	unsigned long __rbx;
+	unsigned long __r12;
+	unsigned long __r13;
+	unsigned long __r14;
+	unsigned long __r15;
+	unsigned int __fcw;
+	unsigned int __mxcsr;
+};
+
+#else
+
 struct jmp_buf_data {
 	unsigned int __ebx;
 	unsigned int __esp;
@@ -17,6 +34,8 @@ struct jmp_buf_data {
 	unsigned int __eip;
 };
 
+#endif
+
 int setjmp(struct jmp_buf_data *jmp_buf);
 void longjmp(struct jmp_buf_data *jmp_buf, int val);
 
#3 lore raw

PATCH v2 1/1 x86: Add 64-bit setjmp/longjmp implementation

Quoted 9 lines · Ivan Gorinov
Quoted 2 lines

But bootefi selftest with your patch leads to an immediate reset of the qemu-x86_64 board.

Reproduced the qemu-x86_64 reset.
The "info registers" command shows CR0.MP = 0. Setting it in 64-bit startup code did not help. I will try to fix that.

On a 64-bit Minnowboard configuration, bootefi works without reset.

The "bootefi selftest" command works on qemu-x86_64 when $loadaddr is changed:
=> env set loadaddr 0x10000000

Another patch "x86: use EFI calling convention for efi_main on x86_64" also needs to be applied.

The self test starts but crashes on 'manage protocols':

Tearing down 'graphical output'
Tearing down 'graphical output' succeeded

Setting up 'manage protocols'

Same effect with a 64-bit build for Minnowboard.

#4 lore raw

PATCH v2 1/1 x86: Add 64-bit setjmp/longjmp implementation

Quoted 24 lines

Am 14.06.2018 um 19:15 schrieb Ivan Gorinov <[email protected]>:

Quoted 9 lines · Ivan Gorinov
Quoted 2 lines

But bootefi selftest with your patch leads to an immediate reset of the qemu-x86_64 board.

Reproduced the qemu-x86_64 reset.
The "info registers" command shows CR0.MP = 0. Setting it in 64-bit startup code did not help. I will try to fix that.

On a 64-bit Minnowboard configuration, bootefi works without reset.

The "bootefi selftest" command works on qemu-x86_64 when $loadaddr is changed:
=> env set loadaddr 0x10000000

Another patch "x86: use EFI calling convention for efi_main on x86_64" also needs to be applied.

The self test starts but crashes on 'manage protocols':

Quoted 7 lines

Tearing down 'graphical output'
Tearing down 'graphical output' succeeded

Setting up 'manage protocols'

Same effect with a 64-bit build for Minnowboard.

I see the same with the sandbox patch set I just sent. IIUC sonething goes wrong in varargs handling.

Alex

#5 lore raw

PATCH v2 1/1 x86: Add 64-bit setjmp/longjmp implementation

In reply to Ivan Gorinov

Hi Ivan,

Quoted 18 lines · Ivan Gorinov

Add setjmp/longjmp functions for x86_64.
The FPU control word and MXCSR control bits are preserved across calls.

---
 arch/x86/cpu/x86_64/setjmp.S  | 66 +++++++++++++++++++++++++++++++++++++++++++
 arch/x86/cpu/x86_64/setjmp.c  | 19 -------------
 arch/x86/include/asm/setjmp.h | 19 +++++++++++++
 3 files changed, 85 insertions(+), 19 deletions(-)
 create mode 100644 arch/x86/cpu/x86_64/setjmp.S
 delete mode 100644 arch/x86/cpu/x86_64/setjmp.c
arch/x86/cpu/x86_64/setjmp.S new file +0 -0
new file mode 100644
index 0000000..ef61a4a

arch/x86/cpu/x86_64/setjmp.S line 4

Quoted 4 lines
arch/x86/cpu/x86_64/setjmp.S new file +4 -0
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+

This line should be put at the first line of this file, otherwise it causes checkpatch to report warnings.

Quoted 4 lines
arch/x86/cpu/x86_64/setjmp.S new file +4 -0
+ *
+ * See arch/x86/include/asm/setjmp.h for jmp_buf format
+ */
+

arch/x86/cpu/x86_64/setjmp.S line 18

Quoted 10 lines
arch/x86/cpu/x86_64/setjmp.S new file +10 -0
+#include <linux/linkage.h>
+
+.text
+.align 8
+
+ENTRY(setjmp)
+
+       pop     %rcx
+       movq    %rcx, (%rdi)    /* Return address */
+       movq    %rsp, 8 (%rdi)

nits: can we eliminate the space between 8 and (%edi)? and others in this file too?

arch/x86/cpu/x86_64/setjmp.S line 26

Quoted 8 lines
arch/x86/cpu/x86_64/setjmp.S new file +8 -0
+       movq    %rbp, 16 (%rdi)
+       movq    %rbx, 24 (%rdi)
+       movq    %r12, 32 (%rdi)
+       movq    %r13, 40 (%rdi)
+       movq    %r14, 48 (%rdi)
+       movq    %r15, 56 (%rdi)
+       fnstcw  64 (%rdi)
+       stmxcsr 68 (%rdi)

I don't think we need worry about these FP registers as U-Boot does not enable them at all. It looks your v1 patch does not include this but was added in v2. See the 32-bit setjmp/longjmp() implementation in U-Boot and there is no FP save/restore too.

arch/x86/cpu/x86_64/setjmp.S line 38

Quoted 12 lines
arch/x86/cpu/x86_64/setjmp.S new file +12 -0
+       xorq    %rax, %rax      /* Direct invocation returns 0 */
+       jmpq    *%rcx
+
+ENDPROC(setjmp)
+
+.align 8
+
+ENTRY(longjmp)
+
+       subq    $8, %rsp
+
+/* Restore the control bits of MXCSR */

nits: comment indention should align to the assembly code

Quoted 82 lines
arch/x86/cpu/x86_64/setjmp.S new file +28 -0
+
+       stmxcsr (%rsp)
+       movl    $0x3f, %eax
+       andl    %eax, (%rsp)
+       notl    %eax
+       andl    68 (%rdi), %eax
+       orl     %eax, (%rsp)
+       ldmxcsr (%rsp)
+
+       fldcw   64 (%rdi)
+
+       movq    (%rdi), %rcx    /* Return address */
+       movq    8 (%rdi), %rsp
+       movq    16 (%rdi), %rbp
+       movq    24 (%rdi), %rbx
+       movq    32 (%rdi), %r12
+       movq    40 (%rdi), %r13
+       movq    48 (%rdi), %r14
+       movq    56 (%rdi), %r15
+
+       movq    %rsi, %rax      /* Value to be returned by setjmp() */
+       testq   %rax, %rax      /* cannot be 0 in this case */
+       jnz     1f
+       incq    %rax            /* Return 1 instead */
+1:
+       jmpq    *%rcx
+
+ENDPROC(longjmp)
arch/x86/cpu/x86_64/setjmp.c deleted +0 -19
deleted file mode 100644
index 5d4a74a..0000000
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2016 Google, Inc
- */
-
-#include <common.h>
-#include <asm/setjmp.h>
-
-int setjmp(struct jmp_buf_data *jmp_buf)
-{
-       printf("WARNING: setjmp() is not supported\n");
-
-       return 0;
-}
-
-void longjmp(struct jmp_buf_data *jmp_buf, int val)
-{
-       printf("WARNING: longjmp() is not supported\n");
-}
arch/x86/include/asm/setjmp.h +17 -0
index f25975f..eae33fb 100644
@@ -8,6 +8,23 @@
 #ifndef __setjmp_h
 #define __setjmp_h
 
+#ifdef CONFIG_X86_64
+
+struct jmp_buf_data {
+       unsigned long __rip;
+       unsigned long __rsp;
+       unsigned long __rbp;
+       unsigned long __rbx;
+       unsigned long __r12;
+       unsigned long __r13;
+       unsigned long __r14;
+       unsigned long __r15;
+       unsigned int __fcw;
+       unsigned int __mxcsr;
+};
+
+#else
+
 struct jmp_buf_data {
        unsigned int __ebx;
        unsigned int __esp;

arch/x86/include/asm/setjmp.h struct jmp_buf_data {

Quoted 9 lines
arch/x86/include/asm/setjmp.h +2 -1
@@ -17,6 +34,8 @@ struct jmp_buf_data {
        unsigned int __eip;
 };
 
+#endif
+
 int setjmp(struct jmp_buf_data *jmp_buf);
 void longjmp(struct jmp_buf_data *jmp_buf, int val);
 
--

Regards,
Bin