CS452 - Real-Time Programming - Spring 2017
Lecture 8 - Initialization
Public Service Annoucements
-
Due date for kernel 1: 26 May, 2017
Initializing a Task
In creating a task you have to do two things
- Allocate the resources needed by the task
- Make the task look as if it had just entered the kernel
- it's ready to execute when it's scheduled
Must initialize the TD
-
tid
-
priority
-
parent tid
-
run_state = READY
-
etc: spsr, extra lr, nxtptr, ...
Must initialize the stack
-
Stuff you left out of the TD must be unrolled from the stack.
-
Exactly as if the task had just done a kernel entry
-
look carefully at what your kernel exit code will do
-
At the end stack pointer must correspond to stack contents
-
I initialize the stack pointer to the top of allocated memory
then change it as I push stuff onto the stack
- imitating the context switch code
-
As a debugging aid I sometimes put distinct bit patterns in the
registers in order to get some easy information about what is going
where.
Must add task to its readyQ
The following implementation decisions are up to you.
-
What should be saved on the stack; what should be saved in
the task descriptor.
-
How memory is laid out.
-
The maximum number of tasks in your system.
-
The argument and return value passing conventions for system
calls.
Initializing the Kernel
Set up the Hardware
RedBoot gives you the processor with
-
caching turned off,
-
slow cpu and bus clock,
-
memory mapped into a single block starting at 0x0, (32 Mbytes
is 2^25 bytes.)
-
the interrupt control unit turned off,
-
interrupts disabled in the CPSR, and
-
MMU turned on to get the memory map above.
There is a subtlety here. There are actually three ways that you
can get into RedBoot and the state of the hardware is slightly
different.
-
You can cycle the power. In this case RedBoot initializes
some memory, and the remainder of the memory is garbage. The
state of I/O devices is random.
-
You can press the reset button. Then Redboot reinitializes
the memory it uses, including low memory, but the remainder
of the memory, including the code and data, but not I/O
devices, remains as it was when the reset button was pressed.
(The memory controllers are not turned off by the reset
button.)
-
You can put RedBoot's link register into the program counter.
The memory of the processor, including low memory, is entact.
Then there are a few things you need to do. While
RedBoot gives you the system as described above, there may be
things done by a previous student that changed what RedBoot thinks
it is giving you.
-
Initialize busy-wait I/O.
-
Initialize low memory.
-
How do you find the kernel entry?
-
To test whether or not you have done this correctly
-
Find the hex value of the first instruction executed on
kernel entry.
-
Read the number in 0x28 using Redboot.
-
Read what's in the word it points to, which should be the
same as the first instruction. (Hint. RedBoot and gcc don't
agree about endedness.)
-
Turn off interrupts in the ICU etc.
-
This should be unnecessary, but what if the previous kernel turned
them on?
-
Later you will initialize the ICU differently.
Prepare the Kernel Data Structures
Where is the kernel's stack pointer, right now? What is on
the bottom of the stack?
-
Do you want it there? Would you rather have it somewhere else?
-
This is your last chance to change it. (If you decide to
change it you might want to keep what you are replacing around.
Why?)
The kernel data structures. At the very least you need
-
an array of empty ready queues
-
a pointer to the TD of the active task
-
an array of TDs
-
a free list of pointers to free TDs. This might take the form
of bits set in a couple of words.
You might also want
-
A place to keep the current request
Prepare the Memory to be Used by Tasks
Some of the state of a task on creation is task-independent. You
can, if you want to, initialize it in every TD and every block
of memory right now.
Create the First User Task
Run with hardware interrupts turned off for now. But
hardware interrupts in user tasks are turned on in kernel 3
though they stay off in the kernel.
What you do in creating the First User Task is exactly what you do
creating any other task. All the same this is a place where you
probably don't want to run the same code. Why?
-
Why did double-entry book-keeping exist?
Reminder. The place where the kernel starts executing has the
global name main, which cannot be re-used.
Other Primitives
These primitives exist mostly so that we, which includes you, can ensure
that task creation and scheduling are working when there is not much else
implemented.
Tid MyTid( )
Self-explanatory
- Doesn't block, but does reschedule.
A question, to which there is a correct answer, or more specifically, a
correct (answer, reason) pair.
- Should the Tid be stored in user space?
Tid MyParentTid( )
Self-explanatory
- Doesn't block, but does reschedule.
Where is the parent Tid, and how does the kernel find it?
void Pass( )
Doesn't block: task calling Pass( )
makes a state
transition from ACTIVE
to READY
.
Does reschedule.
When is Pass( )
a NOP
?
void Exit( )
Calling task is removed from all queues, but its resources are
not reclaimed or reused.
That is, the task goes into a zombie state, and will never become
active or ready, but continues to own all its resources.
Return to: