RISC-V userland now works-ish
authorAndrew Waterman <waterman@s144.Millennium.Berkeley.EDU>
Wed, 2 Nov 2011 09:34:22 +0000 (02:34 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 01:28:06 +0000 (18:28 -0700)
user/parlib/Makefile
user/parlib/include/riscv/atomic.h
user/parlib/include/riscv/bitmask.h
user/parlib/include/riscv/vcore.h
user/parlib/include/vcore.h
user/parlib/riscv/vcore.S [new file with mode: 0644]

index 00a1df4..cba5b46 100644 (file)
@@ -19,10 +19,17 @@ uc = $(shell echo $(1) | tr a-z A-Z)
 LIBUCNAME := $(call uc, $(LIBNAME))
 HEADERS := $(shell find $(INCDIR) -name *.h)
 CFILES  := $(wildcard $(SRCDIR)*.c)
-OBJS    := $(patsubst %.c, $(OBJDIR)/%.o, $(CFILES))
+SFILES  := $(wildcard $(SRCDIR)$(TARGET_ARCH)/*.S)
+OBJS    := $(patsubst %.c, $(OBJDIR)/%.o, $(CFILES)) \
+           $(patsubst %.S, $(OBJDIR)/%.o, $(SFILES))
 
 all: $(FINALLIB)
 
+$(OBJDIR)/$(TARGET_ARCH)/%.o: $(SRCDIR)$(TARGET_ARCH)/%.S $(HEADERS)
+       @echo + as [$(LIBUCNAME)] $<
+       @mkdir -p $(@D)
+       $(V)$(CC) $(CFLAGS) $(INCS) -o $@ -c $<
+
 $(OBJDIR)/%.o: $(SRCDIR)%.c $(HEADERS)
        @echo + cc [$(LIBUCNAME)] $<
        @mkdir -p $(@D)
index 7422ef6..38f0b1c 100644 (file)
@@ -90,12 +90,29 @@ static inline uint32_t atomic_swap_u32(uint32_t *addr, uint32_t val)
        return __sync_lock_test_and_set(addr, val);
 }
 
+// RISC-V has atomic word ops, not byte ops, so we must manipulate addresses
+static inline void atomic_andb(volatile uint8_t* number, uint8_t mask)
+{
+       uintptr_t offset = (uintptr_t)number & 3;
+       uint32_t wmask = (1<<(8*offset+8)) - (1<<(8*offset));
+       wmask = ~wmask | ((uint32_t)mask << (8*offset));
+
+       __sync_fetch_and_and((uint32_t*)((uintptr_t)number & ~3), wmask);
+}
+
+static inline void atomic_orb(volatile uint8_t* number, uint8_t mask)
+{
+       uintptr_t offset = (uintptr_t)number & 3;
+       uint32_t wmask = (uint32_t)mask << (8*offset);
+
+       __sync_fetch_and_or((uint32_t*)((uintptr_t)number & ~3), wmask);
+}
+
 asm (".section .gnu.linkonce.b.__riscv_ros_atomic_lock, \"aw\", %nobits\n"
      "\t.previous");
 
 spinlock_t __riscv_ros_atomic_lock
-  __attribute__ ((nocommon, section (".gnu.linkonce.b.__riscv_ros_atomic_lock"
-                                     __sec_comment),
+  __attribute__ ((nocommon, section(".gnu.linkonce.b.__riscv_ros_atomic_lock\n\t#"),
                   visibility ("hidden")));
 
 static inline bool atomic_cas(atomic_t *addr, long exp_val, long new_val)
@@ -127,7 +144,7 @@ static inline bool atomic_cas_u32(uint32_t *addr, uint32_t exp_val,
 
 static inline void atomic_or_int(volatile int *number, int mask)
 {
-       return __sync_fetch_and_or(number, mask);
+       __sync_fetch_and_or(number, mask);
 }
 
 static inline uint32_t spin_trylock(spinlock_t* lock)
index 1765e3b..0a5d4d9 100644 (file)
@@ -8,33 +8,44 @@
 #include <stdio.h>
 
 #define DECL_BITMASK(name, size) \
-       unsigned long (name)[WORDS_FOR_BITMASK((size))]
+       uint8_t (name)[BYTES_FOR_BITMASK((size))]
 
-#define BPW (CHAR_BIT*sizeof(long))
-#define WORDS_FOR_BITMASK(size) (((size)-1) / BPW + 1)
-#define BYTES_FOR_BITMASK(size) (WORDS_FOR_BITMASK * sizeof(long))
+#define BYTES_FOR_BITMASK(size) \
+       (((size) - 1) / 8 + 1)
 
 #define BYTES_FOR_BITMASK_WITH_CHECK(size) \
-       ((size) ? BYTES_FOR_BITMASK(size) : (0))
+       ((size) ? ((size) - (1)) / (8) + (1) : (0))
 
-static bool GET_BITMASK_BIT(unsigned long* name, size_t bit) 
+static bool GET_BITMASK_BIT(uint8_t* name, size_t bit) 
 {
-       return (((name)[(bit)/BPW] & (1 << ((bit) % BPW))) ? 1 : 0);
+       return (((name)[(bit)/8] & (1 << ((bit) % 8))) ? 1 : 0);
 }
 
 #define SET_BITMASK_BIT(name, bit) \
-       ((name)[(bit)/BPW] |= (1 << ((bit) % BPW)));
+       ((name)[(bit)/8] |= (1 << ((bit) % 8)));
+/*
+static void SET_BITMASK_BIT(uint8_t* name, size_t bit)
+{
+       ((name)[(bit)/8] |= (1 << ((bit) % 8)));
+}
+*/
 
 #define CLR_BITMASK_BIT(name, bit) \
-       ((name)[(bit)/BPW] &= ~(1 << ((bit) % BPW)));
+       ((name)[(bit)/8] &= ~(1 << ((bit) % 8)));
+/*
+static void CLR_BITMASK_BIT(uint8_t* name, size_t bit) 
+{
+       ((name)[(bit)/8] &= ~(1 << ((bit) % 8)));
+}
+*/
 
-static void SET_BITMASK_BIT_ATOMIC(unsigned long* name, size_t bit) 
+static void SET_BITMASK_BIT_ATOMIC(uint8_t* name, size_t bit) 
 {
-       __sync_fetch_and_or(&name[bit/BPW], bit % BPW);
+       (atomic_orb(&(name)[(bit)/8], (1 << ((bit) % 8))));
 }
 
 #define CLR_BITMASK_BIT_ATOMIC(name, bit) \
-       (__sync_fetch_and_and(&(name)[(bit)/BPW], ~(1UL << ((bit) % BPW))))
+       (atomic_andb(&(name)[(bit)/8], ~(1 << ((bit) % 8))))
 
 #define CLR_BITMASK(name, size) \
 ({ \
@@ -48,8 +59,7 @@ static void SET_BITMASK_BIT_ATOMIC(unsigned long* name, size_t bit)
        {TRUSTEDBLOCK \
        memset((void*)((uintptr_t)(name)), 255, BYTES_FOR_BITMASK((size))); \
        } \
-       if ((size) % BPW) \
-         (name)[WORDS_FOR_BITMASK((size))-1] >>= BPW - ((size) % BPW); \
+       (name)[BYTES_FOR_BITMASK((size))-1] >>= (((size) % 8) ? (8 - ((size) % 8)) : 0 ); \
 }) 
 
 #define COPY_BITMASK(newmask, oldmask, size) \
@@ -63,32 +73,26 @@ static void SET_BITMASK_BIT_ATOMIC(unsigned long* name, size_t bit)
 
 // this checks the entire last byte, so keep it 0 in the other macros
 #define BITMASK_IS_CLEAR(name, size) ({ \
-       size_t __n = WORDS_FOR_BITMASK(size); \
-       unsigned long clear = 1; \
-       while (clear != 0 && __n-- > 0) \
-               clear = (name)[__n]; \
-       clear != 0; })
+       uint32_t __n = BYTES_FOR_BITMASK((size)); \
+       bool clear = 1; \
+       while (__n-- > 0) { \
+               if ((name)[__n]) { \
+                       clear = 0; \
+                       break;\
+               }\
+       } \
+       clear; })
 
