/* * Copyright (C) 2005 Philippe Gerum . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "umm.h" #include "internal.h" /** * @ingroup cobalt * @defgroup cobalt_api POSIX interface * @anchor cobalt_api * * The Cobalt/POSIX interface is an implementation of a subset of the * * Single Unix specification over the Cobalt core. * * The routines from this subset are implemented as wrapper functions * as defined by the linker (--wrap option, see man ld(1)). The * linker flags for enabling symbol wrapping can be obtained from the * following command: *xeno-config --posix --ldflags*. * The full documentation for *xeno-config* can be found at * https://xenomai.org/documentation/xenomai-3/html/man1/xeno-config/index.html. * * When symbol wrapping is enabled: * * - calls to POSIX services for which Cobalt provides a (real-time) * implementation are redirected to the library implementing the * wrapper, by default libcobalt. A list of wrapped symbols libcobalt * overrides can be found in the source tree, in * lib/cobalt/cobalt.wrappers. * * With or without symbol wrapping: * * - the wrapper function of a POSIX routine can be explicitly * invoked by enclosing the function call with the __RT() macro. Since * the wrapper symbol is weak, it may be overriden by a 3rd party * library, typically to implement its own version of the POSIX call, * instead or on top of libcobalt's. e.g. __RT(sem_init(&sem, 0, 0)) * would initialize a real-time semaphore, usually from libcobalt * unless a stronger sem_init() wrapper has been provided by a 3rd * party library. * * - the libcobalt implementation of a POSIX routine can be * explicitly invoked by enclosing the function call with the * __COBALT() macro. e.g. __COBALT(sem_init(&sem, 0, 0)) would always * initialize a Cobalt semaphore (strong symbol). * * - the regular *libc implementation of a POSIX routine can be * explicitly invoked by enclosing the function call with the __STD() * macro. This form basically prevents the symbol wrapping to take * place. e.g. __STD(sem_init(&sem, 0, 0)) would always initialize a * regular *libc semaphore. This is strictly equivalent to calling the * __real_* form of such routine as documented for ld(1). * * Qualifying POSIX calls explicitly as described above may prove * useful for invoking real-time services selectively within a large * POSIX code base, for which globally enabling symbol wrapping would * be unpractical. This may also help in implementing real-time * service libraries for which depending on the linker's symbol * wrapping mechanism is not suitable. * * This said, conforming to the POSIX standard unspoiled by macro * tricks for developing an application may be a significant upside as * well. YMMV. */ __weak int __cobalt_control_bind = 0; int __cobalt_main_prio = -1; struct sigaction __cobalt_orig_sigdebug; static const struct option cobalt_options[] = { { #define main_prio_opt 0 .name = "main-prio", .has_arg = required_argument, }, { #define print_bufsz_opt 1 .name = "print-buffer-size", .has_arg = required_argument, }, { #define print_bufcnt_opt 2 .name = "print-buffer-count", .has_arg = required_argument, }, { #define print_syncdelay_opt 3 .name = "print-sync-delay", .has_arg = required_argument, }, { /* Sentinel */ } }; static void sigill_handler(int sig) { const char m[] = "no Xenomai/cobalt support in kernel?\n"; ssize_t rc __attribute__ ((unused)); rc = write(2, m, sizeof(m) - 1); exit(EXIT_FAILURE); } static void low_init(void) { sighandler_t old_sigill_handler; struct cobalt_bindreq breq; struct cobalt_featinfo *f; int ret; old_sigill_handler = signal(SIGILL, sigill_handler); if (old_sigill_handler == SIG_ERR) early_panic("signal(SIGILL): %s", strerror(errno)); f = &breq.feat_ret; breq.feat_req = XENOMAI_FEAT_DEP; if (__cobalt_control_bind) breq.feat_req |= __xn_feat_control; breq.abi_rev = XENOMAI_ABI_REV; ret = XENOMAI_SYSBIND(&breq); signal(SIGILL, old_sigill_handler); switch (ret) { case 0: break; case -EINVAL: early_panic("missing feature: %s", f->feat_mis_s); case -ENOEXEC: early_panic("ABI mismatch: required r%lu, provided r%lu", XENOMAI_ABI_REV, f->feat_abirev); case -EAGAIN: early_panic("Cobalt core present but stopped " "(use corectl --start)"); case -ENOSYS: early_panic("Cobalt core not enabled in kernel"); default: early_panic("binding failed: %s", strerror(-ret)); } trace_me("connected to Cobalt"); if (mlockall(MCL_CURRENT | MCL_FUTURE)) early_panic("mlockall: %s", strerror(errno)); trace_me("memory locked"); cobalt_ticks_init(f->clock_freq); cobalt_features_init(f); cobalt_init_umm(f->vdso_offset); trace_me("memory heaps mapped"); cobalt_init_current_keys(); } static int cobalt_init_2(void); static void cobalt_fork_handler(void) { cobalt_unmap_umm(); cobalt_clear_tsd(); cobalt_print_init_atfork(); if (cobalt_init_2()) exit(EXIT_FAILURE); } static inline void commit_stack_memory(void) { char stk[PTHREAD_STACK_MIN / 2]; cobalt_commit_memory(stk); } static void cobalt_init_1(void) { struct sigaction sa; sa.sa_sigaction = cobalt_sigdebug_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; sigaction(SIGDEBUG, &sa, &__cobalt_orig_sigdebug); /* * NOTE: a placeholder for pthread_atfork() may return an * error status with uClibc, so we don't check the return * value on purpose. */ pthread_atfork(NULL, NULL, cobalt_fork_handler); if (sizeof(struct cobalt_mutex_shadow) > sizeof(pthread_mutex_t)) early_panic("sizeof(pthread_mutex_t): %Zd <" " sizeof(cobalt_mutex_shadow): %Zd!", sizeof(pthread_mutex_t), sizeof(struct cobalt_mutex_shadow)); if (sizeof(struct cobalt_cond_shadow) > sizeof(pthread_cond_t)) early_panic("sizeof(pthread_cond_t): %Zd <" " sizeof(cobalt_cond_shadow): %Zd!", sizeof(pthread_cond_t), sizeof(struct cobalt_cond_shadow)); if (sizeof(struct cobalt_sem_shadow) > sizeof(sem_t)) early_panic("sizeof(sem_t): %Zd <" " sizeof(cobalt_sem_shadow): %Zd!", sizeof(sem_t), sizeof(struct cobalt_sem_shadow)); } static int cobalt_init_2(void) { pthread_t ptid = pthread_self(); struct sched_param parm; int policy, ret; commit_stack_memory(); /* We only need this for the main thread */ cobalt_default_condattr_init(); low_init(); cobalt_mutex_init(); cobalt_sched_init(); cobalt_thread_init(); cobalt_print_init(); if (__cobalt_control_bind) return 0; ret = __STD(pthread_getschedparam(ptid, &policy, &parm)); if (ret) { early_warning("pthread_getschedparam failed"); return -ret; } /* * Turn the main thread into a Cobalt thread. * __cobalt_main_prio might have been overriden by some * compilation unit which has been linked in, to force the * scheduling parameters. Otherwise, the current policy and * priority are reused, for declaring the thread to the * Cobalt scheduler. * * SCHED_FIFO is assumed for __cobalt_main_prio > 0. */ if (__cobalt_main_prio > 0) { policy = SCHED_FIFO; parm.sched_priority = __cobalt_main_prio; } else if (__cobalt_main_prio == 0) { policy = SCHED_OTHER; parm.sched_priority = 0; } ret = __RT(pthread_setschedparam(ptid, policy, &parm)); if (ret) { early_warning("pthread_setschedparam failed { policy=%d, prio=%d }", policy, parm.sched_priority); return -ret; } return 0; } int cobalt_init(void) { cobalt_init_1(); return cobalt_init_2(); } static int get_int_arg(const char *name, const char *arg, int *valp, int min) { int value, ret; char *p; errno = 0; value = (int)strtol(arg, &p, 10); if (errno || *p || value < min) { ret = -errno ?: -EINVAL; early_warning("invalid value for %s: %s", name, arg); return ret; } *valp = value; return 0; } static int cobalt_parse_option(int optnum, const char *optarg) { int value, ret; switch (optnum) { case main_prio_opt: ret = get_int_arg("--main-prio", optarg, &value, INT32_MIN); if (ret) return ret; __cobalt_main_prio = value; break; case print_bufsz_opt: ret = get_int_arg("--print-buffer-size", optarg, &value, 0); if (ret) return ret; __cobalt_print_bufsz = value; break; case print_bufcnt_opt: ret = get_int_arg("--print-buffer-count", optarg, &value, 0); if (ret) return ret; __cobalt_print_bufcount = value; break; case print_syncdelay_opt: ret = get_int_arg("--print-sync-delay", optarg, &value, 0); if (ret) return ret; __cobalt_print_syncdelay = value; break; default: /* Paranoid, can't happen. */ return -EINVAL; } return 0; } static void cobalt_help(void) { fprintf(stderr, "--main-prio= main thread priority\n"); fprintf(stderr, "--print-buffer-size= size of a print relay buffer (16k)\n"); fprintf(stderr, "--print-buffer-count= number of print relay buffers (4)\n"); fprintf(stderr, "--print-sync-delay= max delay of output synchronization (100 ms)\n"); } static struct setup_descriptor cobalt_interface = { .name = "cobalt", .init = cobalt_init, .options = cobalt_options, .parse_option = cobalt_parse_option, .help = cobalt_help, }; core_setup_call(cobalt_interface);