get rid of pthreads
authornotaz <notasas@gmail.com>
Sat, 9 Jan 2016 00:56:28 +0000 (02:56 +0200)
committernotaz <notasas@gmail.com>
Sat, 9 Jan 2016 01:09:56 +0000 (03:09 +0200)
loader/Makefile
loader/emu.c
loader/header.h
loader/llibc.c [new file with mode: 0644]
loader/llibc.h [new file with mode: 0644]
loader/patches.c
loader/sys_cacheflush.S [deleted file]
loader/sys_cacheflush.h [deleted file]
loader/syscalls.S [new file with mode: 0644]
loader/syscalls.h [new file with mode: 0644]

index 90b6839..eff5e2f 100644 (file)
@@ -3,7 +3,7 @@ CROSS_COMPILE ?= arm-linux-
 CC = $(CROSS_COMPILE)gcc
 AS = $(CROSS_COMPILE)as
 CFLAGS += -Wall -ggdb -DLOADER
-LDFLAGS += -ggdb -lpthread -lrt
+LDFLAGS += -ggdb
 ifndef DEBUG
 CFLAGS += -O2 -fno-strict-aliasing
 LDFLAGS += -O2
@@ -20,7 +20,7 @@ ASFLAGS += --32
 endif
 ifeq "$(ARCH)" "arm"
 ASFLAGS += -mfloat-abi=soft
-OBJ += sys_cacheflush.o emu_arm.o
+OBJ += syscalls.o emu_arm.o
 endif
 ifdef PND
 CFLAGS += -DPND
@@ -38,7 +38,7 @@ vpath %.s = ../common/
 TARGET_S = ginge_sloader$(TAG)
 TARGET_D = ginge_dyn$(TAG)
 
-OBJ += emu.o host.o host_fb.o cmn.o
+OBJ += emu.o host.o host_fb.o cmn.o llibc.o
 OBJ_S += $(OBJ) loader.o loader_$(ARCH).o patches.o
 OBJ_D += $(OBJ) dl.o
 
index f14b0fc..64c2c99 100644 (file)
 #include <unistd.h>
 #include <signal.h>
 #include <asm/ucontext.h>
-#include <pthread.h>
 #include <errno.h>
 #include <time.h>
+#include <sched.h>
 #include <sys/resource.h>
 #include <sys/ioctl.h>
+#include <sys/syscall.h>
 #include <linux/soundcard.h>
 #include <linux/fb.h>
+#include <linux/futex.h>
 
 #include "header.h"
 #include "../common/host_fb.h"
 #include "../common/cmn.h"
-#include "sys_cacheflush.h"
+#include "syscalls.h"
 #include "realfuncs.h"
+#include "llibc.h"
 
 #if (DBG & 2) && !(DBG & 4)
 #define LOG_IO_UNK
@@ -56,7 +59,7 @@
 #endif
 
 #ifdef LOG_SEGV
-#define segvlog printf
+#define segvlog g_printf
 #else
 #define segvlog(...)
 #endif
@@ -70,8 +73,10 @@ typedef unsigned int   u32;
 typedef unsigned short u16;
 typedef unsigned char  u8;
 
-static pthread_mutex_t fb_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t fb_cond = PTHREAD_COND_INITIALIZER;
+#define THREAD_STACK_SIZE 0x200000
+
+static int fb_sync_thread_paused;
+static int fb_sync_thread_futex;
 
 static struct {
   u32 dstctrl;
@@ -147,7 +152,7 @@ static void log_io(const char *pfx, u32 a, u32 d, int size)
   if ((a & ~0xffff) == 0x7f000000)
     reg = regnames[a & 0xffff];
 
-  printf(fmt, pfx, a, d, reg);
+  g_printf(fmt, pfx, a, d, reg);
 }
 #endif
 
@@ -188,9 +193,9 @@ static void blt_tr(void *dst, void *src, u32 trc, int w)
   u32 *r = &blitter.dstctrl; \
   int i; \
   for (i = 0; i < 4*4; i++, r++) { \
-    printf("%08x ", *r); \
+    g_printf("%08x ", *r); \
     if ((i & 3) == 3) \
-      printf("\n"); \
+      g_printf("\n"); \
   } \
 }
 
@@ -281,8 +286,10 @@ static void blitter_do(void)
     }
   }
 
-  if (to_screen)
-    pthread_cond_signal(&fb_cond);
+  if (to_screen) {
+    fb_sync_thread_futex = 1;
+    g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
+  }
   return;
 
 bad_blit:
@@ -329,65 +336,55 @@ static void mlc_flip(void *src, int bpp, int stride)
   }
 }
 
