/* * Copyright (C) 2013 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 "boilerplate/atomic.h" #include "boilerplate/lock.h" #include "boilerplate/time.h" #include "boilerplate/scope.h" #include "boilerplate/setup.h" #include "boilerplate/debug.h" #include "boilerplate/ancillaries.h" #include "boilerplate/signal.h" #include "boilerplate/namegen.h" #include "xenomai/init.h" pthread_mutex_t __printlock; static struct timespec init_date; static int init_done; static void __do_printout(const char *name, const char *header, unsigned int ms, unsigned int us, const char *fmt, va_list ap) { FILE *fp = stderr; __RT(fprintf(fp, "%4u\"%.3u.%.3u| ", ms / 1000, ms % 1000, us)); if (header) __RT(fputs(header, fp)); __RT(fprintf(fp, "[%s] ", name ?: "main")); __RT(vfprintf(fp, fmt, ap)); __RT(fputc('\n', fp)); } static void __do_early_printout(const char *name, const char *header, unsigned int ms, unsigned int us, const char *fmt, va_list ap) { FILE *fp = stderr; __STD(fprintf(fp, "%4u\"%.3u.%.3u| ", ms / 1000, ms % 1000, us)); if (header) __STD(fputs(header, fp)); __STD(fprintf(fp, "[%s] ", name ?: "main")); __STD(vfprintf(fp, fmt, ap)); __STD(fputc('\n', fp)); fflush(fp); } void __printout(const char *name, const char *header, const char *fmt, va_list ap) { struct timespec now, delta; unsigned long long ns; unsigned int ms, us; /* * Catch early printouts, when the init sequence is not * completed yet. In such event, we don't care for serializing * output, since we must be running over the main thread * uncontended. */ if (!init_done) { __do_early_printout(name, header, 0, 0, fmt, ap); return; } __RT(clock_gettime(CLOCK_MONOTONIC, &now)); timespec_sub(&delta, &now, &init_date); ns = delta.tv_sec * 1000000000ULL; ns += delta.tv_nsec; ms = ns / 1000000ULL; us = (ns % 1000000ULL) / 1000ULL; SIGSAFE_LOCK_ENTRY(&__printlock); __do_printout(name, header, ms, us, fmt, ap); SIGSAFE_LOCK_EXIT(&__printlock); } void __warning(const char *name, const char *fmt, va_list ap) { __printout(name, "WARNING: ", fmt, ap); } void __notice(const char *name, const char *fmt, va_list ap) { __printout(name, NULL, fmt, ap); } void ___panic(const char *fn, const char *name, const char *fmt, va_list ap) { char *p; if (asprintf(&p, "BUG in %s(): ", fn) < 0) p = "BUG: "; __printout(name, p, fmt, ap); #ifdef CONFIG_XENO_COBALT rt_print_flush_buffers(); #endif /* CONFIG_XENO_COBALT */ exit(1); } __weak void error_hook(struct error_frame *ef) /* NULL in non-debug mode */ { } #define __esym_def(e) [e] = #e static const char *__esym_map[] = { [0] = "OK", __esym_def(EPERM), __esym_def(ENOENT), __esym_def(ESRCH), __esym_def(EINTR), __esym_def(EIO), __esym_def(ENXIO), __esym_def(E2BIG), __esym_def(ENOEXEC), __esym_def(EBADF), __esym_def(ECHILD), __esym_def(EAGAIN), __esym_def(ENOMEM), __esym_def(EACCES), __esym_def(EFAULT), __esym_def(ENOTBLK), __esym_def(EBUSY), __esym_def(EEXIST), __esym_def(EXDEV), __esym_def(ENODEV), __esym_def(ENOTDIR), __esym_def(EISDIR), __esym_def(EINVAL), __esym_def(ENFILE), __esym_def(EMFILE), __esym_def(ENOTTY), __esym_def(ETXTBSY), __esym_def(EFBIG), __esym_def(ENOSPC), __esym_def(ESPIPE), __esym_def(EROFS), __esym_def(EMLINK), __esym_def(EPIPE), __esym_def(EDOM), __esym_def(ERANGE), __esym_def(ENOSYS), __esym_def(ETIMEDOUT), __esym_def(ENOMSG), __esym_def(EIDRM), __esym_def(EADDRINUSE), __esym_def(EPROTO), }; #define __esym_max (sizeof(__esym_map) / sizeof(__esym_map[0])) const char *symerror(int errnum) { int v = -errnum; size_t ebufsz; char *ebuf; if (v < 0 || v >= (int)__esym_max || __esym_map[v] == NULL) { /* Catch missing codes in the error map. */ ebuf = __get_error_buf(&ebufsz); snprintf(ebuf, ebufsz, "%d?", errnum); return ebuf; } return __esym_map[v]; } void __run_cleanup_block(struct cleanup_block *cb) { __RT(pthread_mutex_unlock(cb->lock)); cb->handler(cb->arg); } void __early_panic(const char *fn, const char *fmt, ...) { va_list ap; va_start(ap, fmt); ___panic(fn, NULL, fmt, ap); va_end(ap); } void __panic(const char *fn, const char *fmt, ...) __attribute__((alias("__early_panic"), weak)); void early_warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __warning(NULL, fmt, ap); va_end(ap); } void warning(const char *fmt, ...) __attribute__((alias("early_warning"), weak)); void early_notice(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __notice(NULL, fmt, ap); va_end(ap); } void notice(const char *fmt, ...) __attribute__((alias("early_notice"), weak)); char *generate_name(char *buf, const char *radix, struct name_generator *ngen) { int len = ngen->length - 1, tag; if (radix && *radix) { strncpy(buf, radix, len); buf[len] = '\0'; } else { tag = atomic_add_fetch(&ngen->serial, 1); #ifdef CONFIG_XENO_PSHARED snprintf(buf, len, "%s@%d[%d]", ngen->radix, tag, __node_id); #else snprintf(buf, len, "%s@%d", ngen->radix, tag); #endif } return buf; } #ifdef CONFIG_XENO_PSHARED /* * Client libraries may override these symbols for implementing heap * pointer validation in their own context (e.g. copperplate). */ __weak int pshared_check(void *heap, void *addr) { return 1; } __weak void *__main_heap = NULL; #endif /* !CONFIG_XENO_PSHARED */ #ifdef CONFIG_XENO_DEBUG int __check_cancel_type(const char *locktype) { int oldtype; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); if (oldtype == PTHREAD_CANCEL_DEFERRED) return 0; warning("%s() section is NOT cancel-safe", locktype); abort(); return __bt(-EINVAL); } #endif /* CONFIG_XENO_DEBUG */ int get_static_cpu_count(void) { char buf[BUFSIZ]; int count = 0; FILE *fp; /* * We want the maximum # of CPU the running kernel was * configured for, not the current online/present/possible * count of CPU devices. */ fp = fopen("/sys/devices/system/cpu/kernel_max", "r"); if (fp == NULL) return -1; if (fgets(buf, sizeof(buf), fp)) count = atoi(buf); fclose(fp); return count; } static int __get_online_cpus_from_proc(cpu_set_t *cpuset) { char buf[BUFSIZ]; FILE *fp; int cpu; /* If no hotplug support, fall back to reading /proc/stat. */ fp = fopen("/proc/stat", "r"); if (fp == NULL) return -ENOENT; while (fgets(buf, sizeof(buf), fp)) { /* * Like the glibc, assume cpu* entries are at the * front of /proc/stat and will stay this way. */ if (strncmp(buf, "cpu", 3)) break; if (!isdigit(buf[3])) continue; cpu = atoi(buf + 3); if (cpu >= 0 && cpu < CPU_SETSIZE) CPU_SET(cpu, cpuset); } fclose(fp); return 0; } int get_online_cpu_set(cpu_set_t *cpuset) { char buf[BUFSIZ], *range, *save_range, *save_bound, *lo_bound, *hi_bound, *p; int cpu_lo, cpu_hi; FILE *fp; CPU_ZERO(cpuset); fp = fopen("/sys/devices/system/cpu/online", "r"); if (fp == NULL) return __get_online_cpus_from_proc(cpuset); if (fgets(buf, sizeof(buf), fp) == NULL) { fclose(fp); return -EBADF; } p = buf; for (;;) { range = strtok_r(p, " \t", &save_range); if (range == NULL) break; lo_bound = strtok_r(range, "-", &save_bound); if (lo_bound) { cpu_lo = atoi(lo_bound); hi_bound = strtok_r(NULL, "-", &save_bound); if (hi_bound) { cpu_hi = atoi(hi_bound); do { CPU_SET(cpu_lo, cpuset); } while (cpu_lo++ < cpu_hi); } else CPU_SET(cpu_lo, cpuset); } p = NULL; } fclose(fp); return 0; } #ifdef CONFIG_XENO_COBALT #include int get_realtime_cpu_set(cpu_set_t *cpuset) { unsigned long long cpumask; char buf[BUFSIZ], *p; FILE *fp; int cpu; fp = fopen("/sys/module/xenomai/parameters/supported_cpus", "r"); if (fp == NULL) return -ENOENT; p = fgets(buf, sizeof(buf), fp); fclose(fp); if (p == NULL) return -EBADF; errno = 0; cpumask = strtoll(p, NULL, 10); if (cpumask == LLONG_MAX && errno == ERANGE) cpumask = ULLONG_MAX; for (cpu = 0; cpumask != 0; cpu++, cpumask >>= 1) { if (cpumask & 1ULL) CPU_SET(cpu, cpuset); } return 0; } int get_current_cpu(void) /* No mode migration */ { struct cobalt_threadstat stat; int ret; ret = cobalt_thread_stat(0, &stat); if (ret) return ret; return stat.cpu; } #else /* CONFIG_XENO_MERCURY */ int get_realtime_cpu_set(cpu_set_t *cpuset) { return get_online_cpu_set(cpuset); } int get_current_cpu(void) { int cpu = sched_getcpu(); if (cpu < 0) return -errno; return cpu; } #endif /* CONFIG_XENO_MERCURY */ pid_t get_thread_pid(void) { return syscall(__NR_gettid); } char *lookup_command(const char *cmd) { const char *dirs[] = { "/bin", "/sbin", "/usr/bin", "/usr/sbin", }; char *path; int n, ret; for (n = 0; n < sizeof(dirs) / sizeof(dirs[0]); n++) { ret = asprintf(&path, "%s/%s", dirs[n], cmd); if (ret < 0) return NULL; if (access(path, X_OK) == 0) return path; free(path); } return NULL; } size_t get_mem_size(const char *arg) { size_t size; char *p; size = strtol(arg, &p, 0); if (size == LONG_MIN || size == LONG_MAX) return 0; if (*p == '\0') return size; switch (tolower(*p)) { case 'k': size *= 1024; break; case 'm': size *= (1024 * 1024); break; case 'g': size *= (1024 * 1024 * 1024); break; default: size = 0; } return size; } const char *config_strings[] = { #include "config-dump.h" NULL, }; void __boilerplate_init(void) { __RT(clock_gettime(CLOCK_MONOTONIC, &init_date)); __RT(pthread_mutex_init(&__printlock, NULL)); debug_init(); init_done = 1; } static int boilerplate_init(void) { pthread_atfork(NULL, NULL, __boilerplate_init); __boilerplate_init(); return 0; } static struct setup_descriptor boilerplate_interface = { .name = "boilerplate", .init = boilerplate_init, }; boilerplate_setup_call(boilerplate_interface);