Added setjmp/longjmp support to the kernel
authorKevin Klues <klueska@cs.berkeley.edu>
Thu, 8 Aug 2013 04:23:27 +0000 (21:23 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 8 Aug 2013 04:23:27 +0000 (21:23 -0700)
Full implementations for 32/64 bit x86.
Stubs in place for riscv with TODOs.
Test and example usage in kern/src/testing.c

kern/arch/riscv/setjmp.S [new file with mode: 0644]
kern/arch/riscv/setjmp.h [new file with mode: 0644]
kern/arch/x86/Kbuild
kern/arch/x86/setjmp.h [new file with mode: 0644]
kern/arch/x86/setjmp32.S [new file with mode: 0644]
kern/arch/x86/setjmp64.S [new file with mode: 0644]
kern/include/setjmp.h [new file with mode: 0644]
kern/src/testing.c

diff --git a/kern/arch/riscv/setjmp.S b/kern/arch/riscv/setjmp.S
new file mode 100644 (file)
index 0000000..fdcb1f7
--- /dev/null
@@ -0,0 +1,23 @@
+# Kernel implementations for setjmp/longjmp.
+# TODO: Implement for riscv
+#
+# int setjmp(struct jmpbuf *env);
+# void longjmp(struct jmpbuf *env, int val);
+
+.text
+.align 4
+.globl setjmp
+.type setjmp, @function
+setjmp:
+       ret
+       
+.size setjmp,.-setjmp
+
+.text
+.align 4
+.globl longjmp
+.type longjmp, @function
+longjmp:
+       ret
+       
+.size longjmp,.-longjmp
diff --git a/kern/arch/riscv/setjmp.h b/kern/arch/riscv/setjmp.h
new file mode 100644 (file)
index 0000000..bc6adc5
--- /dev/null
@@ -0,0 +1,10 @@
+// Arch specific struct definitions for setjmp/longjmp.
+// TODO: Implement for riscv
+
+#ifndef ROS_ARCH_SETJMP_H
+#define ROS_ARCH_SETJMP_H
+
+struct jmpbuf {
+};
+
+#endif /* !ROS_ARCH_SETJMP_H */
index 409a7f6..bdc132e 100644 (file)
@@ -15,6 +15,7 @@ obj-y                                         += perfmon.o
 obj-y                                          += pmap.o pmap$(BITS).o
 obj-y                                          += process$(BITS).o
 obj-y                                          += rdtsc_test.o
+obj-y                                          += setjmp$(BITS).o
 obj-y                                          += smp.o
 obj-y                                          += smp_boot.o
 obj-y                                          += smp_entry$(BITS).o
diff --git a/kern/arch/x86/setjmp.h b/kern/arch/x86/setjmp.h
new file mode 100644 (file)
index 0000000..187eaa0
--- /dev/null
@@ -0,0 +1,30 @@
+// Arch specific struct definitions for setjmp/longjmp.
+
+#ifndef ROS_ARCH_SETJMP_H
+#define ROS_ARCH_SETJMP_H
+
+#include <common.h>
+
+#ifdef __x86_64__
+struct jmpbuf {
+       uintptr_t retaddr; // return address
+       uintreg_t rsp;     // post-return rsp
+       uintreg_t rbx;     // callee saved registers
+       uintreg_t rbp;
+       uintreg_t r12;
+       uintreg_t r13;
+       uintreg_t r14;
+       uintreg_t r15;
+};
+#else
+struct jmpbuf {
+       uintptr_t retaddr; // return address
+       uintreg_t esp;     // post-return esp
+       uintreg_t ebx;     // callee saved registers
+       uintreg_t ebp;
+       uintreg_t esi;
+       uintreg_t edi;
+};
+#endif
+
+#endif /* !ROS_ARCH_SETJMP_H */
diff --git a/kern/arch/x86/setjmp32.S b/kern/arch/x86/setjmp32.S
new file mode 100644 (file)
index 0000000..b4172e9
--- /dev/null
@@ -0,0 +1,49 @@
+# Kernel implementations for setjmp/longjmp.
+#
+# int setjmp(struct jmpbuf *env);
+# void longjmp(struct jmpbuf *env, int val);
+
+# The jmpbuf struct is defined as below:
+# struct jmpbuf {
+#      uintptr_t retaddr; // return address
+#      uintreg_t esp;     // post-return esp
+#      uintreg_t ebx;     // callee saved registers
+#      uintreg_t ebp;
+#      uintreg_t esi;
+#      uintreg_t edi;
+# };
+
+.text
+.align 4
+.globl setjmp
+.type setjmp, @function
+setjmp:
+       movl 4(%esp),%edx  # Grab a reference to the jmpbuf passed in
+       xorl %eax,%eax     # Zero out the return value for our first return
+       popl %ecx          # Temporarily grab the return address and adjust %rsp
+       movl %ecx,(%edx)   # Save the return address
+       movl %esp,4(%edx)  # The adjusted %esp is the post-return %esp (see longjmp)
+       movl %ebx,8(%edx)  # Save all the callee saved registers into the jmpbuf
+       movl %ebp,12(%edx)
+       movl %esi,16(%edx)
+       movl %edi,20(%edx)
+       pushl %ecx         # Restore stuff to make the call/return stack happy
+       ret
+.size setjmp,.-setjmp
+.text
+.align 4
+.globl longjmp
+.type longjmp, @function
+longjmp:
+       movl 4(%esp),%edx  # Grab a reference to the jmpbuf passed in
+       movl 8(%esp),%eax  # Set the return value to val (32-bit int)
+       movl 20(%edx),%edi # Restore all the callee saved registers from the jmpbuf
+       movl 16(%edx),%esi
+       movl 12(%edx),%ebp
+       movl 8(%edx),%ebx
+       movl 4(%edx),%esp  # Set the post-return %rsp
+       jmp *(%edx)        # Jump back to setjmp callsite (no ret necessary)
+.size longjmp,.-longjmp
diff --git a/kern/arch/x86/setjmp64.S b/kern/arch/x86/setjmp64.S
new file mode 100644 (file)
index 0000000..93cf0ee
--- /dev/null
@@ -0,0 +1,53 @@
+# Kernel implementations for setjmp/longjmp.
+#
+# int setjmp(struct jmpbuf *env);
+# void longjmp(struct jmpbuf *env, int val);
+
+# The jmpbuf struct is defined as below:
+# struct jmpbuf {
+#      uintptr_t retaddr; // return address
+#      uintreg_t rsp;     // post-return rsp
+#      uintreg_t rbx;
+#      uintreg_t rbp;
+#      uintreg_t r12;
+#      uintreg_t r13;
+#      uintreg_t r14;
+#      uintreg_t r15;
+# };
+
+.text
+.align 4
+.globl setjmp
+.type setjmp, @function
+setjmp:
+       xorl %eax,%eax     # Zero out the return value for our first return
+       pop  %rsi          # Temporarily grab the return address and adjust %rsp
+       movq %rsi,(%rdi)   # Save the return address
+       movq %rsp,8(%rdi)  # The adjusted %rsp is the post-return %rsp (see longjmp)
+       movq %rbx,16(%rdi) # Save all the callee saved registers into the jmpbuf
+       movq %rbp,24(%rdi) 
+       movq %r12,32(%rdi) 
+       movq %r13,40(%rdi) 
+       movq %r14,48(%rdi) 
+       movq %r15,56(%rdi) 
+       push %rsi          # Restore stuff to make the call/return stack happy
+       ret
+       
+.size setjmp,.-setjmp
+
+.text
+.align 4
+.globl longjmp
+.type longjmp, @function
+longjmp:
+       movl %esi,%eax     # Set the return value to val (32-bit int)
+       movq 56(%rdi),%r15 # Restore all the callee saved registers from the jmpbuf
+       movq 48(%rdi),%r14  
+       movq 40(%rdi),%r13
+       movq 32(%rdi),%r12
+       movq 24(%rdi),%rbp
+       movq 16(%rdi),%rbx
+       movq 8(%rdi),%rsp  # Set the post-return %rsp
+       jmp *(%rdi)        # Jump back to setjmp callsite (no ret necessary)
+       
+.size longjmp,.-longjmp
diff --git a/kern/include/setjmp.h b/kern/include/setjmp.h
new file mode 100644 (file)
index 0000000..88f7ccd
--- /dev/null
@@ -0,0 +1,11 @@
+// Kernel implementations for setjmp/longjmp.
+
+#ifndef ROS_SETJMP_H
+#define ROS_SETJMP_H
+
+#include <arch/setjmp.h>
+
+int setjmp(struct jmpbuf *env) __attribute__((returns_twice));
+void longjmp(struct jmpbuf *env, int val) __attribute__((noreturn));
+
+#endif /* !ROS_SETJMP_H */
index c63ad08..672d321 100644 (file)
@@ -37,6 +37,7 @@
 #include <schedule.h>
 #include <umem.h>
 #include <ucq.h>
+#include <setjmp.h>
 
 #define l1 (available_caches.l1)
 #define l2 (available_caches.l2)
@@ -1597,3 +1598,27 @@ void test_memset(void)
        run_check(bytes, 0xc0fe, 20);
        printk("Done!\n");
 }
+
+void __attribute__((noinline)) __longjmp_wrapper(struct jmpbuf* jb)
+{
+       asm ("");
+       printk("Starting: %s\n", __FUNCTION__);
+       longjmp(jb, 1);
+       // Should never get here
+       printk("Exiting: %s\n", __FUNCTION__); 
+}
+
+void test_setjmp()
+{
+       struct jmpbuf jb;
+       printk("Starting: %s\n", __FUNCTION__);
+       if (setjmp(&jb)) {
+         printk("After second setjmp return: %s\n", __FUNCTION__);
+    }
+    else {
+         printk("After first setjmp return: %s\n", __FUNCTION__);
+      __longjmp_wrapper(&jb);
+    }
+       printk("Exiting: %s\n", __FUNCTION__);
+}
+