diff --git a/include/nuttx/fdpic.h b/include/nuttx/fdpic.h index 27b997f7f9073..b96757a8a9878 100644 --- a/include/nuttx/fdpic.h +++ b/include/nuttx/fdpic.h @@ -96,33 +96,77 @@ static inline FAR void *fdpic_callback(FAR void *fn) return fn; } +/**************************************************************************** + * Name: fdpic_desc_init + * + * Description: + * Record a callback for a later call from a thread that carries no module + * base. Runs in the caller's own context, which is the only place the + * answer can be had. + * + * A module's function pointer is the address of a descriptor, and the base + * comes from there: a module can hand over a callback that belongs to + * another one. A firmware pointer is a code address and has no base. + * + * Input Parameters: + * desc - The descriptor to fill. + * fn - The callback, as the caller received it. + * + ****************************************************************************/ + +static inline void fdpic_desc_init(FAR struct fdpic_desc_s *desc, + FAR void *fn) +{ + if (fn != NULL && fdpic_base() != 0) + { + *desc = *(FAR struct fdpic_desc_s *)fn; + } + else + { + desc->entry = (uintptr_t)fn; + desc->got = 0; + } +} + /**************************************************************************** * Name: fdpic_invoke * * Description: - * Call a resolved module entry point with the module data base in the PIC - * base register. For a callback that runs on a shared thread, which - * carries no module base. Elsewhere fdpic_callback() is enough. + * Enter a callback recorded by fdpic_desc_init(), with the data base it + * carries in the PIC base register. For a callback that runs on a shared + * thread, which has no base of its own. Elsewhere fdpic_callback() is + * enough. + * + * A zero base means the callback is not a module's, and it is branched to + * directly. * * Input Parameters: - * arg - The one word argument. - * entry - The code address to enter, already resolved from the descriptor. - * got - The module data base to install. + * arg - The one word argument. + * desc - The recorded callback. * ****************************************************************************/ -static inline void fdpic_invoke(uintptr_t arg, uintptr_t entry, - uintptr_t got) +static inline void fdpic_invoke(uintptr_t arg, + FAR const struct fdpic_desc_s *desc) { - up_fdpic_invoke(arg, entry, got); + if (desc->got != 0) + { + up_fdpic_invoke(arg, desc->entry, desc->got); + } + else + { + ((CODE void (*)(uintptr_t))desc->entry)(arg); + } } #else # define fdpic_base() (0) # define fdpic_callback(fn) (fn) -# define fdpic_invoke(arg, entry, got) \ - ((void)(got), (((CODE void (*)(uintptr_t))(uintptr_t)(entry))(arg))) +# define fdpic_desc_init(desc, fn) \ + ((desc)->entry = (uintptr_t)(fn), (desc)->got = 0) +# define fdpic_invoke(arg, desc) \ + (((CODE void (*)(uintptr_t))(desc)->entry)(arg)) #endif /* CONFIG_FDPIC */ diff --git a/include/nuttx/signal.h b/include/nuttx/signal.h index 79fa22b39d501..41d42dba5689b 100644 --- a/include/nuttx/signal.h +++ b/include/nuttx/signal.h @@ -35,6 +35,9 @@ #include #include +#ifdef CONFIG_FDPIC +# include +#endif /**************************************************************************** * Pre-processor Definitions @@ -67,7 +70,14 @@ struct sigwork_s { struct work_s work; /* Work queue structure */ union sigval value; /* Data passed with notification */ +#ifdef CONFIG_FDPIC + struct fdpic_desc_s func; /* Notification function, and the data base + * of a module callback or zero. The base is + * captured at registration and installed + * around the call on the worker thread. */ +#else sigev_notify_function_t func; /* Notification function */ +#endif }; #ifdef __cplusplus diff --git a/libs/libc/dirent/lib_scandir.c b/libs/libc/dirent/lib_scandir.c index c734ff1d243cb..bf81022fb17fa 100644 --- a/libs/libc/dirent/lib_scandir.c +++ b/libs/libc/dirent/lib_scandir.c @@ -31,6 +31,10 @@ #include #include +#ifdef CONFIG_FDPIC +# include +#endif + #include "libc.h" /* The scandir() function is not appropriate for use within the kernel in its @@ -91,6 +95,15 @@ int scandir(FAR const char *path, FAR struct dirent ***namelist, * the original errno value to be able to restore it in case of success. */ +#ifdef CONFIG_FDPIC + /* Resolve the filter descriptor. compar is not resolved here: qsort() + * does it, and resolving twice reads a code address as a descriptor. + */ + + filter = (CODE int (*)(FAR const struct dirent *)) + fdpic_callback((FAR void *)filter); +#endif + errsv = get_errno(); dirp = opendir(path); @@ -191,6 +204,7 @@ int scandir(FAR const char *path, FAR struct dirent ***namelist, if (compar) { typedef int (*compar_fn_t)(FAR const void *, FAR const void *); + qsort(list, cnt, sizeof(*list), (compar_fn_t)compar); } diff --git a/libs/libc/pthread/pthread_create.c b/libs/libc/pthread/pthread_create.c index 6c87140361cd4..6e9e8e82542d0 100644 --- a/libs/libc/pthread/pthread_create.c +++ b/libs/libc/pthread/pthread_create.c @@ -30,6 +30,10 @@ #include +#ifdef CONFIG_FDPIC +# include +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -88,6 +92,16 @@ static void pthread_startup(pthread_startroutine_t entry, int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr, pthread_startroutine_t pthread_entry, pthread_addr_t arg) { +#ifdef CONFIG_FDPIC + /* Resolve the descriptor once, in the public entry point. The new + * thread inherits the creator's D-Space, so it needs only the code + * address. + */ + + pthread_entry = (pthread_startroutine_t) + fdpic_callback((FAR void *)pthread_entry); +#endif + return nx_pthread_create(pthread_startup, thread, attr, pthread_entry, arg); } diff --git a/libs/libc/pthread/pthread_once.c b/libs/libc/pthread/pthread_once.c index ccd854b878843..7b3d9eb3ac3f1 100644 --- a/libs/libc/pthread/pthread_once.c +++ b/libs/libc/pthread/pthread_once.c @@ -33,6 +33,10 @@ #include #include +#ifdef CONFIG_FDPIC +# include +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -73,6 +77,15 @@ int pthread_once(FAR pthread_once_t *once_control, return EINVAL; } +#ifdef CONFIG_FDPIC + /* Resolve the descriptor here. The value is a local copy, so a later + * call through the same once_control resolves afresh. + */ + + init_routine = (CODE void (*)(void)) + fdpic_callback((FAR void *)init_routine); +#endif + if (!once_control->done) { pthread_mutex_lock(&once_control->mutex); diff --git a/libs/libc/stdlib/lib_bsearch.c b/libs/libc/stdlib/lib_bsearch.c index a4e3047bf9030..c449480ad9152 100644 --- a/libs/libc/stdlib/lib_bsearch.c +++ b/libs/libc/stdlib/lib_bsearch.c @@ -37,6 +37,10 @@ ****************************************************************************/ #include + +#ifdef CONFIG_FDPIC +# include +#endif #include /**************************************************************************** @@ -114,6 +118,13 @@ FAR void *bsearch(FAR const void *key, FAR const void *base, size_t nel, DEBUGASSERT(base != NULL || nel == 0); DEBUGASSERT(compar != NULL); +#ifdef CONFIG_FDPIC + /* See qsort(): an FDPIC caller passes a descriptor, not a code address */ + + compar = (CODE int (*)(FAR const void *, FAR const void *)) + fdpic_callback((FAR void *)compar); +#endif + for (lim = nel, lower = (const char *)base; lim != 0; lim >>= 1) { middle = lower + (lim >> 1) * width; diff --git a/libs/libc/stdlib/lib_qsort.c b/libs/libc/stdlib/lib_qsort.c index 5646452388f1b..61742d17ef415 100644 --- a/libs/libc/stdlib/lib_qsort.c +++ b/libs/libc/stdlib/lib_qsort.c @@ -45,6 +45,10 @@ #include #include +#ifdef CONFIG_FDPIC +# include +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -156,8 +160,9 @@ static inline FAR char *med3(FAR char *a, FAR char *b, FAR char *c, * ****************************************************************************/ -void qsort(FAR void *base, size_t nel, size_t width, - CODE int(*compar)(FAR const void *, FAR const void *)) +static void qsort_internal(FAR void *base, size_t nel, size_t width, + CODE int(*compar)(FAR const void *, + FAR const void *)) { FAR char *pa; FAR char *pb; @@ -277,7 +282,7 @@ void qsort(FAR void *base, size_t nel, size_t width, if ((r = pb - pa) > width) { - qsort(base, r / width, width, compar); + qsort_internal(base, r / width, width, compar); } if ((r = pd - pc) > width) @@ -289,3 +294,27 @@ void qsort(FAR void *base, size_t nel, size_t width, goto loop; } } + +/**************************************************************************** + * Name: qsort + * + * Description: + * Public entry point. Resolves the comparison function once, then hands + * an ordinary pointer to the implementation, which recurses. + * + ****************************************************************************/ + +void qsort(FAR void *base, size_t nel, size_t width, + CODE int(*compar)(FAR const void *, FAR const void *)) +{ +#ifdef CONFIG_FDPIC + /* An FDPIC module passes the address of a function descriptor, not a + * code address. + */ + + compar = (CODE int (*)(FAR const void *, FAR const void *)) + fdpic_callback((FAR void *)compar); +#endif + + qsort_internal(base, nel, width, compar); +} diff --git a/sched/mqueue/mq_notify.c b/sched/mqueue/mq_notify.c index ea1f35fc27933..9da34cae9766e 100644 --- a/sched/mqueue/mq_notify.c +++ b/sched/mqueue/mq_notify.c @@ -34,6 +34,10 @@ #include #include +#if defined(CONFIG_FDPIC) && defined(CONFIG_SIG_EVTHREAD) +# include +#endif + #include "sched/sched.h" #include "mqueue/mqueue.h" @@ -156,6 +160,27 @@ int mq_notify(mqd_t mqdes, FAR const struct sigevent *notification) sizeof(struct sigevent)); msgq->ntpid = rtcb->pid; + +#if defined(CONFIG_FDPIC) && defined(CONFIG_SIG_EVTHREAD) + /* Record the callback here, where this still runs in the + * module's context. It fires later on a worker that carries no + * data base, so the base travels with it. The function shares a + * union with the thread ID, so only a SIGEV_THREAD event has one + * to record. + */ + + msgq->ntwork.func.got = 0; + + if ((notification->sigev_notify & SIGEV_THREAD) != 0) + { + FAR void *fn = (FAR void *)notification->sigev_notify_function; + + fdpic_desc_init(&msgq->ntwork.func, fn); + + msgq->ntevent.sigev_notify_function = + (sigev_notify_function_t)msgq->ntwork.func.entry; + } +#endif } } diff --git a/sched/signal/sig_action.c b/sched/signal/sig_action.c index ab37db83d262b..bf348d431000e 100644 --- a/sched/signal/sig_action.c +++ b/sched/signal/sig_action.c @@ -38,6 +38,10 @@ #include #include +#ifdef CONFIG_FDPIC +# include +#endif + #include "sched/sched.h" #include "group/group.h" #include "signal/signal.h" @@ -326,6 +330,19 @@ int nxsig_action(int signo, FAR const struct sigaction *act, handler = act->sa_handler; +#ifdef CONFIG_FDPIC + /* Resolve the handler here, the innermost common code, so it happens + * exactly once. SIG_ERR, SIG_IGN, SIG_DFL and SIG_HOLD are small + * integers rather than addresses, so exclude them by hand. + */ + + if (handler != SIG_ERR && handler != SIG_IGN && handler != SIG_DFL && + handler != SIG_HOLD) + { + handler = (_sa_handler_t)fdpic_callback((FAR void *)handler); + } +#endif + #ifdef CONFIG_SIG_DEFAULT /* If the caller is setting the handler to SIG_DFL, then we need to * replace this with the correct, internal default signal action handler. diff --git a/sched/signal/sig_notification.c b/sched/signal/sig_notification.c index 86909faf62bc5..a7948c33974b7 100644 --- a/sched/signal/sig_notification.c +++ b/sched/signal/sig_notification.c @@ -34,6 +34,10 @@ #include +#ifdef CONFIG_FDPIC +# include +#endif + #include "sched/sched.h" #include "signal/signal.h" @@ -70,7 +74,16 @@ static void nxsig_notification_worker(FAR void *arg) /* Perform the callback */ +#ifdef CONFIG_FDPIC + /* The worker does not carry the module's data base. fdpic_invoke() + * installs the one recorded at registration, or branches straight to a + * callback that is not a module's. + */ + + fdpic_invoke((uintptr_t)work->value.sival_ptr, &work->func); +#else work->func(work->value); +#endif } #endif /* CONFIG_SIG_EVTHREAD */ @@ -155,7 +168,11 @@ int nxsig_notification(pid_t pid, FAR struct sigevent *event, /* Initialize the work information */ work->value = event->sigev_value; +#ifdef CONFIG_FDPIC + work->func.entry = (uintptr_t)event->sigev_notify_function; +#else work->func = event->sigev_notify_function; +#endif /* Then queue the work */ diff --git a/sched/task/task_create.c b/sched/task/task_create.c index 4530a742491a0..7b90822cda9f7 100644 --- a/sched/task/task_create.c +++ b/sched/task/task_create.c @@ -37,6 +37,10 @@ #include #include +#ifdef CONFIG_FDPIC +# include +#endif + #include "sched/sched.h" #include "group/group.h" #include "task/task.h" @@ -202,8 +206,18 @@ int task_create_with_stack(FAR const char *name, int priority, FAR void *stack_addr, int stack_size, main_t entry, FAR char * const argv[]) { - int ret = nxtask_create(name, priority, stack_addr, - stack_size, entry, argv, NULL); + int ret; + +#ifdef CONFIG_FDPIC + /* Resolve here, once: this covers task_create() too, which is a plain + * forwarder. The new task inherits the creator's D-Space. + */ + + entry = (main_t)fdpic_callback((FAR void *)entry); +#endif + + ret = nxtask_create(name, priority, stack_addr, + stack_size, entry, argv, NULL); if (ret < 0) { set_errno(-ret); diff --git a/sched/task/task_spawn.c b/sched/task/task_spawn.c index a29db7f893c0a..d223ff259b569 100644 --- a/sched/task/task_spawn.c +++ b/sched/task/task_spawn.c @@ -38,6 +38,10 @@ #include #include +#ifdef CONFIG_FDPIC +# include +#endif + #include "sched/sched.h" #include "group/group.h" #include "task/spawn.h" @@ -335,6 +339,14 @@ int task_spawn(FAR const char *name, main_t entry, pid_t pid = INVALID_PROCESS_ID; int ret; +#ifdef CONFIG_FDPIC + /* Resolve the descriptor once, in the public entry point. The new task + * inherits the creator's D-Space. + */ + + entry = (main_t)fdpic_callback((FAR void *)entry); +#endif + sinfo("name=%s entry=%p file_actions=%p attr=%p argv=%p\n", name, entry, file_actions, attr, argv); diff --git a/sched/timer/timer_create.c b/sched/timer/timer_create.c index 1811f3e68af00..d074389405d49 100644 --- a/sched/timer/timer_create.c +++ b/sched/timer/timer_create.c @@ -37,6 +37,10 @@ #include #include +#if defined(CONFIG_FDPIC) && defined(CONFIG_SIG_EVTHREAD) +# include +#endif + #include "sched/sched.h" #include "timer/timer.h" @@ -196,6 +200,26 @@ int timer_create(clockid_t clockid, FAR struct sigevent *evp, /* Yes, copy the entire struct sigevent content */ memcpy(&ret->pt_event, evp, sizeof(struct sigevent)); + +#if defined(CONFIG_FDPIC) && defined(CONFIG_SIG_EVTHREAD) + /* Record the callback here, where this still runs in the + * module's context. It fires later on a worker that carries + * no data base, so the base travels with it. The function + * shares a union with the thread ID, so only a SIGEV_THREAD + * event has one to record. + */ + + ret->pt_work.func.got = 0; + + if ((evp->sigev_notify & SIGEV_THREAD) != 0) + { + fdpic_desc_init(&ret->pt_work.func, + (FAR void *)evp->sigev_notify_function); + + ret->pt_event.sigev_notify_function = + (sigev_notify_function_t)ret->pt_work.func.entry; + } +#endif } else {