-static inline bool BITMASK_IS_FULL(unsigned long* map, size_t size)
+static inline bool BITMASK_IS_FULL(uint8_t* map, size_t size)
 {
-       size_t extra = size % BPW;
-       for (size_t i = 0; i < WORDS_FOR_BITMASK(size) - (extra != 0); i++)
-         if (map[i] != ~0UL)
-               return FALSE;
-       
-       return !extra || map[WORDS_FOR_BITMASK(size)-1] == ((1UL << extra) - 1);
-}
-
-#define PRINT_BITMASK(name, size) { \
-       int i;  \
-       int _size = size; \
-       for (i = 0; i < WORDS_FOR_BITMASK(size); i++) { \
-               int j;  \
-               for (j = 0; j < MIN(BPW,_size); j++) \
-                       printf("%x", ((name)[i] >> j) & 1);     \
-                       _size--; \
-       } \
-       printf("\n"); \
+       int _size = size;
+       for (int i = 0; i < BYTES_FOR_BITMASK(size); i++) {
+               for (int j = 0; j < MIN(8,_size); j++)
+                       if(!((map[i] >> j) &1))
+                               return FALSE;
+                       _size--;
+       }
+       return TRUE;
 }
 
 #endif /* PARLIB_ARCH_BITMASK_H */
