summaryrefslogtreecommitdiffstats
path: root/pk/kernel/pk_kernel.h
blob: 7e88b8286ecba5733b7c6e593c97d9ba2b791751 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#ifndef __PK_KERNEL_H__
#define __PK_KERNEL_H__
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file pk_kernel.h
/// \brief PK portable kernel (non-API) data and data structures
///
/// \todo In theory, as long as the critical section entry/exit macros use GCC
/// memory barriers, we should be able to eliminate all of the 'volatile'
/// declarations in PK code.  These have been added to the port, so
/// we should try it.

#ifdef __PK_CORE_C__
#define IF__PK_CORE_C__(x) x
#define UNLESS__PK_CORE_C__(x) 
#else
#define IF__PK_CORE_C__(x)
#define UNLESS__PK_CORE_C__(x) x
#endif

#if PK_MINIMIZE_KERNEL_CODE_SPACE
#define IF_PK_MINIMIZE_KERNEL_CODE_SPACE(x) x
#define UNLESS_PK_MINIMIZE_KERNEL_CODE_SPACE(x) 
#else
#define IF_PK_MINIMIZE_KERNEL_CODE_SPACE(x)
#define UNLESS_PK_MINIMIZE_KERNEL_CODE_SPACE(x) x
#endif


#ifndef __ASSEMBLER__

/// This is the stack pointer saved when switching from a thread or
/// non-critical interrupt context to a full-mode critical interrupt context.

UNLESS__PK_CORE_C__(extern) 
volatile 
PkAddress __pk_saved_sp_critical;

/// The critical interrupt stack; constant once defined by the call of
/// pk_initialize(). 

UNLESS__PK_CORE_C__(extern)
volatile 
PkAddress __pk_critical_stack;

/// This is the stack pointer saved when switching from a thread context to a
/// full-mode non-critical interrupt context.

UNLESS__PK_CORE_C__(extern)
volatile
PkAddress __pk_saved_sp_noncritical;

/// The non-critical interrupt stack; constant once defined by the call of
/// pk_initialize(). 

UNLESS__PK_CORE_C__(extern)
volatile
PkAddress __pk_noncritical_stack;

/// This is the run queue - the queue of mapped runnable tasks.
UNLESS__PK_CORE_C__(extern)
volatile
PkThreadQueue __pk_run_queue;

/// This flag is set by \c __pk_schedule() if a new highest-priority thread
/// becomes runnable during an interrupt handler.  The context switch will
/// take place at the end of non-critical interrupt processing, and the
/// interrupt handling code will clear the flag. 

UNLESS__PK_CORE_C__(extern)
volatile
int __pk_delayed_switch;

/// The currently running thread, or NULL (0) to indicate the idle thread
///
/// \a __pk_current_thread holds a pointer to the currently executing
/// thread.  This pointer will be NULL (0) under the following conditions:
///
/// - After pk_initialize() but prior to pk_start_threads()
///
/// - After pk_start_threads(), when no threads are runnable.  In this case
/// the NULL (0) value indicates that the PK idle thread is 'running'. 
///
/// - After pk_start_threads(), when the current (non-idle) thread has
/// completed or been deleted. 
/// 
/// If \a __pk_current_thread == 0 then there is no requirement to save any
/// register state on a context switch, either because the PK idle thread has
/// no permanent context, or because any thread context on the kernel stack is
/// associated with a deleted thread.
///
/// If \a __pk_current_thread != 0 then \a __pk_current_thread is a pointer
/// to the currently executing thread.  In an interrupt handler \a
/// pk_current_thread is a pointer to the thread whose context is saved on
/// the kernel stack.
UNLESS__PK_CORE_C__(extern)
volatile
PkThread* __pk_current_thread;

/// The thread to switch to during the next context switch, or NULL (0).
///
/// \a __pk_next_thread is computed by __pk_schedule().  \a
/// __pk_next_thread holds a pointer to the thread to switch to at the next
/// context switch.  In a thread context the switch happens immediately if \a
/// __pk_next_thread == 0 or \a __pk_next_thread != \a __pk_current_thread.
/// In an interrupt context the check happens at the end of processing all
/// interrupts.
///
/// \a __pk_next_thread may be NULL (0) under the following
/// conditions: 
///
/// - After pk_initialize() but prior to pk_start_threads(), assuming no
/// threads have been made runnable.
///
/// - After pk_start_threads(), when no threads are runnable.  In this case
/// the NULL (0) value indicates that the PK idle thread is the next thread
/// to 'run'. 
/// 
/// If \a __pk_next_thread == 0 then there is no requirement to restore
/// any register state on a context switch, because the PK idle thread has
/// no permanent context.
///
/// If \a __pk_next_thread != 0 then \a __pk_next_thread is a pointer
/// to the thread whose context will be restored at the next context switch.
UNLESS__PK_CORE_C__(extern)
volatile
PkThread* __pk_next_thread;

