You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
496 lines
14 KiB
496 lines
14 KiB
/**************************************************************************** |
|
* sched/os_start.c |
|
* |
|
* Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. |
|
* Author: Gregory Nutt <gnutt@nuttx.org> |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in |
|
* the documentation and/or other materials provided with the |
|
* distribution. |
|
* 3. Neither the name NuttX nor the names of its contributors may be |
|
* used to endorse or promote products derived from this software |
|
* without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
* POSSIBILITY OF SUCH DAMAGE. |
|
* |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Included Files |
|
****************************************************************************/ |
|
|
|
#include <sys/types.h> |
|
#include <stdbool.h> |
|
#include <debug.h> |
|
#include <string.h> |
|
|
|
#include <nuttx/arch.h> |
|
#include <nuttx/compiler.h> |
|
#include <nuttx/fs/fs.h> |
|
#include <nuttx/net/net.h> |
|
#include <nuttx/lib.h> |
|
#include <nuttx/kmalloc.h> |
|
#include <nuttx/init.h> |
|
|
|
#include "os_internal.h" |
|
#include "sig_internal.h" |
|
#include "wd_internal.h" |
|
#include "sem_internal.h" |
|
#ifndef CONFIG_DISABLE_MQUEUE |
|
# include "mq_internal.h" |
|
#endif |
|
#ifndef CONFIG_DISABLE_PTHREAD |
|
# include "pthread_internal.h" |
|
#endif |
|
#include "clock_internal.h" |
|
#include "timer_internal.h" |
|
#include "irq_internal.h" |
|
#ifdef HAVE_TASK_GROUP |
|
#include "group_internal.h" |
|
#endif |
|
|
|
/**************************************************************************** |
|
* Pre-processor Definitions |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Private Type Declarations |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Global Variables |
|
****************************************************************************/ |
|
|
|
/* Task Lists ***************************************************************/ |
|
/* The state of a task is indicated both by the task_state field of the TCB |
|
* and by a series of task lists. All of these tasks lists are declared |
|
* below. Although it is not always necessary, most of these lists are |
|
* prioritized so that common list handling logic can be used (only the |
|
* g_readytorun, the g_pendingtasks, and the g_waitingforsemaphore lists need |
|
* to be prioritized). |
|
*/ |
|
|
|
/* This is the list of all tasks that are ready to run. The head of this |
|
* list is the currently active task; the tail of this list is always the |
|
* IDLE task. |
|
*/ |
|
|
|
volatile dq_queue_t g_readytorun; |
|
|
|
/* This is the list of all tasks that are ready-to-run, but cannot be placed |
|
* in the g_readytorun list because: (1) They are higher priority than the |
|
* currently active task at the head of the g_readytorun list, and (2) the |
|
* currently active task has disabled pre-emption. |
|
*/ |
|
|
|
volatile dq_queue_t g_pendingtasks; |
|
|
|
/* This is the list of all tasks that are blocked waiting for a semaphore */ |
|
|
|
volatile dq_queue_t g_waitingforsemaphore; |
|
|
|
/* This is the list of all tasks that are blocked waiting for a signal */ |
|
|
|
#ifndef CONFIG_DISABLE_SIGNALS |
|
volatile dq_queue_t g_waitingforsignal; |
|
#endif |
|
|
|
/* This is the list of all tasks that are blocked waiting for a message |
|
* queue to become non-empty. |
|
*/ |
|
|
|
#ifndef CONFIG_DISABLE_MQUEUE |
|
volatile dq_queue_t g_waitingformqnotempty; |
|
#endif |
|
|
|
/* This is the list of all tasks that are blocked waiting for a message |
|
* queue to become non-full. |
|
*/ |
|
|
|
#ifndef CONFIG_DISABLE_MQUEUE |
|
volatile dq_queue_t g_waitingformqnotfull; |
|
#endif |
|
|
|
/* This is the list of all tasks that are blocking waiting for a page fill */ |
|
|
|
#ifdef CONFIG_PAGING |
|
volatile dq_queue_t g_waitingforfill; |
|
#endif |
|
|
|
/* This the list of all tasks that have been initialized, but not yet |
|
* activated. NOTE: This is the only list that is not prioritized. |
|
*/ |
|
|
|
volatile dq_queue_t g_inactivetasks; |
|
|
|
/* This is the list of dayed memory deallocations that need to be handled |
|
* within the IDLE loop. These deallocations get queued by sched_free() |
|
* if the OS attempts to deallocate memory while it is within an interrupt |
|
* handler. |
|
*/ |
|
|
|
volatile sq_queue_t g_delayeddeallocations; |
|
|
|
/* This is the value of the last process ID assigned to a task */ |
|
|
|
volatile pid_t g_lastpid; |
|
|
|
/* The following hash table is used for two things: |
|
* |
|
* 1. This hash table greatly speeds the determination of |
|
* a new unique process ID for a task, and |
|
* 2. Is used to quickly map a process ID into a TCB. |
|
* It has the side effects of using more memory and limiting |
|
* |
|
* the number of tasks to CONFIG_MAX_TASKS. |
|
*/ |
|
|
|
pidhash_t g_pidhash[CONFIG_MAX_TASKS]; |
|
|
|
/* This is a table of task lists. This table is indexed by |
|
* the task state enumeration type (tstate_t) and provides |
|
* a pointer to the associated static task list (if there |
|
* is one) as well as a boolean indication as to if the list |
|
* is an ordered list or not. |
|
*/ |
|
|
|
const tasklist_t g_tasklisttable[NUM_TASK_STATES] = |
|
{ |
|
{ NULL, false }, /* TSTATE_TASK_INVALID */ |
|
{ &g_pendingtasks, true }, /* TSTATE_TASK_PENDING */ |
|
{ &g_readytorun, true }, /* TSTATE_TASK_READYTORUN */ |
|
{ &g_readytorun, true }, /* TSTATE_TASK_RUNNING */ |
|
{ &g_inactivetasks, false }, /* TSTATE_TASK_INACTIVE */ |
|
{ &g_waitingforsemaphore, true } /* TSTATE_WAIT_SEM */ |
|
#ifndef CONFIG_DISABLE_SIGNALS |
|
, |
|
{ &g_waitingforsignal, false } /* TSTATE_WAIT_SIG */ |
|
#endif |
|
#ifndef CONFIG_DISABLE_MQUEUE |
|
, |
|
{ &g_waitingformqnotempty, true }, /* TSTATE_WAIT_MQNOTEMPTY */ |
|
{ &g_waitingformqnotfull, true } /* TSTATE_WAIT_MQNOTFULL */ |
|
#endif |
|
#ifdef CONFIG_PAGING |
|
, |
|
{ &g_waitingforfill, true } /* TSTATE_WAIT_PAGEFILL */ |
|
#endif |
|
}; |
|
|
|
/**************************************************************************** |
|
* Private Variables |
|
****************************************************************************/ |
|
/* This is the task control block for this thread of execution. This thread |
|
* of execution is the IDLE task. NOTE: the system boots into the IDLE |
|
* task. The IDLE task spawns the user initialization task (user_start) and |
|
* that user init task is responsible for bringing up the rest of the system |
|
*/ |
|
|
|
static FAR _TCB g_idletcb; |
|
|
|
/* This is the name of the idle task */ |
|
|
|
static FAR const char g_idlename[] = "Idle Task"; |
|
|
|
/**************************************************************************** |
|
* Private Function Prototypes |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Public Functions |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Name: os_start |
|
* |
|
* Description: |
|
* This function is called to initialize the operating system and to spawn |
|
* the user initization thread of execution |
|
* |
|
****************************************************************************/ |
|
|
|
void os_start(void) |
|
{ |
|
int i; |
|
|
|
slldbg("Entry\n"); |
|
|
|
/* Initialize all task lists */ |
|
|
|
dq_init(&g_readytorun); |
|
dq_init(&g_pendingtasks); |
|
dq_init(&g_waitingforsemaphore); |
|
#ifndef CONFIG_DISABLE_SIGNALS |
|
dq_init(&g_waitingforsignal); |
|
#endif |
|
#ifndef CONFIG_DISABLE_MQUEUE |
|
dq_init(&g_waitingformqnotfull); |
|
dq_init(&g_waitingformqnotempty); |
|
#endif |
|
#ifdef CONFIG_PAGING |
|
dq_init(&g_waitingforfill); |
|
#endif |
|
dq_init(&g_inactivetasks); |
|
sq_init(&g_delayeddeallocations); |
|
|
|
/* Initialize the logic that determine unique process IDs. */ |
|
|
|
g_lastpid = 0; |
|
for (i = 0; i < CONFIG_MAX_TASKS; i++) |
|
{ |
|
g_pidhash[i].tcb = NULL; |
|
g_pidhash[i].pid = INVALID_PROCESS_ID; |
|
} |
|
|
|
/* Assign the process ID of ZERO to the idle task */ |
|
|
|
g_pidhash[ PIDHASH(0)].tcb = &g_idletcb; |
|
g_pidhash[ PIDHASH(0)].pid = 0; |
|
|
|
/* Initialize a TCB for this thread of execution. NOTE: The default |
|
* value for most components of the g_idletcb are zero. The entire |
|
* structure is set to zero. Then only the (potentially) non-zero |
|
* elements are initialized. NOTE: The idle task is the only task in |
|
* that has pid == 0 and sched_priority == 0. |
|
*/ |
|
|
|
bzero((void*)&g_idletcb, sizeof(_TCB)); |
|
g_idletcb.task_state = TSTATE_TASK_RUNNING; |
|
g_idletcb.entry.main = (main_t)os_start; |
|
|
|
#if CONFIG_TASK_NAME_SIZE > 0 |
|
strncpy(g_idletcb.name, g_idlename, CONFIG_TASK_NAME_SIZE-1); |
|
g_idletcb.argv[0] = g_idletcb.name; |
|
#else |
|
g_idletcb.argv[0] = (char*)g_idlename; |
|
#endif /* CONFIG_TASK_NAME_SIZE */ |
|
|
|
/* Then add the idle task's TCB to the head of the ready to run list */ |
|
|
|
dq_addfirst((FAR dq_entry_t*)&g_idletcb, (FAR dq_queue_t*)&g_readytorun); |
|
|
|
/* Initialize the processor-specific portion of the TCB */ |
|
|
|
up_initial_state(&g_idletcb); |
|
|
|
/* Initialize the semaphore facility(if in link). This has to be done |
|
* very early because many subsystems depend upon fully functional |
|
* semaphores. |
|
*/ |
|
|
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (sem_initialize != NULL) |
|
#endif |
|
{ |
|
sem_initialize(); |
|
} |
|
|
|
/* Initialize the memory manager */ |
|
|
|
#ifndef CONFIG_HEAP_BASE |
|
{ |
|
FAR void *heap_start; |
|
size_t heap_size; |
|
up_allocate_heap(&heap_start, &heap_size); |
|
kmm_initialize(heap_start, heap_size); |
|
} |
|
#else |
|
kmm_initialize((void*)CONFIG_HEAP_BASE, CONFIG_HEAP_SIZE); |
|
#endif |
|
|
|
/* Initialize tasking data structures */ |
|
|
|
#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) |
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (task_initialize != NULL) |
|
#endif |
|
{ |
|
task_initialize(); |
|
} |
|
#endif |
|
|
|
/* Allocate the IDLE group and suppress child status. */ |
|
|
|
#ifdef HAVE_TASK_GROUP |
|
(void)group_allocate(&g_idletcb); |
|
#endif |
|
|
|
/* Initialize the interrupt handling subsystem (if included) */ |
|
|
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (irq_initialize != NULL) |
|
#endif |
|
{ |
|
irq_initialize(); |
|
} |
|
|
|
/* Initialize the watchdog facility (if included in the link) */ |
|
|
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (wd_initialize != NULL) |
|
#endif |
|
{ |
|
wd_initialize(); |
|
} |
|
|
|
/* Initialize the POSIX timer facility (if included in the link) */ |
|
|
|
#ifndef CONFIG_DISABLE_CLOCK |
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (clock_initialize != NULL) |
|
#endif |
|
{ |
|
clock_initialize(); |
|
} |
|
#endif |
|
|
|
#ifndef CONFIG_DISABLE_POSIX_TIMERS |
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (timer_initialize != NULL) |
|
#endif |
|
{ |
|
timer_initialize(); |
|
} |
|
#endif |
|
|
|
/* Initialize the signal facility (if in link) */ |
|
|
|
#ifndef CONFIG_DISABLE_SIGNALS |
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (sig_initialize != NULL) |
|
#endif |
|
{ |
|
sig_initialize(); |
|
} |
|
#endif |
|
|
|
/* Initialize the named message queue facility (if in link) */ |
|
|
|
#ifndef CONFIG_DISABLE_MQUEUE |
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (mq_initialize != NULL) |
|
#endif |
|
{ |
|
mq_initialize(); |
|
} |
|
#endif |
|
|
|
/* Initialize the thread-specific data facility (if in link) */ |
|
|
|
#ifndef CONFIG_DISABLE_PTHREAD |
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (pthread_initialize != NULL) |
|
#endif |
|
{ |
|
pthread_initialize(); |
|
} |
|
#endif |
|
|
|
/* Initialize the file system (needed to support device drivers) */ |
|
|
|
#if CONFIG_NFILE_DESCRIPTORS > 0 |
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (fs_initialize != NULL) |
|
#endif |
|
{ |
|
fs_initialize(); |
|
} |
|
#endif |
|
|
|
/* Initialize the network system */ |
|
|
|
#ifdef CONFIG_NET |
|
#if 0 |
|
if (net_initialize != NULL) |
|
#endif |
|
{ |
|
net_initialize(); |
|
} |
|
#endif |
|
|
|
/* The processor specific details of running the operating system |
|
* will be handled here. Such things as setting up interrupt |
|
* service routines and starting the clock are some of the things |
|
* that are different for each processor and hardware platform. |
|
*/ |
|
|
|
up_initialize(); |
|
|
|
/* Initialize the C libraries (if included in the link). This |
|
* is done last because the libraries may depend on the above. |
|
*/ |
|
|
|
#ifdef CONFIG_HAVE_WEAKFUNCTIONS |
|
if (lib_initialize != NULL) |
|
#endif |
|
{ |
|
lib_initialize(); |
|
} |
|
|
|
/* Create stdout, stderr, stdin on the IDLE task. These will be |
|
* inherited by all of the threads created by the IDLE task. |
|
*/ |
|
|
|
(void)group_setupidlefiles(&g_idletcb); |
|
|
|
/* Complete initialization of the IDLE group. Suppress retention |
|
* of child status in the IDLE group. |
|
*/ |
|
|
|
#ifdef HAVE_TASK_GROUP |
|
(void)group_initialize(&g_idletcb); |
|
g_idletcb.group->tg_flags = GROUP_FLAG_NOCLDWAIT; |
|
#endif |
|
|
|
/* Create initial tasks and bring-up the system */ |
|
|
|
(void)os_bringup(); |
|
|
|
/* When control is return to this point, the system is idle. */ |
|
|
|
sdbg("Beginning Idle Loop\n"); |
|
for (;;) |
|
{ |
|
/* Perform garbage collection (if it is not being done by the worker |
|
* thread). This cleans-up memory de-allocations that were queued |
|
* because they could not be freed in that execution context (for |
|
* example, if the memory was freed from an interrupt handler). |
|
*/ |
|
|
|
#ifndef CONFIG_SCHED_WORKQUEUE |
|
/* We must have exclusive access to the memory manager to do this |
|
* BUT the idle task cannot wait on a semaphore. So we only do |
|
* the cleanup now if we can get the semaphore -- this should be |
|
* possible because if the IDLE thread is running, no other task is! |
|
*/ |
|
|
|
if (kmm_trysemaphore() == 0) |
|
{ |
|
sched_garbagecollection(); |
|
kmm_givesemaphore(); |
|
} |
|
#endif |
|
|
|
/* Perform any processor-specific idle state operations */ |
|
|
|
up_idle(); |
|
} |
|
}
|
|
|