index ad2a49e..c062c68 100644 (file)
@@ -32,47 +32,32 @@ static inline void set_tls_desc(void *tls_desc, uint32_t vcoreid)
        __vcoreid = vcoreid;
 }
 
-extern void __pop_ros_tf(struct user_trapframe *tf, bool* notif_enabled_p,
-                         bool* notif_pending_p, uint32_t vcoreid);
-extern void __save_ros_tf(struct user_trapframe *tf);
-
-/* Pops an ROS kernel-provided TF, reanabling notifications at the same time.
- * A Userspace scheduler can call this when transitioning off the transition
- * stack.
- *
- * Make sure you clear the notif_pending flag, and then check the queue before
- * calling this.  If notif_pending is not clear, this will self_notify this
- * core, since it should be because we missed a notification message while
- * notifs were disabled. 
- *
- * The important thing is that it can a notification after it enables
- * notifications, and when it gets resumed it can ultimately run the new
- * context.  Enough state is saved in the running context and stack to continue
- * running. */
-static inline void pop_ros_tf(struct user_trapframe *tf, uint32_t vcoreid)
+/* Register saves and restores happen in asm. */
+void __pop_ros_tf_regs(struct user_trapframe *tf, struct preempt_data* vcpd,
+                    uint32_t vcoreid, void* helper) __attribute__((noreturn));
+void __save_ros_tf_regs(struct user_trapframe *tf);
+
+/* Helper function that may handle notifications after re-enabling them. */
+static void __pop_ros_tf_notifs(struct user_trapframe *tf,
+                                struct preempt_data* vcpd, uint32_t vcoreid)
 {
-       // since we're changing the stack, move stuff into regs for now
-       register uint32_t _vcoreid = vcoreid;
-       register struct user_trapframe* _tf = tf;
+       vcpd->notif_enabled = true;
 
-       set_stack_pointer((void*)tf->gpr[30]);
+       __sync_synchronize();
 
-       tf = _tf;
-       vcoreid = _vcoreid;
-       struct preempt_data* vcpd = &__procdata.vcore_preempt_data[vcoreid];
-
-       // if this is a trap frame we just init'ed, we need to set up TLS
-       if(tf->gpr[31] == 0)
-               tf->gpr[31] = (long)get_tls_desc(vcoreid);
-       else
-               assert(tf->gpr[31] == (long)get_tls_desc(vcoreid));
+       if(vcpd->notif_pending)
+               ros_syscall(SYS_self_notify, vcoreid, 0, 0, 0, 0, 0);
+}
 
-       __pop_ros_tf(tf, &vcpd->notif_enabled, &vcpd->notif_pending, vcoreid);
+/* Helper function that won't handle notifications after re-enabling them. */
+static void __pop_ros_tf_notifs_raw(struct user_trapframe *tf,
+                                    struct preempt_data* vcpd, uint32_t vcoreid)
+{
+       vcpd->notif_enabled = true;
 }
 