/// The priority of \a __pk_next_thread
///
/// If \a __pk_next_thread == 0, the \a __pk_next_priority == PK_THREADS.
UNLESS__PK_CORE_C__(extern)
volatile
PkThreadPriority __pk_next_priority;

/// This variable holds the default thread machine context for newly created
/// threads. The idle thread also uses this context. This variable is normally
/// constant after the call of \c pk_initialize().

UNLESS__PK_CORE_C__(extern)
volatile
PkMachineContext __pk_thread_machine_context_default;


/// The size of the noncritical stack (bytes).

UNLESS__PK_CORE_C__(extern)
volatile
size_t __pk_noncritical_stack_size;

/// The size of the critical stack (bytes).

UNLESS__PK_CORE_C__(extern)
volatile
size_t __pk_critical_stack_size;

/// This table maps priorities to threads, and contains PK_THREADS + 1
/// entries. The final entry is for the idle thread and will always be null
/// after initizlization.

UNLESS__PK_CORE_C__(extern)
volatile
PkThread* __pk_priority_map[PK_THREADS + 1];

/// The PK time queue structure
///
/// This structure is defined for use by the kernel, however applications
/// could also use this structure to define their own time queues.

typedef struct {

    /// A sentinel node for the time queue.
    ///
    /// The time queue is an PkDeque managed as a FIFO queue for queue
    /// management purpose, although events time out in time order.
    ///
    /// This pointer container is defined as the first element of the
    /// structure to allow the PkTimeQueue to be cast to an PkDeque.
    PkDeque queue;

    /// The next timeout in absolute time.
    PkTimebase next_timeout;

    /// A pointer to allow preemption of time queue processing
    ///
    /// If non-0, then this is the next timer in the time queue to handle, or
    /// a pointer to the \a queue object indicating no more timers to handle.
    ///
    /// \a cursor != 0 implies that time queue handler is in the midst of
    /// processing the time queue, but has enabled interrupt preemption for
    /// processing a timer handler. This means that 1) if the timer pointed to
    /// by \a cursor is deleted then the cursor must be assigned to the
    /// next timer in the queue; and 2) if a new timer is scheduled then
    /// activating the next timeout will be handled by the timer handler.
    PkDeque* cursor;

} PkTimeQueue;

UNLESS__PK_CORE_C__(extern)
PkTimeQueue __pk_time_queue;

/// Return a pointer to the PkThread object of the currently running thread,
/// or NULL (0) if PK is idle or has not been started.
///
/// In this API the current thread is not volatile - it will never change
/// inside application code - thus the 'volatile' is cast away. The PK kernel
/// does not (must not) use this API.

UNLESS__PK_CORE_C__(extern)
inline PkThread *
pk_current(void)
{
    return (PkThread *)__pk_current_thread;
}

/// Schedule the next timeout in a machine-specific way.

void
__pk_schedule_hardware_timeout(PkTimebase timeout);

/// The thread timeout handler. Portable.

PK_TIMER_CALLBACK(__pk_thread_timeout);

/// Generic stack initialization. Portable.

int
__pk_stack_init(PkAddress *stack,
                 size_t     *size);

/// Machine-specific thread context initialization.

void 
__pk_thread_context_initialize(PkThread        *thread, 
                                PkThreadRoutine thread_routine, 
                                void             *arg);

/// Machine specific resumption of __pk_next_thread at __pk_next_priority
/// without saving the current context.
void
__pk_next_thread_resume(void);

/// Schedule a timer in the time queue. Portable.
void
__pk_timer_schedule(PkTimer *timer);

/// Remove a timer from the time queue. Portable.
int
__pk_timer_cancel(PkTimer *timer);

void
__pk_schedule(void);


// Call the application main(). Portable.

void
__pk_main(int argc, char **argv);

#endif  /* __ASSEMBLER__ */

#endif /* __PK_KERNEL_H__ */
OpenPOWER on IntegriCloud