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>
360 lines
12 KiB
C
360 lines
12 KiB
C
/****************************************************************************
|
|
* sched/task/task_spawn.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 <sys/wait.h>
|
|
#include <sched.h>
|
|
#include <spawn.h>
|
|
#include <assert.h>
|
|
#include <nuttx/debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/sched.h>
|
|
#include <nuttx/kthread.h>
|
|
#include <nuttx/spawn.h>
|
|
|
|
#ifdef CONFIG_FDPIC
|
|
# include <nuttx/fdpic.h>
|
|
#endif
|
|
|
|
#include "sched/sched.h"
|
|
#include "group/group.h"
|
|
#include "task/spawn.h"
|
|
#include "task/task.h"
|
|
|
|
#ifndef CONFIG_BUILD_KERNEL
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: nxtask_spawn_create
|
|
*
|
|
* Description:
|
|
* This function creates and activates a new thread of the specified type
|
|
* with a specified priority and returns its system-assigned ID. It is the
|
|
* internal, common implementation of task_create() and kthread_create().
|
|
* See comments with task_create() for further information.
|
|
*
|
|
* Input Parameters:
|
|
* name - Name of the new task
|
|
* ttype - Type of the new task
|
|
* priority - Priority of the new task
|
|
* stack_addr - Address of the stack needed
|
|
* stack_size - Size (in bytes) of the stack needed
|
|
* entry - Entry point of a new task
|
|
* arg - A pointer to an array of input parameters. The array
|
|
* should be terminated with a NULL argv[] value. If no
|
|
* parameters are required, argv may be NULL.
|
|
* envp - A pointer to an array of environment strings. Terminated
|
|
* with a NULL entry.
|
|
* actions - The spawn file actions
|
|
*
|
|
* Returned Value:
|
|
* Returns the positive, non-zero process ID of the new task or a negated
|
|
* errno value to indicate the nature of any failure. If memory is
|
|
* insufficient or the task cannot be created -ENOMEM will be returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxtask_spawn_create(FAR const char *name, int priority,
|
|
FAR void *stack_addr, int stack_size,
|
|
main_t entry, FAR char * const argv[],
|
|
FAR char * const envp[],
|
|
FAR const posix_spawn_file_actions_t *actions,
|
|
FAR const posix_spawnattr_t *attr)
|
|
{
|
|
FAR struct tcb_s *tcb;
|
|
pid_t pid;
|
|
int ret;
|
|
|
|
/* Allocate a TCB for the new task. */
|
|
|
|
tcb = kmm_zalloc(sizeof(struct tcb_s));
|
|
if (tcb == NULL)
|
|
{
|
|
serr("ERROR: Failed to allocate TCB\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Setup the task type */
|
|
|
|
tcb->flags = TCB_FLAG_TTYPE_TASK | TCB_FLAG_FREE_TCB;
|
|
|
|
/* Initialize the task */
|
|
|
|
ret = nxtask_init(tcb, name, priority, stack_addr, stack_size,
|
|
entry, argv, envp, actions);
|
|
if (ret < OK)
|
|
{
|
|
kmm_free(tcb);
|
|
return ret;
|
|
}
|
|
|
|
/* Get the assigned pid before we start the task */
|
|
|
|
pid = tcb->pid;
|
|
|
|
/* Set the attributes */
|
|
|
|
if (attr)
|
|
{
|
|
ret = spawn_execattrs(pid, attr);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_taskinit;
|
|
}
|
|
}
|
|
|
|
/* Activate the task */
|
|
|
|
nxtask_activate(tcb);
|
|
|
|
return pid;
|
|
|
|
errout_with_taskinit:
|
|
nxtask_uninit(tcb);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: nxtask_spawn_exec
|
|
*
|
|
* Description:
|
|
* Execute the task from the file system.
|
|
*
|
|
* Input Parameters:
|
|
*
|
|
* pidp - Upon successful completion, this will return the task ID of the
|
|
* child task in the variable pointed to by a non-NULL 'pid' argument.|
|
|
*
|
|
* name - The name to assign to the child task.
|
|
*
|
|
* entry - The child task's entry point (an address in memory)
|
|
*
|
|
* actions - The spawn file actions
|
|
*
|
|
* attr - If the value of the 'attr' parameter is NULL, the all default
|
|
* values for the POSIX spawn attributes will be used. Otherwise, the
|
|
* attributes will be set according to the spawn flags. The
|
|
* following spawn flags are supported:
|
|
*
|
|
* - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param
|
|
* value.
|
|
* - POSIX_SPAWN_SETSCHEDULER: Set the new tasks scheduler priority to
|
|
* the sched_policy value.
|
|
*
|
|
* NOTE: POSIX_SPAWN_SETSIGMASK is handled in nxtask_spawn_proxy().
|
|
*
|
|
* argv - argv[] is the argument list for the new task. argv[] is an
|
|
* array of pointers to null-terminated strings. The list is terminated
|
|
* with a null pointer.
|
|
*
|
|
* envp - A pointer to an array of environment strings. Terminated with
|
|
* a NULL entry.
|
|
*
|
|
* Returned Value:
|
|
* This function will return zero on success. Otherwise, an error number
|
|
* will be returned as the function return value to indicate the error.
|
|
* This errno value may be that set by execv(), sched_setpolicy(), or
|
|
* sched_setparam().
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int nxtask_spawn_exec(FAR pid_t *pidp, FAR const char *name,
|
|
main_t entry,
|
|
FAR const posix_spawn_file_actions_t *actions,
|
|
FAR const posix_spawnattr_t *attr,
|
|
FAR char * const *argv, FAR char * const envp[])
|
|
{
|
|
FAR void *stackaddr = NULL;
|
|
size_t stacksize;
|
|
int priority;
|
|
int pid;
|
|
int ret = OK;
|
|
|
|
/* Use the default priority and stack size if no attributes are provided */
|
|
|
|
if (attr)
|
|
{
|
|
priority = attr->priority;
|
|
stacksize = attr->stacksize;
|
|
stackaddr = attr->stackaddr;
|
|
}
|
|
else
|
|
{
|
|
struct sched_param param;
|
|
|
|
/* Set the default priority to the same priority as this task */
|
|
|
|
ret = nxsched_get_param(0, ¶m);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
priority = param.sched_priority;
|
|
stacksize = CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE;
|
|
}
|
|
|
|
/* A zero priority/stacksize means "use the default": inherit the parent
|
|
* task's priority and fall back to CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE.
|
|
* This lets posix_spawnattr_init() leave these fields zero so that the
|
|
* binary loader can supply them from the loaded ELF (binp->priority /
|
|
* binp->stacksize) instead.
|
|
*/
|
|
|
|
if (priority == 0)
|
|
{
|
|
struct sched_param param;
|
|
|
|
nxsched_get_param(0, ¶m);
|
|
priority = param.sched_priority;
|
|
}
|
|
|
|
if (stacksize == 0)
|
|
{
|
|
stacksize = CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE;
|
|
}
|
|
|
|
/* Start the task */
|
|
|
|
pid = nxtask_spawn_create(name, priority, stackaddr,
|
|
stacksize, entry, argv,
|
|
envp ? envp : environ, actions, attr);
|
|
if (pid < 0)
|
|
{
|
|
ret = pid;
|
|
serr("ERROR: nxtask_spawn_create failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Return the task ID to the caller */
|
|
|
|
if (pid)
|
|
{
|
|
*pidp = pid;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: task_spawn
|
|
*
|
|
* Description:
|
|
* The task_spawn() function will create a new, child task, where the
|
|
* entry point to the task is an address in memory.
|
|
*
|
|
* Input Parameters:
|
|
*
|
|
* name - The name to assign to the child task.
|
|
*
|
|
* entry - The child task's entry point (an address in memory)
|
|
*
|
|
* file_actions - If 'file_actions' is a null pointer, then file
|
|
* descriptors open in the calling process will remain open in the
|
|
* child process (unless CONFIG_FDCLONE_STDIO is defined). If
|
|
* 'file_actions' is not NULL, then the file descriptors open in the
|
|
* child process will be those open in the calling process as modified
|
|
* by the spawn file actions object pointed to by file_actions.
|
|
*
|
|
* attr - If the value of the 'attr' parameter is NULL, the all default
|
|
* values for the POSIX spawn attributes will be used. Otherwise, the
|
|
* attributes will be set according to the spawn flags. The
|
|
* posix_spawnattr_t spawn attributes object type is defined in spawn.h.
|
|
* It will contains these attributes, not all of which are supported by
|
|
* NuttX:
|
|
*
|
|
* - POSIX_SPAWN_SETPGROUP: Setting of the new task's process group is
|
|
* not supported. NuttX does not support process groups.
|
|
* - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param
|
|
* value.
|
|
* - POSIX_SPAWN_SETSCHEDULER: Set the new task's scheduler policy to
|
|
* the sched_policy value.
|
|
* - POSIX_SPAWN_RESETIDS: Resetting of the effective user ID of the
|
|
* child process is not supported. NuttX does not support effective
|
|
* user IDs.
|
|
* - POSIX_SPAWN_SETSIGMASK: Set the new task's signal mask.
|
|
* - POSIX_SPAWN_SETSIGDEF: Resetting signal default actions is not
|
|
* supported. NuttX does not support default signal actions.
|
|
*
|
|
* And the non-standard:
|
|
*
|
|
* - TASK_SPAWN_SETSTACKSIZE: Set the stack size for the new task.
|
|
*
|
|
* argv - argv[] is the argument list for the new task. argv[] is an
|
|
* array of pointers to null-terminated strings. The list is terminated
|
|
* with a null pointer.
|
|
*
|
|
* envp - envp[] is an array of character pointers to null-terminated
|
|
* strings that provide the environment for the new process image.
|
|
*
|
|
* Returned Value:
|
|
* task_spawn() will return process ID of new task on success.
|
|
* Otherwise, a negative number will be returned as the function return
|
|
* value to indicate the error:
|
|
*
|
|
* - EINVAL: The value specified by 'file_actions' or 'attr' is invalid.
|
|
* - Any errors that might have been return if vfork() and excec[l|v]()
|
|
* had been called.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int task_spawn(FAR const char *name, main_t entry,
|
|
FAR const posix_spawn_file_actions_t *file_actions,
|
|
FAR const posix_spawnattr_t *attr,
|
|
FAR char * const argv[], FAR char * const envp[])
|
|
{
|
|
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);
|
|
|
|
ret = nxtask_spawn_exec(&pid, name, entry,
|
|
file_actions != NULL ? *file_actions : NULL,
|
|
attr, argv, envp);
|
|
|
|
return ret >= 0 ? pid : ret;
|
|
}
|
|
|
|
#endif /* CONFIG_BUILD_KERNEL */
|