-/* Like the regular pop_ros_tf, but this one doesn't check or clear
- * notif_pending.  TODO: someone from sparc should look at this. */
-static inline void pop_ros_tf_raw(struct user_trapframe *tf, uint32_t vcoreid)
+static inline void __pop_ros_tf(struct user_trapframe *tf, uint32_t vcoreid,
+                                void* helper)
 {
        // since we're changing the stack, move stuff into regs for now
        register uint32_t _vcoreid = vcoreid;
@@ -90,7 +75,32 @@ static inline void pop_ros_tf_raw(struct user_trapframe *tf, uint32_t vcoreid)
        else
                assert(tf->gpr[31] == (long)get_tls_desc(vcoreid));
 
-       __pop_ros_tf(tf, &vcpd->notif_enabled, 0, 0);
+       __pop_ros_tf_regs(tf, vcpd, vcoreid, helper);
+}
+
+/* Pops an ROS kernel-provided TF, reanabling notifications at the same time.
+ * A Userspace scheduler can call this when transitioning off the transition
+ * stack.
+ *
+ * Make sure you clear the notif_pending flag, and then check the queue before
+ * calling this.  If notif_pending is not clear, this will self_notify this
+ * core, since it should be because we missed a notification message while
+ * notifs were disabled. 
+ *
+ * The important thing is that it can a notification after it enables
+ * notifications, and when it gets resumed it can ultimately run the new
+ * context.  Enough state is saved in the running context and stack to continue
+ * running. */
+static inline void pop_ros_tf(struct user_trapframe *tf, uint32_t vcoreid)
+{
+       __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs);
+}
+
+/* Like the regular pop_ros_tf, but this one doesn't check or clear
+ * notif_pending. */
+static inline void pop_ros_tf_raw(struct user_trapframe *tf, uint32_t vcoreid)
+{
+       __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs_raw);
 }
 
 /* Save the current context/registers into the given tf, setting the pc of the
@@ -98,12 +108,12 @@ static inline void pop_ros_tf_raw(struct user_trapframe *tf, uint32_t vcoreid)
  * restore with pop_ros_tf(). */
 static inline void save_ros_tf(struct user_trapframe *tf)
 {
-       __save_ros_tf(tf);
+       __save_ros_tf_regs(tf);
 }
 
 /* This assumes a user_tf looks like a regular kernel trapframe */
 static __inline void
-init_user_tf(struct user_trapframe *u_tf, uint32_t entry_pt, uint32_t stack_top)
+init_user_tf(struct user_trapframe *u_tf, long entry_pt, long stack_top)
 {
        memset(u_tf, 0, sizeof(*u_tf));
        u_tf->gpr[30] = stack_top;
index 3641658..2706a24 100644 (file)
@@ -35,6 +35,7 @@ extern __thread bool __vcore_context;
 /* Utility Functions */
 void *allocate_tls(void);
 void free_tls(void *tcb);
+void *reinit_tls(void *tcb);
 
 /* Vcore API functions */
 static inline size_t max_vcores(void);
diff --git a/user/parlib/riscv/vcore.S b/user/parlib/riscv/vcore.S
new file mode 100644 (file)
index 0000000..846f149
--- /dev/null
@@ -0,0 +1,47 @@
+#include <sys/asm.h>
+
+.abicalls
+
+.global __pop_ros_tf_regs
+.ent __pop_ros_tf_regs
+__pop_ros_tf_regs:
+
+  REG_L s0,20*SZREG(a0)
+  REG_L s1,21*SZREG(a0)
+  REG_L s2,22*SZREG(a0)
+  REG_L s3,23*SZREG(a0)
+  REG_L s4,24*SZREG(a0)
+  REG_L s5,25*SZREG(a0)
+  REG_L s6,26*SZREG(a0)
+  REG_L s7,27*SZREG(a0)
+  REG_L s8,28*SZREG(a0)
+  REG_L s9,29*SZREG(a0)
+  REG_L sp,30*SZREG(a0)
+  REG_L tp,31*SZREG(a0)
+
+  REG_L ra,33*SZREG(a0)
+
+  jr    a3
+.end __pop_ros_tf_regs
+
+.global __save_ros_tf_regs
+.ent __save_ros_tf_regs
+__save_ros_tf_regs:
+
+  REG_S s0,20*SZREG(a0)
+  REG_S s1,21*SZREG(a0)
+  REG_S s2,22*SZREG(a0)
+  REG_S s3,23*SZREG(a0)
+  REG_S s4,24*SZREG(a0)
+  REG_S s5,25*SZREG(a0)
+  REG_S s6,26*SZREG(a0)
+  REG_S s7,27*SZREG(a0)
+  REG_S s8,28*SZREG(a0)
+  REG_S s9,29*SZREG(a0)
+  REG_S sp,30*SZREG(a0)
+  REG_S tp,31*SZREG(a0)
+
+  REG_S ra,33*SZREG(a0)
+
+  ret
+.end __save_ros_tf_regs