mirror of
https://github.com/apache/nuttx.git
synced 2026-09-30 11:03:53 +00:00
The base firmware and an FDPIC module disagree about what a function pointer is. Firmware is not built FDPIC, so to it a pointer is a code address and it branches there. A module passes the address of a two word descriptor instead, because its code and data are placed independently and a bare code address would leave the callee unable to find its own data. A firmware routine that takes a callback therefore branches into the module's data segment and faults. So the ten entry points that can be handed a callback by a module resolve the descriptor before storing or branching to it: qsort, bsearch, pthread_create, signal, sigaction, task_create and task_create_with_stack, task_spawn, pthread_once, scandir, and mq_notify and timer_create with SIGEV_THREAD. Which one resolves matters as much as that one does. Resolving twice would take an already resolved code address for a descriptor and read two words from the instruction stream, so each pointer is resolved exactly once, at the outermost point that sees it. signal() passes its argument through untouched because sigaction() and then nxsig_action() will resolve it, which covers a module calling sigaction() directly as well. qsort() is split so that the public entry resolves and the recursive implementation does not. scandir() resolves its filter but not its comparison function, which it hands to qsort(). Whether a caller is a module at all is asked of the PIC base register, which up_initial_state() sets only for a task that has a D-Space. A plain kernel task therefore reads zero and is left alone. SIGEV_THREAD is the case the register cannot answer, because the callback runs later on a work queue worker that carries no module's base at all. The base is captured instead when the notification is registered, in the module's own context, and installed around the call. All of it is behind CONFIG_FDPIC, which defaults off. Built for mps3-an547:picostest both ways; with it off the entry points compile to what they were. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
252 lines
8.4 KiB
C
252 lines
8.4 KiB
C
/****************************************************************************
|
|
* sched/timer/timer_create.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/wdog.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/spinlock.h>
|
|
|
|
#if defined(CONFIG_FDPIC) && defined(CONFIG_SIG_EVTHREAD)
|
|
# include <nuttx/fdpic.h>
|
|
#endif
|
|
|
|
#include "sched/sched.h"
|
|
#include "timer/timer.h"
|
|
|
|
#ifndef CONFIG_DISABLE_POSIX_TIMERS
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: timer_allocate
|
|
*
|
|
* Description:
|
|
* Allocate one POSIX timer and place it into the allocated timer list.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static FAR struct posix_timer_s *timer_allocate(void)
|
|
{
|
|
FAR struct posix_timer_s *ret;
|
|
irqstate_t flags;
|
|
uint8_t pt_flags;
|
|
|
|
/* Try to get a preallocated timer from the free list */
|
|
|
|
#if CONFIG_PREALLOC_TIMERS > 0
|
|
flags = spin_lock_irqsave(&g_locktimers);
|
|
ret = (FAR struct posix_timer_s *)
|
|
sq_remfirst((FAR sq_queue_t *)&g_freetimers);
|
|
spin_unlock_irqrestore(&g_locktimers, flags);
|
|
|
|
/* Did we get one? */
|
|
|
|
if (ret)
|
|
{
|
|
pt_flags = PT_FLAGS_PREALLOCATED;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Allocate a new timer from the heap */
|
|
|
|
ret = (FAR struct posix_timer_s *)
|
|
kmm_malloc(sizeof(struct posix_timer_s));
|
|
pt_flags = 0u;
|
|
}
|
|
|
|
/* If we have a timer, then put it into the allocated timer list */
|
|
|
|
if (ret)
|
|
{
|
|
/* Initialize the timer structure */
|
|
|
|
memset(ret, 0, sizeof(struct posix_timer_s));
|
|
ret->pt_flags = pt_flags;
|
|
|
|
/* And add it to the end of the list of allocated timers */
|
|
|
|
flags = spin_lock_irqsave(&g_locktimers);
|
|
sq_addlast((FAR sq_entry_t *)ret, (FAR sq_queue_t *)&g_alloctimers);
|
|
spin_unlock_irqrestore(&g_locktimers, flags);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: timer_create
|
|
*
|
|
* Description:
|
|
* The timer_create() function creates per-thread timer using the
|
|
* specified clock, clock_id, as the timing base. The timer_create()
|
|
* function returns, in the location referenced by timerid, a timer ID of
|
|
* type timer_t used to identify the timer in timer requests. This timer
|
|
* ID is unique until the timer is deleted. The particular clock, clock_id,
|
|
* is defined in <time.h>. The timer whose ID is returned will be in a
|
|
* disarmed state upon return from timer_create().
|
|
*
|
|
* The evp argument, if non-NULL, points to a sigevent structure. This
|
|
* structure is allocated by the called and defines the asynchronous
|
|
* notification to occur. If the evp argument is NULL, the effect is as
|
|
* if the evp argument pointed to a sigevent structure with the
|
|
* sigev_notify member having the value SIGEV_SIGNAL, the sigev_signo
|
|
* having a default signal number, and the sigev_value member having the
|
|
* value of the timer ID.
|
|
*
|
|
* Each implementation defines a set of clocks that can be used as timing
|
|
* bases for per-thread timers.
|
|
*
|
|
* Input Parameters:
|
|
* clockid - Specifies the clock to use as the timing base.
|
|
* evp - Refers to a user allocated sigevent structure that defines the
|
|
* asynchronous notification. evp may be NULL (see above).
|
|
* timerid - The pre-thread timer created by the call to timer_create().
|
|
*
|
|
* Returned Value:
|
|
* If the call succeeds, timer_create() will return 0 (OK) and update the
|
|
* location referenced by timerid to a timer_t, which can be passed to the
|
|
* other per-thread timer calls. If an error occurs, the function will
|
|
* return a value of -1 (ERROR) and set errno to indicate the error.
|
|
*
|
|
* EAGAIN - The system lacks sufficient signal queuing resources to honor
|
|
* the request.
|
|
* EAGAIN - The calling process has already created all of the timers it
|
|
* is allowed by this implementation.
|
|
* EINVAL - The specified clock ID is not defined.
|
|
* ENOTSUP - The implementation does not support the creation of a timer
|
|
* attached to the CPU-time clock that is specified by clock_id and
|
|
* associated with a thread different thread invoking timer_create().
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
int timer_create(clockid_t clockid, FAR struct sigevent *evp,
|
|
FAR timer_t *timerid)
|
|
{
|
|
FAR struct posix_timer_s *ret = NULL;
|
|
FAR struct tcb_s *tcb = this_task();
|
|
|
|
/* Sanity checks. */
|
|
|
|
if (timerid == NULL || (clockid != CLOCK_REALTIME &&
|
|
clockid != CLOCK_MONOTONIC && clockid != CLOCK_BOOTTIME) ||
|
|
(evp != NULL && evp->sigev_notify == SIGEV_SIGNAL &&
|
|
!GOOD_SIGNO(evp->sigev_signo)))
|
|
{
|
|
set_errno(EINVAL);
|
|
}
|
|
else
|
|
{
|
|
/* Allocate a timer instance to contain the watchdog */
|
|
|
|
ret = timer_allocate();
|
|
if (!ret)
|
|
{
|
|
set_errno(EAGAIN);
|
|
}
|
|
else
|
|
{
|
|
/* Initialize the timer instance */
|
|
|
|
ret->pt_clock = clockid;
|
|
ret->pt_crefs = 1u;
|
|
ret->pt_owner = tcb->pid;
|
|
ret->pt_delay = 0u;
|
|
ret->pt_expected = 0u;
|
|
|
|
/* Was a struct sigevent provided? */
|
|
|
|
if (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.
|
|
*/
|
|
|
|
if ((evp->sigev_notify & SIGEV_THREAD) != 0)
|
|
{
|
|
FAR void *fn = (FAR void *)evp->sigev_notify_function;
|
|
|
|
fdpic_init(&ret->pt_work.func, fn);
|
|
|
|
ret->pt_event.sigev_notify_function =
|
|
(sigev_notify_function_t)fdpic_callback(fn);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* "If the evp argument is NULL, the effect is as if the evp
|
|
* argument pointed to a sigevent structure with the
|
|
* sigev_notify member having the value SIGEV_SIGNAL,
|
|
* the sigev_signo having a default signal number,
|
|
* and the sigev_value member having the value of the
|
|
* timer ID."
|
|
*/
|
|
|
|
ret->pt_event.sigev_notify = SIGEV_SIGNAL;
|
|
ret->pt_event.sigev_signo = SIGALRM;
|
|
ret->pt_event.sigev_value.sival_ptr = ret;
|
|
|
|
#ifdef CONFIG_SIG_EVTHREAD
|
|
ret->pt_event.sigev_notify_function = NULL;
|
|
ret->pt_event.sigev_notify_attributes = NULL;
|
|
#endif
|
|
}
|
|
|
|
/* Return the timer */
|
|
|
|
*timerid = ret;
|
|
}
|
|
}
|
|
|
|
return ret ? OK : ERROR;
|
|
}
|
|
|
|
#endif /* CONFIG_DISABLE_POSIX_TIMERS */
|