-#define ts_add_nsec(ts, ns) { \
-  ts.tv_nsec += ns; \
-  if (ts.tv_nsec >= 1000000000) { \
-    ts.tv_sec++; \
-    ts.tv_nsec -= 1000000000; \
-  } \
-}
-
-static int fb_sync_thread_paused;
-
 static void *fb_sync_thread(void *arg)
 {
+  unsigned long sigmask[2] = { ~0ul, ~0ul };
+  struct timespec ts = { 0, 0 };
   int invalid_fb_addr = 1;
   int manual_refresh = 0;
   int frame_counter = 0;
-  struct timespec ts;
-  int ret, wait_ret;
+  int wait_ret;
+
+  // this thread can't run any signal handlers since the
+  // app's stack/tls stuff will never be set up here
+  sigmask[0] &= ~(1ul << (SIGSEGV - 1));
+  g_rt_sigprocmask_raw(SIG_SETMASK, sigmask, NULL, sizeof(sigmask));
 
-  //ret = pthread_setschedprio(pthread_self(), -1);
-  //log("pthread_setschedprio %d\n", ret);
   //ret = setpriority(PRIO_PROCESS, 0, -1);
   //log("setpriority %d\n", ret);
 
-  ret = clock_gettime(CLOCK_REALTIME, &ts);
-  if (ret != 0) {
-    perror(PFX "clock_gettime");
-    exit(1);
-  }
+  // tell the main thread we're done init
+  fb_sync_thread_futex = 0;
+  g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
 
   while (1) {
     u8 *gp2x_fb, *gp2x_fb_end;
 
-    ret =  pthread_mutex_lock(&fb_mutex);
-    wait_ret = pthread_cond_timedwait(&fb_cond, &fb_mutex, &ts);
-    ret |= pthread_mutex_unlock(&fb_mutex);
+    wait_ret = g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 0, &ts);
 
-    if (ret != 0) {
-      err("fb_thread: mutex error: %d\n", ret);
-      sleep(1);
-      goto check_keys;
-    }
-    if (wait_ret != 0 && wait_ret != ETIMEDOUT) {
-      err("fb_thread: cond error: %d\n", wait_ret);
-      sleep(1);
+    // this is supposed to be done atomically, but to make life
+    // easier ignore it for now, race impact is low anyway
+    fb_sync_thread_futex = 0;
+
+    if (wait_ret != 0 && wait_ret != -EWOULDBLOCK
+        && wait_ret != -ETIMEDOUT)
+    {
+      err("fb_thread: futex error: %d\n", wait_ret);
+      g_sleep(1);
       goto check_keys;
     }
     if (fb_sync_thread_paused) {
-      ts_add_nsec(ts, 100000000);
+      ts.tv_nsec = 100000000;
       goto check_keys;
     }
 
-    if (wait_ret != ETIMEDOUT) {
-      clock_gettime(CLOCK_REALTIME, &ts);
-      ts_add_nsec(ts, 50000000);
+    if (wait_ret == 0) {
+      ts.tv_nsec = 50000000;
       manual_refresh++;
       if (manual_refresh == 2)
         dbg("fb_thread: switch to manual refresh\n");
     } else {
-      ts_add_nsec(ts, 16666667);
+      ts.tv_nsec = 16666667;
       if (manual_refresh > 1)
         dbg("fb_thread: switch to auto refresh\n");
       manual_refresh = 0;
@@ -574,7 +571,7 @@ static u32 xread32(u32 a)
 
     switch (a_) {
     case 0x0a00: // TCOUNT, 1/7372800s
-      clock_gettime(CLOCK_REALTIME, &ts);
+      g_clock_gettime_raw(CLOCK_REALTIME, &ts);
       t64 = (u64)ts.tv_sec * 1000000000 + ts.tv_nsec;
       // t * 7372800.0 / 1000000000 * 0x100000000 ~= t * 31665935
       t64 *= 31665935;
@@ -639,9 +636,11 @@ static void xwrite16(u32 a, u32 d)
         return;
       case 0x2914:
         mmsp2.mlc_stl_adrh = d;
-        if (mmsp2.mlc_stl_adr != mmsp2.old_mlc_stl_adr)
+        if (mmsp2.mlc_stl_adr != mmsp2.old_mlc_stl_adr) {
           // ask for refresh
-          pthread_cond_signal(&fb_cond);
+          fb_sync_thread_futex = 1;
+          g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
+        }
         mmsp2.old_mlc_stl_adr = mmsp2.mlc_stl_adr;
         return;
       case 0x2958:
@@ -682,9 +681,11 @@ static void xwrite32(u32 a, u32 d)
     case 0x4038: // MLCADDRESS0
     case 0x406c: // MLCADDRESS1
       pollux.mlc_stl_adr = d;
-      if (d != mmsp2.old_mlc_stl_adr)
+      if (d != mmsp2.old_mlc_stl_adr) {
         // ask for refresh
-        pthread_cond_signal(&fb_cond);
+        fb_sync_thread_futex = 1;
+        g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
+      }
       mmsp2.old_mlc_stl_adr = d;
       return;
     case 0x403c: // MLCPALETTE0
@@ -971,7 +972,6 @@ void emu_init(void *map_bottom)
     .sa_sigaction = segv_sigaction,
     .sa_flags = SA_SIGINFO,
   };
-  pthread_t tid;
   void *pret;
   int ret;
 
@@ -1021,12 +1021,22 @@ void emu_init(void *map_bottom)
     exit(1);
   }
 
-  ret = pthread_create(&tid, NULL, fb_sync_thread, NULL);
-  if (ret != 0) {
-    err("failed to create fb_sync_thread: %d\n", ret);
+  pret = mmap(NULL, THREAD_STACK_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC,
+              MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
+  if (mmsp2.umem == MAP_FAILED) {
+    perror(PFX "mmap thread stack");
+    exit(1);
+  }
+  fb_sync_thread_futex = 1;
+  ret = g_clone(CLONE_VM | CLONE_FS | CLONE_FILES
+                | CLONE_SIGHAND | CLONE_THREAD,
+                (char *)pret + THREAD_STACK_SIZE, 0, 0, 0,
+                fb_sync_thread);
+  if (ret == 0 || ret == -1) {
+    perror(PFX "start fb thread");
     exit(1);
   }
-  pthread_detach(tid);
+  g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 1, NULL);
 
   // defaults
   mmsp2.mlc_stl_adr = 0x03101000; // fb2 is at 0x03381000
index f24457f..4bc6e1b 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef INCLUDE_sQt5fY5eUJn5tKV0IBTDxK0zqQutTqTp
 #define INCLUDE_sQt5fY5eUJn5tKV0IBTDxK0zqQutTqTp 1
 
+#include "llibc.h"
+
 #define PFX "ginge: "
-#define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
-#define log(f, ...) fprintf(stdout, PFX f, ##__VA_ARGS__)
+#define err(f, ...) g_fprintf(2, PFX f, ##__VA_ARGS__)
+#define log(f, ...) g_fprintf(1, PFX f, ##__VA_ARGS__)
 #ifdef DBG
 #define dbg log
 #define dbg_c printf
diff --git a/loader/llibc.c b/loader/llibc.c
new file mode 100644 (file)
index 0000000..484b52d
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * GINGE - GINGE Is Not Gp2x Emulator
+ * (C) notaz, 2016
+ *
+ * This work is licensed under the MAME license, see COPYING file for details.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "syscalls.h"
+#include "llibc.h"
+
+// lame, broken and slow, but enough for ginge's needs
+static void format_number(char **dst_, int dst_len, unsigned int n,
+  char fmt, int justify, int zeropad)
+{
+  char buf[32], *p = buf, *dst;
+  int printing = 0;
+  unsigned int div;
+  unsigned int t;
+  unsigned int w;
+  int spaces;
+  int neg = 0;
+  int left;
+
+  w = justify < 0 ? -justify : justify;
+  if (w >= 32)
+    w = 31;
+
+  switch (fmt) {
+  case 'i':
+  case 'd':
+    if ((signed int)n < 0) {
+      n = -n;
+      neg = 1;
+    }
+  case 'u':
+    div = 1000000000;
+    left = 10;
+    while (w > left) {
+      *p++ = ' ';
+      w--;
+      continue;
+    }
+    while (left > 0) {
+      t = n / div;
+      n -= t * div;
+      div /= 10;
+      if (t || left == 1) {
+        if (neg && t && !printing) {
+          *p++ = '-';
+          if (w > 0) w--;
+        }
+        printing = 1;
+      }
+      if (printing)
+        *p++ = t + '0';
+      else if (w >= left) {
+        *p++ = ' ';
+        w--;
+      }
+      left--;
+    }
+    break;
+
+  case 'p':
+    w = 8;
+    zeropad = 1;
+  case 'x':
+    left = 8;
+    while (w > left) {
+      *p++ = zeropad ? '0' : ' ';
+      w--;
+      continue;
+    }
+    while (left > 0) {
+      t = n >> (left * 4 - 4);
+      t &= 0x0f;
+      if (t || left == 1)
+        printing = 1;
+      if (printing)
+        *p++ = t < 10 ? t + '0' : t + 'a' - 10;
+      else if (w >= left) {
+        *p++ = zeropad ? '0' : ' ';
+        w--;
+      }
+      left--;
+    }
+    break;
+
+  default:
+    memcpy(buf, "<FMTODO>", 9);
+    break;
+  }
+  *p = 0;
+
+  spaces = 0;
+  p = buf;
+  if (justify < 0) {
+    while (*p == ' ') {
+      spaces++;
+      p++;
+    }
+  }
+
+  dst = *dst_;
+  while (*p != 0 && dst_len > 1) {
+    *dst++ = *p++;
+    dst_len--;
+  }
+  while (spaces > 0 && dst_len > 1) {
+    *dst++ = ' ';
+    spaces--;
+    dst_len--;
+  }
+  *dst = 0;
+  *dst_ = dst;
+}
+
+int parse_dec(const char **p_)
+{
+  const char *p = *p_;
+  int neg = 0;
+  int r = 0;
+
+  if (*p == '-') {
+    neg = 1;
+    p++;
+  }
+
+  while ('0' <= *p && *p <= '9') {
+    r = r * 10 + *p - '0';
+    p++;
+  }
+
+  *p_ = p;
+  return neg ? -r : r;
+}
+
+void g_fprintf(int fd, const char *fmt, ...)
+{
+  char buf[256], *d = buf;
+  const char *s = fmt;
+  int left = sizeof(buf);;
+  int justify;
+  int zeropad;
+  va_list ap;
+
+  va_start(ap, fmt);
+  while (*s != 0 && left > 1) {
+    if (*s != '%') {
+      *d++ = *s++;
+      left--;
+      continue;
+    }
+    s++;
+    if (*s == 0)
+      break;
+    if (*s == '%') {
+      *d++ = *s++;
+      left--;
+      continue;
+    }
+
+    zeropad = *s == '0';
+    justify = parse_dec(&s);
+    if (*s == 'l')
+      s++; // ignore for now
+    if (*s == 's') {
+      char *ns = va_arg(ap, char *);
+      int len = strlen(ns);
+      while (justify > len && left > 1) {
+        *d++ = ' ';
+        justify--;
+        left--;
+      }
+      if (len > left - 1) {
+        memcpy(d, ns, left - 1);
+        break;
+      }
+      memcpy(d, ns, len);
+      d += len;
+      left -= len;
+      while (justify < -len && left > 1) {
+        *d++ = ' ';
+        justify++;
+        left--;
+      }
+      s++;
+      continue;
+    }
+
+    format_number(&d, left, va_arg(ap, int), *s++, justify, zeropad);
+  }
+  *d = 0;
+  va_end(ap);
+
+  g_write_raw(fd, buf, d - buf);
+}
+
+void g_sleep(unsigned int seconds)
+{
+  struct timespec ts = { seconds, 0 };
+  g_nanosleep_raw(&ts, NULL);
+}
+
+// vim:shiftwidth=2:expandtab
diff --git a/loader/llibc.h b/loader/llibc.h
new file mode 100644 (file)
index 0000000..5381972
--- /dev/null
@@ -0,0 +1,7 @@
+void g_fprintf(int fd, const char *fmt, ...)
+       __attribute__((format(printf, 2, 3)));
+
+#define g_printf(fmt, ...) \
+       g_fprintf(1, fmt, ##__VA_ARGS__)
+
+void g_sleep(unsigned int seconds);
index c8ff2fd..a6e3192 100644 (file)
@@ -7,7 +7,7 @@
 #include <stdio.h>
 
 #include "header.h"
-#include "sys_cacheflush.h"
+#include "syscalls.h"
 
 #include "override.c"
 
diff --git a/loader/sys_cacheflush.S b/loader/sys_cacheflush.S
deleted file mode 100644 (file)
index d6ecebe..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-@ vim:filetype=armasm
-#include <sys/syscall.h>
-
-
-.global sys_cacheflush @ const void *start_addr, const void *end_addr
-
-sys_cacheflush:
-    mov     r2, #0
-#ifdef __ARM_EABI__
-    /* EABI version */
-    str     r7, [sp, #-4]!
-    mov     r7, #(__ARM_NR_cacheflush & 0xff)
-#if (__ARM_NR_cacheflush & 0x00ff00)
-    orr     r7, r7, #(__ARM_NR_cacheflush & 0x00ff00)
-#endif
-#if (__ARM_NR_cacheflush & 0xff0000)
-    orr     r7, r7, #(__ARM_NR_cacheflush & 0xff0000)
-#endif
-    swi     0
-    ldr     r7, [sp], #4
-#else
-    /* OABI */
-    swi     __ARM_NR_cacheflush
-#endif
-    bx      lr
-
diff --git a/loader/sys_cacheflush.h b/loader/sys_cacheflush.h
deleted file mode 100644 (file)
index a35c00e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-void sys_cacheflush(const void *start_addr, const void *end_addr);
diff --git a/loader/syscalls.S b/loader/syscalls.S
new file mode 100644 (file)
index 0000000..e72e7d2
--- /dev/null
@@ -0,0 +1,88 @@
+@ vim:filetype=armasm
+#include <sys/syscall.h>
+
+
+.global sys_cacheflush @ const void *start_addr, const void *end_addr
+sys_cacheflush:
+    mov     r2, #0
+#ifdef __ARM_EABI__
+    /* EABI version */
+    str     r7, [sp, #-4]!
+    mov     r7, #(__ARM_NR_cacheflush & 0xff)
+#if (__ARM_NR_cacheflush & 0x00ff00)
+    orr     r7, r7, #(__ARM_NR_cacheflush & 0x00ff00)
+#endif
+#if (__ARM_NR_cacheflush & 0xff0000)
+    orr     r7, r7, #(__ARM_NR_cacheflush & 0xff0000)
+#endif
+    swi     0
+    ldr     r7, [sp], #4
+#else
+    /* OABI */
+    swi     __ARM_NR_cacheflush
+#endif
+    bx      lr
+
+
+#ifdef __ARM_EABI__
+#error hm?
+#endif
+
+.global g_syscall
+g_syscall:
+    mov   r12, sp
+    stmfd sp!, {r4, r5, r6}
+    ldmia r12, {r4, r5, r6}
+    swi   __NR_syscall
+    ldmfd sp!, {r4, r5, r6}
+
+.global g_syscall_error
+g_syscall_error:
+    cmn   r0, #4096
+    bxcc  lr
+    stmfd sp!, {r4, lr}
+    rsb   r4, r0, #0
+    bl    __errno_location
+    str   r4, [r0]
+    mov   r0, #-1
+    ldmfd sp!, {r4, pc}
+
+.global g_clone
+g_clone:
+    ldr   r12,[sp, #4]      @ arg6 - convenience func ptr
+    str   r4, [sp, #-4]!
+    ldr   r4, [sp, #4]      @ arg5
+    swi   __NR_clone
+    tst   r0, r0
+    bxeq  r12               @ child
+    cmn   r0, #4096
+    ldr   r4, [sp], #4
+    bcs   g_syscall_error
+    bx    lr
+
+@ raw - no errno
+.macro raw_syscall_easy name nr
+.global \name
+\name:
+    swi   \nr
+    bx    lr
+.endm
+
+.macro raw_syscall name nr
+.global \name
+\name:
+    mov   r12, sp
+    stmfd sp!, {r4, r5, r6, lr}
+    ldmia r12, {r4, r5, r6}
+    swi   \nr
+    stmfd sp!, {r4, r5, r6, pc}
+.endm
+
+raw_syscall_easy g_open_raw,           __NR_open
+raw_syscall_easy g_read_raw,           __NR_read
+raw_syscall_easy g_write_raw,          __NR_write
+raw_syscall_easy g_futex_raw,          __NR_futex
+raw_syscall_easy g_nanosleep_raw,      __NR_nanosleep
+raw_syscall_easy g_clock_gettime_raw,  __NR_clock_gettime
+raw_syscall_easy g_rt_sigprocmask_raw, __NR_rt_sigprocmask
+
diff --git a/loader/syscalls.h b/loader/syscalls.h
new file mode 100644 (file)
index 0000000..e82e0fa
--- /dev/null
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+
+struct timespec;
+
+void sys_cacheflush(const void *start_addr, const void *end_addr);
+
+long g_syscall(long number, ...);
+
+// arg6 is func ptr, for convenience
+long g_clone(unsigned long flags, void *child_stack, ...);
+
+int  g_syscall_error(long kret);
+
+// raw - no errno handling
+long g_open_raw(const char *pathname, int flags, ...);
+long g_read_raw(int fd, void *buf, size_t count);
+long g_write_raw(int fd, const void *buf, size_t count);
+long g_futex_raw(int *uaddr, int op, int val,
+                 const struct timespec *timeout);
+long g_nanosleep_raw(const struct timespec *req, struct timespec *rem);
+long g_clock_gettime_raw(int clk_id, const struct timespec *tp);
+long g_rt_sigprocmask_raw(int how, const void *set, void *oldset,
+                         size_t sigsetsize);