aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNIIBE Yutaka <gniibe@fsij.org>2017-06-23 09:05:11 +0900
committerNIIBE Yutaka <gniibe@fsij.org>2017-06-23 09:05:11 +0900
commit78c825afe83ff20e43fb680e16d5b86ccbe04fe1 (patch)
treeeadfb251e612c00cc309a3335082fe8714ba69a5
parent1bd14d8d40b7132fb2648df8cccb36f73532b17c (diff)
Add GNU/Linux support (not yet makefile).
-rw-r--r--ChangeLog2
-rw-r--r--chopstx-gnu-linux.c342
-rw-r--r--chopstx-gnu-linux.h9
3 files changed, 353 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 4d043e9..cce33a2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
2017-06-23 NIIBE Yutaka <gniibe@fsij.org>
+ * chopstx-gnu-linux.c, chopstx-gnu-linux.h: New.
+
* chopstx.c (chx_init): Use chx_init_arch.
(chopstx_create): Use chopstx_create_arch.
(chx_systick_reset, chx_systick_reload, chx_systick_get)
diff --git a/chopstx-gnu-linux.c b/chopstx-gnu-linux.c
new file mode 100644
index 0000000..b7b6767
--- /dev/null
+++ b/chopstx-gnu-linux.c
@@ -0,0 +1,342 @@
+/*
+ * chopstx-gnu-linux.c - Threads and only threads: Arch specific code
+ * for GNU/Linux emulation
+ *
+ * Copyright (C) 2017 Flying Stone Technology
+ * Author: NIIBE Yutaka <gniibe@fsij.org>
+ *
+ * This file is a part of Chopstx, a thread library for embedded.
+ *
+ * Chopstx is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chopstx is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As additional permission under GNU GPL version 3 section 7, you may
+ * distribute non-source form of the Program without the copy of the
+ * GNU GPL normally required by section 4, provided you inform the
+ * receipents of GNU GPL by a written offer.
+ *
+ */
+
+#include <unistd.h>
+#include <ucontext.h>
+#include <signal.h>
+#include <sys/time.h>
+
+static sigset_t ss_cur;
+
+static void
+chx_systick_reset (void)
+{
+ const struct itimerval it = { {0, 0}, {0, 0} };
+
+ setitimer (ITIMER_REAL, &it, 0);
+}
+
+static void
+chx_systick_reload (uint32_t ticks)
+{
+ struct itimerval it;
+
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_usec = (ticks / MHZ);
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 0;
+
+ setitimer (ITIMER_REAL, &it, 0);
+}
+
+static uint32_t
+chx_systick_get (void)
+{
+ struct itimerval it;
+ getitimer (ITIMER_REAL, &it);
+ return it.it_value.tv_usec * 72;
+}
+
+static uint32_t
+usec_to_ticks (uint32_t usec)
+{
+ return usec * MHZ;
+}
+
+
+static void
+chx_enable_intr (uint8_t irq_num)
+{
+ sigdelset (&ss_cur, irq_num);
+}
+
+static void
+chx_clr_intr (uint8_t irq_num)
+{ /* Clear pending interrupt. */
+ (void)irq_num;
+}
+
+static void
+chx_disable_intr (uint8_t irq_num)
+{
+ sigaddset (&ss_cur, irq_num);
+}
+
+static void
+chx_set_intr_prio (uint8_t n)
+{
+ (void)n;
+}
+
+static void
+chx_prio_init (void)
+{
+}
+
+static void
+chx_cpu_sched_lock (void)
+{
+ sigset_t ss;
+
+ sigfillset (&ss);
+ pthread_sigmask (SIG_BLOCK, &ss, &ss_cur);
+}
+
+static void
+chx_cpu_sched_unlock (void)
+{
+ pthread_sigmask (SIG_SETMASK, &ss_cur, NULL);
+}
+
+static void
+idle (void)
+{
+ for (;;)
+ pause ();
+}
+
+void
+chx_handle_intr (uint32_t irq_num)
+{
+ struct chx_pq *p;
+
+ chx_disable_intr (irq_num);
+ chx_spin_lock (&q_intr.lock);
+ for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next)
+ if (p->v == irq_num)
+ { /* should be one at most. */
+ struct chx_px *px = (struct chx_px *)p;
+
+ ll_dequeue (p);
+ chx_wakeup (p);
+ chx_spin_unlock (&q_intr.lock);
+ chx_request_preemption (px->master->prio);
+ return;
+ }
+ chx_spin_unlock (&q_intr.lock);
+}
+
+
+static ucontext_t idle_tc;
+static char idle_stack[4096];
+
+static struct chx_thread main_thread;
+
+void
+chx_sigmask (ucontext_t *uc)
+{
+ /* Modify oldmask to SS_CUR, so that the signal mask will
+ * be set to SS_CUR.
+ *
+ * In user-level, sigset_t is big, but only the first word
+ * is used by the kernel.
+ */
+ memcpy (&uc->uc_sigmask, &ss_cur, sizeof (uint64_t));
+}
+
+static void
+sigalrm_handler (int sig, siginfo_t *siginfo, void *arg)
+{
+ ucontext_t *uc = arg;
+ (void)sig;
+ (void)siginfo;
+ chx_timer_expired ();
+ chx_sigmask (uc);
+}
+
+static void
+chx_init_arch (struct chx_thread *tp)
+{
+ struct sigaction sa;
+
+ sigemptyset (&ss_cur);
+
+ sa.sa_sigaction = sigalrm_handler;
+ sigfillset (&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO|SA_RESTART;
+ sigaction (SIGALRM, &sa, NULL);
+
+ getcontext (&idle_tc);
+ idle_tc.uc_stack.ss_sp = idle_stack;
+ idle_tc.uc_stack.ss_size = sizeof (idle_stack);
+ idle_tc.uc_link = NULL;
+ makecontext (&idle_tc, idle, 0);
+
+ getcontext (&tp->tc);
+}
+
+static void
+chx_request_preemption (uint16_t prio)
+{
+ struct chx_thread *tp, *tp_prev;
+ ucontext_t *tcp;
+
+ if (running && (uint16_t)running->prio >= prio)
+ return;
+
+ /* Change the context to another thread with higher priority. */
+ tp = tp_prev = running;
+ if (tp)
+ {
+ if (tp->flag_sched_rr)
+ {
+ if (tp->state == THREAD_RUNNING)
+ {
+ chx_timer_dequeue (tp);
+ chx_ready_enqueue (tp);
+ }
+ }
+ else
+ chx_ready_push (tp);
+ running = NULL;
+ }
+
+ tp = running = chx_ready_pop ();
+ if (tp)
+ {
+ tcp = &tp->tc;
+ if (tp->flag_sched_rr)
+ {
+ chx_spin_lock (&q_timer.lock);
+ tp = chx_timer_insert (tp, PREEMPTION_USEC);
+ chx_spin_unlock (&q_timer.lock);
+ }
+ }
+ else
+ tcp = &idle_tc;
+
+ if (tp_prev)
+ {
+ /*
+ * The swapcontext implementation may reset sigmask in the
+ * middle of its execution, unfortunately. It is best if
+ * sigmask restore is done at the end of the routine, but we
+ * can't assume that.
+ *
+ * Thus, there might be a race condition with regards to the
+ * user context TCP, if signal mask is cleared and signal comes
+ * in. To avoid this situation, we block signals.
+ *
+ * We don't need to fill the mask here. It keeps the condition
+ * of blocking signals before&after swapcontext call. It is
+ * done by the signal mask for sigaction, the initial creation
+ * of the thread, and the condition of chx_sched function which
+ * mandates holding cpu_sched_lock.
+ */
+ swapcontext (&tp_prev->tc, tcp);
+ }
+ else if (tp)
+ {
+ setcontext (tcp);
+ }
+}
+
+/*
+ * chx_sched: switch to another thread.
+ *
+ * There are two cases:
+ * YIELD=0 (SLEEP): Current RUNNING thread is already connected to
+ * something (mutex, cond, intr, etc.)
+ * YIELD=1 (YIELD): Current RUNNING thread is active,
+ * it is needed to be enqueued to READY queue.
+ *
+ * Returns:
+ * 1 on wakeup by others.
+ * 0 on normal wakeup.
+ * -1 on cancellation.
+ */
+static uintptr_t
+chx_sched (uint32_t yield)
+{
+ struct chx_thread *tp, *tp_prev;
+ uintptr_t v;
+ ucontext_t *tcp;
+
+ tp = tp_prev = running;
+ if (yield)
+ {
+ if (tp->flag_sched_rr)
+ chx_timer_dequeue (tp);
+ chx_ready_enqueue (tp);
+ }
+
+ running = tp = chx_ready_pop ();
+ if (tp)
+ {
+ v = tp->v;
+ if (tp->flag_sched_rr)
+ {
+ chx_spin_lock (&q_timer.lock);
+ tp = chx_timer_insert (tp, PREEMPTION_USEC);
+ chx_spin_unlock (&q_timer.lock);
+ }
+ tcp = &tp->tc;
+ }
+ else
+ {
+ v = 0;
+ tcp = &idle_tc;
+ }
+
+ swapcontext (&tp_prev->tc, tcp);
+ chx_cpu_sched_unlock ();
+ return v;
+}
+
+static void __attribute__((__noreturn__))
+chx_thread_start (voidfunc thread_entry, void *arg)
+{
+ chx_cpu_sched_unlock ();
+ thread_entry (arg);
+ chopstx_exit (0);
+}
+
+static struct chx_thread *
+chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
+ voidfunc thread_entry, void *arg)
+{
+ tp = malloc (sizeof (struct chx_thread));
+ if (!tp)
+ chx_fatal (CHOPSTX_ERR_THREAD_CREATE);
+
+ /*
+ * Calling getcontext with sched_lock held, the context is with
+ * signal blocked. The sigmask will be cleared in chx_thread_start.
+ */
+ chx_cpu_sched_lock ();
+ getcontext (&tp->tc);
+ tp->tc.uc_stack.ss_sp = (void *)stack_addr;
+ tp->tc.uc_stack.ss_size = stack_size;
+ tp->tc.uc_link = NULL;
+
+ makecontext (&tp->tc, (void (*)(void))chx_thread_start,
+ 4, thread_entry, arg);
+ chx_cpu_sched_unlock ();
+ return tp;
+}
diff --git a/chopstx-gnu-linux.h b/chopstx-gnu-linux.h
new file mode 100644
index 0000000..cce34f1
--- /dev/null
+++ b/chopstx-gnu-linux.h
@@ -0,0 +1,9 @@
+/*
+ * The thread context: specific to GNU/Linux.
+ *
+ * We use the type ucontext_t, which includes all registers;
+ * Note that signal mask is also included in ucontext_t.
+ *
+ */
+
+typedef ucontext_t tcontext_t;