summaryrefslogtreecommitdiffstats
path: root/c/src/exec/libcsupport/src/newlibc.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/exec/libcsupport/src/newlibc.c')
-rw-r--r--c/src/exec/libcsupport/src/newlibc.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/c/src/exec/libcsupport/src/newlibc.c b/c/src/exec/libcsupport/src/newlibc.c
new file mode 100644
index 0000000000..ac8b146a48
--- /dev/null
+++ b/c/src/exec/libcsupport/src/newlibc.c
@@ -0,0 +1,430 @@
+
+/*
+ * COPYRIGHT (c) 1994 by Division Incorporated
+ *
+ * The license and distribution terms for this file may in
+ * the file LICENSE in this distribution or at
+ * http://www.OARcorp.com/rtems/license.html.
+ *
+ * Description:
+ * Implementation of hooks for the CYGNUS newlib libc
+ * These hooks set things up so that:
+ * '_REENT' is switched at task switch time.
+ *
+ *
+ * TODO:
+ *
+ * NOTE:
+ *
+ * $Id$
+ *
+ */
+
+#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
+#include <rtems.h>
+
+#if defined(RTEMS_NEWLIB)
+#include <libcsupport.h>
+#include <stdlib.h> /* for free() */
+#include <string.h> /* for memset() */
+
+#include <sys/reent.h> /* for extern of _REENT (aka _impure_ptr) */
+#include <errno.h>
+
+/*
+ * NOTE: When using RTEMS fake stat, fstat, and isatty, all output
+ * is line buffered so this setvbuf is not necessary. This
+ * setvbuf insures that we can redirect the output of a test
+ * on the UNIX simulator and it is in the same order as for a
+ * real target.
+ * NOTE:
+ * There is some problem with doing this on the hpux version
+ * of the UNIX simulator (symptom is printf core dumps), so
+ * we just don't for now.
+ * Not sure if this is a problem with hpux, newlib, or something else.
+ */
+
+#if defined(RTEMS_UNIX) && !defined(hpux)
+#define NEED_SETVBUF
+#endif
+
+#ifdef NEED_SETVBUF
+#include <stdio.h>
+#endif
+
+#include "internal.h"
+
+#define LIBC_NOTEPAD RTEMS_NOTEPAD_LAST
+
+
+int libc_reentrant; /* do we think we are reentrant? */
+struct _reent libc_global_reent = _REENT_INIT(libc_global_reent);
+
+/*
+ * CYGNUS newlib routine that does atexit() processing and flushes
+ * stdio streams
+ * undocumented
+ */
+
+extern void _wrapup_reent(struct _reent *);
+extern void _reclaim_reent(struct _reent *);
+
+void
+libc_wrapup(void)
+{
+ /*
+ * In case RTEMS is already down, don't do this. It could be
+ * dangerous.
+ */
+
+ if (!_System_state_Is_up(_System_state_Get()))
+ return;
+
+ _wrapup_reent(0);
+ if (_REENT != &libc_global_reent)
+ {
+ _wrapup_reent(&libc_global_reent);
+#if 0
+ /* don't reclaim this one, just in case we do printfs */
+ /* on our way out to ROM */
+ _reclaim_reent(&libc_global_reent);
+#endif
+ _REENT = &libc_global_reent;
+ }
+}
+
+
+rtems_boolean
+libc_create_hook(rtems_tcb *current_task,
+ rtems_tcb *creating_task)
+{
+ MY_task_set_note(creating_task, LIBC_NOTEPAD, 0);
+ return TRUE;
+}
+
+/*
+ * Called for all user TASKS (system tasks are MPCI Receive Server and IDLE)
+ */
+
+rtems_extension
+libc_start_hook(rtems_tcb *current_task,
+ rtems_tcb *starting_task)
+{
+ struct _reent *ptr;
+
+ /* NOTE: our malloc is reentrant without a reent ptr since
+ * it is based on region manager
+ */
+
+ ptr = (struct _reent *) calloc(1, sizeof(struct _reent));
+
+ if (!ptr)
+ rtems_fatal_error_occurred(RTEMS_NO_MEMORY);
+
+#ifdef __GNUC__
+ /* GCC extension: structure constants */
+ *ptr = (struct _reent) _REENT_INIT((*ptr));
+#else
+ /*
+ * Warning: THIS IS VERY DEPENDENT ON NEWLIB!!! WRITTEN FOR 1.7.0
+ */
+ ptr->_errno=0;
+ ptr->_stdin=&ptr->__sf[0];
+ ptr->_stdout=&ptr->__sf[1];
+ ptr->_stderr=&ptr->__sf[2];
+ ptr->_scanpoint=0;
+ ptr->_asctime[0]=0;
+ ptr->_next=1;
+ ptr->__sdidinit=0;
+#endif
+
+ MY_task_set_note(starting_task, LIBC_NOTEPAD, (rtems_unsigned32) ptr);
+}
+
+/*
+ * Called for all user TASKS (system tasks are MPCI Receive Server and IDLE)
+ *
+ * NOTE: When using RTEMS fake stat, fstat, and isatty, all output
+ * is line buffered so this setvbuf is not necessary. This
+ * setvbuf insures that we can redirect the output of a test
+ * on the UNIX simulator and it is in the same order as for a
+ * real target.
+ */
+
+#ifdef NEED_SETVBUF
+rtems_extension
+libc_begin_hook(rtems_tcb *current_task)
+{
+ setvbuf( stdout, NULL, _IOLBF, BUFSIZ );
+}
+#endif
+
+rtems_extension
+libc_switch_hook(rtems_tcb *current_task,
+ rtems_tcb *heir_task)
+{
+ rtems_unsigned32 impure_value;
+
+ /* XXX We can't use rtems_task_set_note() here since SYSI task has a
+ * tid of 0, which is treated specially (optimized, actually)
+ * by rtems_task_set_note
+ *
+ * NOTE: The above comment is no longer true and we need to use
+ * the extension data areas added about the same time.
+ */
+
+ /*
+ * Don't touch the outgoing task if it has been deleted.
+ */
+
+ if ( !_States_Is_transient( current_task->current_state ) ) {
+ impure_value = (rtems_unsigned32) _REENT;
+ MY_task_set_note(current_task, LIBC_NOTEPAD, impure_value);
+ }
+
+ _REENT = (struct _reent *) MY_task_get_note(heir_task, LIBC_NOTEPAD);
+
+}
+
+/*
+ * Function: libc_delete_hook
+ * Created: 94/12/10
+ *
+ * Description:
+ * Called when a task is deleted.
+ * Must restore the new lib reentrancy state for the new current
+ * task.
+ *
+ * Parameters:
+ *
+ *
+ * Returns:
+ *
+ *
+ * Side Effects:
+ *
+ * Notes:
+ *
+ *
+ * Deficiencies/ToDo:
+ *
+ *
+ */
+rtems_extension
+libc_delete_hook(rtems_tcb *current_task,
+ rtems_tcb *deleted_task)
+{
+ struct _reent *ptr;
+
+ /*
+ * The reentrancy structure was allocated by newlib using malloc()
+ */
+
+ if (current_task == deleted_task)
+ {
+ ptr = _REENT;
+ }
+ else
+ {
+ ptr = (struct _reent *) MY_task_get_note(deleted_task, LIBC_NOTEPAD);
+ }
+
+ /* if (ptr) */
+ if (ptr && ptr != &libc_global_reent)
+ {
+ _wrapup_reent(ptr);
+ _reclaim_reent(ptr);
+ free(ptr);
+ }
+
+ MY_task_set_note(deleted_task, LIBC_NOTEPAD, 0);
+
+ /*
+ * Require the switch back to another task to install its own
+ */
+
+ if (current_task == deleted_task)
+ {
+ _REENT = 0;
+ }
+}
+
+/*
+ * Function: libc_init
+ * Created: 94/12/10
+ *
+ * Description:
+ * Init libc for CYGNUS newlib
+ * Set up _REENT to use our global libc_global_reent.
+ * (newlib provides a global of its own, but we prefer our
+ * own name for it)
+ *
+ * If reentrancy is desired (which it should be), then
+ * we install the task extension hooks to maintain the
+ * newlib reentrancy global variable _REENT on task
+ * create, delete, switch, exit, etc.
+ *
+ * Parameters:
+ * reentrant non-zero if reentrant library desired.
+ *
+ * Returns:
+ *
+ * Side Effects:
+ * installs libc extensions if reentrant.
+ *
+ * Notes:
+ *
+ *
+ * Deficiencies/ToDo:
+ *
+ */
+
+void
+libc_init(int reentrant)
+{
+ rtems_extensions_table libc_extension;
+ rtems_id extension_id;
+ rtems_status_code rc;
+
+ _REENT = &libc_global_reent;
+
+ if (reentrant)
+ {
+ memset(&libc_extension, 0, sizeof(libc_extension));
+
+ libc_extension.thread_create = libc_create_hook;
+ libc_extension.thread_start = libc_start_hook;
+#ifdef NEED_SETVBUF
+ libc_extension.thread_begin = libc_begin_hook;
+#endif
+ libc_extension.thread_switch = libc_switch_hook;
+ libc_extension.thread_delete = libc_delete_hook;
+
+ rc = rtems_extension_create(rtems_build_name('L', 'I', 'B', 'C'),
+ &libc_extension, &extension_id);
+ if (rc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred(rc);
+
+ libc_reentrant = reentrant;
+ }
+}
+
+#if 0
+/*
+ * Routines required by the gnat runtime.
+ */
+
+int get_errno()
+{
+ return errno;
+}
+#endif
+
+/*
+ * Function: _exit
+ * Created: 94/12/10
+ *
+ * Description:
+ * Called from exit() after it does atexit() processing and stdio fflush's
+ *
+ * called from bottom of exit() to really delete the task.
+ * If we are using reentrant libc, then let the delete extension
+ * do all the work, otherwise if a shutdown is in progress,
+ * then just do it.
+ *
+ * Parameters:
+ * exit status
+ *
+ * Returns:
+ * does not return
+ *
+ * Side Effects:
+ *
+ * Notes:
+ *
+ *
+ * Deficiencies/ToDo:
+ *
+ *
+ */
+
+#include <stdio.h>
+
+/* #if !defined(RTEMS_UNIX) && !defined(__GO32__) && !defined(_AM29K) */
+#if !defined(RTEMS_UNIX) && !defined(_AM29K)
+void _exit(int status)
+{
+ rtems_shutdown_executive(status);
+}
+
+#else
+
+void exit(int status)
+{
+ libc_wrapup();
+ rtems_shutdown_executive(status);
+}
+#endif
+
+
+/*
+ * These are directly supported (and completely correct) in the posix api.
+ */
+
+#if !defined(RTEMS_POSIX_API)
+pid_t __getpid(void)
+{
+ return 0;
+}
+#endif
+
+/* #if !defined(RTEMS_POSIX_API) || defined(__GO32__) */
+#if !defined(RTEMS_POSIX_API)
+pid_t getpid(void)
+{
+ return __getpid();
+}
+#endif
+
+/* #if !defined(RTEMS_POSIX_API) || defined(__GO32__) */
+#if !defined(RTEMS_POSIX_API)
+int kill( pid_t pid, int sig )
+{
+ return 0;
+}
+#endif
+
+int __kill( pid_t pid, int sig )
+{
+ return 0;
+}
+
+#if !defined(RTEMS_POSIX_API)
+unsigned int sleep(
+ unsigned int seconds
+)
+{
+ rtems_status_code status;
+ rtems_interval ticks_per_second;
+ rtems_interval ticks;
+
+ status = rtems_clock_get(
+ RTEMS_CLOCK_GET_TICKS_PER_SECOND,
+ &ticks_per_second
+ );
+
+ ticks = seconds * ticks_per_second;
+
+ status = rtems_task_wake_after( ticks );
+
+ /*
+ * Returns the "unslept" amount of time. In RTEMS signals are not
+ * interruptable, so tasks really sleep all of the requested time.
+ */
+
+ return 0;
+}
+#endif
+
+
+#endif